net.codecrete.qrbill.canvas.SVGCanvas Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of qrbill-generator Show documentation
Show all versions of qrbill-generator Show documentation
Java library for generating Swiss QR bills
//
// Swiss QR Bill Generator
// Copyright (c) 2018 Manuel Bleichenbacher
// Licensed under MIT License
// https://opensource.org/licenses/MIT
//
package net.codecrete.qrbill.canvas;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
/**
* Canvas for generating SVG files.
*/
public class SVGCanvas extends AbstractCanvas implements ByteArrayResult {
private ByteArrayOutputStream buffer;
private Writer stream;
private boolean isInGroup;
private boolean isFirstMoveInPath;
private double lastPositionX;
private double lastPositionY;
private int approxPathLength;
/**
* Creates a new instance of the specified size.
*
* For all text, the specified font family list will be used.
*
* @param width width of image, in mm
* @param height height of image, in mm
* @param fontFamilyList font family list (comma separated list, CSS syntax)
* @throws IOException thrown if the instance cannot be created
*/
public SVGCanvas(double width, double height, String fontFamilyList) throws IOException {
setupFontMetrics(fontFamilyList);
buffer = new ByteArrayOutputStream();
stream = new OutputStreamWriter(buffer, StandardCharsets.UTF_8);
stream.write("\n"
+ "\n"
+ "\n");
stream.close();
stream = null;
}
}
@Override
public void startPath() throws IOException {
stream.write(" 255) {
stream.write("\n");
approxPathLength = 0;
}
}
@Override
public void fillPath(int color) throws IOException {
stream.write("\" fill=\"#");
stream.write(formatColor(color));
stream.write("\"/>\n");
isFirstMoveInPath = true;
}
@Override
public void strokePath(double strokeWidth, int color) throws IOException {
strokePath(strokeWidth, color, LineStyle.Solid);
}
@Override
public void strokePath(double strokeWidth, int color, LineStyle lineStyle) throws IOException {
stream.write("\" stroke=\"#");
stream.write(formatColor(color));
if (strokeWidth != 1) {
stream.write("\" stroke-width=\"");
stream.write(formatNumber(strokeWidth));
}
if (lineStyle == LineStyle.Dashed) {
stream.write("\" stroke-dasharray=\"");
stream.write(formatNumber(strokeWidth * 4));
} else if (lineStyle == LineStyle.Dotted) {
stream.write("\" stroke-linecap=\"round\" stroke-dasharray=\"0 ");
stream.write(formatNumber(strokeWidth * 3));
}
stream.write("\" fill=\"none\"/>\n");
isFirstMoveInPath = true;
}
@Override
public void putText(String text, double x, double y, int fontSize, boolean isBold) throws IOException {
y = -y;
stream.write("");
stream.write(escapeXML(text));
stream.write(" \n");
}
@Override
public void setTransformation(double translateX, double translateY, double rotate, double scaleX, double scaleY) throws IOException {
if (isInGroup) {
stream.write("\n");
isInGroup = false;
}
if (translateX != 0 || translateY != 0 || scaleX != 1 || scaleY != 1) {
stream.write("\n");
isInGroup = true;
}
}
@Override
public byte[] toByteArray() throws IOException {
close();
return buffer.toByteArray();
}
/**
* Writes the resulting SVG image to the specified output stream.
* @param os the output stream
* @throws IOException thrown if the image cannot be written
*/
public void writeTo(OutputStream os) throws IOException {
close();
buffer.writeTo(os);
}
/**
* Saves the resulting SVG image to the specified path.
* @param path the path to write to
* @throws IOException thrown if the image cannot be written
*/
public void saveAs(Path path) throws IOException {
close();
try (OutputStream os = Files.newOutputStream(path)) {
buffer.writeTo(os);
}
}
private static final DecimalFormat NUMBER_FORMAT = new DecimalFormat("#.###", new DecimalFormatSymbols(Locale.UK));
private static final DecimalFormat ANGLE_FORMAT = new DecimalFormat("#.#####", new DecimalFormatSymbols(Locale.UK));
private static String formatNumber(double value) {
return NUMBER_FORMAT.format(value);
}
private static String formatCoordinate(double value) {
return NUMBER_FORMAT.format(value * MM_TO_PT);
}
private static String formatColor(int color) {
return String.format(Locale.US, "%06x", color);
}
private static String escapeXML(String text) {
int length = text.length();
int lastCopiedPosition = 0;
StringBuilder result = null;
for (int i = 0; i < length; i++) {
char ch = text.charAt(i);
if (ch == '<' || ch == '>' || ch == '&' || ch == '\'' || ch == '"') {
if (result == null)
result = new StringBuilder(length + 10);
if (i > lastCopiedPosition)
result.append(text, lastCopiedPosition, i);
String entity;
switch (ch) {
case '<':
entity = "<";
break;
case '>':
entity = ">";
break;
case '&':
entity = "&";
break;
case '\'':
entity = "'";
break;
default:
entity = """;
}
result.append(entity);
lastCopiedPosition = i + 1;
}
}
if (result == null)
return text;
if (length > lastCopiedPosition)
result.append(text, lastCopiedPosition, length);
return result.toString();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy