package com.kodcu.service.convert.ebook; import com.kodcu.controller.ApplicationController; import com.kodcu.other.Current; import com.kodcu.other.ExtensionFilters; import com.kodcu.other.IOHelper; import com.kodcu.service.DirectoryService; import com.kodcu.service.PathResolverService; import com.kodcu.service.ThreadService; import com.kodcu.service.convert.docbook.DocBookConverter; import com.kodcu.service.ui.IndikatorService; import org.joox.Match; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import java.io.FileOutputStream; import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; import java.nio.file.*; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.zip.Deflater; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; import static java.nio.file.StandardOpenOption.WRITE; import static org.joox.JOOX.$; /** * Created by usta on 30.08.2014. */ @Component public class EpubConverter { private final Logger logger = LoggerFactory.getLogger(EpubConverter.class); private final ApplicationController asciiDocController; private final Current current; private final ThreadService threadService; private final DirectoryService directoryService; private final IndikatorService indikatorService; private final DocBookConverter docBookConverter; private final PathResolverService pathResolverService; private Path epubPath; @Autowired public EpubConverter(final ApplicationController asciiDocController, final Current current, final ThreadService threadService, final DirectoryService directoryService, final IndikatorService indikatorService, final DocBookConverter docBookConverter, PathResolverService pathResolverService) { this.asciiDocController = asciiDocController; this.current = current; this.threadService = threadService; this.directoryService = directoryService; this.indikatorService = indikatorService; this.docBookConverter = docBookConverter; this.pathResolverService = pathResolverService; } public Path produceEpub3Temp() { return produceEpub3(false, true); } public Path produceEpub3(boolean askPath) { return produceEpub3(askPath, false); } private Path produceEpub3(boolean askPath, boolean isTemp) { try { Path currentTabPath = current.currentPath().get(); Path currentTabPathDir = currentTabPath.getParent(); Path configPath = asciiDocController.getConfigPath(); String tabText = current.getCurrentTabText().replace("*", "").trim(); epubPath = directoryService.getSaveOutputPath(ExtensionFilters.EPUB, askPath); if (isTemp) { epubPath = IOHelper.createTempFile(".epub"); } try { if (!isTemp) { indikatorService.startProgressBar(); logger.debug("Epub conversion started"); } Path epubTemp = Files.createTempDirectory("epub"); TransformerFactory factory = TransformerFactory.newInstance(); Transformer transformer = factory.newTransformer(new StreamSource(configPath.resolve("docbook/epub3/chunk.xsl").toFile())); docBookConverter.convert(false, docbook -> { threadService.runTaskLater(() -> { Path oebpsPath = epubTemp.resolve("OEBPS"); transformer.setParameter("base.dir", oebpsPath.toString()); try (StringReader reader = new StringReader(docbook); StringWriter fakeWriter = new StringWriter();) { StreamSource src = new StreamSource(reader); transformer.transform(src, new StreamResult(fakeWriter)); } catch (Exception e) { logger.error("Problem occured while converting to Epub", e); return; } Path containerXml = epubTemp.resolve("META-INF/container.xml"); Match root = IOHelper.$(containerXml.toFile()); root .find("rootfile") .attr("full-path", "OEBPS/package.opf"); StringBuilder builder = new StringBuilder(); builder.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); Match wrapper = $("wrapper"); wrapper.append(root); builder.append(wrapper.content()); IOHelper.matchWrite(root, containerXml.toFile()); IOHelper.writeToFile(containerXml, builder.toString(), TRUNCATE_EXISTING, WRITE); Path epubOut = epubTemp.resolve("book.epub"); Stream<Path> imageStream = IOHelper.find(currentTabPathDir, Integer.MAX_VALUE, (p, attr) -> pathResolverService.isImage(p)); imageStream.forEach(img -> { IOHelper.copyFile(img.toFile(), oebpsPath.resolve(currentTabPathDir.relativize(img)).toFile()); }); IOHelper.copyDirectoryToDirectory(configPath.resolve("docbook/images/callouts").toFile(), epubTemp.resolve("OEBPS/images") .toFile()); try (FileOutputStream fileOutputStream = new FileOutputStream(epubOut.toFile()); ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream);) { zipOutputStream.setMethod(ZipOutputStream.DEFLATED); zipOutputStream.setLevel(Deflater.NO_COMPRESSION); ZipEntry zipEntry = new ZipEntry("mimetype"); zipOutputStream.putNextEntry(zipEntry); zipOutputStream.write("application/epub+zip".getBytes()); } catch (Exception e) { logger.error("Problem occured while zipping mimetype"); } try (FileSystem zipfs = FileSystems.newFileSystem(epubOut, null)) { iterativelyPackDir(epubTemp.resolve("OEBPS"), epubTemp, zipfs); iterativelyPackDir(epubTemp.resolve("META-INF"), epubTemp, zipfs); } catch (IOException e) { logger.error("Problem occured while packing epub content"); } IOHelper.move(epubOut, epubPath, StandardCopyOption.REPLACE_EXISTING); if (!isTemp) { indikatorService.stopProgressBar(); logger.debug("Epub conversion ended"); asciiDocController.addRemoveRecentList(epubPath); } }); }); } catch (Exception e) { logger.error("Problem occured while converting to Epub", e); } } catch (Exception e) { logger.error("Problem occured while converting to Epub", e); indikatorService.stopProgressBar(); } return epubPath; } private void iterativelyPackDir(Path rootPath, Path realRoot, FileSystem zipfs) throws IOException { List<Path> fileList = IOHelper.list(rootPath).collect(Collectors.toList()); for (Path oebpsFile : fileList) { if (Files.isDirectory(oebpsFile)) { iterativelyPackDir(oebpsFile, realRoot, zipfs); } else { Path relativeFile = realRoot.relativize(oebpsFile); Path relativeRoot = realRoot.relativize(oebpsFile.getParent()); Files.createDirectories(zipfs.getPath(relativeRoot.toString())); Files.copy(oebpsFile, zipfs.getPath(relativeFile.toString()), StandardCopyOption.REPLACE_EXISTING); } } } }