From 2fa0abf03863536ee27b6d2077dd986a1509139c Mon Sep 17 00:00:00 2001 From: Henrik Brix Andersen Date: Sun, 28 May 2017 20:37:12 +0200 Subject: [PATCH 1/4] According to http://wiki.inkscape.org/wiki/index.php/Units_In_Inkscape, Inkscape versions up to and including 0.91 uses 90 DPI. Fix detection. --- host/shiver/src/main/java/dk/osaa/psaw/job/SvgLoader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host/shiver/src/main/java/dk/osaa/psaw/job/SvgLoader.java b/host/shiver/src/main/java/dk/osaa/psaw/job/SvgLoader.java index e637b5a..cacf4e7 100644 --- a/host/shiver/src/main/java/dk/osaa/psaw/job/SvgLoader.java +++ b/host/shiver/src/main/java/dk/osaa/psaw/job/SvgLoader.java @@ -75,7 +75,7 @@ public static JobNodeGroup load(PhotonSawMachineConfig cfg, Job job, String name long major = Long.parseLong(majorMinor.group(1)); long minor = Long.parseLong(majorMinor.group(2)); log.fine("Parsed inkscape version: "+major+"."+minor); - if (major == 0 && minor < 91) { + if (major == 0 && minor <= 91) { dpi = 90; } } From 2220e7fb1e52e1a06ee391aebe3c7477344281c3 Mon Sep 17 00:00:00 2001 From: Henrik Brix Andersen Date: Tue, 20 Jun 2017 19:52:46 +0200 Subject: [PATCH 2/4] Make local copy of SVGRoot.java from svgsalamander in preparation of bugfix. --- .../src/main/java/com/kitfox/svg/SVGRoot.java | 395 ++++++++++++++++++ 1 file changed, 395 insertions(+) create mode 100644 host/shiver/src/main/java/com/kitfox/svg/SVGRoot.java diff --git a/host/shiver/src/main/java/com/kitfox/svg/SVGRoot.java b/host/shiver/src/main/java/com/kitfox/svg/SVGRoot.java new file mode 100644 index 0000000..daa211f --- /dev/null +++ b/host/shiver/src/main/java/com/kitfox/svg/SVGRoot.java @@ -0,0 +1,395 @@ +/* + * SVGRoot.java + * + * + * The Salamander Project - 2D and 3D graphics libraries in Java + * Copyright (C) 2004 Mark McKay + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Mark McKay can be contacted at mark@kitfox.com. Salamander and other + * projects can be found at http://www.kitfox.com + * + * Created on February 18, 2004, 5:33 PM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.NumberWithUnits; +import com.kitfox.svg.xml.StyleAttribute; +import java.awt.geom.*; +import java.awt.*; + +/** + * The root element of an SVG tree. + * + * @author Mark McKay + * @author Mark McKay + */ +public class SVGRoot extends Group +{ + NumberWithUnits x; + NumberWithUnits y; + NumberWithUnits width; + NumberWithUnits height; + + +// final Rectangle2D.Float viewBox = new Rectangle2D.Float(); + Rectangle2D.Float viewBox = null; + + public static final int PA_X_NONE = 0; + public static final int PA_X_MIN = 1; + public static final int PA_X_MID = 2; + public static final int PA_X_MAX = 3; + + public static final int PA_Y_NONE = 0; + public static final int PA_Y_MIN = 1; + public static final int PA_Y_MID = 2; + public static final int PA_Y_MAX = 3; + + public static final int PS_MEET = 0; + public static final int PS_SLICE = 1; + + int parSpecifier = PS_MEET; + int parAlignX = PA_X_MID; + int parAlignY = PA_Y_MID; + + final AffineTransform viewXform = new AffineTransform(); + final Rectangle2D.Float clipRect = new Rectangle2D.Float(); + + /** Creates a new instance of SVGRoot */ + public SVGRoot() + { + } +/* + public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) + { + //Load style string + super.loaderStartElement(helper, attrs, parent); + + x = XMLParseUtil.parseNumberWithUnits(attrs.getValue("x")); + y = XMLParseUtil.parseNumberWithUnits(attrs.getValue("y")); + width = XMLParseUtil.parseNumberWithUnits(attrs.getValue("width")); + height = XMLParseUtil.parseNumberWithUnits(attrs.getValue("height")); + + String viewBox = attrs.getValue("viewBox"); + float[] coords = XMLParseUtil.parseFloatList(viewBox); + + if (coords == null) + { + //this.viewBox.setRect(0, 0, width.getValue(), height.getValue()); + this.viewBox = null; + } + else + { + this.viewBox = new Rectangle2D.Float(coords[0], coords[1], coords[2], coords[3]); + } + + String par = attrs.getValue("preserveAspectRatio"); + if (par != null) + { + String[] parList = XMLParseUtil.parseStringList(par); + + if (parList[0].equals("none")) { parAlignX = PA_X_NONE; parAlignY = PA_Y_NONE; } + else if (parList[0].equals("xMinYMin")) { parAlignX = PA_X_MIN; parAlignY = PA_Y_MIN; } + else if (parList[0].equals("xMidYMin")) { parAlignX = PA_X_MID; parAlignY = PA_Y_MIN; } + else if (parList[0].equals("xMaxYMin")) { parAlignX = PA_X_MAX; parAlignY = PA_Y_MIN; } + else if (parList[0].equals("xMinYMid")) { parAlignX = PA_X_MIN; parAlignY = PA_Y_MID; } + else if (parList[0].equals("xMidYMid")) { parAlignX = PA_X_MID; parAlignY = PA_Y_MID; } + else if (parList[0].equals("xMaxYMid")) { parAlignX = PA_X_MAX; parAlignY = PA_Y_MID; } + else if (parList[0].equals("xMinYMax")) { parAlignX = PA_X_MIN; parAlignY = PA_Y_MAX; } + else if (parList[0].equals("xMidYMax")) { parAlignX = PA_X_MID; parAlignY = PA_Y_MAX; } + else if (parList[0].equals("xMaxYMax")) { parAlignX = PA_X_MAX; parAlignY = PA_Y_MAX; } + + if (parList[1].equals("meet")) parSpecifier = PS_MEET; + else if (parList[1].equals("slice")) parSpecifier = PS_SLICE; + } + + build(); + } +*/ + + public void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + + if (getPres(sty.setName("x"))) x = sty.getNumberWithUnits(); + + if (getPres(sty.setName("y"))) y = sty.getNumberWithUnits(); + + if (getPres(sty.setName("width"))) width = sty.getNumberWithUnits(); + + if (getPres(sty.setName("height"))) height = sty.getNumberWithUnits(); + + if (getPres(sty.setName("viewBox"))) + { + float[] coords = sty.getFloatList(); + viewBox = new Rectangle2D.Float(coords[0], coords[1], coords[2], coords[3]); + } + + if (getPres(sty.setName("preserveAspectRatio"))) + { + String preserve = sty.getStringValue(); + + if (contains(preserve, "none")) { parAlignX = PA_X_NONE; parAlignY = PA_Y_NONE; } + else if (contains(preserve, "xMinYMin")) { parAlignX = PA_X_MIN; parAlignY = PA_Y_MIN; } + else if (contains(preserve, "xMidYMin")) { parAlignX = PA_X_MID; parAlignY = PA_Y_MIN; } + else if (contains(preserve, "xMaxYMin")) { parAlignX = PA_X_MAX; parAlignY = PA_Y_MIN; } + else if (contains(preserve, "xMinYMid")) { parAlignX = PA_X_MIN; parAlignY = PA_Y_MID; } + else if (contains(preserve, "xMidYMid")) { parAlignX = PA_X_MID; parAlignY = PA_Y_MID; } + else if (contains(preserve, "xMaxYMid")) { parAlignX = PA_X_MAX; parAlignY = PA_Y_MID; } + else if (contains(preserve, "xMinYMax")) { parAlignX = PA_X_MIN; parAlignY = PA_Y_MAX; } + else if (contains(preserve, "xMidYMax")) { parAlignX = PA_X_MID; parAlignY = PA_Y_MAX; } + else if (contains(preserve, "xMaxYMax")) { parAlignX = PA_X_MAX; parAlignY = PA_Y_MAX; } + + if (contains(preserve, "meet")) parSpecifier = PS_MEET; + else if (contains(preserve, "slice")) parSpecifier = PS_SLICE; + + /* + String[] parList = sty.getStringList(); + + if (parList[0].equals("none")) { parAlignX = PA_X_NONE; parAlignY = PA_Y_NONE; } + else if (parList[0].equals("xMinYMin")) { parAlignX = PA_X_MIN; parAlignY = PA_Y_MIN; } + else if (parList[0].equals("xMidYMin")) { parAlignX = PA_X_MID; parAlignY = PA_Y_MIN; } + else if (parList[0].equals("xMaxYMin")) { parAlignX = PA_X_MAX; parAlignY = PA_Y_MIN; } + else if (parList[0].equals("xMinYMid")) { parAlignX = PA_X_MIN; parAlignY = PA_Y_MID; } + else if (parList[0].equals("xMidYMid")) { parAlignX = PA_X_MID; parAlignY = PA_Y_MID; } + else if (parList[0].equals("xMaxYMid")) { parAlignX = PA_X_MAX; parAlignY = PA_Y_MID; } + else if (parList[0].equals("xMinYMax")) { parAlignX = PA_X_MIN; parAlignY = PA_Y_MAX; } + else if (parList[0].equals("xMidYMax")) { parAlignX = PA_X_MID; parAlignY = PA_Y_MAX; } + else if (parList[0].equals("xMaxYMax")) { parAlignX = PA_X_MAX; parAlignY = PA_Y_MAX; } + + if (parList[1].equals("meet")) parSpecifier = PS_MEET; + else if (parList[1].equals("slice")) parSpecifier = PS_SLICE; + */ + } + + prepareViewport(); + } + + private boolean contains(String text, String find) + { + return (text.indexOf(find) != -1); + } + + protected void prepareViewport() + { + Rectangle deviceViewport = diagram.getDeviceViewport(); + + Rectangle2D defaultBounds; + try + { + defaultBounds = getBoundingBox(); + } catch (SVGException ex) + { + defaultBounds= new Rectangle2D.Float(); + } + + //Determine destination rectangle + float xx, yy, ww, hh; + if (width != null) + { + xx = (x == null) ? 0 : StyleAttribute.convertUnitsToPixels(x.getUnits(), x.getValue()); + if (width.getUnits() == NumberWithUnits.UT_PERCENT) + { + ww = width.getValue() * deviceViewport.width; + } + else + { + ww = StyleAttribute.convertUnitsToPixels(width.getUnits(), width.getValue()); + } +// setAttribute("x", AnimationElement.AT_XML, "" + xx); +// setAttribute("width", AnimationElement.AT_XML, "" + ww); + } + else if (viewBox != null) + { + xx = (float)viewBox.x; + ww = (float)viewBox.width; + width = new NumberWithUnits(ww, NumberWithUnits.UT_PX); + x = new NumberWithUnits(xx, NumberWithUnits.UT_PX); + } + else + { + //Estimate size from scene bounding box + xx = (float)defaultBounds.getX(); + ww = (float)defaultBounds.getWidth(); + width = new NumberWithUnits(ww, NumberWithUnits.UT_PX); + x = new NumberWithUnits(xx, NumberWithUnits.UT_PX); + } + + if (height != null) + { + yy = (y == null) ? 0 : StyleAttribute.convertUnitsToPixels(y.getUnits(), y.getValue()); + if (height.getUnits() == NumberWithUnits.UT_PERCENT) + { + hh = height.getValue() * deviceViewport.height; + } + else + { + hh = StyleAttribute.convertUnitsToPixels(height.getUnits(), height.getValue()); + } + } + else if (viewBox != null) + { + yy = (float)viewBox.y; + hh = (float)viewBox.height; + height = new NumberWithUnits(hh, NumberWithUnits.UT_PX); + y = new NumberWithUnits(yy, NumberWithUnits.UT_PX); + } + else + { + //Estimate size from scene bounding box + yy = (float)defaultBounds.getY(); + hh = (float)defaultBounds.getHeight(); + height = new NumberWithUnits(hh, NumberWithUnits.UT_PX); + y = new NumberWithUnits(yy, NumberWithUnits.UT_PX); + } + + clipRect.setRect(xx, yy, ww, hh); + + if (viewBox == null) + { + viewXform.setToIdentity(); + } + else + { + viewXform.setToTranslation(clipRect.x, clipRect.y); + viewXform.scale(clipRect.width, clipRect.height); + viewXform.scale(1 / viewBox.width, 1 / viewBox.height); + viewXform.translate(-viewBox.x, -viewBox.y); + } + + + //For now, treat all preserveAspectRatio as 'none' +// viewXform.setToTranslation(viewBox.x, viewBox.y); +// viewXform.scale(clipRect.width / viewBox.width, clipRect.height / viewBox.height); +// viewXform.translate(-clipRect.x, -clipRect.y); + } + + public void render(Graphics2D g) throws SVGException + { + prepareViewport(); + + AffineTransform cachedXform = g.getTransform(); + g.transform(viewXform); + + super.render(g); + + g.setTransform(cachedXform); + } + + public Shape getShape() + { + Shape shape = super.getShape(); + return viewXform.createTransformedShape(shape); + } + + public Rectangle2D getBoundingBox() throws SVGException + { + Rectangle2D bbox = super.getBoundingBox(); + return viewXform.createTransformedShape(bbox).getBounds2D(); + } + + public float getDeviceWidth() + { + return clipRect.width; + } + + public float getDeviceHeight() + { + return clipRect.height; + } + + public Rectangle2D getDeviceRect(Rectangle2D rect) + { + rect.setRect(clipRect); + return rect; + } + + /** + * Updates all attributes in this diagram associated with a time event. + * Ie, all attributes with track information. + * @return - true if this node has changed state as a result of the time + * update + */ + public boolean updateTime(double curTime) throws SVGException + { + boolean changeState = super.updateTime(curTime); + + StyleAttribute sty = new StyleAttribute(); + boolean shapeChange = false; + + if (getPres(sty.setName("x"))) + { + NumberWithUnits newVal = sty.getNumberWithUnits(); + if (!newVal.equals(x)) + { + x = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("y"))) + { + NumberWithUnits newVal = sty.getNumberWithUnits(); + if (!newVal.equals(y)) + { + y = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("width"))) + { + NumberWithUnits newVal = sty.getNumberWithUnits(); + if (!newVal.equals(width)) + { + width = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("height"))) + { + NumberWithUnits newVal = sty.getNumberWithUnits(); + if (!newVal.equals(height)) + { + height = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("viewBox"))) + { + float[] coords = sty.getFloatList(); + Rectangle2D.Float newViewBox = new Rectangle2D.Float(coords[0], coords[1], coords[2], coords[3]); + if (!newViewBox.equals(viewBox)) + { + viewBox = newViewBox; + shapeChange = true; + } + } + + if (shapeChange) + { + build(); + } + + return changeState || shapeChange; + } + +} From 45c92ceb0f444b4d0db126d48d4341ebe9c9a3bb Mon Sep 17 00:00:00 2001 From: Henrik Brix Andersen Date: Tue, 20 Jun 2017 19:54:00 +0200 Subject: [PATCH 3/4] Correctly handle removal of SVG width, height and viewBox attributes in svgsalamander. --- .../src/main/java/com/kitfox/svg/SVGRoot.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/host/shiver/src/main/java/com/kitfox/svg/SVGRoot.java b/host/shiver/src/main/java/com/kitfox/svg/SVGRoot.java index daa211f..1452af5 100644 --- a/host/shiver/src/main/java/com/kitfox/svg/SVGRoot.java +++ b/host/shiver/src/main/java/com/kitfox/svg/SVGRoot.java @@ -342,6 +342,11 @@ public boolean updateTime(double curTime) throws SVGException shapeChange = true; } } + else if (x != null) + { + x = null; + shapeChange = true; + } if (getPres(sty.setName("y"))) { @@ -352,6 +357,11 @@ public boolean updateTime(double curTime) throws SVGException shapeChange = true; } } + else if (y != null) + { + y = null; + shapeChange = true; + } if (getPres(sty.setName("width"))) { @@ -362,6 +372,11 @@ public boolean updateTime(double curTime) throws SVGException shapeChange = true; } } + else if (width != null) + { + width = null; + shapeChange = true; + } if (getPres(sty.setName("height"))) { @@ -372,6 +387,11 @@ public boolean updateTime(double curTime) throws SVGException shapeChange = true; } } + else if (height != null) + { + height = null; + shapeChange = true; + } if (getPres(sty.setName("viewBox"))) { @@ -383,6 +403,11 @@ public boolean updateTime(double curTime) throws SVGException shapeChange = true; } } + else if (viewBox != null) + { + viewBox = null; + shapeChange = true; + } if (shapeChange) { From 6e84039c228fe2e3c401fd4ad9c0cf3861e27adc Mon Sep 17 00:00:00 2001 From: Henrik Brix Andersen Date: Tue, 20 Jun 2017 19:55:31 +0200 Subject: [PATCH 4/4] Make shiver read and understand SVG width, height and viewBox attributes - and use them for scaling the resulting physical object. --- .../dk/osaa/psaw/job/SVGRenderTarget.java | 2 +- .../main/java/dk/osaa/psaw/job/SvgLoader.java | 106 ++++++++++++------ 2 files changed, 72 insertions(+), 36 deletions(-) diff --git a/host/shiver/src/main/java/dk/osaa/psaw/job/SVGRenderTarget.java b/host/shiver/src/main/java/dk/osaa/psaw/job/SVGRenderTarget.java index aa3cb89..e403105 100644 --- a/host/shiver/src/main/java/dk/osaa/psaw/job/SVGRenderTarget.java +++ b/host/shiver/src/main/java/dk/osaa/psaw/job/SVGRenderTarget.java @@ -16,7 +16,7 @@ public class SVGRenderTarget implements JobRenderTarget, AutoCloseable { - private static final double MMTOPX = 90.0/25.4; + private static final double MMTOPX = 96.0/25.4; private int idcnt = 0; ArrayList path; diff --git a/host/shiver/src/main/java/dk/osaa/psaw/job/SvgLoader.java b/host/shiver/src/main/java/dk/osaa/psaw/job/SvgLoader.java index cacf4e7..95a1299 100644 --- a/host/shiver/src/main/java/dk/osaa/psaw/job/SvgLoader.java +++ b/host/shiver/src/main/java/dk/osaa/psaw/job/SvgLoader.java @@ -1,6 +1,7 @@ package dk.osaa.psaw.job; import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.InputStream; import java.net.URI; @@ -14,6 +15,7 @@ import com.kitfox.svg.SVGException; import com.kitfox.svg.SVGUniverse; import com.kitfox.svg.animation.AnimationElement; +import com.kitfox.svg.xml.NumberWithUnits; import com.kitfox.svg.xml.StyleAttribute; import dk.osaa.psaw.config.PhotonSawMachineConfig; @@ -36,62 +38,96 @@ public static JobNodeGroup load(PhotonSawMachineConfig cfg, Job job, String name URI svgURI = su.loadSVG(svgStream, name); SVGDiagram diagram = su.getDiagram(svgURI); val e = diagram.getRoot(); - - /* Try to guess the user units to real world conversion factor, because the nitwits - * at w3c didn't bother to specify a default pixel size or an easy way to specify it. - * - * inkscape seems to just work in 90 DPI implicitly, without stating that anywhere, let alone in an - * attribute in the xml, so let's hope they never change their minds. - * - * Everyone else seems to use either 96 DPI or 72 DPI if they are in a postscript state of mind. - * - * Fuck! - * - * Why? Why, would you do something like that, were you evil or just stupid? - */ - - double pixelsSizeX = 25.4/96; // Default to 96 DPI, perhaps we should default to 2540 DPI, just to make uncertainty very obvious. - double pixelsSizeY = pixelsSizeX; - + + // SVG DPI, default to 96 DPI as recommended by the SVG Working Group (and used in e.g. Inkscape 0.92 and later) + double dpiX = 96; + double dpiY = 96; + + // Page size and viewbox attributes + StyleAttribute widthAttr = new StyleAttribute("width"); + StyleAttribute heightAttr = new StyleAttribute("height"); + StyleAttribute viewBoxAttr = new StyleAttribute("viewBox"); + if (forcedDPI > 0) { - pixelsSizeX = pixelsSizeY = 25.4/forcedDPI; - log.info("Explicit DPI set by SVG loader: "+forcedDPI); - + dpiX = dpiY = forcedDPI; + log.warning("Explicit default DPI set by SVG loader: "+dpiX+"x"+dpiY); } else if (e.hasAttribute("photonsaw-dpi", AnimationElement.AT_XML)) { StyleAttribute dpiAttr = new StyleAttribute("photonsaw-dpi"); e.getPres(dpiAttr); - pixelsSizeX = pixelsSizeY = 25.4/dpiAttr.getDoubleValue(); - log.info("Explicit DPI set by photonsaw-dpi attribute: "+dpiAttr.getDoubleValue()); - + dpiX = dpiY = dpiAttr.getDoubleValue(); + log.warning("Explicit default DPI set by photonsaw-dpi attribute: "+dpiX+"x"+dpiY); } else if (e.hasAttribute("inkscape:version", AnimationElement.AT_XML)) { StyleAttribute versionAttr = new StyleAttribute("inkscape:version"); e.getPres(versionAttr); String inkscapeVersion = versionAttr.getStringValue(); - int dpi = 96; - Matcher majorMinor = MAJOR_MINOR_VERSION.matcher(inkscapeVersion); if (majorMinor.find()) { long major = Long.parseLong(majorMinor.group(1)); long minor = Long.parseLong(majorMinor.group(2)); log.fine("Parsed inkscape version: "+major+"."+minor); if (major == 0 && minor <= 91) { - dpi = 90; + dpiX = dpiY = 90; } } - - pixelsSizeX = pixelsSizeY = 25.4/dpi; - log.info("Inkscape version "+inkscapeVersion+" svg detected: "+name+" assuming "+dpi+" DPI for conversion to mm"); + log.info("Inkscape version "+inkscapeVersion+" svg detected: "+name+" assuming "+dpiX+"x"+dpiY+" default DPI"); + } + + if (e.getPres(widthAttr) && e.getPres(heightAttr) && e.getPres(viewBoxAttr)) { + // Calculate DPI from page size and view box + NumberWithUnits width = widthAttr.getNumberWithUnits(); + NumberWithUnits height = heightAttr.getNumberWithUnits(); + float[] coords = viewBoxAttr.getFloatList(); + Rectangle2D.Float viewBox = new Rectangle2D.Float(coords[0], coords[1], coords[2], coords[3]); + // Calculate DPI from page size and viewbox + if (width.getUnits() == NumberWithUnits.UT_IN) { + dpiX = viewBox.getWidth()/width.getValue(); + } else if (width.getUnits() == NumberWithUnits.UT_MM) { + dpiX = viewBox.getWidth()/width.getValue()*25.4; + } else if (width.getUnits() == NumberWithUnits.UT_CM) { + dpiX = viewBox.getWidth()/width.getValue()*2.54; + } else if (width.getUnits() == NumberWithUnits.UT_PX || width.getUnits() == NumberWithUnits.UT_UNITLESS) { + dpiX = viewBox.getWidth()/(width.getValue()/dpiX); + } else { + log.warning("Unsupported unit "+NumberWithUnits.unitsAsString(width.getUnits())+"on SVG width attribute, ignoring SVG width scale factor"); + } + + if (height.getUnits() == NumberWithUnits.UT_IN) { + dpiY = viewBox.getHeight()/height.getValue(); + } else if (height.getUnits() == NumberWithUnits.UT_MM) { + dpiY = viewBox.getHeight()/height.getValue()*25.4; + } else if (height.getUnits() == NumberWithUnits.UT_CM) { + dpiY = viewBox.getHeight()/height.getValue()*2.54; + } else if (height.getUnits() == NumberWithUnits.UT_PX || height.getUnits() == NumberWithUnits.UT_UNITLESS) { + dpiY = viewBox.getHeight()/(width.getValue()/dpiY); + } else { + log.warning("Unsupported unit "+NumberWithUnits.unitsAsString(height.getUnits())+"on SVG height attribute, ignoring SVG height scale factor"); + } } else { - log.warning("Unable to guess the pixel size used in the svg file "+name+" guessing 96 DPI"); + log.warning("No width/height/viewBox attributes in SVG file "+name+", assuming "+dpiX+"x"+dpiY+" DPI"); } - - // Create the affine transform that is used to convert from svg pixels to mm - AffineTransform pixel2mm = AffineTransform.getScaleInstance(pixelsSizeX, pixelsSizeY); - - JobNodeGroup res = new JobNodeGroup(job.getNodeId(name), pixel2mm); + + // Remove width, height and viewBox attributes - if present - as these cause svgSalamander to do its own scaling of the renderered SVG + if (e.getPres(widthAttr)) { + e.removeAttribute(widthAttr.getName(), AnimationElement.AT_XML); + } + if (e.getPres(heightAttr)) { + e.removeAttribute(heightAttr.getName(), AnimationElement.AT_XML); + } + if (e.getPres(viewBoxAttr)) { + e.removeAttribute(viewBoxAttr.getName(), AnimationElement.AT_XML); + } + su.updateTime(); + + log.info("Scaling SVG file "+name+" from "+dpiX+"x"+dpiY+" DPI to 96x96 DPI"); + + // Create the affine transform that is used to convert from SVG DPI to 96 DPI + AffineTransform scaleTo96dpi = AffineTransform.getScaleInstance(25.4/dpiX, 25.4/dpiY); + + JobNodeGroup res = new JobNodeGroup(job.getNodeId(name), scaleTo96dpi); Graphics2DJobNodeGroup g2d = new Graphics2DJobNodeGroup(job, res); + // TODO: different resolution in x/y? g2d.setResolution(25.4/cfg.getMmPerStep().getAxis(0)); diagram.setIgnoringClipHeuristic(true);