com.amphisoft.epub2pdf.Converter.java Source code

Java tutorial

Introduction

Here is the source code for com.amphisoft.epub2pdf.Converter.java

Source

/*
epub2pdf, version 0.5 - Copyright 2010 Brendan C. LeFebvre
    
This file is part of epub2pdf.
    
epub2pdf is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
    
epub2pdf is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with epub2pdf.  If not, see <http://www.gnu.org/licenses/>.
*/
package com.amphisoft.epub2pdf;

import static com.amphisoft.util.Print.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.SortedSet;
import java.util.StringTokenizer;
import java.util.TreeSet;

import com.amphisoft.util.jgtree.Tree;
import com.amphisoft.util.jgtree.TreeNode;

import com.amphisoft.epub.Epub;
import com.amphisoft.epub.metadata.Ncx;
import com.amphisoft.epub.metadata.Opf;
import com.amphisoft.epub.metadata.Ncx.NavPoint;
import com.amphisoft.epub2pdf.content.TextFactory;
import com.amphisoft.epub2pdf.content.XhtmlHandler;
import com.amphisoft.epub2pdf.metadata.TocTreeNode;
import com.amphisoft.pdf.ITPageSize;
import com.amphisoft.util.units.Length;

import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Paragraph;
import com.lowagie.text.Rectangle;
import com.lowagie.text.RectangleReadOnly;
import com.lowagie.text.pdf.PdfDestination;
import com.lowagie.text.pdf.PdfOutline;
import com.lowagie.text.pdf.PdfWriter;

public class Converter {

    protected static float marginTopPt = 8.0f;
    protected static float marginRightPt = 8.0f;
    protected static float marginBottomPt = 8.0f;
    protected static float marginLeftPt = 8.0f;
    protected static Rectangle pageSize = ITPageSize.FOXIT_ESLICK;
    private static Properties epub2pdfProps;

    private static void loadProperties() {
        epub2pdfProps = new Properties();
        String propsFilename = "epub2pdf.properties";
        try {
            epub2pdfProps.load(new FileInputStream(propsFilename));
        } catch (IOException e) {
            printlnerr("IOException reading properties from " + propsFilename + "; continuing anyway");
        }
    }

    static {
        loadProperties();
    }

    Epub epubIn;
    File outputDir = new File(System.getProperty("user.home"));

    public void applyProperties(Properties props) {
        SortedSet<String> propsSorted = new TreeSet<String>();
        for (Object o : props.keySet()) {
            propsSorted.add(o.toString());
        }
        for (String propName : propsSorted) {
            if ("font.default.name".equals(propName)) {
                setDefaultFont(props);
            }
            if ("font.default.sizebase".equals(propName)) {
                setDefaultFontBaseSize(props);
            }
            if ("font.monospace.name".equals(propName)) {
                setMonospaceFont(props);
            }
            if ("font.monospace.sizebase".equals(propName)) {
                setMonospaceFontBaseSize(props);
            }
            if ("page.size".equals(propName)) {
                setPageSize(props);
            }
            if ("margins.size".equals(propName)) {
                setMargins(props);
            }
            if ("output.dir".equals(propName)) {
                setOutputDir(props);
            }
            if ("full.justify".equals(propName)) {
                setDefaultAlignment(props);
            }
        }
    }

    private void setDefaultAlignment(Properties props) {
        String justify = props.getProperty("full.justify").trim();
        if (justify.toLowerCase().contains("true")) {
            XhtmlHandler.setDefaultAlignment(Paragraph.ALIGN_JUSTIFIED);
            System.err.println("Default paragraph alignment: justified");
        } else {
            XhtmlHandler.setDefaultAlignment(Paragraph.ALIGN_LEFT);
            System.err.println("Default paragraph alignment: left");
        }
    }

    private void setOutputDir(Properties props) {
        String outputDirStr = props.getProperty("output.dir").trim();
        if (outputDirStr.length() == 0) {
            // leave as default
        } else {
            outputDir = new File(outputDirStr);
            if (!(outputDir.canRead() && outputDir.canWrite())) {
                System.err.println("Cannot access output directory " + outputDirStr);
                System.exit(1);
            }
        }
    }

