forked from yuzutech/kroki
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
resolves yuzutech#842 creates SVG and PNG error images
Use svgSalamander to create SVG and PNG images. Batik adds ~7mb of dependencies.
- Loading branch information
1 parent
21dbfca
commit 80f7cc1
Showing
6 changed files
with
225 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
99 changes: 99 additions & 0 deletions
99
server/src/main/java/io/kroki/server/error/ErrorImage.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
package io.kroki.server.error; | ||
|
||
import com.kitfox.svg.SVGDiagram; | ||
import com.kitfox.svg.SVGElement; | ||
import com.kitfox.svg.SVGException; | ||
import com.kitfox.svg.SVGUniverse; | ||
import com.kitfox.svg.app.beans.SVGIcon; | ||
|
||
import java.awt.Dimension; | ||
import java.awt.Graphics2D; | ||
import java.awt.geom.Rectangle2D; | ||
import java.awt.image.BufferedImage; | ||
import java.io.ByteArrayInputStream; | ||
import java.io.IOException; | ||
import java.net.URI; | ||
import java.nio.charset.StandardCharsets; | ||
|
||
public class ErrorImage { | ||
|
||
public static SVGWithDimension buildSVGImage(String errorMessage) throws IOException, SVGException { | ||
String[] lines = errorMessage.split("\\n"); | ||
StringBuilder text = new StringBuilder(); | ||
for (String line : lines) { | ||
// QUESTION: should we use ellipsis or force break if line length is too long? | ||
text.append("<tspan x=\"10\" dy=\"14\">").append(line).append("</tspan>\n"); | ||
} | ||
String svg = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" + | ||
"<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">\n" + | ||
"<rect fill=\"#fff5f7\" width=\"100%\" height=\"100%\"/>\n" + | ||
"<text id=\"text\" xml:space=\"preserve\" font-weight=\"bold\" fill=\"#cd0930\" font-family=\"Roboto\" font-size=\"16px\" x=\"0\" y=\"5\" dy=\"0\">\n" + | ||
text + | ||
"</text>\n" + | ||
"<rect fill=\"#ff3860\" width=\"3\" height=\"100%\"/>\n" + | ||
"</svg>"; | ||
Dimension dimension = computeDimension(svg); | ||
String result = svg | ||
.replaceAll("<svg xmlns=.*>\\n", "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"" + dimension.width + "\" height=\"" + dimension.height + "\" viewBox=\"0 0 " + dimension.width + " " + dimension.height + "\" version=\"1.1\">\n") | ||
.replaceAll("width=\"100%\"", "width=\"" + dimension.width + "\"") | ||
.replaceAll("height=\"100%\"", "height=\"" + dimension.height + "\""); | ||
return new SVGWithDimension(result, dimension); | ||
} | ||
|
||
public static BufferedImage buildPNGImage(String errorMessage) throws IOException, SVGException { | ||
SVGWithDimension svgWithDimension = buildSVGImage(errorMessage); | ||
try (ByteArrayInputStream svgInputStream = new ByteArrayInputStream(svgWithDimension.source.getBytes(StandardCharsets.UTF_8))) { | ||
SVGUniverse svgUniverse = new SVGUniverse(); | ||
URI svgUri = svgUniverse.loadSVG(svgInputStream, "error-message"); | ||
SVGDiagram diagram = svgUniverse.getDiagram(svgUri); | ||
SVGIcon svgIcon = new SVGIcon(); | ||
svgIcon.setSvgUniverse(svgUniverse); | ||
svgIcon.setSvgURI(svgUri); | ||
svgIcon.setAntiAlias(true); | ||
// svgIcon.setInterpolation(SVGIcon.INTERP_NEAREST_NEIGHBOR); | ||
// svgIcon.setInterpolation(SVGIcon.INTERP_BILINEAR); | ||
svgIcon.setInterpolation(SVGIcon.INTERP_BICUBIC); | ||
int width = (int) diagram.getWidth(); | ||
int height = (int) diagram.getHeight(); | ||
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); | ||
Graphics2D g = image.createGraphics(); | ||
g.setClip(0, 0, width, height); | ||
svgIcon.paintIcon(null, g, 0, 0); | ||
g.dispose(); | ||
return image; | ||
// ImageIO.write(image, "png", resultOutputStream); | ||
// SVGCache.getSVGUniverse().clear(); | ||
// resultOutputStream.flush(); | ||
// return image; | ||
} | ||
} | ||
|
||
private static Dimension computeDimension(String svg) throws IOException, SVGException { | ||
try (ByteArrayInputStream svgInputStream = new ByteArrayInputStream(svg.getBytes(StandardCharsets.UTF_8))) { | ||
SVGUniverse svgUniverse = new SVGUniverse(); | ||
URI svgUri = svgUniverse.loadSVG(svgInputStream, "error-message"); | ||
SVGDiagram diagram = svgUniverse.getDiagram(svgUri); | ||
SVGElement textElement = diagram.getElement("text"); | ||
Rectangle2D bbox = textElement.getRoot().getBoundingBox(); | ||
return new Dimension((int) Math.floor(bbox.getWidth() + bbox.getX()) + 10, (int) Math.floor(bbox.getHeight() + bbox.getY()) + 5); | ||
} | ||
} | ||
|
||
public static class SVGWithDimension { | ||
private final String source; | ||
private final Dimension dimension; | ||
|
||
public SVGWithDimension(String source, Dimension dimension) { | ||
this.source = source; | ||
this.dimension = dimension; | ||
} | ||
|
||
public String getSource() { | ||
return source; | ||
} | ||
|
||
public Dimension getDimension() { | ||
return dimension; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package io.kroki.server; | ||
|
||
import com.kitfox.svg.SVGException; | ||
import io.kroki.server.error.ErrorImage; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import java.awt.Dimension; | ||
import java.io.IOException; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
public class ErrorImageTest { | ||
|
||
@Test | ||
public void should_generate_svg_error_image() throws IOException, SVGException { | ||
ErrorImage.SVGWithDimension svgWithDimension = ErrorImage.buildSVGImage("There is no layout engine support for \"nfds\"\nUse one of: circo dot fdp neato nop nop1 nop2 osage patchwork sfdp twopi"); | ||
Dimension dimension = svgWithDimension.getDimension(); | ||
assertThat(dimension.width).isGreaterThan(500); | ||
assertThat(dimension.height).isGreaterThan(40); | ||
assertThat(svgWithDimension.getSource()).contains("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"); | ||
assertThat(svgWithDimension.getSource()).contains("<tspan x=\"10\" dy=\"14\">There is no layout engine support for \"nfds\"</tspan>\n"); | ||
assertThat(svgWithDimension.getSource()).contains("<tspan x=\"10\" dy=\"14\">Use one of: circo dot fdp neato nop nop1 nop2 osage patchwork sfdp twopi</tspan>\n"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters