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(); + } +}