/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * 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: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.modeling.ui.pdf; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.g2d.diagram.DiagramHints; import org.simantics.g2d.diagram.DiagramUtils; import org.simantics.g2d.diagram.IDiagram; import org.simantics.g2d.participant.TransformUtil; import org.simantics.scenegraph.g2d.G2DPDFRenderingHints; import org.simantics.scenegraph.g2d.G2DRenderingHints; import org.simantics.scenegraph.utils.QualityHints; import org.simantics.utils.page.MarginUtils; import org.simantics.utils.page.MarginUtils.Margins; import org.simantics.utils.page.PageDesc; import com.lowagie.text.Rectangle; import com.lowagie.text.pdf.FontMapper; import com.lowagie.text.pdf.PdfContentByte; import com.lowagie.text.pdf.PdfTemplate; import com.lowagie.text.pdf.PdfWriter; /** * Renders ICanvasContext instances into PDF documents. * * @author Tuukka Lehtonen */ public class PDFBuilder { private final PdfWriter writer; private final FontMapper mapper; private final Rectangle pageSize; private final PageDesc pageDesc; private final boolean fitDiagramContentsToPageMargins; /** * @param writer * @param mapper * @param pageSize * @param pageDesc * @param fitDiagramContentsToPageMargins see * {@link PDFExportPlan#fitContentToPageMargins} for a description * of this parameter */ public PDFBuilder(PdfWriter writer, FontMapper mapper, Rectangle pageSize, PageDesc pageDesc, boolean fitDiagramContentsToPageMargins) { assert writer != null; assert mapper != null; assert pageSize != null; assert pageDesc != null; this.writer = writer; this.mapper = mapper; this.pageSize = pageSize; this.pageDesc = pageDesc; this.fitDiagramContentsToPageMargins = fitDiagramContentsToPageMargins; } /** * @param canvasContext the canvas context to paint * @param writeResults true to actually write the resulting PDF * content into the {@link PdfWriter} used to construct this * PDFBuilder */ public void paint(ICanvasContext canvasContext, boolean writeResults) { // Specify rendering template size in points. float pw = pageSize.getWidth(); float ph = pageSize.getHeight(); PdfContentByte cb = writer.getDirectContent(); PdfTemplate tp = cb.createTemplate(pw, ph); Graphics2D g2 = tp.createGraphics(pw, ph, mapper); g2.setRenderingHint(G2DPDFRenderingHints.KEY_PDF_WRITER, tp.getPdfWriter()); g2.setRenderingHint(G2DPDFRenderingHints.KEY_PDF_BYTECONTENT, tp); g2.setRenderingHint(G2DPDFRenderingHints.KEY_PDF_FONTMAPPER, mapper); double w = pageDesc.getOrientedWidth(); double h = pageDesc.getOrientedHeight(); //System.out.println("PDFBuilder.paint: page in millimeters " + w + " x " + h + ", page in points " + pw + " x " + ph); try { QualityHints.HIGH_QUALITY_HINTS.setQuality(g2); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); // Make sure the transformation is reset. AffineTransform tr = new AffineTransform(); Margins m = pageDesc.getMargins(); double mw = pageDesc.getOrientedWidth() - m.left.diagramAbsolute - m.right.diagramAbsolute; double mh = pageDesc.getOrientedHeight() - m.top.diagramAbsolute - m.bottom.diagramAbsolute; Rectangle2D controlArea = new Rectangle2D.Double(m.left.diagramAbsolute, m.top.diagramAbsolute, mw, mh); if (fitDiagramContentsToPageMargins) { IDiagram diagram = canvasContext.getDefaultHintContext().getHint(DiagramHints.KEY_DIAGRAM); final Rectangle2D diagramRect = DiagramUtils.getContentRect(diagram); if (diagramRect != null) { canvasContext.getSingleItem(TransformUtil.class).fitArea(controlArea, diagramRect, MarginUtils.NO_MARGINS); } } // We render in millimeters, but iText expects coordinates in points. // So we scale everything to point space. //System.out.println("PDFBuilder.paint: input transform " + tr); tr.scale(pw / w, ph / h); //System.out.println("PDFBuilder.paint: point space scaled transform " + tr); g2.setTransform(tr); //g2.setClip(new Rectangle2D.Double(0, 0, w, h)); // if (paintMargin) { // g2.setStroke(new BasicStroke()); // g2.setColor(Color.DARK_GRAY); // g2.draw(new Rectangle2D.Double(pageSize.getBorderWidthLeft(), pageSize.getBorderWidthTop(), ww, hh)); // } // DEBUG: Paint page edges // g2.setStroke(new BasicStroke(2)); // g2.setColor(Color.MAGENTA); // g2.draw(new Rectangle2D.Double(pageDesc.getLeftEdgePos(), pageDesc.getTopEdgePos(), pageDesc.getOrientedWidth(), pageDesc.getOrientedHeight())); // DEBUG: paint content area, i.e. the bounds that were fitted to // meet the printed page margins. // g2.setColor(new Color(255, 255, 0, 128)); // g2.draw(controlArea); // TODO: test that this doesn't break anything. g2.setRenderingHint(G2DRenderingHints.KEY_CONTROL_BOUNDS, new Rectangle2D.Double(0, 0, w, h)); if (canvasContext.isLocked()) throw new IllegalStateException("cannot render PDF, canvas context is locked: " + canvasContext); canvasContext.getSceneGraph().render(g2); // Code from iText examples: // double ew = w/2; // double eh = h/2; // Ellipse2D.Double circle, oval, leaf, stem; // Area circ, ov, leaf1, leaf2, st1, st2; // circle = new Ellipse2D.Double(); // oval = new Ellipse2D.Double(); // leaf = new Ellipse2D.Double(); // stem = new Ellipse2D.Double(); // circ = new Area(circle); // ov = new Area(oval); // leaf1 = new Area(leaf); // leaf2 = new Area(leaf); // st1 = new Area(stem); // st2 = new Area(stem); // g2.setColor(Color.green); // // // Creates the first leaf by filling the intersection of two Area objects created from an ellipse. // leaf.setFrame(ew-16, eh-29, 15.0, 15.0); // leaf1 = new Area(leaf); // leaf.setFrame(ew-14, eh-47, 30.0, 30.0); // leaf2 = new Area(leaf); // leaf1.intersect(leaf2); // g2.fill(leaf1); // // // Creates the second leaf. // leaf.setFrame(ew+1, eh-29, 15.0, 15.0); // leaf1 = new Area(leaf); // leaf2.intersect(leaf1); // g2.fill(leaf2); // // g2.setColor(Color.black); // // // Creates the stem by filling the Area resulting from the subtraction of two Area objects created from an ellipse. // stem.setFrame(ew, eh-42, 40.0, 40.0); // st1 = new Area(stem); // stem.setFrame(ew+3, eh-47, 50.0, 50.0); // st2 = new Area(stem); // st1.subtract(st2); // g2.fill(st1); // // g2.setColor(Color.yellow); // // // Creates the pear itself by filling the Area resulting from the union of two Area objects created by two different ellipses. // circle.setFrame(ew-25, eh, 50.0, 50.0); // oval.setFrame(ew-19, eh-20, 40.0, 70.0); // circ = new Area(circle); // ov = new Area(oval); // circ.add(ov); // g2.fill(circ); } finally { g2.dispose(); if (writeResults) { //System.out.println("Storing PDF template"); float x = (float) pageDesc.getLeftEdgePos(); float y = (float) pageDesc.getTopEdgePos(); cb.addTemplate(tp, x, y); } } } }