X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fbe%2Fnikiroo%2Ffanfix%2Foutput%2FEpub.java;fp=src%2Fbe%2Fnikiroo%2Ffanfix%2Foutput%2FEpub.java;h=1706d8b1002c1009ecedb64208857dccf19eb6c6;hb=08fe2e33007063e30fe22dc1d290f8afaa18eb1d;hp=0000000000000000000000000000000000000000;hpb=ed48062ebfb0d611b74834e313bfb0a2b81416e6;p=fanfix.git
diff --git a/src/be/nikiroo/fanfix/output/Epub.java b/src/be/nikiroo/fanfix/output/Epub.java
new file mode 100644
index 0000000..1706d8b
--- /dev/null
+++ b/src/be/nikiroo/fanfix/output/Epub.java
@@ -0,0 +1,471 @@
+package be.nikiroo.fanfix.output;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+import javax.imageio.ImageIO;
+
+import be.nikiroo.fanfix.Instance;
+import be.nikiroo.fanfix.bundles.Config;
+import be.nikiroo.fanfix.bundles.StringId;
+import be.nikiroo.fanfix.data.Chapter;
+import be.nikiroo.fanfix.data.MetaData;
+import be.nikiroo.fanfix.data.Paragraph;
+import be.nikiroo.fanfix.data.Story;
+import be.nikiroo.fanfix.data.Paragraph.ParagraphType;
+import be.nikiroo.utils.IOUtils;
+import be.nikiroo.utils.StringUtils;
+
+class Epub extends BasicOutput {
+ private File tmpDir;
+ private FileWriter writer;
+ private boolean inDialogue = false;
+ private boolean inNormal = false;
+ private File images;
+
+ @Override
+ public File process(Story story, File targetDir, String targetName)
+ throws IOException {
+ String targetNameOrig = targetName;
+ targetName += getDefaultExtension();
+
+ tmpDir = File.createTempFile("fanfic-reader-epub_", ".wip");
+ tmpDir.delete();
+
+ if (!tmpDir.mkdir()) {
+ throw new IOException(
+ "Cannot create a temporary directory: no space left on device?");
+ }
+
+ // "Originals"
+ File data = new File(tmpDir, "DATA");
+ data.mkdir();
+ new InfoText().process(story, data, targetNameOrig);
+ IOUtils.writeSmallFile(data, "version", "3.0");
+
+ super.process(story, targetDir, targetNameOrig);
+
+ // zip/epub
+ File epub = new File(targetDir, targetName);
+ IOUtils.zip(tmpDir, epub, true);
+ IOUtils.deltree(tmpDir);
+ tmpDir = null;
+
+ return epub;
+ }
+
+ @Override
+ protected String getDefaultExtension() {
+ return ".epub";
+ }
+
+ @Override
+ protected void writeStoryHeader(Story story) throws IOException {
+ File ops = new File(tmpDir, "OPS");
+ ops.mkdirs();
+ File css = new File(ops, "css");
+ css.mkdirs();
+ images = new File(ops, "images");
+ images.mkdirs();
+ File metaInf = new File(tmpDir, "META-INF");
+ metaInf.mkdirs();
+
+ // "root"
+ IOUtils.writeSmallFile(tmpDir, "mimetype", "application/epub+zip");
+
+ // META-INF
+ String containerContent = "\n"
+ + "\n"
+ + "\t\n"
+ + "\t\t\n"
+ + "\t\n" + "\n";
+
+ IOUtils.writeSmallFile(metaInf, "container.xml", containerContent);
+
+ // OPS/css
+ InputStream inStyle = getClass().getResourceAsStream("epub.style.css");
+ if (inStyle == null) {
+ throw new IOException("Cannot find style.css resource");
+ }
+ try {
+ IOUtils.write(inStyle, new File(css, "style.css"));
+ } finally {
+ inStyle.close();
+ }
+
+ // OPS/images
+ if (story.getMeta() != null && story.getMeta().getCover() != null) {
+ String format = Instance.getConfig()
+ .getString(Config.IMAGE_FORMAT_COVER).toLowerCase();
+ File file = new File(images, "cover." + format);
+ ImageIO.write(story.getMeta().getCover(), format, file);
+ }
+
+ // OPS/* except chapters
+ IOUtils.writeSmallFile(ops, "epb.ncx", generateNcx(story));
+ IOUtils.writeSmallFile(ops, "epb.opf", generateOpf(story));
+ IOUtils.writeSmallFile(ops, "title.xml", generateTitleXml(story));
+
+ // Resume
+ if (story.getMeta() != null && story.getMeta().getResume() != null) {
+ writeChapter(story.getMeta().getResume());
+ }
+ }
+
+ @Override
+ protected void writeChapterHeader(Chapter chap) throws IOException {
+ String filename = String.format("%s%03d%s", "chapter-",
+ chap.getNumber(), ".xml");
+ writer = new FileWriter(new File(tmpDir + "/OPS", filename));
+ inDialogue = false;
+ inNormal = false;
+ try {
+ String title = "Chapter " + chap.getNumber();
+ String nameOrNum = Integer.toString(chap.getNumber());
+ if (chap.getName() != null && !chap.getName().isEmpty()) {
+ title += ": " + chap.getName();
+ nameOrNum = chap.getName();
+ }
+
+ writer.write("");
+ writer.write("\n");
+ writer.write("\n");
+ writer.write("\n
");
+ writer.write("\n " + StringUtils.xmlEscape(title)
+ + "");
+ writer.write("\n ");
+ writer.write("\n");
+ writer.write("\n");
+ writer.write("\n ");
+ writer.write("\n Chapter "
+ + chap.getNumber() + ": ");
+ writer.write("\n "
+ + StringUtils.xmlEscape(nameOrNum) + "");
+ writer.write("\n
");
+ writer.write("\n ");
+ writer.write("\n \n");
+ } catch (Exception e) {
+ writer.close();
+ throw new IOException(e);
+ }
+ }
+
+ @Override
+ protected void writeChapterFooter(Chapter chap) throws IOException {
+ try {
+ if (inDialogue) {
+ writer.write("
\n");
+ inDialogue = false;
+ }
+ if (inNormal) {
+ writer.write(" \n");
+ inNormal = false;
+ }
+ writer.write(" \n\n\n");
+ } finally {
+ writer.close();
+ writer = null;
+ }
+ }
+
+ @Override
+ protected void writeParagraphHeader(Paragraph para) throws IOException {
+ if (para.getType() == ParagraphType.QUOTE && !inDialogue) {
+ writer.write(" \n");
+ inDialogue = true;
+ } else if (para.getType() != ParagraphType.QUOTE && inDialogue) {
+ writer.write("
\n");
+ inDialogue = false;
+ }
+
+ if (para.getType() == ParagraphType.NORMAL && !inNormal) {
+ writer.write(" \n");
+ inNormal = true;
+ } else if (para.getType() != ParagraphType.NORMAL && inNormal) {
+ writer.write("
\n");
+ inNormal = false;
+ }
+
+ switch (para.getType()) {
+ case BLANK:
+ writer.write(" ");
+ break;
+ case BREAK:
+ writer.write("
");
+ break;
+ case NORMAL:
+ writer.write(" ");
+ break;
+ case QUOTE:
+ writer.write(" — ");
+ break;
+ case IMAGE:
+ File file = new File(images, getCurrentImageBestName(false));
+ Instance.getCache().saveAsImage(new URL(para.getContent()), file);
+ writer.write("
");
+ break;
+ }
+ }
+
+ @Override
+ protected void writeParagraphFooter(Paragraph para) throws IOException {
+ switch (para.getType()) {
+ case NORMAL:
+ writer.write("\n");
+ break;
+ case QUOTE:
+ writer.write("
\n");
+ break;
+ default:
+ writer.write("\n");
+ break;
+ }
+ }
+
+ @Override
+ protected void writeTextLine(ParagraphType type, String line)
+ throws IOException {
+ switch (type) {
+ case QUOTE:
+ case NORMAL:
+ writer.write(decorateText(StringUtils.xmlEscape(line)));
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ protected String enbold(String word) {
+ return "" + word + "";
+ }
+
+ @Override
+ protected String italize(String word) {
+ return "" + word + "";
+ }
+
+ private String generateNcx(Story story) {
+ StringBuilder builder = new StringBuilder();
+
+ String title = "";
+ String uuid = "";
+ String author = "";
+ if (story.getMeta() != null) {
+ MetaData meta = story.getMeta();
+ uuid = meta.getUuid();
+ author = meta.getAuthor();
+ title = meta.getTitle();
+ }
+
+ builder.append("");
+ builder.append("\n");
+ builder.append("\n");
+ builder.append("\n ");
+ builder.append("\n ");
+ builder.append("\n ");
+ builder.append("\n ");
+ builder.append("\n ");
+ builder.append("\n ");
+ builder.append("\n ");
+ builder.append("\n ");
+ builder.append("\n ");
+ builder.append("\n " + StringUtils.xmlEscape(title) + "");
+ builder.append("\n ");
+ builder.append("\n ");
+
+ builder.append("\n " + StringUtils.xmlEscape(author) + "");
+ builder.append("\n ");
+ builder.append("\n ");
+ builder.append("\n ");
+ builder.append("\n ");
+ builder.append("\n Title Page");
+ builder.append("\n ");
+ builder.append("\n ");
+ builder.append("\n ");
+
+ int navPoint = 2; // 1 is above
+
+ if (story.getMeta() != null & story.getMeta().getResume() != null) {
+ Chapter chap = story.getMeta().getResume();
+ generateNcx(chap, builder, navPoint++);
+ }
+
+ for (Chapter chap : story) {
+ generateNcx(chap, builder, navPoint++);
+ }
+
+ builder.append("\n ");
+ builder.append("\n\n");
+
+ return builder.toString();
+ }
+
+ private void generateNcx(Chapter chap, StringBuilder builder, int navPoint) {
+ String name;
+ if (chap.getName() != null && !chap.getName().isEmpty()) {
+ name = Instance.getTrans().getString(StringId.CHAPTER_NAMED,
+ chap.getNumber(), chap.getName());
+ } else {
+ name = Instance.getTrans().getString(StringId.CHAPTER_UNNAMED,
+ chap.getNumber());
+ }
+
+ String nnn = String.format("%03d", (navPoint - 2));
+
+ builder.append("\n ");
+ builder.append("\n ");
+ builder.append("\n " + name + "");
+ builder.append("\n ");
+ builder.append("\n ");
+ builder.append("\n \n");
+ }
+
+ private String generateOpf(Story story) {
+ StringBuilder builder = new StringBuilder();
+
+ String title = "";
+ String uuid = "";
+ String author = "";
+ String date = "";
+ String publisher = "";
+ String subject = "";
+ String source = "";
+ String lang = "";
+ if (story.getMeta() != null) {
+ MetaData meta = story.getMeta();
+ title = meta.getTitle();
+ uuid = meta.getUuid();
+ author = meta.getAuthor();
+ date = meta.getDate();
+ publisher = meta.getPublisher();
+ subject = meta.getSubject();
+ source = meta.getSource();
+ lang = meta.getLang();
+ }
+
+ builder.append("");
+ builder.append("\n");
+ builder.append("\n ");
+ builder.append("\n " + StringUtils.xmlEscape(title)
+ + "");
+ builder.append("\n "
+ + StringUtils.xmlEscape(author) + "");
+ builder.append("\n "
+ + StringUtils.xmlEscape(date) + "");
+ builder.append("\n "
+ + StringUtils.xmlEscape(publisher) + "");
+ builder.append("\n ");
+ builder.append("\n " + StringUtils.xmlEscape(subject)
+ + "");
+ builder.append("\n " + StringUtils.xmlEscape(source)
+ + "");
+ builder.append("\n Not for commercial use.");
+ builder.append("\n "
+ + StringUtils.xmlEscape(uuid) + "");
+ builder.append("\n " + StringUtils.xmlEscape(lang)
+ + "");
+ builder.append("\n ");
+ builder.append("\n ");
+ builder.append("\n ");
+ builder.append("\n - ");
+ for (int i = 0; i <= story.getChapters().size(); i++) {
+ String name = String.format("%s%03d", "chapter-", i);
+ builder.append("\n
- ");
+ }
+
+ builder.append("\n ");
+ builder.append("\n
- ");
+
+ builder.append("\n ");
+
+ if (story.getMeta() != null && story.getMeta().getCover() != null) {
+ String format = Instance.getConfig()
+ .getString(Config.IMAGE_FORMAT_COVER).toLowerCase();
+ builder.append("\n
- ");
+ }
+
+ builder.append("\n ");
+ builder.append("\n
- ");
+ builder.append("\n
");
+ builder.append("\n ");
+ builder.append("\n ");
+ for (int i = 0; i <= story.getChapters().size(); i++) {
+ String name = String.format("%s%03d", "chapter-", i);
+ builder.append("\n ");
+ }
+ builder.append("\n ");
+ builder.append("\n\n");
+
+ return builder.toString();
+ }
+
+ private String generateTitleXml(Story story) {
+ StringBuilder builder = new StringBuilder();
+
+ String title = "";
+ String tags = "";
+ String author = "";
+ if (story.getMeta() != null) {
+ MetaData meta = story.getMeta();
+ title = meta.getTitle();
+ if (meta.getTags() != null) {
+ for (String tag : meta.getTags()) {
+ if (!tags.isEmpty()) {
+ tags += ", ";
+ }
+ tags += tag;
+ }
+
+ if (!tags.isEmpty()) {
+ tags = "(" + tags + ")";
+ }
+ }
+ author = meta.getAuthor();
+ }
+
+ String format = Instance.getConfig()
+ .getString(Config.IMAGE_FORMAT_COVER).toLowerCase();
+
+ builder.append("");
+ builder.append("\n");
+ builder.append("\n");
+ builder.append("\n");
+ builder.append("\n " + StringUtils.xmlEscape(title) + "");
+ builder.append("\n ");
+ builder.append("\n");
+ builder.append("\n");
+ builder.append("\n ");
+ builder.append("\n
" + StringUtils.xmlEscape(title) + "
");
+ builder.append("\n
"
+ + StringUtils.xmlEscape(tags) + "
");
+ builder.append("\n
");
+ builder.append("\n
");
+ builder.append("\n
");
+ builder.append("\n
"
+ + StringUtils.xmlEscape(author) + "
");
+ builder.append("\n
");
+ builder.append("\n");
+ builder.append("\n\n");
+
+ return builder.toString();
+ }
+}