1 /*******************************************************************************
2 * Copyright (c) 2007, 2017 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 * Semantum Oy - (#7084) refactoring
12 *******************************************************************************/
13 package org.simantics.modeling.ui.pdf;
15 import java.util.concurrent.Semaphore;
17 import org.simantics.db.ReadGraph;
18 import org.simantics.db.Resource;
19 import org.simantics.db.Session;
20 import org.simantics.db.common.request.PossibleIndexRoot;
21 import org.simantics.db.common.request.UniqueRead;
22 import org.simantics.db.common.utils.NameUtils;
23 import org.simantics.db.exception.DatabaseException;
24 import org.simantics.db.exception.ValidationException;
25 import org.simantics.db.layer0.variable.Variable;
26 import org.simantics.db.layer0.variable.Variables;
27 import org.simantics.db.request.Read;
28 import org.simantics.diagram.elements.DiagramNodeUtil;
29 import org.simantics.diagram.stubs.DiagramResource;
30 import org.simantics.g2d.canvas.Hints;
31 import org.simantics.g2d.canvas.impl.CanvasContext;
32 import org.simantics.g2d.scenegraph.ICanvasSceneGraphProvider;
33 import org.simantics.modeling.requests.Node;
34 import org.simantics.structural.stubs.StructuralResource2;
35 import org.simantics.utils.datastructures.Pair;
36 import org.simantics.utils.page.PageDesc;
37 import org.simantics.utils.threads.IThreadWorkQueue;
38 import org.simantics.utils.threads.ThreadUtils;
40 import com.lowagie.text.Rectangle;
41 import com.lowagie.text.pdf.FontMapper;
42 import com.lowagie.text.pdf.PdfWriter;
45 * Rasterizes diagram into a PDF document using the com.lowagie.text plug-in.
47 * @author Tuukka Lehtonen
49 public class PDFPainter {
51 public static void render(
52 final IThreadWorkQueue thread,
53 PDFExportPlan exportModel,
55 final PdfWriter writer,
56 final FontMapper mapper,
57 final Rectangle pageSize,
58 final PageDesc pageDesc,
59 final boolean fitDiagramContentsToPageMargins,
61 throws InterruptedException, DatabaseException
63 DatabaseException[] exception = { null };
64 ICanvasSceneGraphProvider[] sgProvider = { null };
66 CanvasContext ctx = new CanvasContext(thread);
67 ctx.getDefaultHintContext().setHint(Hints.KEY_DISABLE_GRAPH_MODIFICATIONS, Boolean.TRUE);
70 final Semaphore done = new Semaphore(0);
71 // IMPORTANT: Load diagram in a different thread than the canvas context thread!
72 ThreadUtils.getBlockingWorkExecutor().execute(() -> {
74 Session s = exportModel.sessionContext.getSession();
76 Pair<Resource, String> modelAndRVI = s.syncRequest( modelAndRVI(node) );
77 Boolean isSymbol = s.syncRequest( isSymbol(node) );
79 ICanvasSceneGraphProvider provider = DiagramNodeUtil.loadSceneGraphProvider(
82 node.getDiagramResource(),
85 sgProvider[0] = provider;
86 ctx.getDefaultHintContext().setHint(Hints.KEY_PAGE_DESC, pageDesc);
88 // System.err.println(NodeUtil.printTreeNodes(ctx.getCanvasNode(), new StringBuilder()).toString());
90 ThreadUtils.asyncExec(thread, () -> {
92 boolean fitToContent = fitDiagramContentsToPageMargins || isSymbol;
94 // Prevent PDF printing from drawing page borders if the
95 // print area is fitted directly to the page size.
96 // This avoids unwanted black half-visible edges.
97 ctx.getDefaultHintContext().setHint(Hints.KEY_DISPLAY_PAGE, false);
100 PDFBuilder chassis = new PDFBuilder(writer, mapper, pageSize, pageDesc, fitToContent);
101 chassis.paint(ctx, true);
102 } catch (Throwable e) {
103 exception[0] = new DatabaseException(e);
108 } catch (DatabaseException e) {
111 } catch (Throwable e) {
113 exception[0] = new DatabaseException(e);
120 if (exception[0] != null)
123 if (sgProvider[0] != null)
124 sgProvider[0].dispose();
129 private static Read<Pair<Resource, String>> modelAndRVI(Node node) {
130 return new UniqueRead<Pair<Resource, String>>() {
132 public Pair<Resource, String> perform(ReadGraph graph) throws DatabaseException {
133 return Pair.make( resolveModel(graph, node), resolveRVI(graph, node) );
138 private static Read<Boolean> isSymbol(Node node) {
139 return new UniqueRead<Boolean>() {
141 public Boolean perform(ReadGraph graph) throws DatabaseException {
142 StructuralResource2 STR = StructuralResource2.getInstance(graph);
143 DiagramResource DIA = DiagramResource.getInstance(graph);
144 Resource possibleSymbol = graph.getPossibleObject(node.getDiagramResource(), STR.Defines);
145 return possibleSymbol != null && graph.isInstanceOf(possibleSymbol, DIA.ElementClass);
150 private static Resource resolveModel(ReadGraph graph, Node node) throws DatabaseException {
151 Resource composite = node.getDefiningResources().head();
152 Resource model = graph.syncRequest(new PossibleIndexRoot(composite));
154 throw new ValidationException("no model found for composite " + NameUtils.getSafeName(graph, composite));
158 private static String resolveRVI(ReadGraph graph, final Node node) throws DatabaseException {
159 String RVI = node.getRVI();
160 if (RVI != null) return RVI;
161 Resource composite = node.getDefiningResources().head();
162 Variable var = Variables.getVariable(graph, composite);
163 org.simantics.db.layer0.variable.RVI rvi = var.getPossibleRVI(graph);
164 return rvi != null ? rvi.toString() : null;