EpubWriter.java 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. package me.ag2s.epublib.epub;
  2. import android.util.Log;
  3. import org.xmlpull.v1.XmlSerializer;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import java.io.OutputStream;
  7. import java.io.OutputStreamWriter;
  8. import java.io.Writer;
  9. import java.util.Objects;
  10. import java.util.zip.CRC32;
  11. import java.util.zip.ZipEntry;
  12. import java.util.zip.ZipOutputStream;
  13. import me.ag2s.epublib.domain.EpubBook;
  14. import me.ag2s.epublib.domain.MediaTypes;
  15. import me.ag2s.epublib.domain.Resource;
  16. import me.ag2s.epublib.util.IOUtil;
  17. /**
  18. * Generates an epub file. Not thread-safe, single use object.
  19. *
  20. * @author paul
  21. */
  22. public class EpubWriter {
  23. // package
  24. static final String EMPTY_NAMESPACE_PREFIX = "";
  25. private static final String TAG = EpubWriter.class.getName();
  26. private BookProcessor bookProcessor;
  27. private EpubWriterProcessor epubWriterProcessor;
  28. public EpubWriter() {
  29. this(BookProcessor.IDENTITY_BOOKPROCESSOR);
  30. this.epubWriterProcessor = new EpubWriterProcessor();
  31. // 写入MimeType、Container,初始化TOCResource整体为1
  32. // 写入PackageDocument 为1
  33. // 关闭流 为1
  34. this.epubWriterProcessor.setTotalProgress(3);
  35. }
  36. public EpubWriter(BookProcessor bookProcessor) {
  37. this.bookProcessor = bookProcessor;
  38. }
  39. public EpubWriter setCallback(EpubWriterProcessor.Callback callback) {
  40. epubWriterProcessor.setCallback(callback);
  41. return this;
  42. }
  43. public void write(EpubBook book, OutputStream out) throws IOException {
  44. if (Objects.nonNull(this.epubWriterProcessor.getCallback())) {
  45. epubWriterProcessor.getCallback().onStart(book);
  46. }
  47. epubWriterProcessor.setTotalProgress(epubWriterProcessor.getTotalProgress() + book.getResources().size());
  48. book = processBook(book);
  49. ZipOutputStream resultStream = new ZipOutputStream(out);
  50. resultStream.setLevel(ZipOutputStream.STORED);
  51. writeMimeType(resultStream);
  52. writeContainer(resultStream);
  53. initTOCResource(book);
  54. epubWriterProcessor.updateCurrentProgress(1);
  55. writeResources(book, resultStream);
  56. writePackageDocument(book, resultStream);
  57. epubWriterProcessor.updateCurrentProgress(epubWriterProcessor.getCurrentProgress() + 1);
  58. resultStream.close();
  59. epubWriterProcessor.updateCurrentProgress(epubWriterProcessor.getCurrentProgress() + 1);
  60. if (Objects.nonNull(epubWriterProcessor.getCallback())) {
  61. epubWriterProcessor.getCallback().onEnd(book);
  62. }
  63. }
  64. private EpubBook processBook(EpubBook book) {
  65. if (bookProcessor != null) {
  66. book = bookProcessor.processBook(book);
  67. }
  68. return book;
  69. }
  70. private void initTOCResource(EpubBook book) {
  71. Resource tocResource;
  72. try {
  73. if (book.isEpub3()) {
  74. tocResource = NCXDocumentV3.createNCXResource(book);
  75. } else {
  76. tocResource = NCXDocumentV2.createNCXResource(book);
  77. }
  78. Resource currentTocResource = book.getSpine().getTocResource();
  79. if (currentTocResource != null) {
  80. book.getResources().remove(currentTocResource.getHref());
  81. }
  82. book.getSpine().setTocResource(tocResource);
  83. book.getResources().add(tocResource);
  84. } catch (Exception ex) {
  85. Log.e(TAG, "Error writing table of contents: " + ex.getClass().getName() + ": " + ex.getMessage(), ex);
  86. }
  87. }
  88. private void writeResources(EpubBook book, ZipOutputStream resultStream) {
  89. for (Resource resource : book.getResources().getAll()) {
  90. writeResource(resource, resultStream);
  91. epubWriterProcessor.updateCurrentProgress(epubWriterProcessor.getCurrentProgress() + 1);
  92. }
  93. }
  94. /**
  95. * Writes the resource to the resultStream.
  96. *
  97. * @param resource resource
  98. * @param resultStream resultStream
  99. */
  100. private void writeResource(Resource resource, ZipOutputStream resultStream) {
  101. if (resource == null) {
  102. return;
  103. }
  104. try {
  105. resultStream.putNextEntry(new ZipEntry("OEBPS/" + resource.getHref()));
  106. InputStream inputStream = resource.getInputStream();
  107. IOUtil.copy(inputStream, resultStream);
  108. inputStream.close();
  109. } catch (Exception e) {
  110. Log.e(TAG, e.getMessage(), e);
  111. }
  112. }
  113. private void writePackageDocument(EpubBook book, ZipOutputStream resultStream) throws IOException {
  114. resultStream.putNextEntry(new ZipEntry("OEBPS/content.opf"));
  115. XmlSerializer xmlSerializer = EpubProcessorSupport.createXmlSerializer(resultStream);
  116. PackageDocumentWriter.write(this, xmlSerializer, book);
  117. xmlSerializer.flush();
  118. // String resultAsString = result.toString();
  119. // resultStream.write(resultAsString.getBytes(Constants.ENCODING));
  120. }
  121. /**
  122. * Writes the META-INF/container.xml file.
  123. *
  124. * @param resultStream resultStream
  125. * @throws IOException IOException
  126. */
  127. private void writeContainer(ZipOutputStream resultStream) throws IOException {
  128. resultStream.putNextEntry(new ZipEntry("META-INF/container.xml"));
  129. Writer out = new OutputStreamWriter(resultStream);
  130. out.write("<?xml version=\"1.0\"?>\n");
  131. out.write("<container version=\"1.0\" xmlns=\"urn:oasis:names:tc:opendocument:xmlns:container\">\n");
  132. out.write("\t<rootfiles>\n");
  133. out.write("\t\t<rootfile full-path=\"OEBPS/content.opf\" media-type=\"application/oebps-package+xml\"/>\n");
  134. out.write("\t</rootfiles>\n");
  135. out.write("</container>");
  136. out.flush();
  137. }
  138. /**
  139. * Stores the mimetype as an uncompressed file in the ZipOutputStream.
  140. *
  141. * @param resultStream resultStream
  142. * @throws IOException IOException
  143. */
  144. private void writeMimeType(ZipOutputStream resultStream) throws IOException {
  145. ZipEntry mimetypeZipEntry = new ZipEntry("mimetype");
  146. mimetypeZipEntry.setMethod(ZipEntry.STORED);
  147. byte[] mimetypeBytes = MediaTypes.EPUB.getName().getBytes();
  148. mimetypeZipEntry.setSize(mimetypeBytes.length);
  149. mimetypeZipEntry.setCrc(calculateCrc(mimetypeBytes));
  150. resultStream.putNextEntry(mimetypeZipEntry);
  151. resultStream.write(mimetypeBytes);
  152. }
  153. private long calculateCrc(byte[] data) {
  154. CRC32 crc = new CRC32();
  155. crc.update(data);
  156. return crc.getValue();
  157. }
  158. String getNcxId() {
  159. return "ncx";
  160. }
  161. String getNcxHref() {
  162. return "toc.ncx";
  163. }
  164. String getNcxMediaType() {
  165. return MediaTypes.NCX.getName();
  166. }
  167. @SuppressWarnings("unused")
  168. public BookProcessor getBookProcessor() {
  169. return bookProcessor;
  170. }
  171. @SuppressWarnings("unused")
  172. public void setBookProcessor(BookProcessor bookProcessor) {
  173. this.bookProcessor = bookProcessor;
  174. }
  175. }