From b77bf92c03d461eb7302b6a32b422e511fbc8d8a Mon Sep 17 00:00:00 2001 From: hneemann Date: Tue, 29 Mar 2016 21:38:49 +0200 Subject: [PATCH] added a simple SVG export --- .../digital/draw/elements/Circuit.java | 7 +- .../digital/draw/graphics/Exporter.java | 11 + .../digital/draw/graphics/Graphic.java | 8 + .../digital/draw/graphics/GraphicSVG.java | 212 ++++++++++++++++++ .../digital/draw/graphics/Polygon.java | 9 + .../java/de/neemann/digital/gui/Main.java | 55 +++++ src/main/resources/lang/lang_de.properties | 3 +- src/main/resources/lang/lang_en.properties | 2 + 8 files changed, 305 insertions(+), 2 deletions(-) create mode 100644 src/main/java/de/neemann/digital/draw/graphics/Exporter.java create mode 100644 src/main/java/de/neemann/digital/draw/graphics/GraphicSVG.java diff --git a/src/main/java/de/neemann/digital/draw/elements/Circuit.java b/src/main/java/de/neemann/digital/draw/elements/Circuit.java index a309ba8ef..740c78750 100644 --- a/src/main/java/de/neemann/digital/draw/elements/Circuit.java +++ b/src/main/java/de/neemann/digital/draw/elements/Circuit.java @@ -99,10 +99,15 @@ public class Circuit implements Drawable { dotsPresent = true; } + graphic.openGroup(); for (Wire w : wires) w.drawTo(graphic); - for (VisualElement p : visualElements) + graphic.closeGroup(); + for (VisualElement p : visualElements) { + graphic.openGroup(); p.drawTo(graphic); + graphic.closeGroup(); + } } public void add(VisualElement visualElement) { diff --git a/src/main/java/de/neemann/digital/draw/graphics/Exporter.java b/src/main/java/de/neemann/digital/draw/graphics/Exporter.java new file mode 100644 index 000000000..bb6d802ee --- /dev/null +++ b/src/main/java/de/neemann/digital/draw/graphics/Exporter.java @@ -0,0 +1,11 @@ +package de.neemann.digital.draw.graphics; + +import java.io.File; +import java.io.IOException; + +/** + * @author hneemann + */ +public interface Exporter { + Graphic create(File file, Vector min, Vector max) throws IOException; +} diff --git a/src/main/java/de/neemann/digital/draw/graphics/Graphic.java b/src/main/java/de/neemann/digital/draw/graphics/Graphic.java index 2c90e86ea..fe876f769 100644 --- a/src/main/java/de/neemann/digital/draw/graphics/Graphic.java +++ b/src/main/java/de/neemann/digital/draw/graphics/Graphic.java @@ -12,4 +12,12 @@ public interface Graphic { void drawCircle(Vector p1, Vector p2, Style style); void drawText(Vector p1, Vector p2, String text, Orientation orientation, Style style); + + default void openGroup() { + } + + default void closeGroup() { + } + + ; } diff --git a/src/main/java/de/neemann/digital/draw/graphics/GraphicSVG.java b/src/main/java/de/neemann/digital/draw/graphics/GraphicSVG.java new file mode 100644 index 000000000..cd9ea1bab --- /dev/null +++ b/src/main/java/de/neemann/digital/draw/graphics/GraphicSVG.java @@ -0,0 +1,212 @@ +package de.neemann.digital.draw.graphics; + +import java.io.*; +import java.util.Date; + +/** + * @author hneemann + */ +public class GraphicSVG implements Graphic, Closeable { + private static final int TEXTSIZE = 20; + private static final int DEF_SCALE = 100; + private final BufferedWriter w; + + public GraphicSVG(File file, Vector min, Vector max) throws IOException { + this(file, min, max, null, DEF_SCALE); + } + + public GraphicSVG(File file, Vector min, Vector max, File source) throws IOException { + this(file, min, max, source, DEF_SCALE); + } + + public GraphicSVG(File file, Vector min, Vector max, File source, int svgScale) throws IOException { + w = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "utf-8")); + w.write("\n" + + "\n"); + w.write("\n"); + if (source != null) { + w.write("\n"); + } + w.write("\n" + + "\n"); + w.write("\n"); + } + + @Override + public void close() throws IOException { + w.write("\n"); + w.write(""); + w.close(); + } + + @Override + public void drawLine(Vector p1, Vector p2, Style style) { + try { + w.write("\n"); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public void drawPolygon(Polygon p, Style style) { + try { + w.write("\n"); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private static double getStrokeWidth(Style style) { + return style.getThickness() * 0.7; + } + + @Override + public void drawCircle(Vector p1, Vector p2, Style style) { + try { + Vector c = p1.add(p2).div(2); + double r = Math.abs(p2.sub(p1).x) / 2; + if (style.isFilled()) + w.write("\n"); + else { + w.write("\n"); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public void drawText(Vector p1, Vector p2, String text, Orientation orientation, Style style) { + if (text != null && text.length() > 0) + try { + text = escapeXML(text); + + boolean rotateText = false; + if (p1.y == p2.y) { // 0 and 180 deg + if (p1.x > p2.x) // 180 + orientation = orientation.rot(2); + } else { + if (p1.y < p2.y) // 270 + orientation = orientation.rot(2); + else // 90 + orientation = orientation.rot(0); + rotateText = true; + } + + Vector p = new Vector(p1); + switch (orientation.getY()) { + case 1: + p = p.add(0, style.getFontSize() / 2 - style.getFontSize() / 8); + break; + case 2: + p = p.add(0, style.getFontSize() * 3 / 4); + break; + case 0: + //p = p.add(0, -style.getFontSize() / 4); + break; + } + + if (rotateText) + w.write("" + text + "\n"); + else + w.write("" + text + "\n"); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private String escapeXML(String text) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < text.length(); i++) { + char c = text.charAt(i); + switch (c) { + case '&': + sb.append("&"); + break; + case '<': + sb.append("<"); + break; + case '>': + sb.append(">"); + break; + case '"': + sb.append("""); + break; + default: + sb.append(c); + } + } + return sb.toString(); + } + + private String getAchor(int x) { + switch (x) { + case 1: + return "middle"; + case 2: + return "end"; + default: + return "start"; + } + } + + + @Override + public void openGroup() { + try { + w.write("\n"); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public void closeGroup() { + try { + w.write("\n"); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + private static void addStrokeDash(Writer w, int[] dashArray) throws IOException { + w.write(" stroke-dasharray=\""); + for (int i = 0; i < dashArray.length; i++) { + if (i != 0) w.write(','); + w.write(Integer.toString(dashArray[i])); + } + w.write('"'); + } + + private String str(Vector p) { + return p.x + "," + p.y; + } + +} diff --git a/src/main/java/de/neemann/digital/draw/graphics/Polygon.java b/src/main/java/de/neemann/digital/draw/graphics/Polygon.java index cbad97013..108877d10 100644 --- a/src/main/java/de/neemann/digital/draw/graphics/Polygon.java +++ b/src/main/java/de/neemann/digital/draw/graphics/Polygon.java @@ -43,4 +43,13 @@ public class Polygon { public ArrayList getPoints() { return points; } + + + public Vector get(int i) { + return points.get(i); + } + + public int size() { + return points.size(); + } } diff --git a/src/main/java/de/neemann/digital/gui/Main.java b/src/main/java/de/neemann/digital/gui/Main.java index 0a2f7dd34..6d37097d2 100644 --- a/src/main/java/de/neemann/digital/gui/Main.java +++ b/src/main/java/de/neemann/digital/gui/Main.java @@ -8,6 +8,10 @@ import de.neemann.digital.core.wiring.Clock; import de.neemann.digital.draw.elements.Circuit; import de.neemann.digital.draw.elements.PinException; import de.neemann.digital.draw.elements.PinOrder; +import de.neemann.digital.draw.graphics.Exporter; +import de.neemann.digital.draw.graphics.Graphic; +import de.neemann.digital.draw.graphics.GraphicMinMax; +import de.neemann.digital.draw.graphics.GraphicSVG; import de.neemann.digital.draw.library.ElementLibrary; import de.neemann.digital.draw.model.ModelDescription; import de.neemann.digital.draw.model.RealTimeClock; @@ -21,6 +25,7 @@ import javax.swing.*; import javax.swing.filechooser.FileNameExtensionFilter; import java.awt.*; import java.awt.event.ActionEvent; +import java.io.Closeable; import java.io.File; import java.io.IOException; import java.util.prefs.Preferences; @@ -151,6 +156,11 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave { } }; + + JMenu export = new JMenu(Lang.get("menu_export")); + export.add(new ExportAction(Lang.get("menu_exportSVG"), "svg", GraphicSVG::new)); + + JMenu file = new JMenu(Lang.get("menu_file")); bar.add(file); file.add(newFile); @@ -158,6 +168,7 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave { file.add(openWin); file.add(save); file.add(saveas); + file.add(export); JMenu edit = new JMenu(Lang.get("menu_edit")); bar.add(edit); @@ -483,4 +494,48 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave { doStep.setEnabled(model.needsUpdate()); } } + + private class ExportAction extends ToolTipAction { + private final String name; + private final String suffix; + private final Exporter exporter; + + public ExportAction(String name, String suffix, Exporter exporter) { + super(name); + this.name = name; + this.suffix = suffix; + this.exporter = exporter; + } + + @Override + public void actionPerformed(ActionEvent e) { + JFileChooser fc = new JFileChooser(); + if (filename != null) { + String name = filename.getName(); + int p = name.lastIndexOf('.'); + if (p >= 0) + name = name.substring(0, p); + File f = new File(filename.getParentFile(), name + "." + suffix); + fc.setSelectedFile(f); + } + fc.addChoosableFileFilter(new FileNameExtensionFilter(name, suffix)); + if (fc.showSaveDialog(Main.this) == JFileChooser.APPROVE_OPTION) { + Circuit circuit = circuitComponent.getCircuit(); + GraphicMinMax minMax = new GraphicMinMax(); + circuit.drawTo(minMax); + try { + Graphic gr = null; + try { + gr = exporter.create(fc.getSelectedFile(), minMax.getMin(), minMax.getMax()); + circuit.drawTo(gr); + } finally { + if (gr instanceof Closeable) + ((Closeable) gr).close(); + } + } catch (IOException e1) { + new ErrorMessage(Lang.get("msg_errorWritingFile")).addCause(e1).show(Main.this); + } + } + } + } } diff --git a/src/main/resources/lang/lang_de.properties b/src/main/resources/lang/lang_de.properties index a6e12d7ec..513a78dd1 100644 --- a/src/main/resources/lang/lang_de.properties +++ b/src/main/resources/lang/lang_de.properties @@ -123,7 +123,8 @@ menu_editAttributes=Model Attribute Bearbeiten menu_editAttributes_tt=Diese Attribute beeinflussen das Modell, wenn es in andere Modelle eingebettet wird. menu_fast=Schneller Lauf menu_fast_tt=F\u00FChrt das Modell aus, bis ein Stopsignal \u00FCber ein BRK-Element detektiert wird. - +menu_export=Export +menu_exportSVG=Export SVG menu_about=\u00DCber Digital win_saveChanges=Ver\u00E4nderungen speichern? diff --git a/src/main/resources/lang/lang_en.properties b/src/main/resources/lang/lang_en.properties index 2c0f907f7..f4cb41f9d 100644 --- a/src/main/resources/lang/lang_en.properties +++ b/src/main/resources/lang/lang_en.properties @@ -124,6 +124,8 @@ menu_editAttributes=Edit Models Attributes menu_editAttributes_tt=These attributes effect the behavior is the model is included in other models. menu_fast=Run Fast menu_fast_tt=Runs the model until a break is detected by the BRK element. +menu_export=Export +menu_exportSVG=Export SVG win_saveChanges=Save Changes?