Skip to content

Commit b84cfa7

Browse files
committed
add SvgSizeAnalyzer
Signed-off-by: Stefan Niederhauser <ghuder5@gmx.ch>
1 parent 1f1cc50 commit b84cfa7

4 files changed

Lines changed: 171 additions & 24 deletions

File tree

graphviz-java/src/main/java/guru/nidi/graphviz/engine/SvgSizeAdjuster.java

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,10 @@
1818
import org.slf4j.Logger;
1919
import org.slf4j.LoggerFactory;
2020

21-
import java.util.regex.Matcher;
22-
import java.util.regex.Pattern;
23-
2421
import static guru.nidi.graphviz.engine.Format.*;
25-
import static java.util.regex.Pattern.DOTALL;
2622

2723
class SvgSizeAdjuster implements GraphvizPostProcessor {
2824
private static final Logger LOG = LoggerFactory.getLogger(SvgSizeAdjuster.class);
29-
private static final Pattern SVG_PATTERN = Pattern.compile(
30-
"<svg width=\"(?<width>\\d+)(?<unit>p[tx])\" height=\"(?<height>\\d+)p[tx]\""
31-
+ "(?<between>.*?>\\R<g.*?)transform=\"scale\\((?<scaleX>[0-9.]+) (?<scaleY>[0-9.]+)\\)",
32-
DOTALL);
3325

3426
@Override
3527
public EngineResult postProcess(EngineResult result, Options options, ProcessOptions procOptions) {
@@ -51,17 +43,20 @@ private static String withoutPrefix(String svg) {
5143
}
5244

5345
private static String pointsToPixels(String svg, double dpi, int width, int height, double scale) {
54-
final Matcher m = SVG_PATTERN.matcher(svg);
55-
if (!m.find()) {
56-
LOG.warn("Generated SVG has not the expected format. There might be image size problems.");
46+
try {
47+
final SvgSizeAnalyzer analyzer = new SvgSizeAnalyzer(svg);
48+
setSize(analyzer, width, height, scale);
49+
setScale(analyzer, dpi);
50+
return analyzer.adjusted();
51+
} catch (IllegalArgumentException e) {
52+
LOG.warn(e.getMessage());
5753
return svg;
5854
}
59-
return m.replaceFirst("<svg " + svgSize(m, width, height, scale) + m.group("between") + svgScale(m, dpi));
6055
}
6156

62-
private static String svgSize(Matcher m, int width, int height, double scale) {
63-
double w = Integer.parseInt(m.group("width"));
64-
double h = Integer.parseInt(m.group("height"));
57+
private static void setSize(SvgSizeAnalyzer analyzer, int width, int height, double scale) {
58+
double w = analyzer.getWidth();
59+
double h = analyzer.getHeight();
6560
if (width > 0 && height > 0) {
6661
w = width;
6762
h = height;
@@ -72,14 +67,14 @@ private static String svgSize(Matcher m, int width, int height, double scale) {
7267
w *= height / h;
7368
h = height;
7469
}
75-
return "width=\"" + Math.round(w * scale) + "px\" height=\"" + Math.round(h * scale) + "px\"";
70+
analyzer.setSize((int) Math.round(w * scale), (int) Math.round(h * scale));
7671
}
7772

78-
private static String svgScale(Matcher m, double dpi) {
79-
final double pixelScale = m.group("unit").equals("px") ? 1 : Math.round(10000 * dpi / 72) / 10000d;
80-
final double scaleX = Double.parseDouble(m.group("scaleX")) / pixelScale;
81-
final double scaleY = Double.parseDouble(m.group("scaleY")) / pixelScale;
82-
return "transform=\"scale(" + scaleX + " " + scaleY + ")";
73+
private static void setScale(SvgSizeAnalyzer analyzer, double dpi) {
74+
final double pixelScale = analyzer.getUnit().equals("px") ? 1 : Math.round(10000 * dpi / 72) / 10000d;
75+
final double scaleX = analyzer.getScaleX() / pixelScale;
76+
final double scaleY = analyzer.getScaleY() / pixelScale;
77+
analyzer.setScale(scaleX, scaleY);
8378
}
8479

8580
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright © 2015 Stefan Niederhauser (nidin@gmx.ch)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package guru.nidi.graphviz.engine;
17+
18+
import javax.annotation.Nullable;
19+
import java.util.regex.Matcher;
20+
import java.util.regex.Pattern;
21+
22+
import static java.util.regex.Pattern.DOTALL;
23+
24+
class SvgSizeAnalyzer {
25+
private static final Pattern SVG_PATTERN = Pattern.compile(
26+
"<svg width=\"(?<width>\\d+)(?<unit>p[tx])\" height=\"(?<height>\\d+)p[tx]\""
27+
+ "(?<between>.*?>\\R<g.*?)transform=\""
28+
+ "scale\\((?<scaleX>[0-9.]+) (?<scaleY>[0-9.]+)\\) "
29+
+ "rotate\\((?<rotate>[0-9.]+)\\) "
30+
+ "translate\\((?<translateX>[0-9.]+) (?<translateY>[0-9.]+)\\)",
31+
DOTALL);
32+
private final Matcher matcher;
33+
@Nullable
34+
private Integer width;
35+
@Nullable
36+
private Integer height;
37+
@Nullable
38+
private Double scaleX;
39+
@Nullable
40+
private Double scaleY;
41+
42+
SvgSizeAnalyzer(String svg) {
43+
matcher = SVG_PATTERN.matcher(svg);
44+
if (!matcher.find()) {
45+
throw new IllegalArgumentException("Generated SVG has not the expected format. "
46+
+ "There might be image size problems.");
47+
}
48+
}
49+
50+
public String adjusted() {
51+
final String size = width == null
52+
? "width=\"" + getWidth() + getUnit() + "\" height=\"" + getHeight() + getUnit() + "\""
53+
: "width=\"" + width + "px\" height=\"" + height + "px\"";
54+
final String scale = scaleX == null
55+
? "scale(" + getScaleX() + " " + getScaleY() + ") "
56+
: "scale(" + scaleX + " " + scaleY + ") ";
57+
final String rotate = "rotate(" + getRotate() + ") ";
58+
final String translate = "translate(" + getTranslateX() + " " + getTranslateY() + ")";
59+
return matcher.replaceFirst("<svg " + size + matcher.group("between") +
60+
"transform=\"" + scale + rotate + translate);
61+
}
62+
63+
public int getWidth() {
64+
return Integer.parseInt(matcher.group("width"));
65+
}
66+
67+
public int getHeight() {
68+
return Integer.parseInt(matcher.group("height"));
69+
}
70+
71+
public String getUnit() {
72+
return matcher.group("unit");
73+
}
74+
75+
public double getScaleX() {
76+
return Double.parseDouble(matcher.group("scaleX"));
77+
}
78+
79+
public double getScaleY() {
80+
return Double.parseDouble(matcher.group("scaleY"));
81+
}
82+
83+
public double getRotate() {
84+
return Double.parseDouble(matcher.group("rotate"));
85+
}
86+
87+
public double getTranslateX() {
88+
return Double.parseDouble(matcher.group("translateX"));
89+
}
90+
91+
public double getTranslateY() {
92+
return Double.parseDouble(matcher.group("translateY"));
93+
}
94+
95+
public void setSize(int width, int height) {
96+
this.width = width;
97+
this.height = height;
98+
}
99+
100+
public void setScale(double scaleX, double scaleY) {
101+
this.scaleX = scaleX;
102+
this.scaleY = scaleY;
103+
}
104+
}

graphviz-java/src/main/java/guru/nidi/graphviz/model/GraphElementFinder.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package guru.nidi.graphviz.model;
1717

18+
import guru.nidi.graphviz.attribute.Named;
1819
import org.w3c.dom.Element;
1920

2021
import java.util.List;
@@ -29,9 +30,13 @@ public class GraphElementFinder extends SvgElementFinder {
2930

3031
GraphElementFinder(SvgElementFinder finder, MutableGraph graph) {
3132
super(finder);
32-
nodes = graph.nodes().stream().collect(toMap(n -> n.name().simpleSerialized(), n -> n));
33-
links = graph.edges().stream().collect(toMap(e -> e.from().name().simpleSerialized() + "--" + e.to().name().simpleSerialized(), e -> e));
34-
graphs = graph.graphs().stream().collect(toMap(g -> g.name().simpleSerialized(), g -> g));
33+
nodes = graph.nodes().stream().collect(toMap(n -> name(n), n -> n));
34+
links = graph.edges().stream().collect(toMap(e -> name(e.from()) + "--" + name(e.to()), e -> e));
35+
graphs = graph.graphs().stream().collect(toMap(g -> name(g), g -> g));
36+
}
37+
38+
private static String name(Named named) {
39+
return named.name().simpleSerialized();
3540
}
3641

3742
public MutableNode nodeOf(Element e) {
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package guru.nidi.graphviz.engine;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import static org.junit.jupiter.api.Assertions.assertEquals;
6+
7+
8+
class SvgSizeAnalyzerTest {
9+
private static final String SVG =
10+
"<svg width=\"1000pt\" height=\"500pt\" viewBox=\"0.00 0.00 7272.00 3618.00\" "
11+
+ "xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n"
12+
+ "<g id=\"graph0\" class=\"graph\" transform=\"scale(1.0 2.5) rotate(30.0) translate(360.0 32.0)\">";
13+
14+
@Test
15+
void getValues() {
16+
final SvgSizeAnalyzer a = new SvgSizeAnalyzer(SVG);
17+
assertEquals(1000, a.getWidth());
18+
assertEquals(500, a.getHeight());
19+
assertEquals("pt", a.getUnit());
20+
assertEquals(1.0, a.getScaleX());
21+
assertEquals(2.5, a.getScaleY());
22+
assertEquals(30, a.getRotate());
23+
assertEquals(360, a.getTranslateX());
24+
assertEquals(32, a.getTranslateY());
25+
}
26+
27+
@Test
28+
void adjustedWithoutChanges() {
29+
final SvgSizeAnalyzer a = new SvgSizeAnalyzer(SVG);
30+
assertEquals(SVG, a.adjusted());
31+
}
32+
33+
@Test
34+
void adjustedWithChanges() {
35+
final SvgSizeAnalyzer a = new SvgSizeAnalyzer(SVG);
36+
a.setSize(1, 2);
37+
a.setScale(3.5, 4.0);
38+
assertEquals("<svg width=\"1px\" height=\"2px\" viewBox=\"0.00 0.00 7272.00 3618.00\" "
39+
+ "xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n"
40+
+ "<g id=\"graph0\" class=\"graph\" transform=\"scale(3.5 4.0) rotate(30.0) translate(360.0 32.0)\">",
41+
a.adjusted());
42+
}
43+
}

0 commit comments

Comments
 (0)