|
package eu.webtoolkit.jwt;
|
|
|
|
import java.io.BufferedInputStream;
|
|
import java.io.ByteArrayInputStream;
|
|
import java.io.ByteArrayOutputStream;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.IOException;
|
|
import java.io.OutputStream;
|
|
import java.lang.reflect.Constructor;
|
|
import java.lang.reflect.InvocationTargetException;
|
|
import java.util.EnumSet;
|
|
import java.util.List;
|
|
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import com.pdfjet.Cap;
|
|
import com.pdfjet.CodePage;
|
|
import com.pdfjet.Embed;
|
|
import com.pdfjet.Font;
|
|
import com.pdfjet.Image;
|
|
import com.pdfjet.ImageType;
|
|
import com.pdfjet.Join;
|
|
import com.pdfjet.PDF;
|
|
import com.pdfjet.Page;
|
|
import com.pdfjet.Point;
|
|
import com.pdfjet.CoreFont;
|
|
|
|
|
|
import eu.webtoolkit.jwt.FontSupport.FontMatch;
|
|
import eu.webtoolkit.jwt.WTransform.TRSSDecomposition;
|
|
import eu.webtoolkit.jwt.servlet.WebRequest;
|
|
import eu.webtoolkit.jwt.servlet.WebResponse;
|
|
import eu.webtoolkit.jwt.utils.EnumUtils;
|
|
|
|
public class WPdfImage extends WResource implements WPaintDevice {
|
|
private static final Logger logger = LoggerFactory.getLogger(WPdfImage.class);
|
|
|
|
private static Constructor< ? > fontConstructor;
|
|
|
|
static {
|
|
for (Constructor< ? > c : Font.class.getConstructors()) {
|
|
Class< ? >[] paramTypes = c.getParameterTypes();
|
|
if (paramTypes.length == 3 &&
|
|
paramTypes[0] == PDF.class &&
|
|
paramTypes[1] == java.io.InputStream.class &&
|
|
//paramTypes[2].isPrimitive() && paramTypes[2].equals(java.lang.Integer.TYPE) &&
|
|
paramTypes[2].isPrimitive() && paramTypes[2].equals(java.lang.Boolean.TYPE)) {
|
|
fontConstructor = c;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public WPdfImage(WLength width, WLength height) {
|
|
this(width, height, null);
|
|
}
|
|
|
|
public WPdfImage(WLength width, WLength height, WObject parent) {
|
|
super(parent);
|
|
|
|
this.width = width;
|
|
this.height = height;
|
|
|
|
this.changeFlags = EnumSet.allOf(ChangeFlag.class);
|
|
|
|
try {
|
|
this.bos = new ByteArrayOutputStream();
|
|
this.pdf = new PDF(bos);
|
|
this.page = new Page(pdf, getSizeArray(width, height));
|
|
this.x = 0;
|
|
this.y = 0;
|
|
|
|
trueTypeFonts = new FontSupport(this);
|
|
}
|
|
catch (Exception e) {
|
|
logger.info("Exception", e);
|
|
}
|
|
}
|
|
|
|
public WPdfImage(PDF pdf, Page page, int x, int y, WLength width, WLength height) {
|
|
this(pdf, page, x, y, width, height, null);
|
|
}
|
|
|
|
public WPdfImage(PDF pdf, Page page, int x, int y, double width, double height) {
|
|
this(pdf, page, x, y, new WLength(width), new WLength(height), null);
|
|
}
|
|
|
|
public WPdfImage(PDF pdf, Page page, int x, int y, double width, double height, WObject parent) {
|
|
this(pdf, page, x, y, new WLength(width), new WLength(height), parent);
|
|
}
|
|
|
|
public WPdfImage(PDF pdf, Page page, int x, int y, WLength width, WLength height, WObject parent) {
|
|
super(parent);
|
|
|
|
this.pdf = pdf;
|
|
this.page = page;
|
|
this.x = x;
|
|
this.y = y;
|
|
this.width = width;
|
|
this.height = height;
|
|
|
|
this.changeFlags = EnumSet.allOf(ChangeFlag.class);
|
|
|
|
trueTypeFonts = new FontSupport(this);
|
|
|
|
setDeviceTransform(new WTransform());
|
|
}
|
|
|
|
public void addFontCollection(String directory, boolean recursive)
|
|
{
|
|
trueTypeFonts.addFontCollection(directory, recursive);
|
|
}
|
|
|
|
private float[] getSizeArray(WLength width, WLength height)
|
|
{
|
|
float[] size = new float[2];
|
|
size[0] = (float)width.getValue();
|
|
size[1] = (float)height.getValue();
|
|
return size;
|
|
}
|
|
|
|
@Override
|
|
public EnumSet<FeatureFlag> getFeatures() {
|
|
return EnumSet.of(FeatureFlag.HasFontMetrics, FeatureFlag.CanWordWrap);
|
|
}
|
|
|
|
@Override
|
|
public WLength getWidth() {
|
|
return this.width;
|
|
}
|
|
|
|
@Override
|
|
public WLength getHeight() {
|
|
return this.height;
|
|
}
|
|
|
|
@Override
|
|
public void setChanged(EnumSet<ChangeFlag> flags) {
|
|
this.changeFlags.addAll(flags);
|
|
}
|
|
|
|
@Override
|
|
public void setChanged(ChangeFlag flag, ChangeFlag... flags) {
|
|
setChanged(EnumSet.of(flag, flags));
|
|
}
|
|
|
|
private void _drawArc(double x, double y, double ray, double ang1, double ang2)
|
|
{
|
|
boolean cont_flg = false;
|
|
|
|
double angle = Math.abs(ang2 - ang1);
|
|
if (angle > 360)
|
|
throw new RuntimeException("Angle out of range: " + angle);
|
|
|
|
while (ang1 < 0 || ang2 < 0) {
|
|
ang1 = ang1 + 360;
|
|
ang2 = ang2 + 360;
|
|
}
|
|
|
|
for (;;) {
|
|
if (Math.abs(ang2 - ang1) <= 90) {
|
|
_internalArc(x, y, ray, ang1, ang2, cont_flg);
|
|
break;
|
|
}
|
|
else {
|
|
double tmp_ang = (ang2 > ang1 ? ang1 + 90 : ang1 - 90);
|
|
|
|
_internalArc(x, y, ray, ang1, tmp_ang, cont_flg);
|
|
|
|
cont_flg = true;
|
|
|
|
ang1 = tmp_ang;
|
|
}
|
|
|
|
if (Math.abs(ang1 - ang2) < 0.1)
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void _internalArc(double x, double y, double ray, double ang1, double ang2, boolean cont_flg) {
|
|
double rx0, ry0, rx1, ry1, rx2, ry2, rx3, ry3;
|
|
double x0, y0, x1, y1, x2, y2, x3, y3;
|
|
double delta_angle;
|
|
double new_angle;
|
|
|
|
delta_angle = (90 - (double)(ang1 + ang2) / 2) / 180 * Math.PI;
|
|
new_angle = (double)(ang2 - ang1) / 2 / 180 * Math.PI;
|
|
|
|
rx0 = ray * Math.cos(new_angle);
|
|
ry0 = ray * Math.sin(new_angle);
|
|
rx2 = (ray * 4.0 - rx0) / 3.0;
|
|
ry2 = ((ray * 1.0 - rx0) * (rx0 - ray * 3.0)) / (3.0 * ry0);
|
|
rx1 = rx2;
|
|
ry1 = -ry2;
|
|
rx3 = rx0;
|
|
ry3 = -ry0;
|
|
|
|
x0 = rx0 * Math.cos(delta_angle) - ry0 * Math.sin(delta_angle) + x;
|
|
y0 = rx0 * Math.sin(delta_angle) + ry0 * Math.cos(delta_angle) + y;
|
|
x1 = rx1 * Math.cos(delta_angle) - ry1 * Math.sin(delta_angle) + x;
|
|
y1 = rx1 * Math.sin(delta_angle) + ry1 * Math.cos(delta_angle) + y;
|
|
x2 = rx2 * Math.cos(delta_angle) - ry2 * Math.sin(delta_angle) + x;
|
|
y2 = rx2 * Math.sin(delta_angle) + ry2 * Math.cos(delta_angle) + y;
|
|
x3 = rx3 * Math.cos(delta_angle) - ry3 * Math.sin(delta_angle) + x;
|
|
y3 = rx3 * Math.sin(delta_angle) + ry3 * Math.cos(delta_angle) + y;
|
|
|
|
if (!cont_flg)
|
|
_lineTo(x0, y0);
|
|
|
|
_cubicBezierCurveTo(x1, y1, x2, y2, x3, y3);
|
|
}
|
|
|
|
@Override
|
|
public void drawArc(WRectF rect, double startAngle, double spanAngle) {
|
|
WPainterPath pp = new WPainterPath();
|
|
pp.arcTo(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight(), startAngle, spanAngle);
|
|
drawPath(pp);
|
|
}
|
|
|
|
@Override
|
|
public void drawImage(WRectF rect, String imgUrl, int imgWidth, int imgHeight, WRectF sourceRect) {
|
|
processChangeFlags();
|
|
|
|
Image image = null;
|
|
|
|
if (DataUri.isDataUri(imgUrl)) {
|
|
DataUri uri = new DataUri(imgUrl);
|
|
try {
|
|
byte[] b = new byte[uri.data.size()];
|
|
for (int i = 0; i < uri.data.size(); ++i)
|
|
b[i] = uri.data.get(i);
|
|
|
|
if ("image/png".equals(uri.mimeType))
|
|
image = new Image(this.pdf, new ByteArrayInputStream(b), ImageType.PNG);
|
|
else if ("image/jpeg".equals(uri.mimeType))
|
|
image = new Image(this.pdf, new ByteArrayInputStream(b), ImageType.JPG);
|
|
else if ("image/bmp".equals(uri.mimeType))
|
|
image = new Image(this.pdf, new ByteArrayInputStream(b), ImageType.BMP);
|
|
}
|
|
catch (Exception e) {
|
|
logger.info("Error converting data URI to image", e);
|
|
logger.trace("Data URI is {}", imgUrl, e);
|
|
}
|
|
}
|
|
else {
|
|
String mimeType = ImageUtils.identifyMimeType(imgUrl);
|
|
try {
|
|
if ("image/png".equals(mimeType))
|
|
image = new Image(this.pdf, new BufferedInputStream(FileUtils.getResourceAsStream(imgUrl)), ImageType.PNG);
|
|
else if ("image/jpeg".equals(mimeType))
|
|
image = new Image(this.pdf, new BufferedInputStream(FileUtils.getResourceAsStream(imgUrl)), ImageType.JPG);
|
|
else if ("image/bmp".equals(mimeType))
|
|
image = new Image(this.pdf, new BufferedInputStream(FileUtils.getResourceAsStream(imgUrl)), ImageType.BMP);
|
|
}
|
|
catch (Exception e) {
|
|
logger.info("Error creating image from {}", imgUrl, e);
|
|
}
|
|
}
|
|
|
|
if (image != null) {
|
|
WPointF p = currentTransform.map(new WPointF(rect.getX(), rect.getY()));
|
|
image.setPosition(p.getX(), p.getY());
|
|
|
|
double w = image.getWidth();
|
|
double h = image.getHeight();
|
|
|
|
TRSSDecomposition d = new TRSSDecomposition();
|
|
currentTransform.decomposeTranslateRotateScaleSkew(d);
|
|
float xScale = (float)((rect.getWidth() / w) * d.sx);
|
|
float yScale = (float)((rect.getHeight() / h) * d.sy);
|
|
image.scaleBy(xScale, yScale);
|
|
try {
|
|
image.drawOn(this.page);
|
|
}
|
|
catch (Exception e) {
|
|
logger.info("Exception while drawing image", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void drawLine(double x1, double y1, double x2, double y2) {
|
|
WPainterPath path = new WPainterPath();
|
|
path.moveTo(x1, y1);
|
|
path.lineTo(x2, y2);
|
|
drawPath(path);
|
|
}
|
|
|
|
private WPointF _transform(double x, double y) {
|
|
return currentTransform.map(new WPointF(x, y));
|
|
}
|
|
|
|
private Point _transformToPoint(double x, double y) {
|
|
WPointF p = _transform(x, y);
|
|
return new Point(p.getX(), p.getY());
|
|
}
|
|
|
|
private void _moveTo(double x, double y) {
|
|
try {
|
|
WPointF p_t = _transform(x, y);
|
|
this.page.moveTo(p_t.getX(), p_t.getY());
|
|
}
|
|
catch (IOException e) {
|
|
logger.info("IOException", e);
|
|
}
|
|
}
|
|
|
|
private void _lineTo(double x, double y) {
|
|
try {
|
|
WPointF p_t = _transform(x, y);
|
|
this.page.lineTo(p_t.getX(), p_t.getY());
|
|
}
|
|
catch (IOException e) {
|
|
logger.info("IOException", e);
|
|
}
|
|
}
|
|
|
|
private void _cubicBezierCurveTo(double x1, double y1, double x2, double y2, double x3, double y3) {
|
|
Point p1 = _transformToPoint(x1, y1);
|
|
Point p2 = _transformToPoint(x2, y2);
|
|
Point p3 = _transformToPoint(x3, y3);
|
|
try {
|
|
this.page.bezierCurveTo(p1, p2, p3);
|
|
}
|
|
catch (IOException e) {
|
|
logger.info("IOException", e);
|
|
}
|
|
}
|
|
|
|
private void drawPlainPath(WPainterPath path) {
|
|
List<WPainterPath.Segment> segments = path.getSegments();
|
|
|
|
if (segments.size() > 0
|
|
&& segments.get(0).getType() != WPainterPath.Segment.Type.MoveTo)
|
|
_moveTo(0, 0);
|
|
|
|
for (int i = 0; i < segments.size(); ++i) {
|
|
WPainterPath.Segment s = segments.get(i);
|
|
|
|
switch (s.getType()) {
|
|
case MoveTo:
|
|
_moveTo(s.getX(), s.getY());
|
|
break;
|
|
case LineTo:
|
|
_lineTo(s.getX(), s.getY());
|
|
break;
|
|
case CubicC1: {
|
|
final double x1 = s.getX();
|
|
final double y1 = s.getY();
|
|
final double x2 = segments.get(i + 1).getX();
|
|
final double y2 = segments.get(i + 1).getY();
|
|
final double x3 = segments.get(i + 2).getX();
|
|
final double y3 = segments.get(i + 2).getY();
|
|
|
|
WPointF current = path.getPositionAtSegment(i);
|
|
_cubicBezierCurveTo(x1, y1, x2, y2, x3, y3);
|
|
|
|
i += 2;
|
|
break;
|
|
}
|
|
case CubicC2:
|
|
case CubicEnd:
|
|
assert(false);
|
|
case ArcC: {
|
|
final double x = s.getX();
|
|
final double y = s.getY();
|
|
final double radius = segments.get(i + 1).getX();
|
|
double ang1 = segments.get(i + 2).getX();
|
|
double ang2 = ang1 + segments.get(i + 2).getY();
|
|
|
|
_drawArc(x, y, radius, ang1 + 90, ang2 + 90);
|
|
|
|
i += 2;
|
|
break;
|
|
}
|
|
case ArcR:
|
|
case ArcAngleSweep:
|
|
assert(false);
|
|
case QuadC: {
|
|
/*
|
|
* There is no quadratic bezier curve in pdfjet, so we emulate
|
|
* it using a cubic bezier curve.
|
|
*/
|
|
WPointF current = path.getPositionAtSegment(i);
|
|
final double cpx = s.getX();
|
|
final double cpy = s.getY();
|
|
final double x = segments.get(i + 1).getX();
|
|
final double y = segments.get(i + 1).getY();
|
|
|
|
final double cp1x = current.getX() + 2.0 / 3.0 * (cpx - current.getX());
|
|
final double cp1y = current.getY() + 2.0 / 3.0 * (cpy - current.getY());
|
|
final double cp2x = cp1x + (x - current.getX()) / 3.0;
|
|
final double cp2y = cp1y + (y - current.getY()) / 3.0;
|
|
|
|
_cubicBezierCurveTo(cp1x, cp1y, cp2x, cp2y, cpx, cpy);
|
|
|
|
i += 1;
|
|
|
|
break;
|
|
}
|
|
case QuadEnd:
|
|
assert(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void drawPath(WPainterPath path) {
|
|
processChangeFlags();
|
|
|
|
try {
|
|
boolean hasPen = getPainter().getPen().getStyle() != PenStyle.NoPen;
|
|
boolean hasBrush = getPainter().getBrush().getStyle() != BrushStyle.NoBrush;
|
|
|
|
if (hasPen || hasBrush) {
|
|
if (hasBrush) {
|
|
prepareBrush();
|
|
drawPlainPath(path);
|
|
page.fillPath();
|
|
}
|
|
|
|
if (hasPen) {
|
|
preparePen();
|
|
drawPlainPath(path);
|
|
page.strokePath();
|
|
}
|
|
}
|
|
else {
|
|
page.closePath();
|
|
}
|
|
}
|
|
catch (Exception e) {
|
|
logger.info("Exception drawing path", e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void drawText(WRectF rect, EnumSet<AlignmentFlag> flags, TextFlag textFlag, CharSequence text, WPointF clipPoint) {
|
|
if (textFlag == TextFlag.TextWordWrap)
|
|
throw new UnsupportedOperationException("drawText(): TextWordWrap not yet implemented");
|
|
|
|
if (clipPoint != null && this.getPainter() != null) {
|
|
if (!this.getPainter().getClipPathTransform().map(this.getPainter().getClipPath())
|
|
.isPointInPath(this.getPainter().getWorldTransform().map(clipPoint))) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
WColor penColor = painter.getPen().getColor();
|
|
try {
|
|
setBrushColor(penColor);
|
|
}
|
|
catch (IOException e2) {
|
|
logger.info("IOException", e2);
|
|
}
|
|
|
|
processChangeFlags();
|
|
|
|
TRSSDecomposition d = new TRSSDecomposition();
|
|
currentTransform.decomposeTranslateRotateScaleSkew(d);
|
|
|
|
try {
|
|
page.setTextDirection(360 - (int)Math.round(d.alpha * 180 / Math.PI));
|
|
}
|
|
catch (Exception e1) {
|
|
logger.info("IOException", e1);
|
|
}
|
|
|
|
double px = 0, py = 0;
|
|
|
|
AlignmentFlag horizontalAlign = EnumUtils.enumFromSet(EnumUtils.mask(flags, AlignmentFlag.AlignHorizontalMask));
|
|
AlignmentFlag verticalAlign = EnumUtils.enumFromSet(EnumUtils.mask(flags, AlignmentFlag.AlignVerticalMask));
|
|
|
|
String s = text.toString();
|
|
|
|
switch (horizontalAlign) {
|
|
case AlignLeft:
|
|
px = rect.getLeft();
|
|
break;
|
|
case AlignRight:
|
|
px = rect.getRight() - this.font.stringWidth(s);
|
|
break;
|
|
case AlignCenter:
|
|
px = rect.getCenter().getX() - this.font.stringWidth(s) / 2;
|
|
break;
|
|
}
|
|
|
|
switch (verticalAlign) {
|
|
case AlignBottom:
|
|
py = rect.getBottom();
|
|
break;
|
|
case AlignTop:
|
|
py = rect.getTop() + getFontMetrics().getHeight();
|
|
break;
|
|
case AlignMiddle:
|
|
py = rect.getCenter().getY() + getFontMetrics().getHeight() / 2;
|
|
}
|
|
|
|
py -= getFontMetrics().getDescent();
|
|
|
|
preparePen();
|
|
try {
|
|
WPointF p = new WPointF(px, py);
|
|
p = currentTransform.map(p);
|
|
|
|
double originalSize = this.font.getSize();
|
|
this.font.setSize(originalSize * (d.sx + d.sy) / 2);
|
|
|
|
page.drawString(font, s, p.getX(), p.getY());
|
|
|
|
this.font.setSize(originalSize);
|
|
}
|
|
catch (IOException e) {
|
|
logger.info("IOException", e);
|
|
}
|
|
|
|
WColor brushColor = painter.getBrush().getColor();
|
|
try {
|
|
setBrushColor(brushColor);
|
|
}
|
|
catch (IOException e2) {
|
|
logger.info("IOException", e2);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public WTextItem measureText(CharSequence text, double maxWidth, boolean wordWrap) {
|
|
processChangeFlags();
|
|
|
|
if (wordWrap) {
|
|
if (maxWidth == -1) {
|
|
return new WTextItem(text, font.stringWidth(text.toString()));
|
|
}
|
|
else {
|
|
String previousWord = null;
|
|
double previousWordWidth = 0;
|
|
|
|
for (int i = 0; i < text.length(); ++i) {
|
|
double w;
|
|
if (Character.isWhitespace(text.charAt(i)))
|
|
w = font.stringWidth(text.subSequence(0, i).toString());
|
|
else if (i == text.length() - 1)
|
|
w = font.stringWidth(text.subSequence(0, i + 1).toString());
|
|
else
|
|
continue;
|
|
|
|
String s = text.subSequence(0, i + 1).toString();
|
|
|
|
if (w > maxWidth) {
|
|
if (previousWord == null)
|
|
return new WTextItem(s, w);
|
|
else
|
|
return new WTextItem(previousWord, previousWordWidth);
|
|
}
|
|
else {
|
|
previousWord = s;
|
|
previousWordWidth = w;
|
|
}
|
|
}
|
|
return new WTextItem(text, font.stringWidth(text.toString()));
|
|
}
|
|
}
|
|
else {
|
|
return new WTextItem(text, font.stringWidth(text.toString()));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public WTextItem measureText(CharSequence text) {
|
|
return this.measureText(text, -1);
|
|
}
|
|
|
|
@Override
|
|
public WTextItem measureText(CharSequence text, double maxWidth) {
|
|
return this.measureText(text, maxWidth, false);
|
|
}
|
|
|
|
@Override
|
|
public WFontMetrics getFontMetrics() {
|
|
processChangeFlags();
|
|
|
|
double ascent = this.font.getAscent();
|
|
double descent = this.font.getDescent();
|
|
//TODO leading??
|
|
double leading = 0;
|
|
|
|
return new WFontMetrics(getPainter().getFont(), leading, ascent, descent);
|
|
}
|
|
|
|
@Override
|
|
public void init() {
|
|
|
|
}
|
|
|
|
@Override
|
|
public void done() {
|
|
}
|
|
|
|
@Override
|
|
public boolean isPaintActive() {
|
|
return painter != null;
|
|
}
|
|
|
|
@Override
|
|
public WPainter getPainter() {
|
|
return this.painter;
|
|
}
|
|
|
|
@Override
|
|
public void setPainter(WPainter painter) {
|
|
this.painter = painter;
|
|
}
|
|
|
|
@Override
|
|
protected void handleRequest(WebRequest request, WebResponse response) throws IOException {
|
|
response.setContentType("application/pdf");
|
|
|
|
write(response.getOutputStream());
|
|
}
|
|
|
|
public void write(OutputStream os) throws IOException {
|
|
try {
|
|
this.pdf.flush();
|
|
}
|
|
catch (Exception e) {
|
|
logger.info("Exception flushing pdf", e);
|
|
}
|
|
|
|
os.write(bos.toByteArray());
|
|
}
|
|
|
|
private void setPenColor(WColor c) throws IOException {
|
|
page.setPenColor(c.getRed() / 255., c.getGreen() / 255., c.getBlue() / 255.);
|
|
}
|
|
|
|
private void preparePen() {
|
|
WColor c = painter.getPen().getColor();
|
|
try {
|
|
setPenColor(c);
|
|
|
|
if (stroke.pattern != null)
|
|
page.setLinePattern(stroke.pattern);
|
|
else
|
|
page.setDefaultLinePattern();
|
|
page.setLineCapStyle(stroke.cap);
|
|
page.setLineJoinStyle(stroke.join);
|
|
page.setPenWidth(stroke.width * 0.75); // pixel to point
|
|
}
|
|
catch (IOException e) {
|
|
logger.info("IOException", e);
|
|
}
|
|
}
|
|
|
|
private void setBrushColor(WColor c) throws IOException {
|
|
page.setBrushColor(c.getRed() / 255., c.getGreen() / 255., c.getBlue() / 255.);
|
|
}
|
|
|
|
private void prepareBrush() {
|
|
WColor c = painter.getBrush().getColor();
|
|
try {
|
|
setBrushColor(c);
|
|
}
|
|
catch (IOException e) {
|
|
logger.info("IOException", e);
|
|
}
|
|
}
|
|
|
|
private class Stroke {
|
|
public Stroke(int cap, int join, float width, String pattern) {
|
|
this.cap = cap;
|
|
this.join = join;
|
|
this.width = width;
|
|
this.pattern = pattern;
|
|
}
|
|
|
|
int cap;
|
|
int join;
|
|
float width;
|
|
String pattern;
|
|
}
|
|
private Stroke createStroke(WPainter painter, WPen pen) {
|
|
int cap = 0;
|
|
switch (pen.getCapStyle()) {
|
|
case FlatCap: cap = Cap.BUTT; break;
|
|
case RoundCap: cap = Cap.ROUND; break;
|
|
case SquareCap: cap = Cap.PROJECTING_SQUARE; break;
|
|
}
|
|
|
|
int join = 0;
|
|
switch (pen.getJoinStyle()) {
|
|
case BevelJoin: join = Join.BEVEL; break;
|
|
case MiterJoin: join = Join.MITER; break;
|
|
case RoundJoin: join = Join.ROUND; break;
|
|
}
|
|
|
|
float width = 0;
|
|
if (pen.getStyle() != PenStyle.NoPen)
|
|
width = (float)painter.normalizedPenWidth(pen.getWidth(), false).toPixels();
|
|
|
|
String pattern = null;
|
|
switch (pen.getStyle()) {
|
|
case DashLine:
|
|
pattern = "[4 2] 0";
|
|
break;
|
|
case DotLine:
|
|
pattern = "[1 2] 0";
|
|
break;
|
|
case DashDotLine:
|
|
pattern = "[4 2 1 2] 0";
|
|
break;
|
|
case DashDotDotLine:
|
|
pattern = "[4 2 1 2 1 2] 0";
|
|
break;
|
|
default:
|
|
pattern = "[] 0";
|
|
break;
|
|
|
|
}
|
|
|
|
return new Stroke(cap, join, width, pattern);
|
|
}
|
|
|
|
private void processChangeFlags() {
|
|
boolean resetTransform = changeFlags.contains(ChangeFlag.Transform);
|
|
|
|
//TODO clipping + transform
|
|
/*
|
|
if (changeFlags.contains(ChangeFlag.Clipping)) {
|
|
setTransform(painter.getClipPathTransform());
|
|
if (painter.getClipPath().isEmpty())
|
|
g2.setClip(null);
|
|
else
|
|
g2.setClip(createShape(painter.getClipPath()));
|
|
resetTransform = true;
|
|
}*/
|
|
|
|
if (resetTransform) {
|
|
currentTransform = painter.getCombinedTransform().clone();
|
|
if (deviceTransform != null)
|
|
currentTransform = deviceTransform.multiply(currentTransform);
|
|
}
|
|
|
|
if (changeFlags.contains(ChangeFlag.Pen))
|
|
stroke = createStroke(painter, painter.getPen());
|
|
|
|
if (resetTransform || changeFlags.contains(ChangeFlag.Font)) {
|
|
TRSSDecomposition d = new TRSSDecomposition();
|
|
currentTransform.decomposeTranslateRotateScaleSkew(d);
|
|
|
|
this.font = createFont(painter.getFont());
|
|
}
|
|
|
|
changeFlags.clear();
|
|
}
|
|
|
|
private Font createFont(WFont font) {
|
|
if (fontConstructor != null) {
|
|
FontMatch fm = trueTypeFonts.matchFont(font);
|
|
if (fm.isMatched()) {
|
|
try {
|
|
FileInputStream fis = new FileInputStream(fm.getFileName());
|
|
//Font f = (Font)fontConstructor.newInstance(pdf, fis, CodePage.UNICODE, Embed.YES);
|
|
Font f = (Font)fontConstructor.newInstance(pdf, fis, Embed.YES);
|
|
f.setSize(font.getSizeLength().toPixels());
|
|
return f;
|
|
}
|
|
catch (IllegalArgumentException e) {
|
|
logger.error("IllegalArgumentException while creating font {}", font.getCssText(), e);
|
|
}
|
|
catch (InstantiationException e) {
|
|
logger.error("InstantiationException while creating font {}", font.getCssText(), e);
|
|
}
|
|
catch (IllegalAccessException e) {
|
|
logger.error("IllegalAccessException while creating font {}", font.getCssText(), e);
|
|
}
|
|
catch (InvocationTargetException e) {
|
|
logger.error("InvocationTargetException while creating font {}", font.getCssText(), e);
|
|
}
|
|
catch (FileNotFoundException e) {
|
|
logger.info("FileNotFoundException while creating font {}", font.getCssText(), e);
|
|
}
|
|
}
|
|
}
|
|
|
|
String name = PdfUtils.toBase14Font(font);
|
|
try {
|
|
Font f = new Font(pdf, NameToCore(name));
|
|
f.setSize(font.getSizeLength().toPixels());
|
|
return f;
|
|
}
|
|
catch (Exception e) {
|
|
logger.info("Error creating font {}", font.getCssText(), e);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private CoreFont NameToCore(String sName)
|
|
{
|
|
CoreFont oCoreFont = null;
|
|
|
|
if ("Helvetica".equals(sName))
|
|
{
|
|
oCoreFont = CoreFont.HELVETICA;
|
|
}
|
|
else
|
|
if ("Times".equals(sName))
|
|
{
|
|
oCoreFont = CoreFont.TIMES_ROMAN;
|
|
}
|
|
else
|
|
if ("Courier".equals(sName))
|
|
{
|
|
oCoreFont = CoreFont.COURIER;
|
|
}
|
|
else
|
|
if ("Symbol".equals(sName))
|
|
{
|
|
oCoreFont = CoreFont.SYMBOL;
|
|
}
|
|
else
|
|
if ("ZapfDingbats".equals(sName))
|
|
{
|
|
oCoreFont = CoreFont.ZAPF_DINGBATS;
|
|
}
|
|
return oCoreFont;
|
|
}
|
|
|
|
public void setDeviceTransform(WTransform transform) {
|
|
this.deviceTransform = new WTransform();
|
|
this.deviceTransform.translate(this.x, this.y);
|
|
this.deviceTransform.multiplyAndAssign(transform);
|
|
changeFlags.add(ChangeFlag.Transform);
|
|
}
|
|
|
|
private WPainter painter;
|
|
private EnumSet<ChangeFlag> changeFlags;
|
|
|
|
private WTransform deviceTransform;
|
|
|
|
private Font font;
|
|
private Stroke stroke;
|
|
|
|
private PDF pdf;
|
|
private Page page;
|
|
|
|
private double x;
|
|
private double y;
|
|
private WLength width;
|
|
private WLength height;
|
|
|
|
private WTransform currentTransform;
|
|
|
|
private ByteArrayOutputStream bos;
|
|
|
|
private FontSupport trueTypeFonts;
|
|
}
|