Adds a better configuration of the SVG export; see #310

This commit is contained in:
hneemann 2019-08-17 17:18:40 +02:00
parent a56a515dff
commit 01d025823c
33 changed files with 513 additions and 391 deletions

View File

@ -228,9 +228,10 @@ public class VisualElement implements Drawable, Movable, AttributeListener {
Graphic gr = new GraphicTransform(graphic, getTransform());
Shape shape = getShape();
shape.drawTo(gr, highLight);
for (Pin p : shape.getPins())
gr.drawCircle(p.getPos().add(-PIN, -PIN), p.getPos().add(PIN, PIN),
p.getDirection() == Pin.Direction.input ? Style.WIRE : Style.WIRE_OUT);
if (!graphic.isFlagSet(GraphicSVG.NO_PIN_MARKER))
for (Pin p : shape.getPins())
gr.drawCircle(p.getPos().add(-PIN, -PIN), p.getPos().add(PIN, PIN),
p.getDirection() == Pin.Direction.input ? Style.WIRE : Style.WIRE_OUT);
}
private Transform getTransform() {

View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2019 Helmut Neemann.
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.draw.graphics;
import java.awt.*;
/**
* Creates a high contrast style best for printing
*/
public class ColorStyleHighContrast implements GraphicSVG.ColorStyle {
@Override
public Color getColor(Style style) {
if (style == Style.WIRE) return Style.NORMAL.getColor();
else if (style == Style.WIRE_OUT) return Style.NORMAL.getColor();
else if (style == Style.WIRE_BITS) return Style.NORMAL.getColor();
else if (style == Style.WIRE_BUS) return Style.NORMAL.getColor();
else if (style == Style.SHAPE_PIN) return Style.NORMAL.getColor();
else if (style == Style.SHAPE_SPLITTER) return Style.NORMAL.getColor();
else return style.getColor();
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2019 Helmut Neemann.
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.draw.graphics;
import java.awt.*;
/**
* Generates monochrome colors.
*/
public class ColorStyleMonochrome implements GraphicSVG.ColorStyle {
private GraphicSVG.ColorStyle parent;
/**
* Creates a new instance
*
* @param parent the parent color style
*/
public ColorStyleMonochrome(GraphicSVG.ColorStyle parent) {
this.parent = parent;
}
@Override
public Color getColor(Style style) {
Color color = parent.getColor(style);
int c = (color.getBlue() + color.getRed() + color.getGreen()) / 3;
return new Color(c, c, c, color.getAlpha());
}
}

View File

@ -14,10 +14,23 @@ import java.io.IOException;
* implementations which create export formats like SVG ({@link GraphicSVG}).
*/
public interface Graphic extends Closeable {
/**
* Flag to enable LaTeX output mode.
* The shape filling flag
*/
String LATEX = "LaTeX";
String NO_SHAPE_FILLING = "noShapeFilling";
/**
* the small IO flag
*/
String SMALL_IO = "smallIO";
/**
* flag used to hide the test cases
*/
String HIDE_TEST = "hideTest";
/**
* flag used to hide the pin marker
*/
String NO_PIN_MARKER = "noPinMarker";
/**
* Sets the bounding box of the future usage of this instance

View File

@ -5,14 +5,16 @@
*/
package de.neemann.digital.draw.graphics;
import de.neemann.digital.core.element.ElementAttributes;
import java.awt.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashSet;
/**
* Used to create a SVG representation of the circuit.
* Don't use this implementation directly. Use {@link GraphicSVGIndex} to create plain SVG or
* {@link GraphicSVGLaTeX} if you want to include your SVG to LaTeX.
*/
public class GraphicSVG implements Graphic {
private static final int DEF_SCALE = 15;
@ -20,15 +22,35 @@ public class GraphicSVG implements Graphic {
private final File source;
private final int svgScale;
private BufferedWriter w;
private TextStyle textStyle = new TextFormatSVG();
private ColorStyle colorStyle = Style::getColor;
private HashSet<String> flags = new HashSet<>();
/**
* Creates a new instance.
*
* @param out the stream
* @throws IOException IOException
*/
public GraphicSVG(OutputStream out) throws IOException {
public GraphicSVG(OutputStream out) {
this(out, null, DEF_SCALE);
ElementAttributes a = SVGSettings.getInstance().getAttributes();
if (a.get(SVGSettings.LATEX))
setTextStyle(new TextFormatLaTeX());
if (a.get(SVGSettings.HIGH_CONTRAST))
setColorStyle(new ColorStyleHighContrast());
if (a.get(SVGSettings.SMALL_IO))
setFlag(SMALL_IO);
if (a.get(SVGSettings.HIDE_TEST))
setFlag(HIDE_TEST);
if (a.get(SVGSettings.NO_SHAPE_FILLING))
setFlag(NO_SHAPE_FILLING);
if (a.get(SVGSettings.NO_SHAPE_FILLING))
setFlag(NO_SHAPE_FILLING);
if (a.get(SVGSettings.NO_PIN_MARKER))
setFlag(NO_PIN_MARKER);
if (a.get(SVGSettings.MONOCHROME))
setColorStyle(new ColorStyleMonochrome(colorStyle));
}
/**
@ -49,9 +71,8 @@ public class GraphicSVG implements Graphic {
* @param out the stream to write the file to
* @param source source file, only used to create a comment in the SVG file
* @param svgScale the scaling
* @throws IOException IOException
*/
public GraphicSVG(OutputStream out, File source, int svgScale) throws IOException {
public GraphicSVG(OutputStream out, File source, int svgScale) {
this.out = out;
this.source = source;
this.svgScale = svgScale;
@ -117,10 +138,14 @@ public class GraphicSVG implements Graphic {
if (p.getEvenOdd() && style.isFilled())
w.write(" fill-rule=\"evenodd\"");
if (style.isFilled() && p.isClosed())
if (style.isFilled() && p.isClosed() && !isFlagSet(NO_SHAPE_FILLING))
w.write(" stroke=\"" + getColor(style) + "\" stroke-width=\"" + getStrokeWidth(style) + "\" fill=\"" + getColor(style) + "\" fill-opacity=\"" + getOpacity(style) + "\"/>\n");
else
w.write(" stroke=\"" + getColor(style) + "\" stroke-width=\"" + getStrokeWidth(style) + "\" fill=\"none\"/>\n");
else {
double strokeWidth = getStrokeWidth(style);
if (strokeWidth == 0)
strokeWidth = getStrokeWidth(Style.THIN);
w.write(" stroke=\"" + getColor(style) + "\" stroke-width=\"" + strokeWidth + "\" fill=\"none\"/>\n");
}
} catch (IOException e) {
throw new RuntimeException(e);
}
@ -152,7 +177,7 @@ public class GraphicSVG implements Graphic {
if (text == null || text.length() == 0) return;
try {
text = formatText(text, style);
text = textStyle.format(text, style);
boolean rotateText = false;
if (p1.getY() == p2.getY()) { // 0 and 180 deg
@ -188,18 +213,6 @@ public class GraphicSVG implements Graphic {
}
}
/**
* Is used by drawText to format the given text to SVG.
* This implementation only calls escapeXML(text).
*
* @param text the text
* @param style the text style
* @return the formatted text
*/
public String formatText(String text, Style style) {
return escapeXML(text);
}
/**
* Creates the color to use from the given Style instance.
* This instance creates the common HTML representation.
@ -207,8 +220,8 @@ public class GraphicSVG implements Graphic {
* @param style the {@link Style}
* @return the COLOR
*/
protected String getColor(Style style) {
return "#" + Integer.toHexString(style.getColor().getRGB()).substring(2);
private String getColor(Style style) {
return "#" + Integer.toHexString(colorStyle.getColor(style).getRGB()).substring(2);
}
private String getOpacity(Style style) {
@ -293,4 +306,49 @@ public class GraphicSVG implements Graphic {
return p.getXFloat() + "," + p.getYFloat();
}
private void setTextStyle(TextStyle textStyle) {
this.textStyle = textStyle;
}
private void setColorStyle(ColorStyle colorStyle) {
this.colorStyle = colorStyle;
}
private void setFlag(String flag) {
flags.add(flag);
}
@Override
public boolean isFlagSet(String name) {
return flags.contains(name);
}
/**
* Defines the text style.
*/
public interface TextStyle {
/**
* Is used by drawText to format the given text to SVG.
* This implementation only calls escapeXML(text).
*
* @param text the text
* @param style the text style
* @return the formatted text
*/
String format(String text, Style style);
}
/**
* Defines the color style
*/
public interface ColorStyle {
/**
* Returns the color to by used for the given style.
*
* @param style the style
* @return the color to be used
*/
Color getColor(Style style);
}
}

View File

@ -1,60 +0,0 @@
/*
* Copyright (c) 2016 Helmut Neemann
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.draw.graphics;
import de.neemann.digital.draw.graphics.text.ParseException;
import de.neemann.digital.draw.graphics.text.Parser;
import de.neemann.digital.draw.graphics.text.formatter.SVGFormatter;
import de.neemann.digital.draw.graphics.text.text.Decorate;
import de.neemann.digital.draw.graphics.text.text.Text;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
/**
* Subclass of {@link GraphicSVG} which creates the correct SVG representation
* of an index if used like "x_0".
*/
public class GraphicSVGIndex extends GraphicSVG {
/**
* Creates new instance
*
* @param out the file
* @throws IOException IOException
*/
public GraphicSVGIndex(OutputStream out) throws IOException {
super(out);
}
/**
* Creates new instance
*
* @param out the output stream to use
* @param source source file, only used to create a comment in the SVG file
* @param svgScale the scaling
* @throws IOException IOException
*/
public GraphicSVGIndex(OutputStream out, File source, int svgScale) throws IOException {
super(out, source, svgScale);
}
@Override
public String formatText(String text, Style style) {
try {
Text t = new Parser(text).parse();
if (style.getFontStyle() == Font.ITALIC)
t = Decorate.math(t);
return SVGFormatter.format(t);
} catch (ParseException e) {
e.printStackTrace();
}
return text;
}
}

View File

@ -1,135 +0,0 @@
/*
* Copyright (c) 2016 Helmut Neemann
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.draw.graphics;
import de.neemann.digital.draw.graphics.text.ParseException;
import de.neemann.digital.draw.graphics.text.Parser;
import de.neemann.digital.draw.graphics.text.formatter.LaTeXFormatter;
import de.neemann.digital.draw.graphics.text.text.Decorate;
import de.neemann.digital.draw.graphics.text.text.Text;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
/**
* Subclass of {@link GraphicSVG} which creates the correct SVG representation
* of an index if used like "x_0". But the text itself is created to be interpreted
* by LaTeX. To include such a SVG file in LaTeX Inkscape is needed to transform the SVG to PDF.
* In this case the image itself is included as a PDF file in which all the text is missing.
* Inkscape also creates a LaTeX overlay containing the text only. So you get best document quality:
* All the graphics as included PDF, all the text set with LaTeX fonts matching the rest of your LaTeX document.
* To run the transformation automatically by the LaTeX compiler see InkscapePDFLaTeX.pdf.
*
* @see <a href="https://Inkscape.org">inkscape</a>
* @see <a href="http://mirrors.ctan.org/info/svg-inkscape/InkscapePDFLaTeX.pdf">InkscapePDFLaTeX.pdf</a>
*/
public class GraphicSVGLaTeX extends GraphicSVG {
private static final ArrayList<FontSize> FONT_SIZES = new ArrayList<>();
private static final class FontSize {
private final String name;
private final int size;
private FontSize(String name, int size) {
this.name = name;
this.size = size;
}
}
static {
add("tiny", 35); // measured pixel sizes in a BEAMER created PDF
add("scriptsize", 46);
add("footnotesize", 52);
add("small", 58);
add("normalsize", 63);
add("large", 69);
add("Large", 83);
add("LARGE", 100);
add("huge", 120);
add("Huge", 143);
}
private static void add(String name, int size) {
FONT_SIZES.add(new FontSize(name, (size * Style.NORMAL.getFontSize()) / 63));
}
private static String getFontSizeName(int fontSize) {
String best = "normalsize";
int diff = Integer.MAX_VALUE;
for (FontSize fs : FONT_SIZES) {
int d = Math.abs(fontSize - fs.size);
if (d < diff) {
diff = d;
best = fs.name;
}
}
return best;
}
/**
* Creates new instance
*
* @param out the file
* @throws IOException IOException
*/
public GraphicSVGLaTeX(OutputStream out) throws IOException {
super(out);
}
/**
* Creates new instance
*
* @param out the output stream to use
* @param source source file, only used to create a comment in the SVG file
* @param svgScale the scaling
* @throws IOException IOException
*/
public GraphicSVGLaTeX(OutputStream out, File source, int svgScale) throws IOException {
super(out, source, svgScale);
}
@Override
public String formatText(String text, Style style) {
try {
Text t = new Parser(text).parse();
if (style.getFontStyle() == Font.ITALIC && isFlagSet(LATEX))
t = Decorate.math(t);
text = LaTeXFormatter.format(t);
} catch (ParseException e) {
e.printStackTrace();
}
if (style.getFontSize() != Style.NORMAL.getFontSize()) {
final String fontSizeName = getFontSizeName(style.getFontSize());
if (!fontSizeName.equals("normalsize"))
text = "{\\" + fontSizeName + " " + text + "}";
}
return escapeXML(text);
}
@Override
public void drawCircle(VectorInterface p1, VectorInterface p2, Style style) {
if (!isFlagSet(LATEX)
|| (style != Style.WIRE && style != Style.WIRE_OUT) || Math.abs(p1.getX() - p2.getX()) > 4)
super.drawCircle(p1, p2, style);
}
@Override
public String getColor(Style style) {
if (style == Style.WIRE) return super.getColor(Style.NORMAL);
else if (style == Style.WIRE_OUT) return super.getColor(Style.NORMAL);
else if (style == Style.WIRE_BITS) return super.getColor(Style.NORMAL);
else if (style == Style.WIRE_BUS) return super.getColor(Style.NORMAL);
else if (isFlagSet(LATEX)) {
if (style == Style.SHAPE_PIN) return super.getColor(Style.NORMAL);
else if (style == Style.SHAPE_SPLITTER) return super.getColor(Style.NORMAL);
}
return super.getColor(style);
}
}

View File

@ -1,31 +0,0 @@
/*
* Copyright (c) 2017 Helmut Neemann
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.draw.graphics;
import java.io.IOException;
import java.io.OutputStream;
/**
* Replaces input and outputs by small circles
*/
public class GraphicSVGLaTeXInOut extends GraphicSVGLaTeX {
/**
* Creates a new instance
*
* @param out the stream to write the data to
* @throws IOException IOException
*/
public GraphicSVGLaTeXInOut(OutputStream out) throws IOException {
super(out);
}
@Override
public boolean isFlagSet(String name) {
return name.equals(LATEX);
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright (c) 2019 Helmut Neemann.
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.draw.graphics;
import de.neemann.digital.core.element.Key;
import de.neemann.digital.gui.SettingsBase;
import java.util.ArrayList;
import java.util.List;
/**
* Settings used by the SVG exporter.
*/
public final class SVGSettings extends SettingsBase {
static final Key<Boolean> HIGH_CONTRAST =
new Key<>("SVG_highContrast", false);
static final Key<Boolean> MONOCHROME =
new Key<>("SVG_monochrome", false);
static final Key<Boolean> SMALL_IO =
new Key<>("SVG_smallIO", false);
static final Key<Boolean> NO_PIN_MARKER =
new Key<>("SVG_noPinMarker", false);
static final Key<Boolean> HIDE_TEST =
new Key<>("SVG_hideTest", false);
static final Key<Boolean> NO_SHAPE_FILLING =
new Key<>("SVG_noShapeFilling", false);
static final Key<Boolean> LATEX =
new Key<>("SVG_LaTeX", false);
private static final class SettingsHolder {
static final SVGSettings INSTANCE = new SVGSettings();
}
/**
* Returns the settings instance
*
* @return the Settings
*/
public static SVGSettings getInstance() {
return SettingsHolder.INSTANCE;
}
private SVGSettings() {
super(createKeyList(), ".svgStyle.cfg");
}
private static List<Key> createKeyList() {
ArrayList<Key> list = new ArrayList<>();
list.add(LATEX);
list.add(HIDE_TEST);
list.add(NO_SHAPE_FILLING);
list.add(SMALL_IO);
list.add(NO_PIN_MARKER);
list.add(HIGH_CONTRAST);
list.add(MONOCHROME);
return list;
}
}

View File

@ -0,0 +1,81 @@
/*
* Copyright (c) 2019 Helmut Neemann.
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.draw.graphics;
import de.neemann.digital.draw.graphics.text.ParseException;
import de.neemann.digital.draw.graphics.text.Parser;
import de.neemann.digital.draw.graphics.text.formatter.LaTeXFormatter;
import de.neemann.digital.draw.graphics.text.text.Decorate;
import de.neemann.digital.draw.graphics.text.text.Text;
import java.awt.*;
import java.util.ArrayList;
/**
* Formats text in LaTeX style.
*/
public class TextFormatLaTeX implements GraphicSVG.TextStyle {
private static final ArrayList<FontSize> FONT_SIZES = new ArrayList<>();
private static final class FontSize {
private final String name;
private final int size;
private FontSize(String name, int size) {
this.name = name;
this.size = size;
}
}
static {
add("tiny", 35); // measured pixel sizes in a BEAMER created PDF
add("scriptsize", 46);
add("footnotesize", 52);
add("small", 58);
add("normalsize", 63);
add("large", 69);
add("Large", 83);
add("LARGE", 100);
add("huge", 120);
add("Huge", 143);
}
private static void add(String name, int size) {
FONT_SIZES.add(new FontSize(name, (size * Style.NORMAL.getFontSize()) / 63));
}
private static String getFontSizeName(int fontSize) {
String best = "normalsize";
int diff = Integer.MAX_VALUE;
for (FontSize fs : FONT_SIZES) {
int d = Math.abs(fontSize - fs.size);
if (d < diff) {
diff = d;
best = fs.name;
}
}
return best;
}
@Override
public String format(String text, Style style) {
try {
Text t = new Parser(text).parse();
if (style.getFontStyle() == Font.ITALIC)
t = Decorate.math(t);
text = LaTeXFormatter.format(t);
} catch (ParseException e) {
e.printStackTrace();
}
if (style.getFontSize() != Style.NORMAL.getFontSize()) {
final String fontSizeName = getFontSizeName(style.getFontSize());
if (!fontSizeName.equals("normalsize"))
text = "{\\" + fontSizeName + " " + text + "}";
}
return GraphicSVG.escapeXML(text);
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2019 Helmut Neemann.
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.draw.graphics;
import de.neemann.digital.draw.graphics.text.ParseException;
import de.neemann.digital.draw.graphics.text.Parser;
import de.neemann.digital.draw.graphics.text.formatter.SVGFormatter;
import de.neemann.digital.draw.graphics.text.text.Decorate;
import de.neemann.digital.draw.graphics.text.text.Text;
import java.awt.*;
final class TextFormatSVG implements GraphicSVG.TextStyle {
@Override
public String format(String text, Style style) {
try {
Text t = new Parser(text).parse();
if (style.getFontStyle() == Font.ITALIC)
t = Decorate.math(t);
return SVGFormatter.format(t);
} catch (ParseException e) {
e.printStackTrace();
}
return text;
}
}

View File

@ -49,7 +49,7 @@ public class AsyncClockShape implements Shape {
@Override
public void drawTo(Graphic graphic, Style highLight) {
if (!graphic.isFlagSet(Graphic.LATEX)) {
if (!graphic.isFlagSet(Graphic.HIDE_TEST)) {
Polygon pol = new Polygon(true)
.add(SIZE2, SIZE2)
.add(SIZE2 + SIZE * 4, SIZE2)

View File

@ -74,7 +74,7 @@ public class ClockShape implements Shape {
@Override
public void drawTo(Graphic graphic, Style heighLight) {
Vector wavePos;
if (graphic.isFlagSet(Graphic.LATEX)) {
if (graphic.isFlagSet(Graphic.SMALL_IO)) {
Vector center = new Vector(-LATEX_RAD.x, 0);
graphic.drawCircle(center.sub(LATEX_RAD), center.add(LATEX_RAD), Style.NORMAL);
Vector textPos = new Vector(-SIZE2 - LATEX_RAD.x, 0);

View File

@ -218,7 +218,7 @@ public class GenericShape implements Shape {
.add(SIZE * width - 1, yBottom)
.add(1, yBottom);
if (color != Color.WHITE && !graphic.isFlagSet(Graphic.LATEX))
if (color != Color.WHITE)
graphic.drawPolygon(polygon, Style.NORMAL.deriveFillStyle(color));
graphic.drawPolygon(polygon, Style.NORMAL);

View File

@ -123,7 +123,7 @@ public class InputShape implements Shape {
@Override
public void drawTo(Graphic graphic, Style heighLight) {
if (graphic.isFlagSet(Graphic.LATEX)) {
if (graphic.isFlagSet(Graphic.SMALL_IO)) {
Vector center = new Vector(-LATEX_RAD.x, 0);
graphic.drawCircle(center.sub(LATEX_RAD), center.add(LATEX_RAD), Style.NORMAL);
Vector textPos = new Vector(-SIZE2 - LATEX_RAD.x, 0);

View File

@ -84,7 +84,7 @@ public class OutputShape implements Shape {
@Override
public void drawTo(Graphic graphic, Style highLight) {
if (graphic.isFlagSet(Graphic.LATEX)) {
if (graphic.isFlagSet(Graphic.SMALL_IO)) {
Vector center = new Vector(LATEX_RAD.x, 0);
graphic.drawCircle(center.sub(LATEX_RAD), center.add(LATEX_RAD), Style.NORMAL);
Vector textPos = new Vector(SIZE2 + LATEX_RAD.x, 0);

View File

@ -49,7 +49,7 @@ public class TestCaseShape implements Shape {
@Override
public void drawTo(Graphic graphic, Style highLight) {
if (!graphic.isFlagSet(Graphic.LATEX)) {
if (!graphic.isFlagSet(Graphic.HIDE_TEST)) {
Polygon pol = new Polygon(true)
.add(SIZE2, SIZE2)
.add(SIZE2 + SIZE * 4, SIZE2)

View File

@ -223,8 +223,7 @@ public class FSMFrame extends JFrame implements ClosingWindowListener.ConfirmSav
}.setAcceleratorCTRLplus('S').setEnabledChain(false);
JMenu export = new JMenu(Lang.get("menu_export"));
export.add(new ExportAction(Lang.get("menu_exportSVG"), "svg", GraphicSVGIndex::new));
export.add(new ExportAction(Lang.get("menu_exportSVGLaTex"), "svg", GraphicSVGLaTeX::new));
export.add(new ExportAction(Lang.get("menu_exportSVG"), "svg", GraphicSVG::new));
JMenu file = new JMenu(Lang.get("menu_file"));

View File

@ -542,20 +542,29 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
}.setAcceleratorCTRLplus('S').setEnabledChain(false);
JMenu export = new JMenu(Lang.get("menu_export"));
export.add(new ExportAction(Lang.get("menu_exportSVG"), "svg", GraphicSVGIndex::new));
export.add(new ExportAction(Lang.get("menu_exportSVGLaTex"), "svg", GraphicSVGLaTeX::new));
export.add(new ExportAction(Lang.get("menu_exportSVGLaTexInOut"), "svg", GraphicSVGLaTeXInOut::new));
export.add(new ExportAction(Lang.get("menu_exportSVG"), "svg", GraphicSVG::new));
export.add(new ToolTipAction(Lang.get("menu_exportSVGSettings")) {
@Override
public void actionPerformed(ActionEvent actionEvent) {
ElementAttributes modified = new AttributeDialog(Main.this, SVGSettings.getInstance().getKeys(), SVGSettings.getInstance().getAttributes()).showDialog();
SVGSettings.getInstance().getAttributes().getValuesFrom(modified);
}
});
export.addSeparator();
export.add(new ExportAction(Lang.get("menu_exportPNGSmall"), "png", (out) -> new GraphicsImage(out, "PNG", 1)));
export.add(new ExportAction(Lang.get("menu_exportPNGLarge"), "png", (out) -> new GraphicsImage(out, "PNG", 2)));
if (isExperimentalMode())
export.add(new ExportGifAction(Lang.get("menu_exportAnimatedGIF")));
export.add(new ExportZipAction(this).createJMenuItem());
export.addSeparator();
export.add(createVHDLExportAction().createJMenuItem());
export.add(createVerilogExportAction().createJMenuItem());
export.addSeparator();
export.add(new ExportZipAction(this).createJMenuItem());
JMenu file = new JMenu(Lang.get("menu_file"));
menuBar.add(file);
file.add(newFile.createJMenuItem());

View File

@ -5,16 +5,10 @@
*/
package de.neemann.digital.gui;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import de.neemann.digital.core.element.AttributeListener;
import de.neemann.digital.core.element.ElementAttributes;
import de.neemann.digital.core.element.Key;
import de.neemann.digital.core.element.Keys;
import de.neemann.digital.draw.elements.Circuit;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -24,7 +18,7 @@ import java.util.List;
* <p>
* Created by Helmut.Neemann on 11.05.2016.
*/
public final class Settings implements AttributeListener {
public final class Settings extends SettingsBase {
private static final class SettingsHolder {
static final Settings INSTANCE = new Settings();
@ -39,11 +33,11 @@ public final class Settings implements AttributeListener {
return SettingsHolder.INSTANCE;
}
private final ElementAttributes attributes;
private final File filename;
private final List<Key> settingsKeys;
private Settings() {
super(createKeyList(), ".digital.cfg");
}
private static List<Key> createKeyList() {
List<Key> intList = new ArrayList<>();
intList.add(Keys.SETTINGS_IEEE_SHAPES);
intList.add(Keys.SETTINGS_LANGUAGE);
@ -63,64 +57,7 @@ public final class Settings implements AttributeListener {
intList.add(Keys.SETTINGS_FONT_SCALING);
intList.add(Keys.SETTINGS_MAC_MOUSE);
settingsKeys = Collections.unmodifiableList(intList);
filename = new File(new File(System.getProperty("user.home")), ".digital.cfg");
ElementAttributes attr = null;
if (filename.exists()) {
XStream xStream = Circuit.getxStream();
try (InputStream in = new FileInputStream(filename)) {
attr = (ElementAttributes) xStream.fromXML(in);
} catch (Exception e) {
e.printStackTrace();
}
}
if (attr == null) {
System.out.println("Use default settings!");
attributes = new ElementAttributes();
} else
attributes = attr;
attributes.addListener(this);
}
/**
* @return the settings
*/
public ElementAttributes getAttributes() {
return attributes;
}
/**
* Gets a value from the settings.
* If the value is not present the default value is returned
*
* @param key the key
* @param <VALUE> the type of the value
* @return the value
*/
public <VALUE> VALUE get(Key<VALUE> key) {
return attributes.get(key);
}
@Override
public void attributeChanged() {
XStream xStream = Circuit.getxStream();
try (Writer out = new OutputStreamWriter(new FileOutputStream(filename), StandardCharsets.UTF_8)) {
out.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
xStream.marshal(attributes, new PrettyPrintWriter(out));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @return the settings keys
*/
public List<Key> getKeys() {
return settingsKeys;
return Collections.unmodifiableList(intList);
}
/**
@ -130,8 +67,8 @@ public final class Settings implements AttributeListener {
* @return true if the modification requires a restart
*/
public boolean requiresRestart(ElementAttributes modified) {
for (Key<?> key : settingsKeys)
if (key.getRequiresRestart() && !attributes.equalsKey(key, modified))
for (Key<?> key : getKeys())
if (key.getRequiresRestart() && !getAttributes().equalsKey(key, modified))
return true;
return false;

View File

@ -0,0 +1,95 @@
/*
* Copyright (c) 2019 Helmut Neemann.
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.gui;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import de.neemann.digital.core.element.AttributeListener;
import de.neemann.digital.core.element.ElementAttributes;
import de.neemann.digital.core.element.Key;
import de.neemann.digital.draw.elements.Circuit;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.List;
/**
* Base class for Settings
*/
public class SettingsBase implements AttributeListener {
private final ElementAttributes attributes;
private final File filename;
private final List<Key> settingsKeys;
/**
* Creates a new instance
*
* @param settingsKeys the list of keys
* @param name the filename
*/
protected SettingsBase(List<Key> settingsKeys, String name) {
this.settingsKeys = settingsKeys;
filename = new File(new File(System.getProperty("user.home")), name);
ElementAttributes attr = null;
if (filename.exists()) {
XStream xStream = Circuit.getxStream();
try (InputStream in = new FileInputStream(filename)) {
attr = (ElementAttributes) xStream.fromXML(in);
} catch (Exception e) {
e.printStackTrace();
}
}
if (attr == null) {
System.out.println("Use default settings!");
attributes = new ElementAttributes();
} else
attributes = attr;
attributes.addListener(this);
}
/**
* @return the settings
*/
public ElementAttributes getAttributes() {
return attributes;
}
/**
* Gets a value from the settings.
* If the value is not present the default value is returned
*
* @param key the key
* @param <VALUE> the type of the value
* @return the value
*/
public <VALUE> VALUE get(Key<VALUE> key) {
return attributes.get(key);
}
@Override
public void attributeChanged() {
XStream xStream = Circuit.getxStream();
try (Writer out = new OutputStreamWriter(new FileOutputStream(filename), StandardCharsets.UTF_8)) {
out.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
xStream.marshal(attributes, new PrettyPrintWriter(out));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @return the settings keys
*/
public List<Key> getKeys() {
return settingsKeys;
}
}

View File

@ -165,8 +165,7 @@ public class GraphDialog extends JDialog implements Observer {
.checkOverwrite(logData::saveCSV);
}
}.setToolTip(Lang.get("menu_saveData_tt")).createJMenuItem());
file.add(new ExportAction(Lang.get("menu_exportSVG"), GraphicSVGIndex::new).createJMenuItem());
file.add(new ExportAction(Lang.get("menu_exportSVGLaTex"), GraphicSVGLaTeX::new).createJMenuItem());
file.add(new ExportAction(Lang.get("menu_exportSVG"), GraphicSVG::new).createJMenuItem());
JMenu view = new JMenu(Lang.get("menu_view"));
bar.add(view);

View File

@ -1360,6 +1360,22 @@ Sind evtl. die Namen der Variablen nicht eindeutig?</string>
<string name="key_showTutorial">Tutorial beim Start anzeigen</string>
<string name="key_showTutorial_tt">Aktiviert das Tutorial.</string>
<string name="menu_exportSVGSettings">SVG Exporteinstellungen</string>
<string name="key_SVG_LaTeX">Text als LaTeX</string>
<string name="key_SVG_LaTeX_tt">Text wird in LaTeX-Notation eingefügt. Inkscape ist für die Weiterverabeitung erforderlich.</string>
<string name="key_SVG_hideTest">Testfälle verbergen</string>
<string name="key_SVG_hideTest__">Die Testfälle werden nicht mit exportiert.</string>
<string name="key_SVG_noShapeFilling">Polygone nicht ausfüllen</string>
<string name="key_SVG_noShapeFilling_tt">Polygone werden nicht ausgefüllt.</string>
<string name="key_SVG_smallIO">kleine Ein- und Ausgänge</string>
<string name="key_SVG_smallIO_tt">Ein- und Ausgänge werden als kleine Kreise dargestellt.</string>
<string name="key_SVG_noPinMarker">keine Pin-Marker</string>
<string name="key_SVG_noPinMarker_tt">Die Pin-Marker an den Symbolen entfallen.</string>
<string name="key_SVG_highContrast">hoher Kontrast</string>
<string name="key_SVG_highContrast_tt">Leitungen und der Text der Pins werden in Schwarz ausgegeben.</string>
<string name="key_SVG_monochrome">Monochrom</string>
<string name="key_SVG_monochrome_tt">Es werden nur Graustufen verwendet.</string>
<string name="mod_insertWire">Leitung eingefügt.</string>
<string name="mod_insertCopied">Aus Zwischenablage eingefügt.</string>
<string name="mod_setKey_N0_in_element_N1">Wert ''{0}'' in Element ''{1}'' verändert.</string>
@ -1421,8 +1437,6 @@ Sind evtl. die Namen der Variablen nicht eindeutig?</string>
<string name="menu_exportPNGLarge">Export PNG groß</string>
<string name="menu_exportPNGSmall">Export PNG klein</string>
<string name="menu_exportSVG">Export SVG</string>
<string name="menu_exportSVGLaTex">Export SVG + LaTeX</string>
<string name="menu_exportSVGLaTexInOut">Export SVG + LaTeX + kleine Ein- und Ausgänge</string>
<string name="menu_exportAnimatedGIF">Export Animated GIF</string>
<string name="menu_fast">Run To Break</string>
<string name="menu_fast_tt">Führt die Schaltung aus, bis ein Stopsignal über ein BRK-Element detektiert wird.</string>

View File

@ -1346,6 +1346,22 @@
<string name="key_showTutorial">Show Tutorial at Startup</string>
<string name="key_showTutorial_tt">Enables the tutorial.</string>
<string name="menu_exportSVGSettings">SVG Export Settings</string>
<string name="key_SVG_LaTeX">Text as LaTeX</string>
<string name="key_SVG_LaTeX_tt">Text is inserted in LaTeX notation. Inkscape is required for further processing.</string>
<string name="key_SVG_hideTest">Hide Test Cases</string>
<string name="key_SVG_hideTest__">The test cases are not exported.</string>
<string name="key_SVG_noShapeFilling">Shapes not filled</string>
<string name="key_SVG_noShapeFilling_tt">Polygons are not filled.</string>
<string name="key_SVG_smallIO">Small Inputs and Outputs</string>
<string name="key_SVG_smallIO_tt">Inputs and outputs are represented as small circles.</string>
<string name="key_SVG_noPinMarker">No Pin Marker</string>
<string name="key_SVG_noPinMarker_tt">The pin markers at the symbols are omitted.</string>
<string name="key_SVG_highContrast">High Contrast</string>
<string name="key_SVG_highContrast_tt">The wires and the text of the pins are displayed in black.</string>
<string name="key_SVG_monochrome">Monochrome</string>
<string name="key_SVG_monochrome_tt">Only gray colors are used.</string>
<string name="mod_insertWire">Inserted wire.</string>
<string name="mod_insertCopied">Insert from clipboard.</string>
<string name="mod_setKey_N0_in_element_N1">Value ''{0}'' in component ''{1}'' modified.</string>
@ -1404,8 +1420,6 @@
<string name="menu_exportPNGLarge">Export PNG large</string>
<string name="menu_exportPNGSmall">Export PNG small</string>
<string name="menu_exportSVG">Export SVG</string>
<string name="menu_exportSVGLaTex">Export SVG + LaTeX</string>
<string name="menu_exportSVGLaTexInOut">Export SVG + LaTeX + small in/out</string>
<string name="menu_exportAnimatedGIF">Export Animated GIF</string>
<string name="menu_fast">Run to Break</string>
<string name="menu_fast_tt">Runs the circuit until a break is detected by a BRK component.</string>

View File

@ -1222,8 +1222,6 @@
<string name="menu_exportPNGLarge">Exportar PNG grande</string>
<string name="menu_exportPNGSmall">Exportar PNG pequeño</string>
<string name="menu_exportSVG">Exportar SVG</string>
<string name="menu_exportSVGLaTex">Exportar SVG + LaTeX</string>
<string name="menu_exportSVGLaTexInOut">Exportar SVG + LaTeX + entradas/salidas pequeñas</string>
<string name="menu_exportAnimatedGIF">Exportar GIF animado</string>
<string name="menu_fast">Ejecutar hasta un Break</string>
<string name="menu_fast_tt">Ejecuta el circuito hasta que un componente BRK detecta un break.</string>

View File

@ -1235,8 +1235,6 @@
<string name="menu_exportPNGLarge">Export PNG large</string>
<string name="menu_exportPNGSmall">Export PNG small</string>
<string name="menu_exportSVG">Export SVG</string>
<string name="menu_exportSVGLaTex">Export SVG + LaTeX</string>
<string name="menu_exportSVGLaTexInOut">Export SVG + LaTeX + small in/out</string>
<string name="menu_exportAnimatedGIF">Export Animated GIF</string>
<string name="menu_fast">Run to Break</string>
<string name="menu_fast_tt">Runs the circuit until a break is detected by a BRK component.</string>

View File

@ -1289,8 +1289,6 @@
<string name="menu_exportPNGLarge">Exportar PNG grande</string>
<string name="menu_exportPNGSmall">Exportar PNG pequeno</string>
<string name="menu_exportSVG">Exportar SVG</string>
<string name="menu_exportSVGLaTex">Exportar SVG + LaTeX</string>
<string name="menu_exportSVGLaTexInOut">Exportar SVG + LaTeX + entrada/saídas pequenas</string>
<string name="menu_exportAnimatedGIF">Exportar GIF animado</string>
<string name="menu_fast">Executar com interrupção</string>
<string name="menu_fast_tt">Executará o circuito até que uma interrupção seja detectada por uso de um componente BRK.</string>

View File

@ -1300,8 +1300,6 @@
<string name="menu_exportPNGLarge">Export PNG large</string>
<string name="menu_exportPNGSmall">Export PNG small</string>
<string name="menu_exportSVG">Export SVG</string>
<string name="menu_exportSVGLaTex">Export SVG + LaTeX</string>
<string name="menu_exportSVGLaTexInOut">Export SVG + LaTeX + small in/out</string>
<string name="menu_exportAnimatedGIF">Export Animated GIF</string>
<string name="menu_fast">Run to Break</string>
<string name="menu_fast_tt">Runs the circuit until a break is detected by a BRK component.</string>

View File

@ -7,12 +7,10 @@ package de.neemann.digital.docu;
import de.neemann.digital.core.NodeException;
import de.neemann.digital.core.element.*;
import de.neemann.digital.core.memory.Counter;
import de.neemann.digital.draw.elements.PinException;
import de.neemann.digital.draw.elements.VisualElement;
import de.neemann.digital.draw.graphics.GraphicMinMax;
import de.neemann.digital.draw.graphics.GraphicSVG;
import de.neemann.digital.draw.graphics.GraphicSVGIndex;
import de.neemann.digital.draw.library.ElementLibrary;
import de.neemann.digital.draw.library.LibraryNode;
import de.neemann.digital.draw.library.NumStringComparator;
@ -160,7 +158,7 @@ public class DocuTest extends TestCase {
private void writeSVG(File imageFile, VisualElement ve) throws IOException {
try (FileOutputStream out = new FileOutputStream(imageFile)) {
try (GraphicSVG svg = new GraphicSVGIndex(out, null, 20)) {
try (GraphicSVG svg = new GraphicSVG(out,null, 20)) {
GraphicMinMax minMax = new GraphicMinMax(true, svg);
ve.drawTo(minMax, null);
svg.setBoundingBox(minMax.getMin(), minMax.getMax());

View File

@ -10,13 +10,12 @@ import junit.framework.TestCase;
/**
*/
public class GraphicSVGIndexTest extends TestCase {
public void testFormatText() throws Exception {
GraphicSVGIndex gs = new GraphicSVGIndex(System.out, null, 30);
gs.setBoundingBox(new Vector(0, 0), new Vector(30, 30));
public void testFormatText() {
GraphicSVG.TextStyle gs = new TextFormatSVG();
assertEquals("Z<tspan style=\"font-size:80%;baseline-shift:sub;\">0</tspan>", gs.formatText("Z_0", Style.NORMAL));
assertEquals("&lt;a&gt;", gs.formatText("<a>", Style.NORMAL));
assertEquals("<tspan style=\"text-decoration:overline;\">Z</tspan>", gs.formatText("~Z", Style.NORMAL));
assertEquals("Z<tspan style=\"font-size:80%;baseline-shift:sub;\">0</tspan>", gs.format("Z_0", Style.NORMAL));
assertEquals("&lt;a&gt;", gs.format("<a>", Style.NORMAL));
assertEquals("<tspan style=\"text-decoration:overline;\">Z</tspan>", gs.format("~Z", Style.NORMAL));
}
}

View File

@ -13,24 +13,24 @@ import java.io.IOException;
*/
public class GraphicSVGLaTeXTest extends TestCase {
public void testFormatText() throws Exception {
GraphicSVGLaTeX gs = new GraphicSVGLaTeX(System.out, null, 30);
GraphicSVG.TextStyle gs = new TextFormatLaTeX();
assertEquals("$Z_0$", gs.formatText("$Z_0$", Style.NORMAL));
assertEquals("$Z_{in}$", gs.formatText("$Z_{in}$", Style.NORMAL));
assertEquals("$Z_0$", gs.formatText("Z_0", Style.NORMAL));
assertEquals("\\&amp;", gs.formatText("&", Style.NORMAL));
assertEquals("$\\geq\\!\\!{}$1", gs.formatText("\u22651", Style.NORMAL));
assertEquals("$\\geq\\!\\!{}1$", gs.formatText("$\u22651$", Style.NORMAL));
assertEquals("$\\overline{\\mbox{Q}}$", gs.formatText("~Q", Style.NORMAL));
assertEquals("$\\overline{Q}$", gs.formatText("$~Q$", Style.NORMAL));
assertEquals("\\textless{}a\\textgreater{}", gs.formatText("<a>", Style.NORMAL));
assertEquals("Grün", gs.formatText("Grün", Style.NORMAL));
assertEquals("$Z_0$", gs.format("$Z_0$", Style.NORMAL));
assertEquals("$Z_{in}$", gs.format("$Z_{in}$", Style.NORMAL));
assertEquals("$Z_0$", gs.format("Z_0", Style.NORMAL));
assertEquals("\\&amp;", gs.format("&", Style.NORMAL));
assertEquals("$\\geq\\!\\!{}$1", gs.format("\u22651", Style.NORMAL));
assertEquals("$\\geq\\!\\!{}1$", gs.format("$\u22651$", Style.NORMAL));
assertEquals("$\\overline{\\mbox{Q}}$", gs.format("~Q", Style.NORMAL));
assertEquals("$\\overline{Q}$", gs.format("$~Q$", Style.NORMAL));
assertEquals("\\textless{}a\\textgreater{}", gs.format("<a>", Style.NORMAL));
assertEquals("Grün", gs.format("Grün", Style.NORMAL));
assertEquals("{\\scriptsize Grün}", gs.formatText("Grün", Style.SHAPE_PIN));
assertEquals("{\\scriptsize $Z_0$}", gs.formatText("Z_0", Style.SHAPE_PIN));
assertEquals("{\\tiny $Z_0$}", gs.formatText("Z_0", Style.SHAPE_SPLITTER));
assertEquals("{\\tiny $Z_0$}", gs.formatText("Z_0", Style.WIRE_BITS));
assertEquals("{\\scriptsize Grün}", gs.format("Grün", Style.SHAPE_PIN));
assertEquals("{\\scriptsize $Z_0$}", gs.format("Z_0", Style.SHAPE_PIN));
assertEquals("{\\tiny $Z_0$}", gs.format("Z_0", Style.SHAPE_SPLITTER));
assertEquals("{\\tiny $Z_0$}", gs.format("Z_0", Style.WIRE_BITS));
}
public void testCleanLabel() throws IOException {
@ -41,7 +41,7 @@ public class GraphicSVGLaTeXTest extends TestCase {
}
private void check(String orig, String LaTeX) throws IOException {
GraphicSVGLaTeX gs = new GraphicSVGLaTeX(System.out, null, 30);
assertEquals(LaTeX, gs.formatText(orig, Style.NORMAL));
GraphicSVG.TextStyle gs = new TextFormatLaTeX();
assertEquals(LaTeX, gs.format(orig, Style.NORMAL));
}
}

View File

@ -11,11 +11,10 @@ import junit.framework.TestCase;
*/
public class GraphicSVGTest extends TestCase {
public void testFormatText() throws Exception {
GraphicSVG gs = new GraphicSVG(System.out, null, 30);
gs.setBoundingBox(new Vector(0, 0), new Vector(30, 30));
GraphicSVG.TextStyle gs = new TextFormatSVG();
assertEquals("Z0", gs.formatText("Z0", Style.NORMAL));
assertEquals("&lt;a&gt;", gs.formatText("<a>", Style.NORMAL));
assertEquals("Z0", gs.format("Z0", Style.NORMAL));
assertEquals("&lt;a&gt;", gs.format("<a>", Style.NORMAL));
}
}

View File

@ -31,19 +31,11 @@ public class TestExport extends TestCase {
public void testSVGExport() throws NodeException, PinException, IOException, ElementNotFoundException {
ByteArrayOutputStream baos
= export("../../main/dig/processor/Processor.dig",
(out) -> new GraphicSVGIndex(out, null, 15));
(out) -> new GraphicSVG(out, null, 15));
assertTrue(baos.size() > 20000);
}
public void testSVGExportLaTeX() throws NodeException, PinException, IOException, ElementNotFoundException {
ByteArrayOutputStream baos
= export("../../main/dig/processor/Processor.dig",
(out) -> new GraphicSVGLaTeX(out, null, 15));
assertTrue(baos.size() > 15000);
}
public void testPNGExport() throws NodeException, PinException, IOException, ElementNotFoundException {
ByteArrayOutputStream baos
= export("../../main/dig/processor/Processor.dig",