org.alfresco.repo.content.transform.TextToPdfContentTransformer.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.repo.content.transform.TextToPdfContentTransformer.java

Source

/*
 * #%L
 * Alfresco Repository
 * %%
 * Copyright (C) 2005 - 2016 Alfresco Software Limited
 * %%
 * This file is part of the Alfresco software. 
 * If the software was purchased under a paid Alfresco license, the terms of 
 * the paid license agreement will prevail.  Otherwise, the software is 
 * provided under the following open source license terms:
 * 
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Alfresco 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 Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */
package org.alfresco.repo.content.transform;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Map;

import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.TransformationOptionLimits;
import org.alfresco.service.cmr.repository.TransformationOptionPair;
import org.alfresco.service.cmr.repository.TransformationOptionPair.Action;
import org.alfresco.service.cmr.repository.TransformationOptions;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.pdfbox.TextToPDF;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.edit.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDTrueTypeFont;
import org.apache.pdfbox.pdmodel.font.PDType1Font;

/**
 * Makes use of the <a href="http://www.pdfbox.org/">PDFBox</a> library's <code>TextToPDF</code> utility.
 * 
 * @author Derek Hulley
 * @since 2.1.0
 */
public class TextToPdfContentTransformer extends AbstractContentTransformer2 {
    private static final Log logger = LogFactory.getLog(TextToPdfContentTransformer.class);

    private PagedTextToPDF transformer;

    public TextToPdfContentTransformer() {
        setPageLimitsSupported(true);
        transformer = new PagedTextToPDF();
    }

    public void setStandardFont(String fontName) {
        try {
            transformer.setFont(PDType1Font.getStandardFont(fontName));
        } catch (Throwable e) {
            throw new AlfrescoRuntimeException("Unable to set Standard Font for PDF generation: " + fontName, e);
        }
    }

    public void setTrueTypeFont(String fontName) {
        try {
            transformer.setFont(PDTrueTypeFont.loadTTF(null, fontName));
        } catch (Throwable e) {
            throw new AlfrescoRuntimeException("Unable to set True Type Font for PDF generation: " + fontName, e);
        }
    }

    public void setFontSize(int fontSize) {
        try {
            transformer.setFontSize(fontSize);
        } catch (Throwable e) {
            throw new AlfrescoRuntimeException("Unable to set Font Size for PDF generation: " + fontSize);
        }
    }

    /**
     * Only supports Text to PDF
     */
    @Override
    public boolean isTransformableMimetype(String sourceMimetype, String targetMimetype,
            TransformationOptions options) {
        if ((!MimetypeMap.MIMETYPE_TEXT_PLAIN.equals(sourceMimetype)
                && !MimetypeMap.MIMETYPE_TEXT_CSV.equals(sourceMimetype)
                && !MimetypeMap.MIMETYPE_XML.equals(sourceMimetype))
                || !MimetypeMap.MIMETYPE_PDF.equals(targetMimetype)) {
            // only support (text/plain OR text/csv OR text/xml) to (application/pdf)
            return false;
        } else {
            return true;
        }
    }

    @Override
    public String getComments(boolean available) {
        return getCommentsOnlySupports(
                Arrays.asList(new String[] { MimetypeMap.MIMETYPE_TEXT_PLAIN, MimetypeMap.MIMETYPE_TEXT_CSV,
                        MimetypeMap.MIMETYPE_XML }),
                Arrays.asList(new String[] { MimetypeMap.MIMETYPE_PDF }), available);
    }

    @Override
    protected void transformInternal(ContentReader reader, ContentWriter writer, TransformationOptions options)
            throws Exception {
        PDDocument pdf = null;
        InputStream is = null;
        InputStreamReader ir = null;
        OutputStream os = null;
        try {
            is = reader.getContentInputStream();
            ir = buildReader(is, reader.getEncoding(), reader.getContentUrl());

            TransformationOptionLimits limits = getLimits(reader, writer, options);
            TransformationOptionPair pageLimits = limits.getPagesPair();
            pdf = transformer.createPDFFromText(ir, pageLimits, reader.getContentUrl());

            // dump it all to the writer
            os = writer.getContentOutputStream();
            pdf.save(os);
        } finally {
            if (pdf != null) {
                try {
                    pdf.close();
                } catch (Throwable e) {
                    e.printStackTrace();
                }
            }
            if (ir != null) {
                try {
                    ir.close();
                } catch (Throwable e) {
                    e.printStackTrace();
                }
            }
            if (is != null) {
                try {
                    is.close();
                } catch (Throwable e) {
                    e.printStackTrace();
                }
            }
            if (os != null) {
                try {
                    os.close();
                } catch (Throwable e) {
                    e.printStackTrace();
                }
            }
        }
    }

