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