lucee.runtime.tag.Document.java Source code

Java tutorial

Introduction

Here is the source code for lucee.runtime.tag.Document.java

Source

/**
 *
 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
 *
 * This library 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 2.1 of the License, or (at your option) any later version.
 * 
 * This library 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 this library.  If not, see <http://www.gnu.org/licenses/>.
 * 
 **/
package lucee.runtime.tag;

import java.awt.Dimension;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;

import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspException;

import lucee.commons.io.IOUtil;
import lucee.commons.io.res.Resource;
import lucee.commons.io.res.util.ResourceUtil;
import lucee.commons.lang.StringUtil;
import lucee.commons.pdf.PDFDocument;
import lucee.commons.pdf.PDFException;
import lucee.commons.pdf.PDFPageMark;
import lucee.runtime.Info;
import lucee.runtime.PageContextImpl;
import lucee.runtime.exp.ApplicationException;
import lucee.runtime.exp.PageException;
import lucee.runtime.exp.TemplateException;
import lucee.runtime.ext.tag.BodyTagImpl;
import lucee.runtime.op.Caster;
import lucee.runtime.text.pdf.PDFUtil;
import lucee.runtime.type.ReadOnlyStruct;

import com.lowagie.text.DocumentException;
import com.lowagie.text.pdf.PdfCopy;
import com.lowagie.text.pdf.PdfImportedPage;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.SimpleBookmark;

public final class Document extends BodyTagImpl {

    //private static final String STYLE_BG_INVISIBLE = "background-color: transparent; background-image: none;";

    private Resource filename = null;
    private boolean overwrite = false;
    private String name = null;
    private Dimension pagetype = PDFDocument.PAGETYPE_LETTER;
    private double pageheight = 0;
    private double pagewidth = 0;
    private boolean isLandscape = false;

    private double unitFactor = PDFDocument.UNIT_FACTOR_IN;
    private int encryption = PDFDocument.ENC_NONE;

    private String ownerpassword = null;
    private String userpassword = "";
    private int scale = -1;

    // TODO impl. tag Document backgroundvisible,fontembed,scale
    private boolean backgroundvisible;
    private int fontembed = PDFDocument.FONT_EMBED_YES;

    private int permissions = 0;
    private PDFDocument _document;

    private ArrayList<PDFDocument> documents = new ArrayList<PDFDocument>();

    public Document() {
        this._document = null;
    }

    @Override
    public void release() {
        super.release();
        filename = null;
        overwrite = false;
        name = null;
        pagetype = PDFDocument.PAGETYPE_LETTER;
        pageheight = 0;
        pagewidth = 0;
        isLandscape = false;
        unitFactor = PDFDocument.UNIT_FACTOR_IN;
        encryption = PDFDocument.ENC_NONE;
        ownerpassword = null;
        userpassword = "empty";
        permissions = 0;
        scale = -1;
        documents.clear();
        _document = null;
        backgroundvisible = false;
        fontembed = PDFDocument.FONT_EMBED_YES;

    }

    private PDFDocument getDocument() {
        //SerialNumber sn = pageContext.getConfig().getSerialNumber();
        if (_document == null) {
            _document = new PDFDocument();
        }
        return _document;
    }

    /** set the value proxyserver
    *  Host name or IP address of a proxy server.
    * @param proxyserver value to set
    **/
    public void setProxyserver(String proxyserver) {
        getDocument().setProxyserver(proxyserver);
    }

    public void setProxyhost(String proxyserver) {
        getDocument().setProxyserver(proxyserver);
    }

    /** set the value proxyport
    *  The port number on the proxy server from which the object is requested. Default is 80. When 
    *    used with resolveURL, the URLs of retrieved documents that specify a port number are automatically 
    *    resolved to preserve links in the retrieved document.
    * @param proxyport value to set
    **/
    public void setProxyport(double proxyport) {
        getDocument().setProxyport((int) proxyport);
    }

    /** set the value username
    *  When required by a proxy server, a valid username.
    * @param proxyuser value to set
    **/
    public void setProxyuser(String proxyuser) {
        getDocument().setProxyuser(proxyuser);
    }

    /** set the value password
    *  When required by a proxy server, a valid password.
    * @param proxypassword value to set
    **/
    public void setProxypassword(String proxypassword) {
        getDocument().setProxypassword(proxypassword);
    }

    public void setSaveasname(String saveAsName) {
        // TODO impl
    }

    /**
     * @param authUser the authUser to set
     */
    public void setAuthuser(String authUser) {
        getDocument().setAuthUser(authUser);
    }

    /**
     * @param authPassword the authPassword to set
     */
    public void setAuthpassword(String authPassword) {
        getDocument().setAuthPassword(authPassword);
    }

    /**
     * @param userAgent the userAgent to set
     */
    public void setUseragent(String userAgent) {
        getDocument().setUserAgent(userAgent);
    }

    /**
     * @param format the format to set
     * @throws ApplicationException 
     */
    public void setFormat(String format) throws ApplicationException {
        format = StringUtil.toLowerCase(format.trim());
        if (!"pdf".equals(format))
            throw new ApplicationException(
                    "invalid format [" + format + "], only the following format is supported [pdf]");
    }

    /**
     * @param filename the filename to set
     * @throws PageException 
     */
    public void setFilename(String filename) throws PageException {
        this.filename = ResourceUtil.toResourceNotExisting(pageContext, filename);
        pageContext.getConfig().getSecurityManager().checkFileLocation(this.filename);
    }

    /**
     * @param overwrite the overwrite to set
     */
    public void setOverwrite(boolean overwrite) {
        this.overwrite = overwrite;
    }

    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * @param pagetype the pagetype to set
     * @throws ApplicationException 
     */
    public void setPagetype(String strPagetype) throws ApplicationException {
        strPagetype = StringUtil.toLowerCase(strPagetype.trim());
        if ("legal".equals(strPagetype))
            pagetype = PDFDocument.PAGETYPE_LEGAL;
        else if ("letter".equals(strPagetype))
            pagetype = PDFDocument.PAGETYPE_LETTER;
        else if ("a4".equals(strPagetype))
            pagetype = PDFDocument.PAGETYPE_A4;
        else if ("a5".equals(strPagetype))
            pagetype = PDFDocument.PAGETYPE_A5;
        else if ("b4".equals(strPagetype))
            pagetype = PDFDocument.PAGETYPE_B4;
        else if ("b5".equals(strPagetype))
            pagetype = PDFDocument.PAGETYPE_B5;
        else if ("b4-jis".equals(strPagetype))
            pagetype = PDFDocument.PAGETYPE_B4_JIS;
        else if ("b4 jis".equals(strPagetype))
            pagetype = PDFDocument.PAGETYPE_B4_JIS;
        else if ("b4_jis".equals(strPagetype))
            pagetype = PDFDocument.PAGETYPE_B4_JIS;
        else if ("b4jis".equals(strPagetype))
            pagetype = PDFDocument.PAGETYPE_B4_JIS;
        else if ("b5-jis".equals(strPagetype))
            pagetype = PDFDocument.PAGETYPE_B5_JIS;
        else if ("b5 jis".equals(strPagetype))
            pagetype = PDFDocument.PAGETYPE_B5_JIS;
        else if ("b5_jis".equals(strPagetype))
            pagetype = PDFDocument.PAGETYPE_B5_JIS;
        else if ("b5jis".equals(strPagetype))
            pagetype = PDFDocument.PAGETYPE_B5_JIS;
        else if ("custom".equals(strPagetype))
            pagetype = PDFDocument.PAGETYPE_CUSTOM;
        else
            throw new ApplicationException("invalid page type [" + strPagetype
                    + "], valid page types are [legal,letter,a4,a5,b4,b5,b4-jis,b5-jis,custom]");

    }

    /**
     * @param pageheight the pageheight to set
     * @throws ApplicationException 
     */
    public void setPageheight(double pageheight) throws ApplicationException {
        if (pageheight < 1)
            throw new ApplicationException("pageheight must be a positive number");
        this.pageheight = pageheight;
    }

    /**
     * @param pagewidth the pagewidth to set
     * @throws ApplicationException 
     */
    public void setPagewidth(double pagewidth) throws ApplicationException {
        if (pagewidth < 1)
            throw new ApplicationException("pagewidth must be a positive number");
        this.pagewidth = pagewidth;
    }

    /**
     * @param orientation the orientation to set
     * @throws ApplicationException 
     */
    public void setOrientation(String strOrientation) throws ApplicationException {
        strOrientation = StringUtil.toLowerCase(strOrientation.trim());
        if ("portrait".equals(strOrientation))
            isLandscape = false;
        else if ("landscape".equals(strOrientation))
            isLandscape = true;
        else
            throw new ApplicationException(
                    "invalid orientation [" + strOrientation + "], valid orientations are [portrait,landscape]");

    }

    /**
     * @param marginbottom the marginbottom to set
     */
    public void setMarginbottom(double marginbottom) {
        getDocument().setMarginbottom(marginbottom);
    }

    /**
     * @param marginleft the marginleft to set
     */
    public void setMarginleft(double marginleft) {
        getDocument().setMarginleft(marginleft);
    }

    /**
     * @param marginright the marginright to set
     */
    public void setMarginright(double marginright) {
        getDocument().setMarginright(marginright);
    }

    /**
     * @param margintop the margintop to set
     */
    public void setMargintop(double margintop) {
        getDocument().setMargintop(margintop);
    }

    /**
     * @param bookmark the bookmark to set
     */
    public void setBookmark(boolean bookmark) {
        getDocument().setBookmark(bookmark);
    }

    public void setHtmlbookmark(boolean bookmark) {
        getDocument().setHtmlBookmark(bookmark);
    }

    /**
     * @param localUrl the localUrl to set
     */
    public void setLocalurl(boolean localUrl) {
        getDocument().setLocalUrl(localUrl);
    }

    /**
     * @param unitFactor the unit to set
     * @throws ApplicationException 
     */
    public void setUnit(String strUnit) throws ApplicationException {
        strUnit = StringUtil.toLowerCase(strUnit.trim());
        if ("in".equals(strUnit))
            unitFactor = PDFDocument.UNIT_FACTOR_IN;
        else if ("cm".equals(strUnit))
            unitFactor = PDFDocument.UNIT_FACTOR_CM;
        else if ("point".equals(strUnit))
            unitFactor = PDFDocument.UNIT_FACTOR_POINT;
        else
            throw new ApplicationException("invalid unit [" + strUnit + "], valid units are [cm,in,point]");
    }

    /**
     * @param encryption the encryption to set
     * @throws ApplicationException 
     */
    public void setEncryption(String strEncryption) throws ApplicationException {
        strEncryption = StringUtil.toLowerCase(strEncryption.trim());
        if ("none".equals(strEncryption))
            encryption = PDFDocument.ENC_NONE;
        else if ("40-bit".equals(strEncryption))
            encryption = PDFDocument.ENC_40BIT;
        else if ("40bit".equals(strEncryption))
            encryption = PDFDocument.ENC_40BIT;
        else if ("40 bit".equals(strEncryption))
            encryption = PDFDocument.ENC_40BIT;
        else if ("40_bit".equals(strEncryption))
            encryption = PDFDocument.ENC_40BIT;
        else if ("128-bit".equals(strEncryption))
            encryption = PDFDocument.ENC_128BIT;
        else if ("128bit".equals(strEncryption))
            encryption = PDFDocument.ENC_128BIT;
        else if ("128 bit".equals(strEncryption))
            encryption = PDFDocument.ENC_128BIT;
        else if ("128_bit".equals(strEncryption))
            encryption = PDFDocument.ENC_128BIT;
        else
            throw new ApplicationException("invalid encryption [" + strEncryption
                    + "], valid encryption values are [none, 40-bit, 128-bit]");
    }

