org.geomajas.plugin.printing.document.SinglePageDocument.java Source code

Java tutorial

Introduction

Here is the source code for org.geomajas.plugin.printing.document.SinglePageDocument.java

Source

/*
 * This is part of Geomajas, a GIS framework, http://www.geomajas.org/.
 *
 * Copyright 2008-2014 Geosparc nv, http://www.geosparc.com/, Belgium.
 *
 * The program is available in open source according to the GNU Affero
 * General Public License. All contributions in this program are covered
 * by the Geomajas Contributors License Agreement. For full licensing
 * details, see LICENSE.txt in the project root.
 */
package org.geomajas.plugin.printing.document;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOInvalidTreeException;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.ImageOutputStream;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.geomajas.plugin.printing.PrintingException;
import org.geomajas.plugin.printing.component.MapComponent;
import org.geomajas.plugin.printing.component.PageComponent;
import org.geomajas.plugin.printing.component.PdfContext;
import org.geomajas.plugin.printing.component.PrintComponent;

import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.PdfWriter;

//import com.sun.pdfview.PDFFile;
//import com.sun.pdfview.PDFPage;

/**
 * Single page document for printing.
 * 
 * @author Jan De Moerloose
 * @author An Buyle
 */
public class SinglePageDocument extends AbstractDocument {

    private static final double DPI_FOR_PNG_OUTPUT = 72 * 2;

    private static final double ONE_INCH_IN_CM = 2.54;

    /**
     * The page to render.
     */
    protected PageComponent page;

    /**
     * Filters to apply to layers.
     */
    protected Map<String, String> filters;

    /**
     * In-memory output stream to know content length.
     */
    private ByteArrayOutputStream baos;

    // private boolean debug = true;

    /**
     * Constructs a document with the specified dimensions.
     * 
     * @param page page
     * @param filters filters
     */
    public SinglePageDocument(PageComponent page, Map<String, String> filters) {
        this.page = page;
        this.filters = (filters == null ? new HashMap<String, String>() : filters);

        // set filters
        for (PrintComponent<?> comp : getPage().getChildren()) {
            if (comp instanceof MapComponent) {
                ((MapComponent<?>) comp).setFilter(filters);
            }
        }
    }

    /**
     * Renders the document to the specified output stream.
     */
    public void render(OutputStream outputStream, Format format, int dpi) throws PrintingException {
        try {
            if (baos == null) {
                prepare();
            }
            writeDocument(outputStream, format, dpi);
        } catch (Exception e) { // NOSONAR
            throw new PrintingException(e, PrintingException.DOCUMENT_RENDER_PROBLEM);
        }
    }

    /**
     * Re-calculates the layout and renders to internal memory stream. Always call this method before calling render()
     * to make sure that the latest document changes have been taken into account for rendering.
     *
     * @throws PrintingException oops
     */
    public void layout() throws PrintingException {
        try {
            prepare();
        } catch (Exception e) { // NOSONAR
            throw new PrintingException(e, PrintingException.DOCUMENT_LAYOUT_PROBLEM);
        }
    }

    /**
     * Prepare the document before rendering.
     * 
     * @param outputStream output stream to render to, null if only for layout
     * @param format format
     * @throws DocumentException oops
     * @throws IOException oops
     * @throws PrintingException oops
     */
    private void prepare() throws IOException, DocumentException, PrintingException {
        if (baos == null) {
            baos = new ByteArrayOutputStream(); // let it grow as much as needed
        }
        baos.reset();
        boolean resize = false;
        if (page.getConstraint().getWidth() == 0 || page.getConstraint().getHeight() == 0) {
            resize = true;
        }
        // Create a document in the requested ISO scale.
        Document document = new Document(page.getBounds(), 0, 0, 0, 0);
        PdfWriter writer;
        writer = PdfWriter.getInstance(document, baos);

        // Render in correct colors for transparent rasters
        writer.setRgbTransparencyBlending(true);

        // The mapView is not scaled to the document, we assume the mapView
        // has the right ratio.

        // Write document title and metadata
        document.open();
        PdfContext context = new PdfContext(writer);
        context.initSize(page.getBounds());
        // first pass of all children to calculate size
        page.calculateSize(context);
        if (resize) {
            // we now know the bounds of the document
            // round 'm up and restart with a new document
            int width = (int) Math.ceil(page.getBounds().getWidth());
            int height = (int) Math.ceil(page.getBounds().getHeight());
            page.getConstraint().setWidth(width);
            page.getConstraint().setHeight(height);

            document = new Document(new Rectangle(width, height), 0, 0, 0, 0);
            writer = PdfWriter.getInstance(document, baos);
            // Render in correct colors for transparent rasters
            writer.setRgbTransparencyBlending(true);

            document.open();
            baos.reset();
            context = new PdfContext(writer);
            context.initSize(page.getBounds());
        }
        // int compressionLevel = writer.getCompressionLevel(); // For testing
        // writer.setCompressionLevel(0);

        // Actual drawing
        document.addTitle("Geomajas");
        // second pass to layout
        page.layout(context);
        // finally render (uses baos)
        page.render(context);

        document.add(context.getImage());
        // Now close the document
        document.close();
    }

    private void writeDocument(OutputStream outputStream, Format format, int dpi)
            throws IOException, DocumentException, PrintingException {
        if (format == Format.PDF) {
            baos.writeTo(outputStream);
        } else {
            PDDocument pdf = PDDocument.load(new ByteArrayInputStream(baos.toByteArray()), true);
            PDFRenderer renderer = new PDFRenderer(pdf);
            BufferedImage bufferedImage = renderer.renderImageWithDPI(0, dpi);
            pdf.close();
            if (format == Format.PNG) {
                final String formatName = format.getExtension();
                for (Iterator<ImageWriter> iw = ImageIO.getImageWritersByFormatName(formatName); iw.hasNext();) {
                    ImageWriter writer1 = iw.next();
                    ImageWriteParam writeParam = writer1.getDefaultWriteParam();
                    ImageTypeSpecifier typeSpecifier = ImageTypeSpecifier
                            .createFromBufferedImageType(BufferedImage.TYPE_INT_ARGB);
                    IIOMetadata metadata = writer1.getDefaultImageMetadata(typeSpecifier, writeParam);
                    if (metadata.isReadOnly() || !metadata.isStandardMetadataFormatSupported()) {
                        continue;
                    }

                    setDPI(metadata);
                    // Write bufferedImage to outputStream
                    final ImageOutputStream stream = ImageIO.createImageOutputStream(outputStream);
                    try {
                        writer1.setOutput(stream);
                        writer1.write(metadata, new IIOImage(bufferedImage, null, metadata), writeParam);
                    } finally {
                        stream.flush();
                        stream.close();
                    }
                    break;
                }
            } else {
                ImageIO.write(bufferedImage, format.getExtension(), outputStream);
            }

        }
    }

    public PageComponent getPage() {
        return page;
    }

    public int getContentLength() {
        return baos == null ? 0 : baos.size();
    }

    private void setDPI(IIOMetadata metadata) throws IIOInvalidTreeException {

        // for PNG, it's dots per millimeter
        double dotsPerMilli = DPI_FOR_PNG_OUTPUT / (10.0 * ONE_INCH_IN_CM);

        IIOMetadataNode horiz = new IIOMetadataNode("HorizontalPixelSize");
        horiz.setAttribute("value", Double.toString(dotsPerMilli));

        IIOMetadataNode vert = new IIOMetadataNode("VerticalPixelSize");
        vert.setAttribute("value", Double.toString(dotsPerMilli));

        IIOMetadataNode dim = new IIOMetadataNode("Dimension");
        dim.appendChild(horiz);
        dim.appendChild(vert);

        IIOMetadataNode root = new IIOMetadataNode("javax_imageio_1.0");
        root.appendChild(dim);

        metadata.mergeTree("javax_imageio_1.0", root);
    }

}