package org.simantics.diagram.svg.export; import java.awt.Point; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicReference; import org.apache.batik.svggen.SVGGraphics2D; import org.simantics.Simantics; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.common.ResourceArray; import org.simantics.db.common.request.PossibleIndexRoot; import org.simantics.db.common.request.UniqueRead; import org.simantics.db.common.utils.NameUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.db.exception.ValidationException; import org.simantics.db.management.ISessionContext; import org.simantics.db.request.Read; import org.simantics.diagram.elements.DiagramNodeUtil; import org.simantics.diagram.export.ImagePrinter; import org.simantics.diagram.stubs.DiagramResource; import org.simantics.g2d.canvas.impl.CanvasContext; import org.simantics.g2d.scenegraph.ICanvasSceneGraphProvider; import org.simantics.layer0.Layer0; import org.simantics.modeling.ModelingResources; import org.simantics.structural.stubs.StructuralResource2; import org.simantics.structural2.StructuralVariables; import org.simantics.utils.DataContainer; import org.simantics.utils.datastructures.Pair; import org.simantics.utils.page.MarginUtils.Margins; import org.simantics.utils.threads.ThreadUtils; import org.simantics.utils.threads.WorkerThread; public class DiagramToSVG { public static String diagramToSVG(Resource diagram) { try { ImagePrinter.ImageExportPlan exportPlan = new ImagePrinter.ImageExportPlan(); exportPlan.margin = 0.05; exportPlan.dpi = 96.0; exportPlan.diagram = diagram; return render(diagram,exportPlan); } catch (Exception e) { e.printStackTrace(); return "error "+ e.getMessage(); } } public static String diagramToSVG(Resource diagram, int width, int height) { try { ImagePrinter.ImageExportPlan exportPlan = new ImagePrinter.ImageExportPlan(); exportPlan.margin = 0.05; exportPlan.size = new Point(width, height); exportPlan.diagram = diagram; return render(diagram,exportPlan); } catch (Exception e) { e.printStackTrace(); return "error "+ e.getMessage(); } } public static String diagramToSVG(Resource diagram, double dpi) { try { ImagePrinter.ImageExportPlan exportPlan = new ImagePrinter.ImageExportPlan(); exportPlan.margin = 0.05; exportPlan.dpi = dpi; exportPlan.diagram = diagram; return render(diagram,exportPlan); } catch (Exception e) { e.printStackTrace(); return "error "+ e.getMessage(); } } public static String diagramToSVG(Resource diagram, ImagePrinter.ImageExportPlan exportPlan, Margins margins, SVGGraphics2D svgGenerator) { try { return render(diagram,exportPlan,margins,svgGenerator); } catch (Exception e) { e.printStackTrace(); return "error "+ e.getMessage(); } } private static String render(final Resource input, final ImagePrinter.ImageExportPlan exportPlan) throws Exception { return render(input, exportPlan, null, SVGBuilder.defaultSVGGenerator()); } private static String render(final Resource input, final ImagePrinter.ImageExportPlan exportPlan, Margins margins, SVGGraphics2D svgExporter) throws Exception { Resource diagram = Simantics.getSession().syncRequest(new Read() { @Override public Resource perform(ReadGraph graph) throws DatabaseException { DiagramResource DIA = DiagramResource.getInstance(graph); if (graph.isInstanceOf(input, DIA.Diagram)) return input; StructuralResource2 SR = StructuralResource2.getInstance(graph); ModelingResources MOD = ModelingResources.getInstance(graph); Layer0 L0 = Layer0.getInstance(graph); if (graph.isInstanceOf(input, SR.Composite)) { Resource possibleDiagram = graph.getPossibleObject(input, MOD.CompositeToDiagram); if (possibleDiagram != null) return possibleDiagram; for (Resource r : graph.getObjects(input, L0.ConsistsOf)) { if (graph.isInstanceOf(r, SR.Composite)) { possibleDiagram = graph.getPossibleObject(input, MOD.CompositeToDiagram); if (possibleDiagram != null) return possibleDiagram; } } } return null; } }); if (diagram == null) throw new DatabaseException("Input " + input + " cannot be resolved as diagram"); final WorkerThread thread = new WorkerThread("Diagram Image Painter"); thread.start(); final CanvasContext ctx = new CanvasContext(thread); final AtomicReference sgProvider = new AtomicReference(); final ISessionContext sessionContext = Simantics.getSessionContext(); final DataContainer result = new DataContainer(null); final DataContainer exception = new DataContainer(null); try { final Semaphore done = new Semaphore(0); // IMPORTANT: Load diagram in a different thread than the canvas context thread! ThreadUtils.getBlockingWorkExecutor().execute(new Runnable() { @Override public void run() { try { Pair modelAndRVI = sessionContext.getSession().syncRequest(new UniqueRead>() { @Override public Pair perform(ReadGraph graph) throws DatabaseException { return new Pair( resolveModel(graph, exportPlan.diagram ), resolveRVI(graph, exportPlan.diagram) ); } }); ICanvasSceneGraphProvider provider = DiagramNodeUtil.loadSceneGraphProvider(ctx, modelAndRVI.first, exportPlan.diagram, modelAndRVI.second); sgProvider.set( provider ); ThreadUtils.asyncExec(thread, new Runnable() { @Override public void run() { try { SVGBuilder chassis = margins != null ? new SVGBuilder(exportPlan.dpi,exportPlan.size,margins) : new SVGBuilder(exportPlan.dpi,exportPlan.size,exportPlan.margin); result.set(chassis.paint(ctx, svgExporter)); } catch (Exception e) { exception.set(e); } finally { done.release(); } } }); } catch (DatabaseException e) { exception.set(e); done.release(); } catch (Throwable e) { exception.set(new DatabaseException(e)); done.release(); } finally { done.release(); } } }); done.acquire(2); if (exception.get() != null) throw exception.get(); return result.get(); } finally { if (sgProvider.get() != null) sgProvider.get().dispose(); ctx.dispose(); } } private static Resource resolveModel(ReadGraph graph, Resource diagram) throws DatabaseException { ModelingResources mod = ModelingResources.getInstance(graph); Resource composite = graph.getSingleObject(diagram, mod.DiagramToComposite); Resource model = graph.syncRequest(new PossibleIndexRoot(composite)); if (model == null) throw new ValidationException("no model found for composite " + NameUtils.getSafeName(graph, composite)); return model; } private static String resolveRVI(ReadGraph graph, Resource diagram) throws DatabaseException { ModelingResources mod = ModelingResources.getInstance(graph); Resource composite = graph.getSingleObject(diagram, mod.DiagramToComposite); final ResourceArray compositePath = StructuralVariables.getCompositeArray(graph, composite); final ResourceArray variablePath = compositePath.removeFromBeginning(1); return StructuralVariables.getRVI(graph, variablePath); } }