001package conexp.fx.core.exporter; 002 003/* 004 * #%L 005 * Concept Explorer FX 006 * %% 007 * Copyright (C) 2010 - 2023 Francesco Kriegel 008 * %% 009 * This program is free software: you can redistribute it and/or modify 010 * it under the terms of the GNU General Public License as 011 * published by the Free Software Foundation, either version 3 of the 012 * License, or (at your option) any later version. 013 * 014 * This program is distributed in the hope that it will be useful, 015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 017 * GNU General Public License for more details. 018 * 019 * You should have received a copy of the GNU General Public 020 * License along with this program. If not, see 021 * <http://www.gnu.org/licenses/gpl-3.0.html>. 022 * #L% 023 */ 024 025import java.io.File; 026import java.io.FileOutputStream; 027import java.io.IOException; 028import java.util.Map; 029 030import javax.xml.transform.Result; 031import javax.xml.transform.Source; 032import javax.xml.transform.Transformer; 033import javax.xml.transform.TransformerException; 034import javax.xml.transform.TransformerFactory; 035import javax.xml.transform.TransformerFactoryConfigurationError; 036import javax.xml.transform.dom.DOMSource; 037import javax.xml.transform.stream.StreamResult; 038 039import org.apache.batik.dom.svg.SVGDOMImplementation; 040import org.w3c.dom.DOMImplementation; 041import org.w3c.dom.Document; 042import org.w3c.dom.Element; 043 044import conexp.fx.core.context.Concept; 045import conexp.fx.core.context.MatrixContext; 046import conexp.fx.core.layout.AdditiveConceptLayout; 047 048public class SVGExporter<G, M> { 049 050 public static <G, M> void export( 051 MatrixContext<G, M> context, 052 Map<Integer, Integer> domainPermutation, 053 Map<Integer, Integer> codomainPermutation, 054 AdditiveConceptLayout<G, M> layout, 055 boolean exportArrows, 056 boolean exportLabels, 057 File file) { 058 try { 059 if (!file.exists()) { 060 if (!file.getParentFile().exists()) 061 file.mkdirs(); 062 file.createNewFile(); 063 } 064 Document doc = toSVGDocument( 065 file.getName(), 066 context, 067 domainPermutation, 068 codomainPermutation, 069 layout, 070 exportArrows, 071 exportLabels); 072 Transformer transformer = TransformerFactory.newInstance().newTransformer(); 073 Result output = new StreamResult(new FileOutputStream(file)); 074 Source input = new DOMSource(doc); 075 transformer.transform(input, output); 076 } catch (TransformerFactoryConfigurationError | TransformerException | IOException e) { 077 System.err.println("Unable to create or write SVGDocument to file " + file); 078 e.printStackTrace(); 079 } 080 } 081 082 private static <G, M> Document toSVGDocument( 083 String name, 084 MatrixContext<G, M> formalContext, 085 Map<Integer, Integer> domainPermutation, 086 Map<Integer, Integer> codomainPermutation, 087 AdditiveConceptLayout<G, M> layout, 088 boolean exportArrows, 089 boolean exportLabels) { 090 final double width = 100d * layout.getCurrentBoundingBox(false, false).getWidth(); 091 final double minX = 100d * layout.getCurrentBoundingBox(false, false).getMinX(); 092 final double height = 100d * layout.getCurrentBoundingBox(false, false).getHeight(); 093 // final double unit = Math.min(160 / width, 230 / height); 094 final int border = 100; 095 final int circleSize = 16; 096 final int textOffset = 12; 097 DOMImplementation impl = SVGDOMImplementation.getDOMImplementation(); 098 String svgNS = SVGDOMImplementation.SVG_NAMESPACE_URI; 099 Document doc = impl.createDocument(svgNS, "svg", null); 100 Element svgRoot = doc.getDocumentElement(); 101 svgRoot.setAttributeNS(null, "width", String.valueOf((int) width + border)); 102 svgRoot.setAttributeNS(null, "height", String.valueOf((int) height + border)); 103 for (int i = 0; i < layout.lattice.rowHeads().size(); i++) { 104 for (int j = 0; j < layout.lattice.rowHeads().size(); j++) { 105 if (layout.lattice._contains(i, j)) { 106 final int x1 = (int) (100d * layout.getPosition(layout.lattice.rowHeads().get(i)).getValue().getX() 107 - minX + (border / 2)); 108 final int y1 = (int) (100d * layout.getPosition(layout.lattice.rowHeads().get(i)).getValue().getY() 109 + (border / 2)); 110 final int x2 = (int) (100d * layout.getPosition(layout.lattice.rowHeads().get(j)).getValue().getX() 111 - minX + (border / 2)); 112 final int y2 = (int) (100d * layout.getPosition(layout.lattice.rowHeads().get(j)).getValue().getY() 113 + (border / 2)); 114 Element line = doc.createElementNS(svgNS, "line"); 115 line.setAttributeNS(null, "x1", String.valueOf(x1)); 116 line.setAttributeNS(null, "y1", String.valueOf(y1)); 117 line.setAttributeNS(null, "x2", String.valueOf(x2)); 118 line.setAttributeNS(null, "y2", String.valueOf(y2)); 119 line.setAttributeNS(null, "stroke-width", "4"); 120 line.setAttributeNS(null, "stroke", "grey"); 121 svgRoot.appendChild(line); 122 } 123 } 124 } 125 for (int i = 0; i < layout.lattice.rowHeads().size(); i++) { 126 Concept<G, M> conceptNode = layout.lattice.rowHeads().get(i); 127 final double x = 100d * layout.getPosition(conceptNode).getValue().getX(); 128 final double y = 100d * layout.getPosition(conceptNode).getValue().getY(); 129 Element circle = doc.createElementNS(svgNS, "circle"); 130 circle.setAttributeNS(null, "cx", String.valueOf((int) (x - minX) + (border / 2))); 131 circle.setAttributeNS(null, "cy", String.valueOf((int) y + (border / 2))); 132 circle.setAttributeNS(null, "r", String.valueOf(circleSize)); 133 svgRoot.appendChild(circle); 134 } 135 if (exportLabels) 136 for (int i = 0; i < layout.lattice.rowHeads().size(); i++) { 137 Concept<G, M> conceptNode = layout.lattice.rowHeads().get(i); 138 String objLabels = layout.lattice.objectLabels(conceptNode).toString().substring( 139 1, 140 layout.lattice.objectLabels(conceptNode).toString().length() - 1); 141 String attLabels = layout.lattice.attributeLabels(conceptNode).toString().substring( 142 1, 143 layout.lattice.attributeLabels(conceptNode).toString().length() - 1); 144 final int x = 145 (int) (100d * layout.getPosition(layout.lattice.rowHeads().get(i)).getValue().getX() - minX); 146 final int y = (int) (100d * layout.getPosition(layout.lattice.rowHeads().get(i)).getValue().getY()); 147 final int ox = x + (border / 2); 148 final int oy = y + (circleSize + textOffset) + (border / 2) + 10; 149 final int ax = x + (border / 2); 150 final int ay = y - (circleSize + textOffset) + (border / 2); 151 Element objText = doc.createElementNS(svgNS, "text"); 152 objText.setAttributeNS(null, "x", String.valueOf(ox)); 153 objText.setAttributeNS(null, "y", String.valueOf(oy)); 154 objText.setAttributeNS(null, "text-anchor", "middle"); 155 objText.setTextContent(objLabels); 156 svgRoot.appendChild(objText); 157 Element attText = doc.createElementNS(svgNS, "text"); 158 attText.setAttributeNS(null, "x", String.valueOf(ax)); 159 attText.setAttributeNS(null, "y", String.valueOf(ay)); 160 attText.setAttributeNS(null, "text-anchor", "middle"); 161 attText.setTextContent(attLabels); 162 svgRoot.appendChild(attText); 163 } 164 return doc; 165 } 166}