All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sejda.sambox.printing.PDFPrintable Maven / Gradle / Ivy

Go to download

An Apache PDFBox fork intended to be used as PDF processor for Sejda and PDFsam related projects

There is a newer version: 3.0.21
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sejda.sambox.printing;

import org.sejda.sambox.pdmodel.PDDocument;
import org.sejda.sambox.pdmodel.PDPage;
import org.sejda.sambox.pdmodel.PDPageTree;
import org.sejda.sambox.pdmodel.common.PDRectangle;
import org.sejda.sambox.rendering.PDFRenderer;
import org.sejda.sambox.rendering.RenderDestination;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterIOException;
import java.io.IOException;

/**
 * Prints pages from a PDF document using any page size or scaling mode.
 *
 * @author John Hewson
 */
public final class PDFPrintable implements Printable
{
    private final PDPageTree pageTree;
    private final PDFRenderer renderer;

    private final boolean showPageBorder;
    private final Scaling scaling;
    private final float dpi;
    private final boolean center;
    private boolean subsamplingAllowed = false;
    private RenderingHints renderingHints = null;

    /**
     * Creates a new PDFPrintable.
     *
     * @param document the document to print
     */
    public PDFPrintable(PDDocument document)
    {
        this(document, Scaling.SHRINK_TO_FIT);
    }

    /**
     * Creates a new PDFPrintable with the given page scaling.
     *
     * @param document the document to print
     * @param scaling  page scaling policy
     */
    public PDFPrintable(PDDocument document, Scaling scaling)
    {
        this(document, scaling, false, 0);
    }

    /**
     * Creates a new PDFPrintable with the given page scaling and with optional page borders shown.
     *
     * @param document       the document to print
     * @param scaling        page scaling policy
     * @param showPageBorder true if page borders are to be printed
     */
    public PDFPrintable(PDDocument document, Scaling scaling, boolean showPageBorder)
    {
        this(document, scaling, showPageBorder, 0);
    }

    /**
     * Creates a new PDFPrintable with the given page scaling and with optional page borders shown.
     * The image will be rasterized at the given DPI before being sent to the printer.
     *
     * @param document       the document to print
     * @param scaling        page scaling policy
     * @param showPageBorder true if page borders are to be printed
     * @param dpi            if non-zero then the image will be rasterized at the given DPI
     */
    public PDFPrintable(PDDocument document, Scaling scaling, boolean showPageBorder, float dpi)
    {
        this(document, scaling, showPageBorder, dpi, true);
    }

    /**
     * Creates a new PDFPrintable with the given page scaling and with optional page borders shown.
     * The image will be rasterized at the given DPI before being sent to the printer.
     *
     * @param document       the document to print
     * @param scaling        page scaling policy
     * @param showPageBorder true if page borders are to be printed
     * @param dpi            if non-zero then the image will be rasterized at the given DPI
     * @param center         true if the content is to be centered on the page (otherwise
     *                       top-left).
     */
    public PDFPrintable(PDDocument document, Scaling scaling, boolean showPageBorder, float dpi,
            boolean center)
    {
        this(document, scaling, showPageBorder, dpi, center, new PDFRenderer(document));
    }

    /**
     * Creates a new PDFPrintable with the given page scaling and with optional page borders shown.
     * The image will be rasterized at the given DPI before being sent to the printer.
     *
     * @param document       the document to print
     * @param scaling        page scaling policy
     * @param showPageBorder true if page borders are to be printed
     * @param dpi            if non-zero then the image will be rasterized at the given DPI
     * @param center         true if the content is to be centered on the page (otherwise
     *                       top-left).
     * @param renderer       the document renderer. Useful if {@link PDFRenderer} has been
     *                       subclassed.
     */
    public PDFPrintable(PDDocument document, Scaling scaling, boolean showPageBorder, float dpi,
            boolean center, PDFRenderer renderer)
    {
        this.pageTree = document.getPages();
        this.renderer = renderer;
        this.scaling = scaling;
        this.showPageBorder = showPageBorder;
        this.dpi = dpi;
        this.center = center;
    }

