Skip to content

Commit 8ffccfa

Browse files
committed
support image attribute also for js engines (see #101)
1 parent ed83a0c commit 8ffccfa

File tree

7 files changed

+342
-33
lines changed

7 files changed

+342
-33
lines changed
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright © 2015 Stefan Niederhauser ([email protected])
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.attribute;
17+
18+
public final class Image implements Attributes<ForNode> {
19+
public enum Position {
20+
TOP_LEFT("tl"),
21+
TOP_CENTER("tc"),
22+
TOP_RIGHT("tr"),
23+
MIDDLE_LEFT("ml"),
24+
MIDDLE_CENTER("mc"),
25+
MIDDLE_RIGHT("mr"),
26+
BOTTOM_LEFT("bl"),
27+
BOTTOM_CENTER("bc"),
28+
BOTTOM_RIGHT("br");
29+
30+
String value;
31+
32+
Position(String value) {
33+
this.value = value;
34+
}
35+
}
36+
37+
public enum Scale {
38+
NONE("false"),
39+
FIT("true"),
40+
WIDTH("width"),
41+
HEIGHT("height"),
42+
BOTH("both");
43+
44+
String value;
45+
46+
Scale(String value) {
47+
this.value = value;
48+
}
49+
}
50+
51+
private final String path;
52+
private final Position position;
53+
private final Scale scale;
54+
55+
private Image(String path, Position position, Scale scale) {
56+
this.path = path;
57+
this.position = position;
58+
this.scale = scale;
59+
}
60+
61+
public static Image of(String path) {
62+
return new Image(path, Position.MIDDLE_CENTER, Scale.NONE);
63+
}
64+
65+
public Image position(Position position) {
66+
return new Image(path, position, scale);
67+
}
68+
69+
public Image scale(Scale scale) {
70+
return new Image(path, position, scale);
71+
}
72+
73+
@Override
74+
public Attributes<? super ForNode> applyTo(MapAttributes<? super ForNode> attrs) {
75+
attrs.add("image", path);
76+
if (position != Position.MIDDLE_CENTER) {
77+
attrs.add("imagepos", position.value);
78+
}
79+
if (scale != Scale.NONE) {
80+
attrs.add("imagescale", scale.value);
81+
}
82+
return attrs;
83+
}
84+
}

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

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

21+
import java.io.File;
2122
import java.util.function.Consumer;
23+
import java.util.function.Function;
24+
import java.util.regex.Matcher;
25+
import java.util.regex.Pattern;
2226

2327
public abstract class AbstractGraphvizEngine implements GraphvizEngine {
2428
private static final Logger LOG = LoggerFactory.getLogger(AbstractGraphvizEngine.class);
29+
protected static final Pattern IMG_SRC = Pattern.compile("<img .*?src\\s*=\\s*['\"]([^'\"]*)");
30+
protected static final Pattern IMAGE_ATTR = Pattern.compile("\"?image\"?\\s*=\\s*\"(.*?)\"");
2531

2632
private final boolean sync;
2733

@@ -48,6 +54,25 @@ private void initTask(Consumer<GraphvizEngine> onOk, Consumer<GraphvizEngine> on
4854
}
4955
}
5056

57+
protected String replacePaths(String src, Pattern pattern, Function<String, String> replacer) {
58+
final Matcher matcher = pattern.matcher(src);
59+
final StringBuilder s = new StringBuilder();
60+
int last = 0;
61+
while (matcher.find()) {
62+
final String attr = matcher.group(1);
63+
s.append(src, last, matcher.start(1));
64+
s.append(replacer.apply(attr));
65+
last = matcher.end(1);
66+
}
67+
return s.append(src.substring(last)).toString();
68+
}
69+
70+
protected String replacePath(String path, File basedir) {
71+
return path.startsWith("http://") || path.startsWith("https://") || new File(path).isAbsolute()
72+
? path
73+
: new File(basedir, path).getAbsolutePath();
74+
}
75+
5176
@Override
5277
public void close() {
5378
}

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

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
import java.io.IOException;
1919
import java.io.InputStream;
20+
import java.util.AbstractMap.SimpleEntry;
21+
import java.util.Map.Entry;
2022

2123
public abstract class AbstractJsGraphvizEngine extends AbstractGraphvizEngine {
2224
public AbstractJsGraphvizEngine(boolean sync) {
@@ -31,12 +33,24 @@ public String execute(String src, Options options) {
3133
protected abstract String jsExecute(String jsCall);
3234

3335
protected String jsVizExec(String src, Options options) {
34-
return src.startsWith("render") ? src : ("render('" + preprocessCode(src) + "'," + options.toJson(false) + ");");
36+
final Entry<String, Options> srcAndOptions = preprocessCode(src, options);
37+
return src.startsWith("render")
38+
? src
39+
: ("render('" + srcAndOptions.getKey() + "'," + srcAndOptions.getValue().toJson(false) + ");");
3540
}
3641

37-
protected String preprocessCode(String src) {
38-
if (src.contains("<img")) throw new IllegalArgumentException("Found <img> tag. This is not supported by JS engines. Either use the GraphvizCmdLineEngine or a node with image attribute.");
39-
return jsEscape(src);
42+
protected Entry<String, Options> preprocessCode(String src, Options options) {
43+
if (src.contains("<img")) {
44+
throw new GraphvizException("Found <img> tag. This is not supported by JS engines. "
45+
+ "Either use the GraphvizCmdLineEngine or a node with image attribute.");
46+
}
47+
final Options[] opts = new Options[]{options};
48+
final String pathsReplaced = replacePaths(src, IMAGE_ATTR, path -> {
49+
final String realPath = replacePath(path, options.basedir);
50+
opts[0] = opts[0].image(realPath);
51+
return realPath;
52+
});
53+
return new SimpleEntry<>(jsEscape(pathsReplaced), opts[0]);
4054
}
4155

4256
protected String jsEscape(String js) {

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

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@
2626
import java.nio.file.Files;
2727
import java.nio.file.Path;
2828
import java.util.Optional;
29-
import java.util.regex.Matcher;
30-
import java.util.regex.Pattern;
3129

3230
import static java.util.Locale.ENGLISH;
3331

@@ -38,7 +36,6 @@
3836
*/
3937
public class GraphvizCmdLineEngine extends AbstractGraphvizEngine {
4038
private static final Logger LOG = LoggerFactory.getLogger(AbstractGraphvizEngine.class);
41-
private static final Pattern IMG_SRC = Pattern.compile("<img .*?src\\s*=\\s*['\"]([^'\"]*)");
4239

4340
private final String envPath;
4441
private final CommandRunner cmdRunner;
@@ -97,18 +94,8 @@ public String execute(String src, Options options) {
9794
}
9895

9996
protected String preprocessCode(String src, Options options) {
100-
final Matcher matcher = IMG_SRC.matcher(src);
101-
final StringBuilder s = new StringBuilder();
102-
int last = 0;
103-
while (matcher.find()) {
104-
final String attr = matcher.group(1);
105-
s.append(src, last, matcher.start(1));
106-
s.append((attr.startsWith("http://") || attr.startsWith("https://") || new File(attr).isAbsolute())
107-
? attr
108-
: new File(options.basedir, attr).getAbsolutePath());
109-
last = matcher.end(1);
110-
}
111-
return s.append(src.substring(last)).toString();
97+
final String imgReplaced = replacePaths(src, IMG_SRC, path -> replacePath(path, options.basedir));
98+
return replacePaths(imgReplaced, IMAGE_ATTR, path -> replacePath(path, options.basedir));
11299
}
113100

114101
private String getEngineExecutable(@Nullable Engine engine) {

0 commit comments

Comments
 (0)