/******************************************************************************* * Copyright (c) 2013 Dirk Fauth and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Dirk Fauth <dirk.fauth@gmail.com> - initial API and implementation *******************************************************************************/ package org.eclipse.nebula.widgets.ganttchart.print; import java.text.SimpleDateFormat; import java.util.Date; import org.eclipse.nebula.widgets.ganttchart.GanttChart; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.graphics.Transform; import org.eclipse.swt.printing.Printer; import org.eclipse.swt.printing.PrinterData; import org.eclipse.swt.widgets.Display; /** * Runnable to print one ore more GanttCharts. */ public class GanttChartPrintJob implements Runnable { private final Printer printer; private final String jobName; private final GanttChart[] ganttCharts; private boolean disposePrinter; /** * Creates a new GanttChartPrintJob that uses the given printer and print job name * to print the specified GanttChart(s). If not changed afterwards, running this job * will dispose the given printer when it is done. * @param printer The printer to use. * @param jobName The job name to use for the print job. * @param charts The GanttCharts that should be printed. */ public GanttChartPrintJob(Printer printer, String jobName, GanttChart... charts) { this(printer, jobName, true, charts); } /** * Creates a new GanttChartPrintJob that uses the given printer and print job name * to print the specified GanttChart(s). * @param printer The printer to use. * @param jobName The job name to use for the print job. * @param charts The GanttCharts that should be printed. * @param disposePrinter Flag to configure whether the given printer should be disposed * after the print job is done. Default is <code>true</code>. Only set this * parameter to <code>false</code> if the printer should be reused for additional * print jobs. You need to ensure that the printer will get disposed yourself * in that case! */ public GanttChartPrintJob(Printer printer, String jobName, boolean disposePrinter, GanttChart... charts) { this.printer = printer; this.jobName = jobName; this.ganttCharts = charts; this.disposePrinter = disposePrinter; } public void run() { if (printer.startJob(jobName)) { GC gc = new GC(printer); int currentPage = 1; for (GanttChart ganttChart : this.ganttCharts) { Image printerImage = null; if (printer.getPrinterData().scope == PrinterData.SELECTION) { //the user selected to only print the selected area //as this is quite difficult in GanttChart, we specify that //this means to print the visible area //but it is possible to configure via settings what "visible" //area means: // - really only the visible area horizontally and vertically // - only the horizontal visible area, but vertically everything printerImage = ganttChart.getSettings().printSelectedVerticallyComplete() ? ganttChart.getGanttComposite().getVerticallyFullImage() : ganttChart.getGanttComposite().getImage(); } else { printerImage = ganttChart.getGanttComposite().getFullImage(); } final Rectangle printerClientArea = PrintUtils.computePrintArea(printer); final Point scaleFactor = PrintUtils.computeScaleFactor(printer); final Point pageCount = PrintUtils.getPageCount(printer, printerImage); // Print pages Left to Right and then Top to Down for (int verticalPageNumber = 0; verticalPageNumber < pageCount.y; verticalPageNumber++) { for (int horizontalPageNumber = 0; horizontalPageNumber < pageCount.x; horizontalPageNumber++) { // Calculate bounds for the next page int printerClientAreaHeight = ganttChart.getSettings().printFooter() ? (printerClientArea.height - PrintUtils.FOOTER_HEIGHT_IN_PRINTER_DPI) : printerClientArea.height; Rectangle printBounds = new Rectangle((printerClientArea.width / scaleFactor.x) * horizontalPageNumber, (printerClientAreaHeight / scaleFactor.y) * verticalPageNumber, printerClientArea.width / scaleFactor.x, printerClientAreaHeight / scaleFactor.y); if (shouldPrint(printer.getPrinterData(), currentPage)) { printer.startPage(); Transform printerTransform = new Transform(printer); // Adjust for DPI difference between display and printer printerTransform.scale(scaleFactor.x, scaleFactor.y); // Adjust for margins printerTransform.translate(printerClientArea.x / scaleFactor.x, printerClientArea.y / scaleFactor.y); // GanttChart will not automatically print the pages at the left margin. // Example: page 1 will print at x = 0, page 2 at x = 100, page 3 at x = 300 // Adjust to print from the left page margin. i.e x = 0 printerTransform.translate(-1 * printBounds.x, -1 * printBounds.y); gc.setTransform(printerTransform); int imgWidthClipping = printBounds.width; if (((horizontalPageNumber * printBounds.width)+printBounds.width) > printerImage.getImageData().width) { imgWidthClipping = printerImage.getImageData().width - (horizontalPageNumber * printBounds.width); } int imgHeightClipping = printBounds.height; if (((verticalPageNumber * printBounds.height)+printBounds.height) > printerImage.getImageData().height) { imgHeightClipping = printerImage.getImageData().height - (verticalPageNumber * printBounds.height); } gc.drawImage(printerImage, horizontalPageNumber * printBounds.width, verticalPageNumber * printBounds.height, imgWidthClipping, imgHeightClipping, printBounds.x, printBounds.y, imgWidthClipping, imgHeightClipping); if (ganttChart.getSettings().printFooter()) printFooter(gc, ganttChart, currentPage, printBounds); printer.endPage(); printerTransform.dispose(); } currentPage++; } } printerImage.dispose(); } printer.endJob(); gc.dispose(); //only dispose the printer after the print job is done if it is configured to do so //this configuration enables the possibility to reuse a printer for several jobs if (disposePrinter) printer.dispose(); } } /** * Render the footer to a print page. * @param gc The graphical context that is used for printing * @param ganttChart The GanttChart which is currently printed. * @param currentPage The number of the current page that is printed * @param printBounds The bounds of the print area */ private void printFooter(GC gc, GanttChart ganttChart, int currentPage, Rectangle printBounds) { String footerDate = new SimpleDateFormat(ganttChart.getSettings().getDateFormat()).format(new Date()); gc.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_BLACK)); gc.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE)); gc.setFont(Display.getCurrent().getSystemFont()); gc.drawLine(printBounds.x, printBounds.y + printBounds.height+10, printBounds.x + printBounds.width, printBounds.y + printBounds.height+10); String pageText = ganttChart.getLanguageManger().getPrintPageText() + " " + currentPage; //$NON-NLS-1$ gc.drawString(pageText, printBounds.x, printBounds.y + printBounds.height + 15); Point dateExtend = gc.stringExtent(footerDate); gc.drawString(footerDate, printBounds.x + printBounds.width - dateExtend.x, printBounds.y + printBounds.height + 15); } /** * Checks if a given page number should be printed. * Page is allowed to print if: * User asked to print all pages or page in a specified range * @param printerData The printer settings made by the user. Needed to determine * if a page should be printed dependent to the scope * @param currentPage The page that should be checked * @return <code>true</code> if the given page should be printed, * <code>false</code> if not */ private boolean shouldPrint(PrinterData printerData, int currentPage) { if (printerData.scope == PrinterData.PAGE_RANGE) { return currentPage >= printerData.startPage && currentPage <= printerData.endPage; } return true; } /** * * @param dispose <code>true</code> if the printer that is set to this GanttChartPrintJob * should be disposed after the print job is done, <code>false</code> if it should * not be disposed so the printer can be reused for additional tasks. */ public void setDisposePrinter(boolean dispose) { this.disposePrinter = dispose; } }