plantuml/src/net/sourceforge/plantuml/eps/EpsGraphics.java

814 lines
25 KiB
Java

/* ========================================================================
* PlantUML : a free UML diagram generator
* ========================================================================
*
* (C) Copyright 2009-2020, Arnaud Roques
*
* Project Info: http://plantuml.com
*
* If you like this project or if you find it useful, you can support us at:
*
* http://plantuml.com/patreon (only 1$ per month!)
* http://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.eps;
import java.awt.Color;
import java.awt.geom.PathIterator;
import java.awt.image.BufferedImage;
import java.util.Locale;
import java.util.StringTokenizer;
import net.sourceforge.plantuml.BackSlash;
import net.sourceforge.plantuml.Log;
import net.sourceforge.plantuml.graphic.HtmlColorGradient;
import net.sourceforge.plantuml.ugraphic.ColorMapper;
import net.sourceforge.plantuml.ugraphic.ShadowManager;
import net.sourceforge.plantuml.ugraphic.UPath;
import net.sourceforge.plantuml.ugraphic.USegment;
import net.sourceforge.plantuml.ugraphic.USegmentType;
import net.sourceforge.plantuml.utils.MathUtils;
import net.sourceforge.plantuml.version.Version;
public class EpsGraphics {
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;
}
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();
}
}
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;
}
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;
public void newpathDot() {
final boolean dashed = isDashed();
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;
}
public void closepathDot() {
final boolean dashed = isDashed();
append("stroke", true);
if (dashed) {
append("[] 0 setdash", true);
}
}
public void epsLine(double x1, double y1, double x2, double y2) {
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);
}
}
public void epsPath(double x, double y, UPath path) {
checkCloseDone();
if (fillcolor != null) {
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 {
Log.println("unknown1 " + seg);
}
}
append("closepath eofill", true);
}
if (color != null) {
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 {
Log.println("unknown2 " + seg);
}
}
append("stroke", true);
}
}
public void epsPolygon(HtmlColorGradient gr, ColorMapper mapper, double... points) {
assert points.length % 2 == 0;
setFillColor(mapper.getMappedColor(gr.getColor1()));
epsPolygon(points);
}
public void epsPolygon(double... points) {
assert points.length % 2 == 0;
checkCloseDone();
double lastX = 0;
double lastY = 0;
if (fillcolor != null) {
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 (color != null) {
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);
}
}
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 (fillcolor != null) {
appendColor(fillcolor);
epsRectangleInternal(x, y, width, height, rx, ry, true);
append("closepath eofill", true);
if (isDashed3()) {
append("[] 0 setdash", true);
}
}
if (color != null) {
append(strokeWidth + " setlinewidth", true);
appendColor(color);
epsRectangleInternal(x, y, width, height, rx, ry, false);
append("closepath stroke", true);
if (isDashed3()) {
append("[] 0 setdash", true);
}
}
}
public void epsRectangle(double x, double y, double width, double height, double rx, double ry,
HtmlColorGradient gr, ColorMapper mapper) {
checkCloseDone();
ensureVisible(x, y);
ensureVisible(x + width, y + height);
setcolorgradientUsed = true;
if (rx == 0 && ry == 0) {
simplerectUsed = true;
appendColorShort(mapper.getMappedColor(gr.getColor1()));
appendColorShort(mapper.getMappedColor(gr.getColor2()));
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(mapper.getMappedColor(gr.getColor1()));
appendColorShort(mapper.getMappedColor(gr.getColor2()));
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;
// }
}
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 (color != null) {
append(strokeWidth + " setlinewidth", true);
appendColor(color);
append("newpath", true);
final double a1 = -start + 180 + 5;
final double a2 = -start - extend + 180 - 5;
append(format(x) + " " + format(y / scale) + " " + format(xRadius) + " " + format(a1) + " " + format(a2)
+ " arc", true);
append("stroke", true);
}
if (scale != 1) {
append("grestore", true);
}
}
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 (fillcolor != null) {
appendColor(fillcolor);
append("newpath", true);
append(format(x) + " " + format(y / scale) + " " + format(xRadius) + " 0 360 arc", true);
append("closepath eofill", true);
}
if (color != null) {
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);
}
}
protected void appendColor(Color c) {
if (c == null) {
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);
}
protected void appendColorShort(Color c) {
if (c == null) {
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);
}
}
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);
}
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;
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;
}
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);
}
public void openLink(String url) {
this.urlArea = new UrlArea(url);
}
// Shadow
final private ShadowManager shadowManager = new ShadowManager(50, 200);
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);
}
}
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));
}
}
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);
}
}
}