    private void setPageSize(Properties props) {
        String pageSizeStr = props.getProperty("page.size").trim();
        pageSizeStr = pageSizeStr.toLowerCase();
        if (pageSizeStr.contains("x")) {
            int sepIdx = pageSizeStr.indexOf('x');
            try {
                String widthStr = pageSizeStr.substring(0, sepIdx);
                String heightStr = pageSizeStr.substring(sepIdx + 1);

                Length width = Length.fromString(widthStr);
                Length height = Length.fromString(heightStr);

                System.err.println("Page size (w x h): " + width.toString() + " x " + height.toString());

                pageSize = new RectangleReadOnly(width.toPoints().getMagnitude(), height.toPoints().getMagnitude());

            } catch (IndexOutOfBoundsException ioobe) {
                pageSizeErrorNotice();
            }
        } else {
            pageSizeErrorNotice();
        }

    }

    private void pageSizeErrorNotice() {
        System.err.println("Could not parse page size string; using default 90mmx115mm");
    }

    private void setMonospaceFontBaseSize(Properties props) {
        String sizeStr = props.getProperty("font.monospace.sizebase").trim();
        if (sizeStr == null) {
            sizeStr = "10pt";
        }
        Length mfbsLength = Length.fromString(sizeStr);
        float size = mfbsLength.toPoints().getMagnitude();
        TextFactory.setDefaultFontMonoSize(size);
        System.err.println("Monospace base size: " + size + "pt");
    }

    private void setMonospaceFont(Properties props) {
        String fontName = props.getProperty("font.monospace.name").trim();
        if (TextFactory.setDefaultFontMonoByName(fontName)) {
            System.err.println("Default monospace font: " + fontName);
        } else {
            System.err
                    .println("Failed to set default monospace font to " + fontName + "; retaining previous value");
        }
    }

    private void setMargins(Properties props) {
        String marginStr = props.getProperty("margins.size").trim();
        StringTokenizer sT = new StringTokenizer(marginStr, ",");
        ArrayList<String> marginParams = new ArrayList<String>();
        while (sT.hasMoreTokens()) {
            marginParams.add(sT.nextToken());
        }
        int marginParamCount = marginParams.size();
        if (marginParamCount < 1 || marginParamCount > 4) {
            throw new IllegalArgumentException("Could not parse margins.size; retaining previous value");
        } else {
            ArrayList<Length> margins = new ArrayList<Length>();
            for (String param : marginParams) {
                margins.add(Length.fromString(param));
            }
            marginTopPt = margins.get(0).toPoints().getMagnitude();
            marginRightPt = margins.get(1).toPoints().getMagnitude();
            marginBottomPt = marginTopPt;
            marginLeftPt = marginRightPt;
            if (margins.size() > 2)
                marginBottomPt = margins.get(2).toPoints().getMagnitude();
            if (margins.size() > 3)
                marginLeftPt = margins.get(3).toPoints().getMagnitude();

            System.err.print("Margins (top right bottom left): ");
            for (Length el : margins) {
                System.err.print(el.toString() + " ");
            }
            System.err.println();
        }
    }

    private void setDefaultFontBaseSize(Properties props) {
        String sizeStr = props.getProperty("font.default.sizebase").trim();
        if (sizeStr == null) {
            sizeStr = "12pt";
        }
        Length dfbsLength = Length.fromString(sizeStr);
        float size = dfbsLength.toPoints().getMagnitude();
        TextFactory.setDefaultFontSize(size);
        System.err.println("Default font base size set to " + size + "pt");
    }

    private void setDefaultFont(Properties props) {
        String fontName = props.getProperty("font.default.name").trim();
        if (TextFactory.setDefaultFontByName(fontName)) {
            System.err.println("Default font set to " + fontName);
        } else {
            System.err.println("Failed to reset default font to " + fontName);
        }
    }

