/* ======================================================================== * PlantUML : a free UML diagram generator * ======================================================================== * * (C) Copyright 2009-2024, Arnaud Roques * * Project Info: https://plantuml.com * * If you like this project or if you find it useful, you can support us at: * * https://plantuml.com/patreon (only 1$ per month!) * https://plantuml.com/paypal * * This file is part of PlantUML. * * PlantUML is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * PlantUML distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public * License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * * Original Author: Arnaud Roques * * */ package net.sourceforge.plantuml; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.IOException; import net.sourceforge.plantuml.braille.BrailleCharFactory; import net.sourceforge.plantuml.braille.UGraphicBraille; import net.sourceforge.plantuml.klimt.drawing.debug.StringBounderDebug; import net.sourceforge.plantuml.klimt.drawing.svg.SvgGraphics; import net.sourceforge.plantuml.klimt.font.StringBounder; import net.sourceforge.plantuml.klimt.font.StringBounderRaw; import net.sourceforge.plantuml.klimt.font.UFont; import net.sourceforge.plantuml.klimt.font.UFontContext; import net.sourceforge.plantuml.klimt.geom.XDimension2D; import net.sourceforge.plantuml.log.Logme; import net.sourceforge.plantuml.png.MetadataTag; import net.sourceforge.plantuml.security.SFile; import net.sourceforge.plantuml.text.SvgCharSizeHack; /** * Format for output files generated by PlantUML. * * @author Arnaud Roques * */ public enum FileFormat { // ::remove file when __HAXE__ // ::comment when __CORE__ EPS("application/postscript"), // EPS_TEXT("application/postscript"), // ATXT("text/plain"), // UTXT("text/plain;charset=UTF-8"), // XMI_STANDARD("application/vnd.xmi+xml"), // XMI_STAR("application/vnd.xmi+xml"), // XMI_ARGO("application/vnd.xmi+xml"), // XMI_SCRIPT("application/vnd.xmi+xml"), // SCXML("application/scxml+xml"), // GRAPHML("application/graphml+xml"), // PDF("application/pdf"), // HTML("text/html"), // HTML5("text/html"), // VDX("application/vnd.visio.xml"), // LATEX("application/x-latex"), // LATEX_NO_PREAMBLE("application/x-latex"), // BASE64("text/plain; charset=x-user-defined"), // BRAILLE_PNG("image/png"), // PREPROC("text/plain"), // DEBUG("text/plain"), // // ::done PNG("image/png"), // RAW("image/raw"), // SVG("image/svg+xml"); // private final String mimeType; FileFormat(String mimeType) { this.mimeType = mimeType; } public String getMimeType() { return mimeType; } /** * Returns the file format to be used for that format. * * @return a string starting by a point. */ public String getFileSuffix() { // ::comment when __CORE__ if (name().startsWith("XMI")) return ".xmi"; if (this == LATEX || this == LATEX_NO_PREAMBLE) return ".tex"; if (this == BRAILLE_PNG) return ".braille.png"; if (this == EPS_TEXT) return EPS.getFileSuffix(); // ::done return "." + StringUtils.goLowerCase(name()); } final static private BufferedImage imDummy = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); final static public Graphics2D gg = imDummy.createGraphics(); static { // KEY_FRACTIONALMETRICS gg.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); } public StringBounder getDefaultStringBounder() { return getDefaultStringBounder(TikzFontDistortion.getDefault(), SvgCharSizeHack.NO_HACK); } public StringBounder getDefaultStringBounder(TikzFontDistortion tikzFontDistortion, SvgCharSizeHack charSizeHack) { // ::comment when __CORE__ if (this == LATEX || this == LATEX_NO_PREAMBLE) return getTikzStringBounder(tikzFontDistortion); if (this == BRAILLE_PNG) return getBrailleStringBounder(); if (this == DEBUG) return new StringBounderDebug(); // ::done if (this == SVG) return getSvgStringBounder(charSizeHack); return getNormalStringBounder(); } private StringBounder getSvgStringBounder(final SvgCharSizeHack charSizeHack) { return new StringBounderRaw(FileFormat.gg.getFontRenderContext()) { public String toString() { return "FileFormat::getSvgStringBounder"; } protected XDimension2D calculateDimensionInternal(UFont font, String text) { text = charSizeHack.transformStringForSizeHack(text); return getJavaDimension(font, text); } public boolean matchesProperty(String propertyName) { return "SVG".equalsIgnoreCase(propertyName); } }; } private StringBounder getNormalStringBounder() { return new StringBounderRaw(FileFormat.gg.getFontRenderContext()) { public String toString() { return "FileFormat::getNormalStringBounder"; } protected XDimension2D calculateDimensionInternal(UFont font, String text) { return getJavaDimension(font, text); } public boolean matchesProperty(String propertyName) { return false; } }; } static private XDimension2D getJavaDimension(UFont font, String text) { final Font javaFont = font.getUnderlayingFont(UFontContext.G2D); final FontMetrics fm = gg.getFontMetrics(javaFont); final Rectangle2D rect = fm.getStringBounds(text, gg); return new XDimension2D(rect.getWidth(), rect.getHeight()); } // ::comment when __CORE__ private StringBounder getBrailleStringBounder() { return new StringBounderRaw(FileFormat.gg.getFontRenderContext()) { public String toString() { return "FileFormat::getBrailleStringBounder"; } protected XDimension2D calculateDimensionInternal(UFont font, String text) { final int nb = BrailleCharFactory.build(text).size(); final double quanta = UGraphicBraille.QUANTA; final double height = 5 * quanta; final double width = 3 * nb * quanta + 1; return new XDimension2D(width, height); } @Override public double getDescent(UFont font, String text) { return UGraphicBraille.QUANTA; } public boolean matchesProperty(String propertyName) { return false; } }; } private StringBounder getTikzStringBounder(final TikzFontDistortion tikzFontDistortion) { return new StringBounderRaw(FileFormat.gg.getFontRenderContext()) { public String toString() { return "FileFormat::getTikzStringBounder"; } protected XDimension2D calculateDimensionInternal(UFont font, String text) { text = text.replace("\t", " "); final XDimension2D w1 = getJavaDimension(font.goTikz(-1), text); final XDimension2D w2 = getJavaDimension(font.goTikz(0), text); final XDimension2D w3 = getJavaDimension(font.goTikz(1), text); final double factor = (w3.getWidth() - w1.getWidth()) / w2.getWidth(); final double distortion = tikzFontDistortion.getDistortion(); final double magnify = tikzFontDistortion.getMagnify(); final double delta = (w2.getWidth() - w1.getWidth()) * factor * distortion; return w2.withWidth(Math.max(w1.getWidth(), magnify * w2.getWidth() - delta)); } public boolean matchesProperty(String propertyName) { return false; } }; } /** * Check if this file format is Encapsulated PostScript. * * @return true for EPS. */ public boolean isEps() { if (this == EPS) return true; if (this == EPS_TEXT) return true; return false; } public String changeName(String fileName, int cpt) { if (cpt == 0) return changeName(fileName, getFileSuffix()); return changeName(fileName, OptionFlags.getInstance().getFileSeparator() + String.format("%03d", cpt) + getFileSuffix()); } // private SFile computeFilename(SFile pngFile, int i) { // if (i == 0) // return pngFile; // // final SFile dir = pngFile.getParentFile(); // return dir.file(computeFilenameInternal(pngFile.getName(), i)); // } private String changeName(String fileName, String replacement) { String result = fileName.replaceAll("\\.\\w+$", replacement); if (result.equals(fileName)) result = fileName + replacement; return result; } // private String computeFilenameInternal(String name, int i) { // if (i == 0) // return name; // // return name.replaceAll("\\" + getFileSuffix() + "$", // OptionFlags.getInstance().getFileSeparator() + String.format("%03d", i) + getFileSuffix()); // } public boolean doesSupportMetadata() { return this == PNG || this == SVG; } public boolean equalsMetadata(String currentMetadata, SFile existingFile) { try { if (this == PNG) { final MetadataTag tag = new MetadataTag(existingFile, "plantuml"); final String previousMetadata = tag.getData(); final boolean sameMetadata = currentMetadata.equals(previousMetadata); return sameMetadata; } if (this == SVG) { final String svg = FileUtils.readSvg(existingFile); if (svg == null) return false; final String currentSignature = SvgGraphics.getMetadataHex(currentMetadata); final int idx = svg.lastIndexOf(SvgGraphics.META_HEADER); if (idx != -1) { final String part = svg.substring(idx + SvgGraphics.META_HEADER.length()); return part.startsWith(currentSignature + "]"); } } } catch (IOException e) { Logme.error(e); } return false; } // ::done }