All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.codecrete.qrbill.canvas.SVGCanvas Maven / Gradle / Ivy

There is a newer version: 3.3.1
Show newest version
//
// 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.write("\n"); stream.write("Swiss QR Bill\n"); } @Override public void close() throws IOException { if (isInGroup) { stream.write("\n"); isInGroup = false; } if (stream != null) { stream.write("\n"); stream.write("\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