    /**
     * @param ownerpassword the ownerpassword to set
     * @throws ApplicationException 
     */
    public void setOwnerpassword(String ownerpassword) {
        this.ownerpassword = ownerpassword;
    }

    /**
     * @param userpassword the userpassword to set
     */
    public void setUserpassword(String userpassword) {
        this.userpassword = userpassword;
    }

    /**
     * @param permissions the permissions to set
     * @throws PageException 
     */
    public void setPermissions(String strPermissions) throws PageException {
        permissions = PDFUtil.toPermissions(strPermissions);
    }

    /**
     * @param scale the scale to set
     * @throws ApplicationException 
     */
    public void setScale(double scale) throws ApplicationException {
        if (scale < 0)
            throw new ApplicationException("scale must be a positive number");
        if (scale > 100)
            throw new ApplicationException("scale must be a number less or equal than 100");
        this.scale = (int) scale;
    }

    /**
     * @param src the src to set
     * @throws ApplicationException 
     */
    public void setSrc(String src) throws ApplicationException {
        try {
            getDocument().setSrc(src);
        } catch (PDFException e) {
            throw new ApplicationException(e.getMessage());
        }
    }

    /**
     * @param srcfile the srcfile to set
     * @throws PageException 
     * @throws  
     */
    public void setSrcfile(String strSrcfile) throws PageException {
        Resource srcfile = ResourceUtil.toResourceExisting(pageContext, strSrcfile);
        pageContext.getConfig().getSecurityManager().checkFileLocation(srcfile);
        try {
            getDocument().setSrcfile(srcfile);
        } catch (PDFException e) {
            throw new ApplicationException(e.getMessage());
        }
    }

    /**
     * @param mimetype the mimetype to set
     */
    public void setMimetype(String strMimetype) {
        getDocument().setMimetype(strMimetype);
        strMimetype = strMimetype.toLowerCase().trim();
    }

    public void setHeader(PDFPageMark header) {
        getDocument().setHeader(header);
    }

    public void setFooter(PDFPageMark footer) {
        getDocument().setFooter(footer);
    }

    public void setBackgroundvisible(boolean backgroundvisible) {
        this.backgroundvisible = backgroundvisible;
    }

    public void setFontembed(String fontembed) throws PDFException {
        Boolean fe = Caster.toBoolean(fontembed, null);
        if (fe == null) {
            fontembed = StringUtil.toLowerCase(fontembed.trim());
            if ("selective".equals(fontembed))
                this.fontembed = PDFDocument.FONT_EMBED_SELECCTIVE;
            else
                throw new PDFException("invalid value for fontembed [" + fontembed
                        + "], valid values for fontembed are [yes,no,selective]");

        } else if (fe.booleanValue())
            this.fontembed = PDFDocument.FONT_EMBED_YES;
        else
            this.fontembed = PDFDocument.FONT_EMBED_NO;
        getDocument().setFontembed(this.fontembed);
    }

    public void addPDFDocument(PDFDocument document) {
        // set proxy settings
        if (_document != null) {
            if (_document.hasProxy()) {
                document.setProxyserver(_document.getProxyserver());
                document.setProxyport(_document.getProxyport());
                document.setProxyuser(_document.getProxyuser());
                document.setProxypassword(_document.getProxypassword());
            }
            document.setBookmark(_document.getBookmark());
            document.setLocalUrl(_document.getLocalUrl());
        }

        documents.add(document);
    }

    @Override
    public int doStartTag() throws PageException {
        // SerialNumber sn = pageContext.getConfig().getSerialNumber();
        //if(sn.getVersion()==SerialNumber.VERSION_COMMUNITY)
        //    throw new SecurityException("no access to this functionality with the "+sn.getStringVersion()+" version of lucee");

        ReadOnlyStruct cfdoc = new ReadOnlyStruct();
        cfdoc.setEL("currentpagenumber", "{currentpagenumber}");
        cfdoc.setEL("totalpagecount", "{totalpagecount}");
        cfdoc.setEL("totalsectionpagecount", "{totalsectionpagecount}");
        cfdoc.setEL("currentsectionpagenumber", "{currentsectionpagenumber}");
        cfdoc.setReadOnly(true);
        pageContext.variablesScope().setEL("cfdocument", cfdoc);

        return EVAL_BODY_BUFFERED;
    }