    /**
     * Value indicating if the renderer is allowed to subsample images before drawing, according to
     * image dimensions and requested scale.
     * 

* Subsampling may be faster and less memory-intensive in some cases, but it may also lead to * loss of quality, especially in images with high spatial frequency. * * @return true if subsampling of images is allowed, false otherwise. */ public boolean isSubsamplingAllowed() { return subsamplingAllowed; } /** * Sets a value instructing the renderer whether it is allowed to subsample images before * drawing. The subsampling frequency is determined according to image size and requested * scale. *

* Subsampling may be faster and less memory-intensive in some cases, but it may also lead to * loss of quality, especially in images with high spatial frequency. * * @param subsamplingAllowed The new value indicating if subsampling is allowed. */ public void setSubsamplingAllowed(boolean subsamplingAllowed) { this.subsamplingAllowed = subsamplingAllowed; } /** * Get the rendering hints. * * @return the rendering hints or null if none are set. */ public RenderingHints getRenderingHints() { return renderingHints; } /** * Set the rendering hints. Use this to influence rendering quality and speed. If you don't set * them yourself or pass null, PDFBox will decide at runtime depending on the * destination. * * @param renderingHints */ public void setRenderingHints(RenderingHints renderingHints) { this.renderingHints = renderingHints; } @Override public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException { if (pageIndex < 0 || pageIndex >= pageTree.getCount()) { return NO_SUCH_PAGE; } try { Graphics2D graphics2D = (Graphics2D) graphics; PDPage page = pageTree.get(pageIndex); PDRectangle cropBox = getRotatedCropBox(page); // the imageable area is the area within the page margins final double imageableWidth = pageFormat.getImageableWidth(); final double imageableHeight = pageFormat.getImageableHeight(); double scale = 1; if (scaling != Scaling.ACTUAL_SIZE) { // scale to fit double scaleX = imageableWidth / cropBox.getWidth(); double scaleY = imageableHeight / cropBox.getHeight(); scale = Math.min(scaleX, scaleY); // only shrink to fit when enabled if (scale > 1 && scaling == Scaling.SHRINK_TO_FIT) { scale = 1; } // only stretch to fit when enabled if (scale < 1 && scaling == Scaling.STRETCH_TO_FIT) { scale = 1; } } // set the graphics origin to the origin of the imageable area (i.e the margins) graphics2D.translate(pageFormat.getImageableX(), pageFormat.getImageableY()); // center on page if (center) { graphics2D.translate((imageableWidth - cropBox.getWidth() * scale) / 2, (imageableHeight - cropBox.getHeight() * scale) / 2); } // rasterize to bitmap (optional) Graphics2D printerGraphics = null; BufferedImage image = null; if (dpi > 0) { float dpiScale = dpi / 72; image = new BufferedImage((int) (imageableWidth * dpiScale / scale), (int) (imageableHeight * dpiScale / scale), BufferedImage.TYPE_INT_ARGB); printerGraphics = graphics2D; graphics2D = image.createGraphics(); // rescale printerGraphics.scale(scale / dpiScale, scale / dpiScale); scale = dpiScale; } // draw to graphics using PDFRender AffineTransform transform = (AffineTransform) graphics2D.getTransform().clone(); graphics2D.setBackground(Color.WHITE); renderer.setSubsamplingAllowed(subsamplingAllowed); renderer.setRenderingHints(renderingHints); renderer.renderPageToGraphics(pageIndex, graphics2D, (float) scale, (float) scale, RenderDestination.PRINT); // draw crop box if (showPageBorder) { graphics2D.setTransform(transform); graphics2D.setClip(0, 0, (int) imageableWidth, (int) imageableHeight); graphics2D.scale(scale, scale); graphics2D.setColor(Color.GRAY); graphics2D.setStroke(new BasicStroke(0.5f)); graphics.drawRect(0, 0, (int) cropBox.getWidth(), (int) cropBox.getHeight()); } // draw rasterized bitmap (optional) if (printerGraphics != null) { printerGraphics.setBackground(Color.WHITE); printerGraphics.clearRect(0, 0, image.getWidth(), image.getHeight()); printerGraphics.drawImage(image, 0, 0, null); graphics2D.dispose(); } return PAGE_EXISTS; } catch (IOException e) { throw new PrinterIOException(e); } } /** * This will find the CropBox with rotation applied, for this page by looking up the hierarchy * until it finds them. * * @return The CropBox at this level in the hierarchy. */ static PDRectangle getRotatedCropBox(PDPage page) { PDRectangle cropBox = page.getCropBox(); int rotationAngle = page.getRotation(); if (rotationAngle == 90 || rotationAngle == 270) { return new PDRectangle(cropBox.getLowerLeftY(), cropBox.getLowerLeftX(), cropBox.getHeight(), cropBox.getWidth()); } return cropBox; } /** * This will find the MediaBox with rotation applied, for this page by looking up the hierarchy * until it finds them. * * @return The MediaBox at this level in the hierarchy. */ static PDRectangle getRotatedMediaBox(PDPage page) { PDRectangle mediaBox = page.getMediaBox(); int rotationAngle = page.getRotation(); if (rotationAngle == 90 || rotationAngle == 270) { return new PDRectangle(mediaBox.getLowerLeftY(), mediaBox.getLowerLeftX(), mediaBox.getHeight(), mediaBox.getWidth()); } return mediaBox; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy