net.sourceforge.plantuml.klimt.drawing.eps.EpsGraphics Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of plantuml-gplv2 Show documentation
Show all versions of plantuml-gplv2 Show documentation
PlantUML is a component that allows to quickly write diagrams from text.
// THIS FILE HAS BEEN GENERATED BY A PREPROCESSOR.
/* +=======================================================================
* |
* | 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/liberapay (only 1€ per month!)
* https://plantuml.com/paypal
*
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License V2.
*
* THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC
* LICENSE ("AGREEMENT"). [GNU General Public License V2]
*
* ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES
* RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
*
* You may obtain a copy of the License at
*
* https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* PlantUML can occasionally display sponsored or advertising messages. Those
* messages are usually generated on welcome or error images and never on
* functional diagrams.
* See https://plantuml.com/professional if you want to remove them
*
* Images (whatever their format : PNG, SVG, EPS...) generated by running PlantUML
* are owned by the author of their corresponding sources code (that is, their
* textual description in PlantUML language). Those images are not covered by
* this GPL v2 license.
*
* The generated images can then be used without any reference to the GPL v2 license.
* It is not even necessary to stipulate that they have been generated with PlantUML,
* although this will be appreciated by the PlantUML team.
*
* There is an exception : if the textual description in PlantUML language is also covered
* by any license, then the generated images are logically covered
* by the very same license.
*
* This is the IGY distribution (Install GraphViz by Yourself).
* You have to install GraphViz and to setup the GRAPHVIZ_DOT environment variable
* (see https://plantuml.com/graphviz-dot )
*
* Icons provided by OpenIconic : https://useiconic.com/open
* Archimate sprites provided by Archi : http://www.archimatetool.com
* Stdlib AWS provided by https://github.com/milo-minderbinder/AWS-PlantUML
* Stdlib Icons provided https://github.com/tupadr3/plantuml-icon-font-sprites
* ASCIIMathML (c) Peter Jipsen http://www.chapman.edu/~jipsen
* ASCIIMathML (c) David Lippman http://www.pierce.ctc.edu/dlippman
* CafeUndZopfli ported by Eugene Klyuchnikov https://github.com/eustas/CafeUndZopfli
* Brotli (c) by the Brotli Authors https://github.com/google/brotli
* Themes (c) by Brett Schwarz https://github.com/bschwarz/puml-themes
* Twemoji (c) by Twitter at https://twemoji.twitter.com/
*
*/
package net.sourceforge.plantuml.klimt.drawing.eps;
import java.awt.Color;
import java.awt.geom.PathIterator;
import java.awt.image.BufferedImage;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;
import net.sourceforge.plantuml.klimt.UPath;
import net.sourceforge.plantuml.klimt.color.ColorMapper;
import net.sourceforge.plantuml.klimt.color.HColorGradient;
import net.sourceforge.plantuml.klimt.geom.USegment;
import net.sourceforge.plantuml.klimt.geom.USegmentType;
import net.sourceforge.plantuml.klimt.geom.XCubicCurve2D;
import net.sourceforge.plantuml.klimt.shape.DotPath;
import net.sourceforge.plantuml.security.SecurityUtils;
import net.sourceforge.plantuml.text.BackSlash;
import net.sourceforge.plantuml.utils.Log;
import net.sourceforge.plantuml.utils.MathUtils;
import net.sourceforge.plantuml.version.Version;
public class EpsGraphics {
// ::remove folder when __CORE__
public static final String END_OF_FILE = "%plantuml done";
protected static final long COEF = 100L;
// http://www.linuxfocus.org/Francais/May1998/article43.html
// http://www.tailrecursive.org/postscript/text.html
private final StringBuilder body = new StringBuilder();
private final StringBuilder header = new StringBuilder();
private Color color = Color.BLACK;
private Color fillcolor = Color.BLACK;
private String strokeWidth = format(1);
// private String strokeDasharray = null;
private final PostScriptCommandMacro setcolorgradient = new PostScriptCommandMacro("setcolorgradient");
private final PostScriptCommandMacro simplerect = new PostScriptCommandMacro("simplerect");
private final PostScriptCommandMacro roundrect = new PostScriptCommandMacro("roundrect");
private boolean setcolorgradientUsed = false;
private boolean simplerectUsed = false;
private boolean roundrectUsed = false;
public EpsGraphics() {
header.append("%!PS-Adobe-3.0 EPSF-3.0\n");
header.append("%%Creator: PlantUML v" + Version.versionString(15) + BackSlash.NEWLINE);
header.append("%%Title: noTitle\n");
// header.append("%%CreationDate: " + new Date() + BackSlash.BS_N);
setcolorgradient.add(new PostScriptCommandRaw("3 index 7 index sub 1 index mul 7 index add", true));
setcolorgradient.add(new PostScriptCommandRaw("3 index 7 index sub 2 index mul 7 index add", true));
setcolorgradient.add(new PostScriptCommandRaw("3 index 7 index sub 3 index mul 7 index add", true));
setcolorgradient.add(new PostScriptCommandRaw("setrgbcolor", true));
// setcolorgradient.add(new PostScriptCommandRaw("0 7 1 {pop} for"));
setcolorgradient.add(new PostScriptCommandRaw("pop pop pop pop pop pop pop ", true));
simplerect.add(new PostScriptCommandRaw("newpath moveto 1 index 0 rlineto", true));
simplerect.add(new PostScriptCommandRaw("0 exch rlineto", true));
simplerect.add(new PostScriptCommandRaw("neg 0 rlineto", true));
roundrect.add(new PostScriptCommandRaw("newpath", true));
roundrect.add(new PostScriptCommandRaw("dup 3 index add 2 index 2 index add 2 index 180 270 arc", true));
roundrect.add(new PostScriptCommandRaw("2 index 5 index add 1 index sub 2 index 2 index add 2 index 270 0 arc",
true));
roundrect.add(new PostScriptCommandRaw(
"2 index 5 index add 1 index sub 2 index 5 index add 2 index sub 2 index 0 90 arc", true));
roundrect.add(
new PostScriptCommandRaw("dup 3 index add 2 index 5 index add 2 index sub 2 index 90 180 arc", true));
roundrect.add(new PostScriptCommandRaw("pop pop pop pop pop ", true));
}
private boolean closeDone = false;
private int maxX = 10;
private int maxY = 10;
final protected void ensureVisible(double x, double y) {
if (x > maxX)
maxX = (int) (x + 1);
if (y > maxY)
maxY = (int) (y + 1);
if (urlArea != null)
urlArea.ensureVisible((int) Math.round(x), (int) Math.round(y));
}
protected final Color getColor() {
return color;
}
final public void close() {
checkCloseDone();
header.append("%%BoundingBox: 0 0 " + maxX + " " + maxY + BackSlash.NEWLINE);
// header.append("%%DocumentData: Clean7Bit\n");
// header.append("%%DocumentProcessColors: Black\n");
header.append("%%ColorUsage: Color\n");
header.append("%%Origin: 0 0\n");
header.append("%%EndComments\n\n");
header.append("gsave\n");
header.append("0 " + maxY + " translate\n");
header.append(".01 -.01 scale\n");
if (setcolorgradientUsed)
header.append(setcolorgradient.getPostStringDefinition());
if (simplerectUsed)
header.append(simplerect.getPostStringDefinition());
if (roundrectUsed)
header.append(roundrect.getPostStringDefinition());
append("grestore", true);
// if(isClipSet())
// writer.write("grestore\n");
append("showpage", true);
append(END_OF_FILE, true);
append("%%EOF", true);
closeDone = true;
}
private void checkCloseDone() {
if (closeDone)
throw new IllegalStateException();
}
final public String getEPSCode() {
if (closeDone == false)
close();
return header.toString() + getBodyString();
}
protected String getBodyString() {
return body.toString();
}
public final void setStrokeColor(Color c) {
checkCloseDone();
this.color = c;
}
final public void setFillColor(Color c) {
checkCloseDone();
this.fillcolor = c;
}
public final void setStrokeWidth(double strokeWidth, double dashVisible, double dashSpace) {
checkCloseDone();
this.strokeWidth = format(strokeWidth);
this.dashVisible = (long) (dashVisible * COEF);
this.dashSpace = (long) (dashSpace * COEF);
}
private long dashVisible = 0;
private long dashSpace = 0;
final public void newpathDot() {
final boolean dashed = isDashed();
if (isNull(color))
throw new IllegalStateException();
checkCloseDone();
append(strokeWidth + " setlinewidth", true);
appendColor(color);
if (dashed)
append("[" + dashSpace + " " + dashVisible + "] 0 setdash", true);
append("newpath", true);
}
private boolean isDashed() {
return dashVisible != 0 || dashSpace != 0;
}
private boolean isDashed2() {
return dashVisible == 0 || dashSpace == 0;
}
private boolean isDashed3() {
return dashSpace != 0 && dashVisible != 0;
}
final public void closepathDot() {
final boolean dashed = isDashed();
append("stroke", true);
if (dashed)
append("[] 0 setdash", true);
}
final public void epsLine(double x1, double y1, double x2, double y2) {
if (isNull(color))
throw new IllegalStateException();
ensureVisible(x1, y1);
ensureVisible(x2, y2);
checkCloseDone();
append(strokeWidth + " setlinewidth", true);
appendColor(color);
append("newpath", true);
if (isDashed2()) {
append(format(x1) + " " + format(y1) + " moveto", true);
append(format(x2 - x1) + " " + format(y2 - y1) + " rlineto", true);
} else if (x1 == x2) {
epsHLine(x1, Math.min(y1, y2), Math.max(y1, y2));
} else if (y1 == y2) {
epsVLine(y1, Math.min(x1, x2), Math.max(x1, x2));
}
append("stroke", true);
ensureVisible(Math.max(x1, x2), Math.max(y1, y2));
}
protected void epsHLine(final double x, final double ymin, final double ymax) {
append(format(x) + " " + format(ymin) + " moveto", true);
for (long y2 = (long) (ymin * COEF); y2 < (long) (ymax * COEF); y2 += (dashVisible + dashSpace)) {
final long v;
if (y2 + dashVisible > (long) (ymax * COEF))
v = y2 - (long) (ymax * COEF);
else
v = dashSpace;
append("0 " + v + " rlineto", true);
append("0 " + dashSpace + " rmoveto", true);
}
}
protected void epsVLine(final double y, final double xmin, final double xmax) {
append(format(xmin) + " " + format(y) + " moveto", true);
for (long x2 = (long) (xmin * COEF); x2 < (long) (xmax * COEF); x2 += (dashVisible + dashSpace)) {
final long v;
if (x2 + dashVisible > (long) (xmax * COEF))
v = x2 - (long) (xmax * COEF);
else
v = dashSpace;
append("" + v + " 0 rlineto", true);
append("" + dashSpace + " 0 rmoveto", true);
}
}
final public void epsPath(double x, double y, UPath path) {
checkCloseDone();
if (isNull(fillcolor) == false) {
appendColor(fillcolor);
append("newpath", true);
for (USegment seg : path) {
final USegmentType type = seg.getSegmentType();
final double coord[] = seg.getCoord();
if (type == USegmentType.SEG_MOVETO) {
movetoNoMacro(coord[0] + x, coord[1] + y);
} else if (type == USegmentType.SEG_LINETO) {
linetoNoMacro(coord[0] + x, coord[1] + y);
} else if (type == USegmentType.SEG_QUADTO) {
throw new UnsupportedOperationException();
} else if (type == USegmentType.SEG_CUBICTO) {
curvetoNoMacro(coord[0] + x, coord[1] + y, coord[2] + x, coord[3] + y, coord[4] + x, coord[5] + y);
} else if (type == USegmentType.SEG_CLOSE) {
// Nothing
} else if (type == USegmentType.SEG_ARCTO) {
linetoNoMacro(coord[5] + x, coord[6] + y);
} else {
Log.println("unknown1 " + seg);
}
}
append("closepath eofill", true);
}
if (isNull(color) == false) {
append(strokeWidth + " setlinewidth", true);
appendColor(color);
append("newpath", true);
for (USegment seg : path) {
final USegmentType type = seg.getSegmentType();
final double coord[] = seg.getCoord();
if (type == USegmentType.SEG_MOVETO) {
movetoNoMacro(coord[0] + x, coord[1] + y);
} else if (type == USegmentType.SEG_LINETO) {
linetoNoMacro(coord[0] + x, coord[1] + y);
} else if (type == USegmentType.SEG_QUADTO) {
throw new UnsupportedOperationException();
} else if (type == USegmentType.SEG_CUBICTO) {
curvetoNoMacro(coord[0] + x, coord[1] + y, coord[2] + x, coord[3] + y, coord[4] + x, coord[5] + y);
} else if (type == USegmentType.SEG_CLOSE) {
// Nothing
} else if (type == USegmentType.SEG_ARCTO) {
linetoNoMacro(coord[5] + x, coord[6] + y);
} else {
Log.println("unknown2 " + seg);
}
}
append("stroke", true);
}
}
private final boolean isNull(Color c) {
return c == null || c.getAlpha() == 0;
}
final public void epsPolygon(HColorGradient gr, ColorMapper mapper, double... points) {
assert points.length % 2 == 0;
setFillColor(gr.getColor1().toColor(mapper));
epsPolygon(points);
}
final public void epsPolygon(double... points) {
assert points.length % 2 == 0;
checkCloseDone();
double lastX = 0;
double lastY = 0;
if (isNull(fillcolor) == false) {
appendColor(fillcolor);
append("newpath", true);
for (int i = 0; i < points.length; i += 2) {
ensureVisible(points[i], points[i + 1]);
if (i == 0)
append(format(points[i]) + " " + format(points[i + 1]) + " moveto", true);
else
append(format(points[i] - lastX) + " " + format(points[i + 1] - lastY) + " rlineto", true);
lastX = points[i];
lastY = points[i + 1];
}
append(format(points[0]) + " " + format(points[1]) + " lineto", true);
append("closepath eofill", true);
}
if (isNull(color) == false) {
append(strokeWidth + " setlinewidth", true);
appendColor(color);
append("newpath", true);
for (int i = 0; i < points.length; i += 2) {
ensureVisible(points[i], points[i + 1]);
if (i == 0)
append(format(points[i]) + " " + format(points[i + 1]) + " moveto", true);
else
append(format(points[i] - lastX) + " " + format(points[i + 1] - lastY) + " rlineto", true);
lastX = points[i];
lastY = points[i + 1];
}
append(format(points[0]) + " " + format(points[1]) + " lineto", true);
append("closepath stroke", true);
}
}
final public void epsRectangle(double x, double y, double width, double height, double rx, double ry) {
checkCloseDone();
ensureVisible(x, y);
ensureVisible(x + width, y + height);
if (isNull(fillcolor) == false) {
appendColor(fillcolor);
epsRectangleInternal(x, y, width, height, rx, ry, true);
append("closepath eofill", true);
if (isDashed3())
append("[] 0 setdash", true);
}
if (isNull(color) == false) {
append(strokeWidth + " setlinewidth", true);
appendColor(color);
epsRectangleInternal(x, y, width, height, rx, ry, false);
append("closepath stroke", true);
if (isDashed3())
append("[] 0 setdash", true);
}
}
final public void epsRectangle(double x, double y, double width, double height, double rx, double ry,
HColorGradient gr, ColorMapper mapper) {
if (isNull(color))
throw new IllegalStateException();
checkCloseDone();
ensureVisible(x, y);
ensureVisible(x + width, y + height);
setcolorgradientUsed = true;
if (rx == 0 && ry == 0) {
simplerectUsed = true;
appendColorShort(gr.getColor1().toColor(mapper));
appendColorShort(gr.getColor2().toColor(mapper));
append(format(width) + " " + format(height) + " " + format(x) + " " + format(y), true);
append("100 -1 1 {", true);
append("100 div", true);
append("newpath", true);
append("2 index 2 index moveto", true);
append("dup 5 index mul 2 mul dup 0 rlineto", true);
append("neg 4 index 2 index mul 2 mul rlineto", true);
append("closepath eoclip", true);
append("10 index 10 index 10 index", true);
append("10 index 10 index 10 index", true);
append("6 index setcolorgradient", true);
append("4 index 4 index 4 index 4 index simplerect", true);
append("closepath eofill", true);
append("pop", true);
append("} for", true);
append("pop pop pop pop", true);
append("pop pop pop", true);
append("pop pop pop", true);
append("initclip", true);
} else {
roundrectUsed = true;
appendColorShort(gr.getColor1().toColor(mapper));
appendColorShort(gr.getColor2().toColor(mapper));
append(format(width) + " " + format(height) + " " + format(x) + " " + format(y) + " "
+ format((rx + ry) / 2), true);
append("100 -1 1 {", true);
append("100 div", true);
append("newpath", true);
append("3 index 3 index moveto", true);
append("dup 6 index mul 2 mul dup 0 rlineto", true);
append("neg 5 index 2 index mul 2 mul rlineto", true);
append("closepath eoclip", true);
append("11 index 11 index 11 index", true);
append("11 index 11 index 11 index", true);
append("6 index setcolorgradient", true);
append("5 index 5 index 5 index 5 index 5 index roundrect", true);
append("closepath eofill", true);
append("pop", true);
append("} for", true);
append("pop pop pop pop pop", true);
append("pop pop pop", true);
append("pop pop pop", true);
append("initclip", true);
}
}
private void epsRectangleInternal(double x, double y, double width, double height, double rx, double ry,
boolean fill) {
if (rx == 0 && ry == 0)
simpleRectangle(x, y, width, height, fill);
else
roundRectangle(x, y, width, height, rx, ry);
}
private void roundRectangle(double x, double y, double width, double height, double rx, double ry) {
if (isDashed3())
append("[" + dashSpace + " " + dashVisible + "] 0 setdash", true);
final double round = MathUtils.min((rx + ry) / 2, width / 2, height / 2);
append(format(width) + " " + format(height) + " " + format(x) + " " + format(y) + " " + format(round)
+ " roundrect", true);
roundrectUsed = true;
}
private void simpleRectangle(double x, double y, double width, double height, boolean fill) {
if (isDashed3())
append("[" + dashSpace + " " + dashVisible + "] 0 setdash", true);
// if (isDashed3() || fill) {
append(format(width) + " " + format(height) + " " + format(x) + " " + format(y) + " simplerect", true);
simplerectUsed = true;
// }
}
/**
* Converts a counter clockwise angle to a clockwise angle. i.e. 0 -> 360, 90 ->
* 270, 180 -> 180, 270 -> 90
*
* @param counterClockwise counter clockwise angle in degrees
* @return clockwise angle in degrees
*/
private int convertToClockwiseAngle(double counterClockwise) {
return (int) (360.0 - counterClockwise);
}
final public void epsEllipse(double x, double y, double xRadius, double yRadius, double start, double extend) {
checkCloseDone();
ensureVisible(x + xRadius, y + yRadius);
double scale = 1;
if (xRadius != yRadius) {
scale = yRadius / xRadius;
append("gsave", true);
append("1 " + format(scale) + " scale", true);
}
// if (fillcolor != null) {
// appendColor(fillcolor);
// append("newpath", true);
// append(format(x) + " " + format(y / scale) + " " + format(xRadius) + " 0 360
// arc", true);
// append("closepath eofill", true);
// }
if (isNull(color) == false) {
append(strokeWidth + " setlinewidth", true);
appendColor(color);
append("newpath", true);
final double a1 = convertToClockwiseAngle(start + extend);
final double a2 = convertToClockwiseAngle(start);
append(format(x) + " " + format(y / scale) + " " + format(xRadius) + " " + a1 + " " + a2 + " arc", true);
append("stroke", true);
}
if (scale != 1)
append("grestore", true);
}
final public void epsEllipse(double x, double y, double xRadius, double yRadius) {
checkCloseDone();
ensureVisible(x + xRadius, y + yRadius);
double scale = 1;
if (xRadius != yRadius) {
scale = yRadius / xRadius;
append("gsave", true);
append("1 " + formatSimple4(scale) + " scale", true);
}
if (isNull(fillcolor) == false) {
appendColor(fillcolor);
append("newpath", true);
append(format(x) + " " + format(y / scale) + " " + format(xRadius) + " 0 360 arc", true);
append("closepath eofill", true);
}
if (isNull(color) == false) {
append(strokeWidth + " setlinewidth", true);
appendColor(color);
append("newpath", true);
append(format(x) + " " + format(y / scale) + " " + format(xRadius) + " 0 360 arc", true);
append("closepath stroke", true);
}
if (scale != 1)
append("grestore", true);
}
final protected void appendColor(Color c) {
if (isNull(c))
return;
final double r = c.getRed() / 255.0;
final double g = c.getGreen() / 255.0;
final double b = c.getBlue() / 255.0;
append(formatSimple2(r) + " " + formatSimple2(g) + " " + formatSimple2(b) + " setrgbcolor", true);
}
final protected void appendColorShort(Color c) {
if (isNull(c))
return;
final double r = c.getRed() / 255.0;
final double g = c.getGreen() / 255.0;
final double b = c.getBlue() / 255.0;
append(formatSimple2(r) + " " + formatSimple2(g) + " " + formatSimple2(b), true);
}
static String format(double x) {
if (x == 0)
return "0";
return Long.toString((long) (x * COEF));
}
public static String formatSimple4(double x) {
if (x == 0)
return "0";
String s = String.format(Locale.US, "%1.4f", x);
s = s.replaceAll("(\\.\\d*?)0+$", "$1");
if (s.endsWith("."))
s = s.substring(0, s.length() - 1);
return s;
}
private static String formatSimple2(double x) {
if (x == 0)
return "0";
String s = String.format(Locale.US, "%1.2f", x);
s = s.replaceAll("(\\.\\d*?)0+$", "$1");
if (s.endsWith("."))
s = s.substring(0, s.length() - 1);
return s;
}
protected void append(String s, boolean checkConsistence) {
if (checkConsistence && s.indexOf(" ") != -1)
throw new IllegalArgumentException(s);
body.append(s + BackSlash.NEWLINE);
}
final public void linetoNoMacro(double x1, double y1) {
append(format(x1) + " " + format(y1) + " lineto", true);
ensureVisible(x1, y1);
}
final public void movetoNoMacro(double x1, double y1) {
append(format(x1) + " " + format(y1) + " moveto", true);
ensureVisible(x1, y1);
}
final public void curvetoNoMacro(double x1, double y1, double x2, double y2, double x3, double y3) {
append(format(x1) + " " + format(y1) + " " + format(x2) + " " + format(y2) + " " + format(x3) + " " + format(y3)
+ " curveto", true);
ensureVisible(x1, y1);
ensureVisible(x2, y2);
ensureVisible(x3, y3);
}
// FONT
public void moveto(double x1, double y1) {
append(format(x1) + " " + format(y1) + " moveto", true);
ensureVisible(x1, y1);
}
public void lineto(double x1, double y1) {
append(format(x1) + " " + format(y1) + " lineto", true);
ensureVisible(x1, y1);
}
public void curveto(double x1, double y1, double x2, double y2, double x3, double y3) {
append(format(x1) + " " + format(y1) + " " + format(x2) + " " + format(y2) + " " + format(x3) + " " + format(y3)
+ " curveto", true);
ensureVisible(x1, y1);
ensureVisible(x2, y2);
ensureVisible(x3, y3);
}
public void quadto(double x1, double y1, double x2, double y2) {
append(format(x1) + " " + format(y1) + " " + format(x1) + " " + format(y1) + " " + format(x2) + " " + format(y2)
+ " curveto", true);
ensureVisible(x1, y1);
ensureVisible(x2, y2);
}
public void newpath() {
append("0 setlinewidth", true);
appendColor(color);
append("newpath", true);
}
public void closepath() {
append("closepath", true);
}
public void fill(int windingRule) {
append("%fill", true);
if (windingRule == PathIterator.WIND_EVEN_ODD)
append("eofill", true);
else if (windingRule == PathIterator.WIND_NON_ZERO)
append("fill", true);
}
final public void drawImage(BufferedImage image, double x, double y) {
final int width = image.getWidth();
final int height = image.getHeight();
append("gsave", true);
append(format(x) + " " + format(y) + " translate", true);
append(format(width) + " " + format(height) + " scale", true);
append("" + width + " " + height + " 8 [" + width + " 0 0 -" + height + " 0 " + height + "]", true);
// append("" + width + " " + height + " 8 [0 0 0 0 0 0]");
append("{<", true);
final StringBuilder sb = new StringBuilder();
for (int j = height - 1; j >= 0; j--)
for (int i = 0; i < width; i++) {
final String hexString = getRgb(image.getRGB(i, j));
assert hexString.length() == 6;
sb.append(hexString);
}
append(sb.toString(), true);
// append(">} image");
append(">} false 3 colorimage", true);
ensureVisible(x + width, y + height);
append("grestore", true);
}
static String getRgb(int x) {
final String s = "000000" + Integer.toHexString(x);
return s.substring(s.length() - 6);
}
final public void drawEps(String eps, double x, double y) {
final int idx = eps.indexOf("%%BoundingBox:");
if (idx == -1)
throw new IllegalArgumentException();
final StringTokenizer st = new StringTokenizer(eps.substring(idx + "%%BoundingBox:".length()), " \n\t\r");
final int x1 = Integer.parseInt(st.nextToken());
final int y1 = Integer.parseInt(st.nextToken());
final int x2 = Integer.parseInt(st.nextToken());
final int y2 = Integer.parseInt(st.nextToken());
assert x2 >= x1;
assert y2 >= y1;
append("gsave", true);
final double dx = x - x1;
final double dy = y + y2;
append(format(dx) + " " + format(dy) + " translate", true);
append("1 -1 scale", true);
append(eps, false);
ensureVisible(x + (x2 - x1), y + (y2 - y1));
append("grestore", true);
}
protected final long getDashVisible() {
return dashVisible;
}
protected final long getDashSpace() {
return dashSpace;
}
static class UrlArea {
private final String url;
private int xmin = Integer.MAX_VALUE;
private int xmax = Integer.MIN_VALUE;
private int ymin = Integer.MAX_VALUE;
private int ymax = Integer.MIN_VALUE;
UrlArea(String url) {
this.url = url;
}
void ensureVisible(int x, int y) {
if (x < xmin)
xmin = x;
if (x > xmax)
xmax = x;
if (y < ymin)
ymin = y;
if (y > ymax)
ymax = y;
}
}
private UrlArea urlArea;
final public void closeLink() {
if (urlArea != null && urlArea.xmin != Integer.MAX_VALUE) {
final int width = urlArea.xmax - urlArea.xmin;
final int height = urlArea.ymax - urlArea.ymin;
assert width >= 0 && height >= 0;
epsUrlLink(urlArea.xmin, urlArea.ymin, width, height, urlArea.url);
}
this.urlArea = null;
}
final public void epsUrlLink(int x, int y, int width, int height, String url) {
append("[ /Rect [ " + x + " " + y + " " + (x + width) + " " + (y + height) + " ]", true);
append("/Border [ 0 0 0 ]", true);
append("/Action << /Subtype /URI /URI (" + url + ") >>", true);
append("/Subtype /Link", true);
append("/ANN pdfmark", true);
}
final public void openLink(String url) {
// javascript: security issue
if (SecurityUtils.ignoreThisLink(url))
return;
this.urlArea = new UrlArea(url);
}
// Shadow
final private ShadowManager shadowManager = new ShadowManager(50, 200);
final public void epsRectangleShadow(double x, double y, double width, double height, double rx, double ry,
double deltaShadow) {
setStrokeColor(null);
for (double i = 0; i <= deltaShadow; i += 0.5) {
setFillColor(shadowManager.getColor(i, deltaShadow));
final double diff = i;
epsRectangle(x + deltaShadow + diff, y + deltaShadow + diff, width - 2 * diff, height - 2 * diff, rx + 1,
ry + 1);
}
}
final public void epsPolygonShadow(double deltaShadow, double... points) {
assert points.length % 2 == 0;
setStrokeColor(null);
for (double i = 0; i <= deltaShadow; i += 0.5) {
setFillColor(shadowManager.getColor(i, deltaShadow));
final double diff = i;
epsPolygon(shadowManager.getShadowDeltaPoints(deltaShadow, diff, points));
}
}
final public void epsEllipseShadow(double x, double y, double xRadius, double yRadius, double deltaShadow) {
setStrokeColor(null);
for (double i = 0; i <= deltaShadow; i += 0.5) {
setFillColor(shadowManager.getColor(i, deltaShadow));
final double diff = i;
epsEllipse(x + deltaShadow, y + deltaShadow, xRadius - diff, yRadius - diff);
}
}
public void drawOk(DotPath dotPath, double x, double y) {
// boolean first = true;
for (XCubicCurve2D bez : dotPath.getBeziers()) {
bez = new XCubicCurve2D(x + bez.x1, y + bez.y1, x + bez.ctrlx1, y + bez.ctrly1, x + bez.ctrlx2,
y + bez.ctrly2, x + bez.x2, y + bez.y2);
this.epsLine(bez.x1, bez.y1, bez.x2, bez.y2);
}
}
public void drawBezier(List beziers, double x, double y) {
this.newpathDot();
final boolean dashed = false;
boolean first = true;
for (XCubicCurve2D bez : beziers) {
bez = new XCubicCurve2D(x + bez.x1, y + bez.y1, x + bez.ctrlx1, y + bez.ctrly1, x + bez.ctrlx2,
y + bez.ctrly2, x + bez.x2, y + bez.y2);
if (first) {
this.movetoNoMacro(bez.x1, bez.y1);
first = dashed;
}
this.curvetoNoMacro(bez.ctrlx1, bez.ctrly1, bez.ctrlx2, bez.ctrly2, bez.x2, bez.y2);
}
this.closepathDot();
}
}