    @Override
    public void doInitBody() {

    }

    @Override
    public int doAfterBody() {
        getDocument().setBody(bodyContent.getString());

        return SKIP_BODY;
    }

    @Override
    public int doEndTag() throws PageException {
        try {
            _doEndTag();
        } catch (Exception e) {
            throw Caster.toPageException(e);
        }
        return EVAL_PAGE;
    }

    public void _doEndTag() throws JspException, IOException, DocumentException {
        // set root header/footer to sections
        boolean doBookmarks = false;
        boolean doHtmlBookmarks = false;
        if (_document != null) {
            PDFPageMark header = _document.getHeader();
            PDFPageMark footer = _document.getFooter();
            boolean hasHeader = header != null;
            boolean hasFooter = footer != null;
            if (hasFooter || hasHeader) {
                Iterator<PDFDocument> it = documents.iterator();
                PDFDocument doc;
                while (it.hasNext()) {
                    doc = it.next();
                    if (hasHeader && doc.getHeader() == null)
                        doc.setHeader(header);
                    if (hasFooter && doc.getFooter() == null)
                        doc.setFooter(footer);
                }
            }
            doBookmarks = _document.getBookmark();
            doHtmlBookmarks = _document.getHtmlBookmark();
        }

        if (filename != null) {
            if (filename.exists() && !overwrite)
                throw new ApplicationException("file [" + filename + "] already exist",
                        "to allow overwrite the resource, set attribute [overwrite] to [true]");

            OutputStream os = null;
            try {
                os = filename.getOutputStream();
                render(os, doBookmarks, doHtmlBookmarks);
            } finally {
                IOUtil.closeEL(os);
            }

        } else if (!StringUtil.isEmpty(name)) {
            render(null, doBookmarks, doHtmlBookmarks);
        } else {
            HttpServletResponse rsp = pageContext.getHttpServletResponse();
            if (rsp.isCommitted())
                throw new ApplicationException("content is already flushed",
                        "you can't rewrite head of response after part of the page is flushed");
            rsp.setContentType("application/pdf");

            OutputStream os = getOutputStream();
            try {
                render(os, doBookmarks, doHtmlBookmarks);
            } finally {
                IOUtil.flushEL(os);
                IOUtil.closeEL(os);
                ((PageContextImpl) pageContext).getRootOut().setClosed(true);
            }
            throw new lucee.runtime.exp.Abort(lucee.runtime.exp.Abort.SCOPE_REQUEST);
        }

    }