    protected InputStreamReader buildReader(InputStream is, String encoding, String node) {
        // If they gave an encoding, try to use it
        if (encoding != null) {
            Charset charset = null;
            try {
                charset = Charset.forName(encoding);
            } catch (Exception e) {
                logger.warn("JVM doesn't understand encoding '" + encoding + "' when transforming " + node);
            }
            if (charset != null) {
                logger.debug("Processing plain text in encoding " + charset.displayName());
                return new InputStreamReader(is, charset);
            }
        }

        // Fall back on the system default
        logger.debug("Processing plain text using system default encoding");
        return new InputStreamReader(is);
    }

    private class PagedTextToPDF extends TextToPDF {
        // The following code is based on the code in TextToPDF with the addition of
        // checks for page limits.
        // The calling code must close the PDDocument once finished with it.
        public PDDocument createPDFFromText(Reader text, TransformationOptionPair pageLimits, String contentUrl)
                throws IOException {
            int pageLimit = (int) pageLimits.getValue();
            PDDocument doc = null;
            int pageCount = 0;
            try {
                final int margin = 40;
                float height = getFont().getFontDescriptor().getFontBoundingBox().getHeight() / 1000;

                //calculate font height and increase by 5 percent.
                height = height * getFontSize() * 1.05f;
                doc = new PDDocument();
                BufferedReader data = new BufferedReader(text);
                String nextLine = null;
                PDPage page = new PDPage();
                PDPageContentStream contentStream = null;
                float y = -1;
                float maxStringLength = page.getMediaBox().getWidth() - 2 * margin;

                // There is a special case of creating a PDF document from an empty string.
                boolean textIsEmpty = true;

                outer: while ((nextLine = data.readLine()) != null) {

                    // The input text is nonEmpty. New pages will be created and added
                    // to the PDF document as they are needed, depending on the length of
                    // the text.
                    textIsEmpty = false;

                    String[] lineWords = nextLine.trim().split(" ");
                    int lineIndex = 0;
                    while (lineIndex < lineWords.length) {
                        StringBuffer nextLineToDraw = new StringBuffer();
                        float lengthIfUsingNextWord = 0;
                        do {
                            nextLineToDraw.append(lineWords[lineIndex]);
                            nextLineToDraw.append(" ");
                            lineIndex++;
                            if (lineIndex < lineWords.length) {
                                String lineWithNextWord = nextLineToDraw.toString() + lineWords[lineIndex];
                                lengthIfUsingNextWord = (getFont().getStringWidth(lineWithNextWord) / 1000)
                                        * getFontSize();
                            }
                        } while (lineIndex < lineWords.length && lengthIfUsingNextWord < maxStringLength);
                        if (y < margin) {
                            if (pageLimit > 0 && pageCount++ >= pageLimit) {
                                pageLimits.getAction().throwIOExceptionIfRequired(
                                        "Page limit (" + pageLimit + ") reached.", transformerDebug);
                                break outer;
                            }

                            // We have crossed the end-of-page boundary and need to extend the
                            // document by another page.
                            page = new PDPage();
                            doc.addPage(page);
                            if (contentStream != null) {
                                contentStream.endText();
                                contentStream.close();
                            }
                            contentStream = new PDPageContentStream(doc, page);
                            contentStream.setFont(getFont(), getFontSize());
                            contentStream.beginText();
                            y = page.getMediaBox().getHeight() - margin + height;
                            contentStream.moveTextPositionByAmount(margin, y);
                        }
                        //System.out.println( "Drawing string at " + x + "," + y );

                        if (contentStream == null) {
                            throw new IOException("Error:Expected non-null content stream.");
                        }
                        contentStream.moveTextPositionByAmount(0, -height);
                        y -= height;
                        contentStream.drawString(nextLineToDraw.toString());
                    }
                }

                // If the input text was the empty string, then the above while loop will have short-circuited
                // and we will not have added any PDPages to the document.
                // So in order to make the resultant PDF document readable by Adobe Reader etc, we'll add an empty page.
                if (textIsEmpty) {
                    doc.addPage(page);
                }

                if (contentStream != null) {
                    contentStream.endText();
                    contentStream.close();
                }
            } catch (IOException io) {
                if (doc != null) {
                    doc.close();
                }
                throw io;
            }
            return doc;
        }
    }
}