    public void convert(String epubPath) throws IOException, DocumentException {
        File epubFile = new File(epubPath);
        if (!(epubFile.canRead())) {
            throw new IOException("Could not read " + epubPath);
        } else {
            System.err.println("Converting " + epubFile.getAbsolutePath());
        }
        String epubFilename = epubFile.getName();
        String epubFilenameBase = epubFilename.substring(0, epubFilename.length() - 5);
        String pdfFilename = epubFilenameBase + ".pdf";

        File outputFile = new File(outputDir.getAbsolutePath() + File.separator + pdfFilename);

        epubIn = Epub.fromFile(epubPath);
        XhtmlHandler.setSourceEpub(epubIn);

        Opf opf = epubIn.getOpf();
        List<String> contentPaths = opf.spineHrefs();
        List<File> contentFiles = new ArrayList<File>();
        for (String path : contentPaths) {
            contentFiles.add(new File(epubIn.getContentRoot(), path));
        }
        Ncx ncx = epubIn.getNcx();

        List<NavPoint> ncxToc = new ArrayList<NavPoint>();
        if (ncx != null) {
            ncxToc.addAll(ncx.getNavPointsFlat());
        }

        Tree<TocTreeNode> tocTree = TocTreeNode.buildTocTree(ncx);
        XhtmlHandler.setTocTree(tocTree);

        Document doc = new Document();
        boolean pageSizeOK = doc.setPageSize(pageSize);
        boolean marginsOK = doc.setMargins(marginLeftPt, marginRightPt, marginTopPt, marginBottomPt);

        System.err.println("Writing PDF to " + outputFile.getAbsolutePath());
        PdfWriter writer = PdfWriter.getInstance(doc, new FileOutputStream(outputFile));
        writer.setStrictImageSequence(true);
        PdfOutline bookmarkRoot = null;

        if (!(pageSizeOK && marginsOK)) {
            throw new RuntimeException("Failed to set PDF page size a/o margins");
        }

        int fileCount = contentFiles.size();
        printlnerr("Processing " + fileCount + " HTML file(s): ");
        int currentFile = 0;

        for (File file : contentFiles) {
            currentFile++;

            char progressChar;

            int mod10 = currentFile % 10;
            if (mod10 == 5)
                progressChar = '5';
            else if (mod10 == 0)
                progressChar = '0';
            else
                progressChar = '.';

            printerr(progressChar);
            if (!(doc.isOpen())) {
                doc.open();
                doc.newPage();
                bookmarkRoot = writer.getRootOutline();
                XhtmlHandler.setBookmarkRoot(bookmarkRoot);
            }
            NavPoint fileLevelNP = Ncx.findNavPoint(ncxToc, file.getName());
            TreeNode<TocTreeNode> npNode = TocTreeNode.findInTreeByNavPoint(tocTree, fileLevelNP);

            if (fileLevelNP != null) {
                doc.newPage();
                PdfOutline pdfOutlineParent = bookmarkRoot;
                if (npNode != null) {
                    TreeNode<TocTreeNode> parent = npNode.getParent();
                    if (parent != null) {
                        TocTreeNode parentTTN = parent.getValue();
                        if (parentTTN != null && parentTTN.getPdfOutline() != null) {
                            pdfOutlineParent = parentTTN.getPdfOutline();
                        }
                    }
                }

                PdfDestination here = new PdfDestination(PdfDestination.FIT);
                PdfOutline pdfTocEntry = new PdfOutline(pdfOutlineParent, here, fileLevelNP.getNavLabelText());
                if (npNode != null) {
                    npNode.getValue().setPdfDestination(here);
                    npNode.getValue().setPdfOutline(pdfTocEntry);
                }
            }
            XhtmlHandler.process(file.getCanonicalPath(), doc);
        }
        printlnerr();

        doc.close();
        System.err.println("PDF written to " + outputFile.getAbsolutePath());
        epubIn.cleanup();
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        if (args.length < 1)
            usage();
        else {
            for (String infile : args) {
                System.err.println();
                try {
                    Converter c = new Converter();
                    c.applyProperties(epub2pdfProps);
                    c.convert(infile);
                } catch (Exception e) {
                    System.err.println("Exception converting " + infile + ":");
                    System.err.println("\t" + e.getClass().getSimpleName() + " at:");
                    StackTraceElement topElem = e.getStackTrace()[0];
                    System.err.print("\t\t" + topElem.getClassName() + ".");
                    System.err.print(topElem.getMethodName());
                    int line = topElem.getLineNumber();
                    if (line > 9)
                        System.err.print(":" + topElem.getLineNumber());
                    System.err.println();
                }
            }
        }
    }

    public static void usage() {
        println("Usage: com.amphisoft.epub2pdf.Converter <path-to-epub>");
    }

    /**
     * @return a new Properties object populated with the properties 
     * as read from the epub2pdf.properties file.
     */
    public static Properties getProperties() {
        return new Properties(epub2pdfProps);
    }
}