    private void render(OutputStream os, boolean doBookmarks, boolean doHtmlBookmarks)
            throws IOException, PageException, DocumentException {
        byte[] pdf = null;
        // merge multiple docs to 1
        if (documents.size() > 1) {
            PDFDocument[] pdfDocs = new PDFDocument[documents.size()];
            PdfReader[] pdfReaders = new PdfReader[pdfDocs.length];
            Iterator<PDFDocument> it = documents.iterator();
            int index = 0;
            // generate pdf with pd4ml
            while (it.hasNext()) {
                pdfDocs[index] = it.next();
                pdfReaders[index] = new PdfReader(
                        pdfDocs[index].render(getDimension(), unitFactor, pageContext, doHtmlBookmarks));
                index++;
            }

            // collect together
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            com.lowagie.text.Document document = new com.lowagie.text.Document(
                    pdfReaders[0].getPageSizeWithRotation(1));
            PdfCopy copy = new PdfCopy(document, baos);
            document.open();
            String name;
            ArrayList bookmarks = doBookmarks ? new ArrayList() : null;
            try {
                int size, totalPage = 0;
                Map parent;
                for (int doc = 0; doc < pdfReaders.length; doc++) {
                    size = pdfReaders[doc].getNumberOfPages();

                    PdfImportedPage ip;

                    // bookmarks
                    if (doBookmarks) {
                        name = pdfDocs[doc].getName();
                        if (!StringUtil.isEmpty(name)) {
                            bookmarks.add(parent = PDFUtil.generateGoToBookMark(name, totalPage + 1));
                        } else
                            parent = null;

                        if (doHtmlBookmarks) {
                            java.util.List pageBM = SimpleBookmark.getBookmark(pdfReaders[doc]);
                            if (pageBM != null) {
                                if (totalPage > 0)
                                    SimpleBookmark.shiftPageNumbers(pageBM, totalPage, null);
                                if (parent != null)
                                    PDFUtil.setChildBookmarks(parent, pageBM);
                                else
                                    bookmarks.addAll(pageBM);
                            }
                        }
                    }

                    totalPage++;
                    for (int page = 1; page <= size; page++) {
                        if (page > 1)
                            totalPage++;
                        ip = copy.getImportedPage(pdfReaders[doc], page);

                        //ip.getPdfDocument().setHeader(arg0);
                        //ip.getPdfDocument().setFooter(arg0);
                        copy.addPage(ip);
                    }
                }
                if (doBookmarks && !bookmarks.isEmpty())
                    copy.setOutlines(bookmarks);
            } finally {
                document.close();
            }
            pdf = baos.toByteArray();
        } else if (documents.size() == 1) {
            pdf = (documents.get(0)).render(getDimension(), unitFactor, pageContext, doHtmlBookmarks);
        } else {
            pdf = getDocument().render(getDimension(), unitFactor, pageContext, doHtmlBookmarks);
        }

        // permission/encryption
        if (PDFDocument.ENC_NONE != encryption) {
            PdfReader reader = new PdfReader(pdf);
            com.lowagie.text.Document document = new com.lowagie.text.Document(reader.getPageSize(1));
            document.addCreator("Lucee " + Info.getVersionAsString() + " " + Info.getStateAsString());
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            PdfCopy copy = new PdfCopy(document, baos);
            //PdfWriter writer = PdfWriter.getInstance(document, pdfOut);
            copy.setEncryption(PDFDocument.ENC_128BIT == encryption, userpassword, ownerpassword, permissions);
            document.open();
            int size = reader.getNumberOfPages();
            for (int page = 1; page <= size; page++) {
                copy.addPage(copy.getImportedPage(reader, page));
            }
            document.close();
            pdf = baos.toByteArray();
        }

        // write out
        if (os != null)
            IOUtil.copy(new ByteArrayInputStream(pdf), os, true, false);
        if (!StringUtil.isEmpty(name)) {
            pageContext.setVariable(name, pdf);
        }
    }

    private OutputStream getOutputStream() throws PageException, IOException {
        try {
            return ((PageContextImpl) pageContext).getResponseStream();
        } catch (IllegalStateException ise) {
            throw new TemplateException("content is already send to user, flush");
        }
    }

    private Dimension getDimension() throws ApplicationException {
        // page size custom
        if (isCustom(pagetype)) {
            if (pageheight == 0 || pagewidth == 0)
                throw new ApplicationException(
                        "when attribute pagetype has value [custom], the attributes [pageheight, pagewidth] must have a positive numeric value");
            pagetype = new Dimension(PDFDocument.toPoint(pagewidth, unitFactor),
                    PDFDocument.toPoint(pageheight, unitFactor));
        }
        // page orientation
        if (isLandscape)
            pagetype = new Dimension(pagetype.height, pagetype.width);
        return pagetype;
    }

    private boolean isCustom(Dimension d) throws ApplicationException {
        if (d.height <= 0 || d.width <= 0)
            throw new ApplicationException(
                    "if you define pagetype as custom, you have to define attribute pageheight and pagewith with a positive numeric value");

        return (d.width + d.height) == 2;
    }

    /**
     * sets if has body or not
     * @param hasBody
     */
    public void hasBody(boolean hasBody) {

    }

}