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

net.shredzone.jshred.swing.JPrintPreview Maven / Gradle / Ivy

/**
 * jshred - Shred's Toolbox
 *
 * Copyright (C) 2009 Richard "Shred" Körber
 *   http://jshred.shredzone.org
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License / 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.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *
 * This program 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.
 *
 */
package net.shredzone.jshred.swing;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Pageable;
import java.awt.print.Printable;
import java.awt.print.PrinterException;

import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;

/**
 * A Swing component that renders printable pages for a printer preview. The view can be
 * zoomed, and it is possible to turn over the pages of the printout.
 * 

* The pages are rendered into an internal image buffer, for best performance. The * tradeoff is that a lot of memory is required for large zoom factors, so you should * limit it within a reasonable range. *

* The given {@link Printable} must be able to render the same page several times, but * does not need to be able to give random access to all the pages. * * @author Richard "Shred" Körber * @since R8 */ public class JPrintPreview extends JPanel { private static final long serialVersionUID = 3256723966038390833L; private final static int SHADOW_X = 4; // Shadow X size private final static int SHADOW_Y = 4; // Shadow Y size private final static int BORDER_SIZE = 3; // Size of the border around the page private int currentPage; private double currentZoom; private Pageable pageable = null; private Printable printable = null; private PageFormat format = null; private final JLabel jlContent; private boolean lowMem = false; private transient int rectX, rectY, mouseX, mouseY; private transient Cursor oldCursor; /** * Creates an empty {@link JPrintPreview} pane. It will show nothing. */ public JPrintPreview() { setLayout(new BorderLayout()); jlContent = new JLabel(); jlContent.setHorizontalAlignment(SwingConstants.CENTER); add(jlContent, BorderLayout.CENTER); setBorder(BorderFactory.createEmptyBorder(BORDER_SIZE, BORDER_SIZE, BORDER_SIZE, BORDER_SIZE)); MouseListener lMouse = new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { final Rectangle rv = getVisibleRect(); // --- Remember the rectangle when the mouse was pressed --- rectX = rv.x; rectY = rv.y; // --- Remember the mouse position relative to it --- mouseX = e.getX() - rv.x; mouseY = e.getY() - rv.y; // --- Set the cursor --- oldCursor = getCursor(); if (rv.width < getWidth() || rv.height < getHeight()) { setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); } } @Override public void mouseReleased(MouseEvent e) { // --- Restore the cursor --- setCursor(oldCursor); } }; MouseMotionListener lMotion = new MouseMotionAdapter() { @Override public void mouseDragged(MouseEvent e) { Rectangle rv = new Rectangle(getVisibleRect()); // --- Compute the mouse delta movement --- // This are the number of pixels the mouse has been moved // on the viewport. int dX = mouseX - (e.getX() - rv.x); int dY = mouseY - (e.getY() - rv.y); // --- Compute the new rectangle --- // This is the position of the rectangle when the mouse was // clicked, added by the delta mouse move since then. rv.x = rectX + dX; rv.y = rectY + dY; // --- Make visible --- ((JComponent) e.getSource()).scrollRectToVisible(rv); } }; jlContent.addMouseListener(lMouse); jlContent.addMouseMotionListener(lMotion); } /** * Creates a {@link JPrintPreview} pane and initialize it with a {@link Printable} * object, which is to be shown in the given {@link PageFormat}. * * @param printable * {@link Printable} to be shown * @param format * {@link PageFormat} to be used */ public JPrintPreview(Printable printable, PageFormat format) { this(); setPrintable(printable, format); } /** * Creates a {@link JPrintPreview} pane and initialize it with a {@link Pageable} * object. * * @param pageable * {@link Pageable} to be shown */ public JPrintPreview(Pageable pageable) { this(); setPageable(pageable); } /** * Sets a {@link Printable} to be shown in the given {@link PageFormat}. The first * page of the {@link Printable} will be shown with a standard zoom factor. *

* You can always change to other {@link Printable} and {@link Pageable} objects. * * @param printable * {@link Printable} to be shown * @param format * {@link PageFormat} to be used */ public void setPrintable(Printable printable, PageFormat format) { this.pageable = null; this.printable = printable; this.format = format; currentPage = 0; initZoom(format); } /** * Sets a {@link Pageable} to be shown. The first page of the {@link Pageable} will be * shown with a standard zoom factor. *

* You can always change to other {@link Printable} and {@link Pageable} objects. * * @param pageable * {@link Pageable} to be shown */ public void setPageable(Pageable pageable) { this.pageable = pageable; this.printable = null; this.format = null; currentPage = 0; initZoom(pageable.getPageFormat(currentPage)); } /** * Sets if a low memory footprint is required. JPrintPreview will then try to reduce * memory consumption. For example, the printout will be grayscaled, which reduces * memory by about two third. * * @param val * true: low memory */ public void setLowMem(boolean val) { lowMem = val; } /** * Returns if a low memory footprint is required. * * @return true: low memory */ public boolean isLowMem() { return lowMem; } /** * Initializes the zoom factor. A default factor is chosen that the resulting image * will fit to the current size of this {@link Component}. If the {@link Component} * has currently no size (i.e. is not shown), a default size of 500x500 pixels will be * used. You can change the default behaviour by overriding this method. * * @param format * {@link PageFormat} to be used. */ protected void initZoom(PageFormat format) { double zoom = 1.0; if (format.getOrientation() == PageFormat.PORTRAIT) { int height = getHeight(); if (height == 0) height = 500; zoom = height / format.getHeight(); } else { int width = getWidth(); if (width == 0) width = 500; zoom = width / format.getWidth(); } setZoomFactor(zoom); } /** * Draws the paper. The printout will be printed on this graphics. The default * behaviour is to print a white sheet (size: width x height) and a darker shadow at * the right and bottom side. You can change this behaviour by overriding this method. *

* The paper must start at the coordinates (0,0). There is no scaling set yet, and * antialiasing is disabled here. * * @param g * {@link Graphics}. It is safe to cast it to {@link Graphics2D}. * @param width * Width of the paper. The actual image is at least three pixels broader, * to give space for the shadow. * @param height * Height of the paper. The actual image is at least three pixels taller, * to give space for the shadow. */ protected void drawPaper(Graphics g, int width, int height) { // --- Draw the shadow --- g.setColor(this.getBackground().darker()); g.fillRect(SHADOW_X, height, width, SHADOW_Y); // bottom side g.fillRect(width, SHADOW_Y, SHADOW_X, height); // right side // --- Draw the paper --- g.setColor(Color.white); g.fillRect(0, 0, width, height); } /** * Recreates the internal image cache. This tries to print the currently selected * page. If something goes wrong while printing, nothing will be shown instead. *

* Note that this method may require some time and memory. */ protected void recreate() throws PrinterException { try { jlContent.setIcon(new ImageIcon(createImage())); } catch (PrinterException e) { jlContent.setIcon(null); throw e; } } /** * Creates an {@link Image} of the page that is currently to be printed. The image * will show the page content, scaled to the current zoom factor. *

* Note that this method may require some time and memory. * * @return {@link Image} containing the printed and scaled page. * @throws PrinterException * Could not print to this image, e.g. if the current page does not exist. */ protected Image createImage() throws PrinterException { PageFormat cFormat; Printable cPrintable; // --- Get the printable and format to be used --- if (printable != null && format != null) { cPrintable = printable; cFormat = format; } else { cPrintable = pageable.getPrintable(currentPage); cFormat = pageable.getPageFormat(currentPage); } // --- Compute pane size --- int width = (int) Math.ceil(cFormat.getWidth() * currentZoom); int height = (int) Math.ceil(cFormat.getHeight() * currentZoom); // --- Create the image buffer --- BufferedImage img = new BufferedImage( width + SHADOW_X, height + SHADOW_Y, (isLowMem() ? BufferedImage.TYPE_BYTE_GRAY : BufferedImage.TYPE_4BYTE_ABGR)); Graphics2D g2d = (Graphics2D) img.getGraphics(); // --- Draw the paper --- drawPaper(g2d, width, height); // --- Turn on antialiasing --- g2d.addRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)); // --- Scale to the Zoom factor --- g2d.scale(currentZoom, currentZoom); // --- Draw the content --- int stat = cPrintable.print(g2d, cFormat, currentPage); g2d.dispose(); if (stat == Printable.NO_SUCH_PAGE) throw new PrinterException("Page " + currentPage + ": NO_SUCH_PAGE"); // --- Return the image --- return img; } /** * Gets the page number of the currently shown page. * * @return Page number */ public int getPage() { return currentPage; } /** * Sets the page number of the page to be shown. If the page does not exist, an empty * image will be shown instead. *

* NOTE: some Printables are unable to give random access to the * printed document. You cannot go back to a page that has been printed already, or go * forward by more than one page. * * @param page * New page index (starting from 0). */ public void setPage(int page) throws PrinterException { try { currentPage = page; recreate(); } catch (IndexOutOfBoundsException e) {} } /** * Turns forward one page. If the page does not exist, nothing will happen (except of * some wasted CPU time ;-) ). */ public void turnForward() throws PrinterException { final int oldPage = currentPage; try { setPage(currentPage + 1); } catch (PrinterException e) { currentPage = oldPage; recreate(); throw e; } } /** * Turns back one page. If the page does not exist, nothing will happen. If the * {@link Printable} is unable to go backward, weird things may happen. */ public void turnBack() throws PrinterException { final int oldPage = currentPage; if (currentPage > 0) { try { setPage(currentPage - 1); } catch (PrinterException e) { currentPage = oldPage; recreate(); throw e; } } } /** * Gets the current zoom factor. Values smaller than 1 reduces the page, while values * larger than 1 magnifies it. * * @return The current zoom factor */ public double getZoomFactor() { return currentZoom; } /** * Sets the current zoom factor. Values smaller than 1 reduces the page, while values * larger than 1 magnifies it. The smallest possible zoom factor is 0.01. *

* Note that this class creates an internal {@link Image} representation of the * current page, for performance reasons. It will consume a lot of memory on large * zoom factors! Setting {@link #setLowMem(boolean)} to {@code true} could help * you save some memory. * * @param zoom * The new zoom factor. */ public void setZoomFactor(double zoom) { final double oldZoom = currentZoom; try { currentZoom = Math.max(0.01, zoom); recreate(); } catch (PrinterException e) { currentZoom = oldZoom; try { recreate(); } catch (PrinterException e2) { // we're boned! :( } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy