1 package org.simantics.diagram.export;
\r
3 import java.awt.Graphics2D;
\r
4 import java.awt.RenderingHints;
\r
5 import java.awt.geom.Rectangle2D;
\r
6 import java.util.Collection;
\r
7 import java.util.Collections;
\r
8 import java.util.List;
\r
10 import org.eclipse.core.runtime.IProgressMonitor;
\r
11 import org.eclipse.core.runtime.OperationCanceledException;
\r
12 import org.osgi.service.prefs.Preferences;
\r
13 import org.simantics.databoard.Accessors;
\r
14 import org.simantics.databoard.accessor.RecordAccessor;
\r
15 import org.simantics.databoard.accessor.UnionAccessor;
\r
16 import org.simantics.databoard.accessor.error.AccessorConstructionException;
\r
17 import org.simantics.databoard.accessor.error.AccessorException;
\r
18 import org.simantics.databoard.accessor.reference.ChildReference;
\r
19 import org.simantics.databoard.accessor.reference.LabelReference;
\r
20 import org.simantics.databoard.binding.mutable.Variant;
\r
21 import org.simantics.databoard.type.RecordType;
\r
22 import org.simantics.databoard.type.UnionType;
\r
23 import org.simantics.db.Resource;
\r
24 import org.simantics.db.common.ResourceArray;
\r
25 import org.simantics.db.common.primitiverequest.SingleObject;
\r
26 import org.simantics.db.common.request.Queries;
\r
27 import org.simantics.db.exception.DatabaseException;
\r
28 import org.simantics.db.layer0.request.PossibleModel;
\r
29 import org.simantics.db.layer0.util.SessionGarbageCollection;
\r
30 import org.simantics.diagram.elements.DiagramNodeUtil;
\r
31 import org.simantics.diagram.query.DiagramRequests;
\r
32 import org.simantics.diagram.stubs.DiagramResource;
\r
33 import org.simantics.export.core.ExportContext;
\r
34 import org.simantics.export.core.error.ExportException;
\r
35 import org.simantics.export.core.intf.ExportClass;
\r
36 import org.simantics.export.core.manager.Content;
\r
37 import org.simantics.export.core.pdf.ExportPdfWriter;
\r
38 import org.simantics.export.core.pdf.ExportPdfWriter.Page;
\r
39 import org.simantics.export.core.util.ExportQueries;
\r
40 import org.simantics.export.core.util.ExporterUtils;
\r
41 import org.simantics.g2d.canvas.Hints;
\r
42 import org.simantics.g2d.canvas.ICanvasContext;
\r
43 import org.simantics.g2d.canvas.impl.CanvasContext;
\r
44 import org.simantics.g2d.diagram.DiagramHints;
\r
45 import org.simantics.g2d.diagram.DiagramUtils;
\r
46 import org.simantics.g2d.diagram.IDiagram;
\r
47 import org.simantics.g2d.participant.TransformUtil;
\r
48 import org.simantics.g2d.scenegraph.ICanvasSceneGraphProvider;
\r
49 import org.simantics.layer0.Layer0;
\r
50 import org.simantics.modeling.ModelingResources;
\r
51 import org.simantics.modeling.template2d.ontology.Template2dResource;
\r
52 import org.simantics.scenegraph.g2d.G2DPDFRenderingHints;
\r
53 import org.simantics.scenegraph.g2d.G2DRenderingHints;
\r
54 import org.simantics.scenegraph.utils.QualityHints;
\r
55 import org.simantics.simulation.ontology.SimulationResource;
\r
56 import org.simantics.structural.stubs.StructuralResource2;
\r
57 import org.simantics.structural2.StructuralVariables;
\r
58 import org.simantics.utils.datastructures.MapList;
\r
59 import org.simantics.utils.page.MarginUtils;
\r
60 import org.simantics.utils.page.MarginUtils.Margins;
\r
61 import org.simantics.utils.page.PageDesc;
\r
62 import org.simantics.utils.threads.ThreadUtils;
\r
63 import org.simantics.utils.threads.WorkerThread;
\r
65 import com.kitfox.svg.SVGCache;
\r
67 public class ExportDiagramPdf implements ExportClass {
\r
69 public static LabelReference P_DIAGRAM_OPTIONS = new LabelReference("Diagram Options");
\r
70 public static String S_CONTENT_FIT = "Content Fit";
\r
71 public static ChildReference P_CONTENT_FIT = ChildReference.parsePath("Diagram Options/"+S_CONTENT_FIT);
\r
72 public static String S_PAGE_SIZE = "Page Size";
\r
73 public static ChildReference P_PAGE_SIZE = ChildReference.parsePath("Diagram Options/"+S_PAGE_SIZE);
\r
75 // Diagram export options
\r
76 RecordType deo, options;
\r
77 UnionType contentUt, pageUt;
\r
79 public ExportDiagramPdf() {
\r
80 options = new RecordType();
\r
81 deo = new RecordType();
\r
82 contentUt = UnionType.newEnum("Use the diagram specific borders", "Fit page by its contents");
\r
83 pageUt = UnionType.newEnum("Use the page size from this wizard", "Use diagram specific page sizes");
\r
84 deo.addComponent(S_CONTENT_FIT, contentUt);
\r
85 deo.addComponent(S_PAGE_SIZE, pageUt);
\r
86 options.addComponent(P_DIAGRAM_OPTIONS.label, deo);
\r
90 public RecordType options(ExportContext context, Collection<String> content) throws ExportException {
\r
95 public void fillDefaultPrefs(ExportContext ctx, Variant options) throws ExportException {
\r
97 RecordAccessor ra = Accessors.getAccessor(options);
\r
98 ExporterUtils.setUnionValue(ra, P_CONTENT_FIT, 0);
\r
99 ExporterUtils.setUnionValue(ra, P_PAGE_SIZE, 1);
\r
100 } catch (AccessorConstructionException e) {
\r
105 public void export(List<Content> contents,
\r
107 ExportContext ctx,
\r
109 IProgressMonitor monitor,
\r
110 MapList<Content, Content> attachmentMap
\r
111 ) throws ExportException {
\r
113 final ExportPdfWriter writer = (ExportPdfWriter) handle;
\r
115 WorkerThread workerThread = new WorkerThread("Diagram PDF Painter");
\r
116 workerThread.start();
\r
120 ModelingResources MOD = ModelingResources.getInstance( ctx.session );
\r
121 SimulationResource SIMU = SimulationResource.getInstance( ctx.session );
\r
122 StructuralResource2 STR = StructuralResource2.getInstance( ctx.session );
\r
123 DiagramResource DIA = DiagramResource.getInstance( ctx.session );
\r
124 final Template2dResource TMPL = Template2dResource.getInstance( ctx.session );
\r
125 Layer0 L0 = Layer0.getInstance( ctx.session );
\r
127 for ( Content content : contents ) {
\r
128 if (monitor.isCanceled())
\r
129 throw new OperationCanceledException();
\r
131 // Diagram's composite resource
\r
132 Resource resource = ctx.session.syncRequest( ExportQueries.toResource(content.url) );
\r
133 boolean isComposite = ctx.session.syncRequest( Queries.isInstanceOf(resource, STR.Composite) );
\r
134 if ( !isComposite ) {
\r
135 resource = ctx.session.syncRequest( Queries.possibleObjectWithType(resource, L0.ConsistsOf, STR.Composite) );
\r
136 isComposite = ctx.session.syncRequest( Queries.isInstanceOf(resource, STR.Composite) );
\r
138 if ( !isComposite ) {
\r
139 throw new ExportException(content.url+" doesnt contain a diagram.");
\r
141 final Resource composite = resource;
\r
142 final Resource diagram = ctx.session.syncRequest( new SingleObject( composite, MOD.CompositeToDiagram ) );
\r
143 final Resource drawingTemplate = ctx.session.syncRequest( Queries.possibleObject( diagram, TMPL.HasDrawingTemplate ) );
\r
144 final Resource activeProfile = ctx.session.syncRequest( Queries.possibleObject( diagram, DIA.HasActiveProfile ) );
\r
145 final Collection<Resource> activeProfileEntries = activeProfile != null ? ctx.session.syncRequest( Queries.objects(activeProfile, SIMU.IsActive) ) : Collections.<Resource>emptyList();
\r
146 final Resource model = ctx.session.syncRequest( new PossibleModel( composite ) );
\r
147 if ( model==null ) throw new ExportException("Cannot export diagram. Model was not found.");
\r
148 final String diagramName = content.label;
\r
150 ResourceArray compositePath = StructuralVariables.getCompositeArray(ctx.session, composite);
\r
151 ResourceArray variablePath = compositePath.removeFromBeginning(1);
\r
152 final String modelRVI = StructuralVariables.getRVI(ctx.session, variablePath);
\r
154 // PageDesc, Read page desc from the graph
\r
155 int tag = getPageFitArea(options);
\r
156 PageDesc diagramPageDesc = ctx.session.syncRequest( DiagramRequests.getPageDesc(diagram, writer.defaultPageDesc));
\r
157 PageDesc wizardPageDesc = writer.defaultPageDesc;
\r
158 PageDesc marginaaliViiva = diagramPageDesc;
\r
160 // Close previous page before starting a new one.
\r
161 if (page != null) {
\r
167 // top,left margin translation is applied to G2D on createGraphics(..)
\r
168 page = writer.createPage( wizardPageDesc );
\r
169 marginaaliViiva = diagramPageDesc;
\r
171 marginaaliViiva = diagramPageDesc;
\r
172 page = writer.createPage( diagramPageDesc );
\r
174 final PageDesc _marginaaliViiva = marginaaliViiva;
\r
175 final PageDesc _diagramPageDesc = diagramPageDesc;
\r
176 final Page _page = page;
\r
177 final boolean fitDiagramContentsToPageMargins = getContentFitArea(options) == 1;
\r
179 final CanvasContext cctx = new CanvasContext( workerThread );
\r
180 final Exception[] errors = new Exception[1];
\r
181 final ICanvasSceneGraphProvider provider = DiagramNodeUtil.loadSceneGraphProvider(cctx, model, diagram, modelRVI);
\r
183 ThreadUtils.syncExec(workerThread, new Runnable() {
\r
185 public void run() {
\r
187 cctx.getDefaultHintContext().setHint(Hints.KEY_PAGE_DESC, _marginaaliViiva);
\r
189 String bottomLabel = diagramName;
\r
190 if ( drawingTemplate != null && activeProfileEntries.contains(TMPL.DrawingTemplate) ) bottomLabel = null;
\r
192 paint(cctx, writer, _page, fitDiagramContentsToPageMargins, bottomLabel, _diagramPageDesc);
\r
193 // } catch (DatabaseException e) {
\r
195 // } catch (InterruptedException e) {
\r
198 provider.dispose();
\r
203 if ( errors[0] != null ) {
\r
204 throw new ExportException( errors[0] );
\r
208 // Add page specific attachments
\r
209 if ( attachmentMap!=null ) {
\r
210 List<Content> ats = attachmentMap.getValues(content);
\r
211 if ( ats != null ) for ( Content at : ats ) page.addAttachment(at);
\r
215 // These still have to be done to keep memory consumption down
\r
217 SVGCache.getSVGUniverse().clearUnreferenced();
\r
218 SessionGarbageCollection.gc(null, writer.ctx.session, true, null);
\r
221 } catch (DatabaseException e) {
\r
222 throw new ExportException( e.getClass().getName()+": "+e.getMessage(), e );
\r
223 } catch (InterruptedException e) {
\r
224 throw new ExportException( e.getClass().getName()+": "+e.getMessage(), e );
\r
226 if ( workerThread!=null ) workerThread.stopDispatchingEvents(true);
\r
227 if ( page!=null ) page.close();
\r
233 public void paint(ICanvasContext canvasContext, ExportPdfWriter writer, Page page, boolean fitDiagramContentsToPageMargins, String diagramName, PageDesc diagramPageDesc) {
\r
234 // Specify rendering template size in points.
\r
235 Graphics2D g2 = page.createGraphics(false);
\r
237 QualityHints.HIGH_QUALITY_HINTS.setQuality(g2);
\r
238 g2.setRenderingHint(G2DPDFRenderingHints.KEY_EXPORT_PDF_WRITER, writer);
\r
239 g2.setRenderingHint(G2DPDFRenderingHints.KEY_PDF_WRITER, writer.pdfCopy);
\r
240 g2.setRenderingHint(G2DPDFRenderingHints.KEY_PDF_BYTECONTENT, writer.cb);
\r
241 g2.setRenderingHint(G2DPDFRenderingHints.KEY_PDF_FONTMAPPER, writer.fontMapper);
\r
242 g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
\r
245 double pw = page.pageDesc.getWidth();
\r
246 double ph = page.pageDesc.getHeight();
\r
247 // Drawable area size in mm
\r
248 double w = page.getWidth();
\r
249 double h = page.getHeight();
\r
251 // Get margins in mm
\r
252 if (fitDiagramContentsToPageMargins) {
\r
253 Rectangle2D controlArea = new Rectangle2D.Double(0, 0, w, h);
\r
254 IDiagram diagram = canvasContext.getDefaultHintContext().getHint(DiagramHints.KEY_DIAGRAM);
\r
255 final Rectangle2D diagramRect = DiagramUtils.getContentRect(diagram);
\r
256 if (diagramRect != null) {
\r
257 canvasContext.getSingleItem(TransformUtil.class).fitArea(controlArea, diagramRect, MarginUtils.NO_MARGINS);
\r
262 // Margin differences
\r
263 Margins diagramMargins = diagramPageDesc.getMargins();
\r
264 Margins wizardMargins = page.pageDesc.getMargins();
\r
266 g2.translate( wizardMargins.left.diagramAbsolute, wizardMargins.top.diagramAbsolute);
\r
268 double diagramPageWidth = diagramPageDesc.getOrientedWidth();
\r
269 double diagramPageHeight = diagramPageDesc.getOrientedHeight();
\r
271 double wizardPageWidth = page.pageDesc.getOrientedWidth();
\r
272 double wizardPageHeight = page.pageDesc.getOrientedHeight();
\r
274 double diagramContentWidth = diagramPageDesc.getOrientedWidth() - diagramMargins.left.diagramAbsolute - diagramMargins.right.diagramAbsolute;
\r
275 double diagramContentHeight = diagramPageDesc.getOrientedHeight() - diagramMargins.top.diagramAbsolute - diagramMargins.bottom.diagramAbsolute;
\r
277 double wizardContentWidth = page.pageDesc.getOrientedWidth() - wizardMargins.left.diagramAbsolute - wizardMargins.right.diagramAbsolute;
\r
278 double wizardContentHeight = page.pageDesc.getOrientedHeight() - wizardMargins.top.diagramAbsolute - wizardMargins.bottom.diagramAbsolute;
\r
280 if ( diagramContentWidth!=wizardContentWidth || diagramContentHeight!=wizardContentHeight ) {
\r
281 double r1 = wizardContentWidth / diagramContentWidth;
\r
282 double r2 = wizardContentHeight / diagramContentHeight;
\r
283 double r = Math.min(r1, r2);
\r
285 g2.translate(0, wizardContentHeight/2);
\r
287 g2.translate( wizardContentWidth/2, 0);
\r
291 g2.translate(0, -diagramContentHeight/ 2);
\r
293 g2.translate(-diagramContentWidth/ 2, 0);
\r
296 g2.translate( -diagramMargins.left.diagramAbsolute, -diagramMargins.top.diagramAbsolute);
\r
297 g2.setRenderingHint(G2DRenderingHints.KEY_CONTROL_BOUNDS, new Rectangle2D.Double(0, 0, w, h));
\r
299 if (canvasContext.isLocked())
\r
300 throw new IllegalStateException("cannot render PDF, canvas context is locked: " + canvasContext);
\r
302 canvasContext.getSceneGraph().render(g2);
\r
304 // // Write diagram name
\r
305 // if ( diagramName != null ) {
\r
306 // g2.setColor(Color.black);
\r
307 // java.awt.Font arial = new java.awt.Font("Arial", java.awt.Font.ITALIC, 3);
\r
308 // g2.setFont(arial);
\r
309 // FontMetrics metrics = g2.getFontMetrics();
\r
310 // int width = metrics.stringWidth(diagramName);
\r
311 //// System.out.println(diagramName + ", w: " + w + ", width: " + width);
\r
312 //// System.out.println(diagramName + ", wizardPageHeight: " + wizardPageHeight + ", wizardMargins: " + wizardMargins);
\r
313 //// System.out.println(PageDesc.toPoints(1));
\r
314 // float x = (float) ((w - width) / 2);
\r
315 // float y = (float) (wizardPageHeight - wizardMargins.bottom.diagramAbsolute - PageDesc.toPoints(1));
\r
316 // g2.drawString(diagramName, x, y);
\r
317 //// System.out.println(diagramName + ", X: " + x + ", Y: " + y);
\r
327 public void savePref(Variant options, Preferences contentScopeNode, Preferences workbenchScopeNode) throws ExportException {
\r
328 int tag = getContentFitArea(options);
\r
329 if ( tag>=0 ) contentScopeNode.putInt(S_CONTENT_FIT, tag);
\r
331 tag = getPageFitArea(options);
\r
332 if ( tag>=0 ) contentScopeNode.putInt(S_PAGE_SIZE, tag);
\r
336 public void loadPref(Variant options, Preferences contentScopeNode, Preferences workbenchScopeNode) throws ExportException {
\r
338 RecordAccessor ra = Accessors.getAccessor(options);
\r
340 int tag = contentScopeNode.getInt(S_CONTENT_FIT, -1);
\r
341 if ( tag>=0 ) ExporterUtils.setUnionValue(ra, P_CONTENT_FIT, tag);
\r
343 tag = contentScopeNode.getInt(S_PAGE_SIZE, -1);
\r
344 if ( tag>=0 ) ExporterUtils.setUnionValue(ra, P_PAGE_SIZE, tag);
\r
346 } catch (AccessorConstructionException e) {
\r
351 public List<String> validate(String contentUri, ExportContext context, Variant options) {
\r
352 return Collections.emptyList();
\r
355 public static int getContentFitArea(Variant options) {
\r
357 RecordAccessor ra = Accessors.getAccessor(options);
\r
358 UnionAccessor ua = ra.getComponent(P_CONTENT_FIT);
\r
359 return ua.getTag();
\r
360 } catch (AccessorConstructionException e) {
\r
362 } catch (AccessorException e) {
\r
367 public static int getPageFitArea(Variant options) {
\r
369 RecordAccessor ra = Accessors.getAccessor(options);
\r
370 UnionAccessor ua = ra.getComponent(P_PAGE_SIZE);
\r
371 return ua.getTag();
\r
372 } catch (AccessorConstructionException e) {
\r
374 } catch (AccessorException e) {
\r