Merge branch 'master' into subtree
[nikiroo-utils.git] / output / Html.java
1 package be.nikiroo.fanfix.output;
2
3 import java.io.BufferedWriter;
4 import java.io.File;
5 import java.io.FileOutputStream;
6 import java.io.IOException;
7 import java.io.InputStream;
8 import java.io.OutputStreamWriter;
9
10 import be.nikiroo.fanfix.Instance;
11 import be.nikiroo.fanfix.bundles.Config;
12 import be.nikiroo.fanfix.data.Chapter;
13 import be.nikiroo.fanfix.data.MetaData;
14 import be.nikiroo.fanfix.data.Paragraph;
15 import be.nikiroo.fanfix.data.Paragraph.ParagraphType;
16 import be.nikiroo.fanfix.data.Story;
17 import be.nikiroo.utils.IOUtils;
18 import be.nikiroo.utils.StringUtils;
19
20 class Html extends BasicOutput {
21 private File dir;
22 protected BufferedWriter writer;
23 private boolean inDialogue = false;
24 private boolean inNormal = false;
25
26 @Override
27 public File process(Story story, File targetDir, String targetName)
28 throws IOException {
29 String targetNameOrig = targetName;
30
31 File target = new File(targetDir, targetName);
32 target.mkdir();
33 dir = target;
34
35 target = new File(targetDir, targetName + getDefaultExtension(true));
36
37 writer = new BufferedWriter(new OutputStreamWriter(
38 new FileOutputStream(target), "UTF-8"));
39 try {
40 super.process(story, targetDir, targetNameOrig);
41 } finally {
42 writer.close();
43 writer = null;
44 }
45
46 // write a copy of the originals inside
47 InfoCover.writeInfo(dir, targetName, story.getMeta());
48 InfoCover.writeCover(dir, targetName, story.getMeta());
49 BasicOutput.getOutput(OutputType.TEXT, isWriteInfo(), isWriteCover())
50 .process(story, dir, targetNameOrig);
51
52 if (story.getMeta().getCover() != null) {
53 Instance.getInstance().getCache().saveAsImage(story.getMeta().getCover(), new File(dir, "cover"), true);
54 }
55
56 return target;
57 }
58
59 @Override
60 public String getDefaultExtension(boolean readerTarget) {
61 if (readerTarget) {
62 return File.separator + "index.html";
63 }
64
65 return "";
66 }
67
68 @Override
69 protected void writeStoryHeader(Story story) throws IOException {
70 String title = "";
71 String tags = "";
72 String author = "";
73 Chapter resume = null;
74 if (story.getMeta() != null) {
75 MetaData meta = story.getMeta();
76 title = meta.getTitle();
77 resume = meta.getResume();
78 if (meta.getTags() != null) {
79 for (String tag : meta.getTags()) {
80 if (!tags.isEmpty()) {
81 tags += ", ";
82 }
83 tags += tag;
84 }
85
86 if (!tags.isEmpty()) {
87 tags = "(" + tags + ")";
88 }
89 }
90 author = meta.getAuthor();
91 }
92
93 String format = Instance.getInstance().getConfig()
94 .getString(Config.FILE_FORMAT_IMAGE_FORMAT_COVER).toLowerCase();
95
96 InputStream inStyle = getClass().getResourceAsStream("html.style.css");
97 if (inStyle == null) {
98 throw new IOException("Cannot find style.css resource");
99 }
100 try {
101 IOUtils.write(inStyle, new File(dir, "style.css"));
102 } finally {
103 inStyle.close();
104 }
105
106 writer.write("<!DOCTYPE html>");
107 writer.write("\n<html>");
108 writer.write("\n<head>");
109 writer.write("\n <meta http-equiv='content-type' content='text/html; charset=utf-8'>");
110 writer.write("\n <meta name='viewport' content='width=device-width, initial-scale=1.0'>");
111 writer.write("\n <link rel='stylesheet' type='text/css' href='style.css'>");
112 writer.write("\n <title>" + StringUtils.xmlEscape(title) + "</title>");
113 writer.write("\n</head>");
114 writer.write("\n<body>\n");
115
116 writer.write("\n <div class=\"titlepage\">");
117 writer.write("\n <h1>" + StringUtils.xmlEscape(title) + "</h1>");
118 writer.write("\n <div class=\"type\">" + StringUtils.xmlEscape(tags)
119 + "</div>");
120 writer.write("\n <div class=\"cover\">");
121 writer.write("\n <img src=\"cover." + format + "\"></img>");
122 writer.write("\n </div>");
123 writer.write("\n <div class=\"author\">"
124 + StringUtils.xmlEscape(author) + "</div>");
125 writer.write("\n </div>");
126
127 writer.write("\n <hr/><br/>");
128
129 if (resume != null) {
130 for (Paragraph para : resume) {
131 writeParagraph(para);
132 }
133 if (inDialogue) {
134 writer.write(" </div>\n");
135 inDialogue = false;
136 }
137 if (inNormal) {
138 writer.write(" </div>\n");
139 inNormal = false;
140 }
141 }
142
143 writer.write("\n <br/>");
144 }
145
146 @Override
147 protected void writeStoryFooter(Story story) throws IOException {
148 writer.write("</body>\n");
149 }
150
151 @Override
152 protected void writeChapterHeader(Chapter chap) throws IOException {
153 String nameOrNumber;
154 if (chap.getName() != null && !chap.getName().isEmpty()) {
155 nameOrNumber = chap.getName();
156 } else {
157 nameOrNumber = Integer.toString(chap.getNumber());
158 }
159
160 writer.write("\n <h2>");
161 writer.write("\n <span class='chap'>Chapter <span class='chapnumber'>"
162 + chap.getNumber() + "</span>:</span> ");
163 writer.write("\n <span class='chaptitle'>"
164 + StringUtils.xmlEscape(nameOrNumber) + "</span>");
165 writer.write("\n </h2>");
166 writer.write("\n ");
167 writer.write("\n <div class='chapter_content'>\n");
168
169 inDialogue = false;
170 inNormal = false;
171 }
172
173 @Override
174 protected void writeChapterFooter(Chapter chap) throws IOException {
175 if (inDialogue) {
176 writer.write(" </div>\n");
177 inDialogue = false;
178 }
179 if (inNormal) {
180 writer.write(" </div>\n");
181 inNormal = false;
182 }
183
184 writer.write("\n </div>");
185 }
186
187 @Override
188 protected void writeParagraphHeader(Paragraph para) throws IOException {
189 if (para.getType() == ParagraphType.QUOTE && !inDialogue) {
190 writer.write(" <div class='dialogues'>\n");
191 inDialogue = true;
192 } else if (para.getType() != ParagraphType.QUOTE && inDialogue) {
193 writer.write(" </div>\n");
194 inDialogue = false;
195 }
196
197 if (para.getType() == ParagraphType.NORMAL && !inNormal) {
198 writer.write(" <div class='normals'>\n");
199 inNormal = true;
200 } else if (para.getType() != ParagraphType.NORMAL && inNormal) {
201 writer.write(" </div>\n");
202 inNormal = false;
203 }
204
205 switch (para.getType()) {
206 case BLANK:
207 writer.write(" <div class='blank'></div>");
208 break;
209 case BREAK:
210 writer.write(" <hr class='break'/>");
211 break;
212 case NORMAL:
213 writer.write(" <span class='normal'>");
214 break;
215 case QUOTE:
216 writer.write(" <div class='dialogue'>&mdash; ");
217 break;
218 case IMAGE:
219 // TODO check if images work OK
220 writer.write("<a href='"
221 + StringUtils.xmlEscapeQuote(para.getContent()) + "'>"
222 + StringUtils.xmlEscape(para.getContent()) + "</a>");
223 break;
224 }
225 }
226
227 @Override
228 protected void writeParagraphFooter(Paragraph para) throws IOException {
229 switch (para.getType()) {
230 case NORMAL:
231 writer.write("</span>\n");
232 break;
233 case QUOTE:
234 writer.write("</div>\n");
235 break;
236 default:
237 writer.write("\n");
238 break;
239 }
240 }
241
242 @Override
243 protected void writeTextLine(ParagraphType type, String line)
244 throws IOException {
245 switch (type) {
246 case QUOTE:
247 case NORMAL:
248 writer.write(decorateText(StringUtils.xmlEscape(line)));
249 break;
250 default:
251 break;
252 }
253 }
254
255 @Override
256 protected String enbold(String word) {
257 return "<strong>" + word + "</strong>";
258 }
259
260 @Override
261 protected String italize(String word) {
262 return "<emph>" + word + "</emph>";
263 }
264 }