- /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * 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.
- */
-
- /* $Id$ */
-
- package org.apache.fop.svg;
-
- import java.awt.AlphaComposite;
- import java.awt.BasicStroke;
- import java.awt.Color;
- import java.awt.Dimension;
- import java.awt.GradientPaint;
- import java.awt.Graphics;
- import java.awt.Graphics2D;
- import java.awt.GraphicsConfiguration;
- import java.awt.Image;
- import java.awt.Paint;
- import java.awt.PaintContext;
- import java.awt.Rectangle;
- import java.awt.Shape;
- import java.awt.Stroke;
- import java.awt.color.ColorSpace;
- import java.awt.geom.AffineTransform;
- import java.awt.geom.PathIterator;
- import java.awt.geom.Point2D;
- import java.awt.geom.Rectangle2D;
- import java.awt.image.BufferedImage;
- import java.awt.image.ColorModel;
- import java.awt.image.DataBuffer;
- import java.awt.image.DirectColorModel;
- import java.awt.image.ImageObserver;
- import java.awt.image.Raster;
- import java.awt.image.RenderedImage;
- import java.awt.image.WritableRaster;
- import java.awt.image.renderable.RenderableImage;
- import java.io.IOException;
- import java.io.OutputStream;
- import java.io.StringWriter;
- import java.util.List;
- import java.util.Map;
-
- import org.apache.batik.ext.awt.LinearGradientPaint;
- import org.apache.batik.ext.awt.MultipleGradientPaint;
- import org.apache.batik.ext.awt.RadialGradientPaint;
- import org.apache.batik.ext.awt.RenderingHintsKeyExt;
- import org.apache.batik.gvt.GraphicsNode;
- import org.apache.batik.gvt.PatternPaint;
-
- import org.apache.xmlgraphics.image.GraphicsConstants;
- import org.apache.xmlgraphics.image.loader.ImageInfo;
- import org.apache.xmlgraphics.image.loader.ImageSize;
- import org.apache.xmlgraphics.image.loader.impl.ImageRawCCITTFax;
- import org.apache.xmlgraphics.image.loader.impl.ImageRawJPEG;
- import org.apache.xmlgraphics.image.loader.impl.ImageRendered;
- import org.apache.xmlgraphics.java2d.AbstractGraphics2D;
- import org.apache.xmlgraphics.java2d.GraphicContext;
-
- import org.apache.fop.fonts.Font;
- import org.apache.fop.fonts.FontInfo;
- import org.apache.fop.fonts.FontSetup;
- import org.apache.fop.pdf.BitmapImage;
- import org.apache.fop.pdf.PDFAnnotList;
- import org.apache.fop.pdf.PDFColor;
- import org.apache.fop.pdf.PDFColorHandler;
- import org.apache.fop.pdf.PDFConformanceException;
- import org.apache.fop.pdf.PDFDeviceColorSpace;
- import org.apache.fop.pdf.PDFDocument;
- import org.apache.fop.pdf.PDFGState;
- import org.apache.fop.pdf.PDFImage;
- import org.apache.fop.pdf.PDFImageXObject;
- import org.apache.fop.pdf.PDFLink;
- import org.apache.fop.pdf.PDFNumber;
- import org.apache.fop.pdf.PDFPaintingState;
- import org.apache.fop.pdf.PDFPattern;
- import org.apache.fop.pdf.PDFResourceContext;
- import org.apache.fop.pdf.PDFResources;
- import org.apache.fop.pdf.PDFText;
- import org.apache.fop.pdf.PDFXObject;
- import org.apache.fop.render.pdf.ImageRawCCITTFaxAdapter;
- import org.apache.fop.render.pdf.ImageRawJPEGAdapter;
- import org.apache.fop.render.pdf.ImageRenderedAdapter;
-
- /**
- * <p>PDF Graphics 2D.
- * Used for drawing into a pdf document as if it is a graphics object.
- * This takes a pdf document and draws into it.</p>
- *
- * <p>This work was authored by Keiron Liddle (keiron@aftexsw.com).</p>
- *
- * @see org.apache.batik.ext.awt.g2d.AbstractGraphics2D
- */
- public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHandler {
- private static final AffineTransform IDENTITY_TRANSFORM = new AffineTransform();
-
- /** The number of decimal places. */
- private static final int DEC = 8;
-
- /** Convenience constant for full opacity */
- static final int OPAQUE = 255;
-
- /**
- * the PDF Document being created
- */
- protected PDFDocument pdfDoc;
-
- /**
- * The current resource context for adding fonts, patterns etc.
- */
- protected PDFResourceContext resourceContext;
-
- /**
- * The PDF reference of the current page.
- */
- protected String pageRef;
-
- /**
- * The PDF painting state
- */
- protected PDFPaintingState paintingState;
-
- /** the PDF color handler */
- protected PDFColorHandler colorHandler;
-
- /**
- * The PDF graphics state level that this svg is being drawn into.
- */
- protected int baseLevel = 0;
-
- /**
- * The count of natively handled images added to document so they receive
- * unique keys.
- */
- protected int nativeCount = 0;
-
- /**
- * The current font information.
- */
- protected FontInfo fontInfo;
-
- /**
- * The override font state used when drawing text and the font cannot be
- * set using java fonts.
- */
- protected Font ovFontState = null;
-
- /**
- * the current stream to add PDF commands to
- */
- protected StringWriter currentStream = new StringWriter();
-
- /**
- * the current (internal) font name
- */
- protected String currentFontName;
-
- /**
- * the current font size in millipoints
- */
- protected float currentFontSize;
-
- /**
- * The output stream for the pdf document.
- * If this is set then it can progressively output
- * the pdf document objects to reduce memory.
- * Especially with images.
- */
- protected OutputStream outputStream = null;
-
- /**
- * Create a new PDFGraphics2D with the given pdf document info.
- * This is used to create a Graphics object for use inside an already
- * existing document.
- *
- * @param textAsShapes if true then draw text as shapes
- * @param fi the current font information
- * @param doc the pdf document for creating pdf objects
- * @param page the current resource context or page
- * @param pref the PDF reference of the current page
- * @param font the current font name
- * @param size the current font size
- */
- public PDFGraphics2D(boolean textAsShapes, FontInfo fi, PDFDocument doc,
- PDFResourceContext page, String pref, String font, float size) {
- this(textAsShapes);
- pdfDoc = doc;
- this.colorHandler = new PDFColorHandler(doc.getResources());
- resourceContext = page;
- currentFontName = font;
- currentFontSize = size;
- fontInfo = fi;
- pageRef = pref;
- paintingState = new PDFPaintingState();
- }
-
- /**
- * Create a new PDFGraphics2D.
- *
- * @param textAsShapes true if drawing text as shapes
- */
- protected PDFGraphics2D(boolean textAsShapes) {
- super(textAsShapes);
- }
-
- /**
- * This constructor supports the create method.
- * This is not implemented properly.
- *
- * @param g the PDF graphics to make a copy of
- */
- public PDFGraphics2D(PDFGraphics2D g) {
- super(g);
- this.pdfDoc = g.pdfDoc;
- this.colorHandler = g.colorHandler;
- this.resourceContext = g.resourceContext;
- this.currentFontName = g.currentFontName;
- this.currentFontSize = g.currentFontSize;
- this.fontInfo = g.fontInfo;
- this.pageRef = g.pageRef;
- this.paintingState = g.paintingState;
- this.currentStream = g.currentStream;
- this.nativeCount = g.nativeCount;
- this.outputStream = g.outputStream;
- this.ovFontState = g.ovFontState;
- }
-
- /**
- * Creates a new <code>Graphics</code> object that is
- * a copy of this <code>Graphics</code> object.
- * @return a new graphics context that is a copy of
- * this graphics context.
- */
- @Override
- public Graphics create() {
- return new PDFGraphics2D(this);
- }
-
- /**
- * Central handler for IOExceptions for this class.
- * @param ioe IOException to handle
- */
- protected void handleIOException(IOException ioe) {
- //TODO Surely, there's a better way to do this.
- ioe.printStackTrace();
- }
-
- /**
- * This method is used by PDFDocumentGraphics2D to prepare a new page if
- * necessary.
- */
- protected void preparePainting() {
- //nop, used by PDFDocumentGraphics2D
- }
-
- /**
- * Set the PDF state to use when starting to draw
- * into the PDF graphics.
- *
- * @param state the PDF state
- */
- public void setPaintingState(PDFPaintingState state) {
- paintingState = state;
- baseLevel = paintingState.getStackLevel();
- }
-
- /**
- * Set the output stream that this PDF document is
- * being drawn to. This is so that it can progressively
- * use the PDF document to output data such as images.
- * This results in a significant saving on memory.
- *
- * @param os the output stream that is being used for the PDF document
- */
- public void setOutputStream(OutputStream os) {
- outputStream = os;
- }
-
- /**
- * Get the string containing all the commands written into this
- * Graphics.
- * @return the string containing the PDF markup
- */
- public String getString() {
- return currentStream.toString();
- }
-
- /**
- * Get the string buffer from the currentStream, containing all
- * the commands written into this Graphics so far.
- * @return the StringBuffer containing the PDF markup
- */
- public StringBuffer getBuffer() {
- return currentStream.getBuffer();
- }
-
- /**
- * Gets the PDF reference of the current page.
- * @return the PDF reference of the current page
- */
- public String getPageReference() {
- return this.pageRef;
- }
-
- /**
- * Set the Graphics context.
- * @param c the graphics context to use
- */
- public void setGraphicContext(GraphicContext c) {
- gc = c;
- setPrivateHints();
- }
-
- private void setPrivateHints() {
- setRenderingHint(RenderingHintsKeyExt.KEY_AVOID_TILE_PAINTING,
- RenderingHintsKeyExt.VALUE_AVOID_TILE_PAINTING_ON);
- }
-
- /**
- * Set the override font state for drawing text.
- * This is used by the PDF text painter so that it can temporarily
- * set the font state when a java font cannot be used.
- * The next drawString will use this font state.
- *
- * @param infont the font state to use
- */
- public void setOverrideFontState(Font infont) {
- ovFontState = infont;
- }
-
- /**
- * Restore the PDF graphics state to the starting state level.
- */
- /* seems not to be used
- public void restorePDFState() {
- for (int count = graphicsState.getStackLevel(); count > baseLevel; count--) {
- currentStream.write("Q\n");
- }
- graphicsState.restoreLevel(baseLevel);
- }*/
-
- private void concatMatrix(double[] matrix) {
- currentStream.write(PDFNumber.doubleOut(matrix[0], DEC) + " "
- + PDFNumber.doubleOut(matrix[1], DEC) + " "
- + PDFNumber.doubleOut(matrix[2], DEC) + " "
- + PDFNumber.doubleOut(matrix[3], DEC) + " "
- + PDFNumber.doubleOut(matrix[4], DEC) + " "
- + PDFNumber.doubleOut(matrix[5], DEC) + " cm\n");
- }
-
- private void concatMatrix(AffineTransform transform) {
- if (!transform.isIdentity()) {
- double[] matrix = new double[6];
- transform.getMatrix(matrix);
- concatMatrix(matrix);
- }
- }
-
- /**
- * This is mainly used for shading patterns which use the document-global coordinate system
- * instead of the local one.
- * @return the transformation matrix that established the basic user space for this document
- */
- protected AffineTransform getBaseTransform() {
- AffineTransform at = new AffineTransform(paintingState.getTransform());
- return at;
- }
-
- /**
- * This is a pdf specific method used to add a link to the
- * pdf document.
- *
- * @param bounds the bounds of the link in user coordinates
- * @param trans the transform of the current drawing position
- * @param dest the PDF destination
- * @param linkType the type of link, internal or external
- */
- public void addLink(Rectangle2D bounds, AffineTransform trans, String dest, int linkType) {
- if (!pdfDoc.getProfile().isAnnotationAllowed()) {
- return;
- }
- preparePainting();
- AffineTransform at = getTransform();
- Shape b = at.createTransformedShape(bounds);
- b = trans.createTransformedShape(b);
- if (b != null) {
- Rectangle rect = b.getBounds();
-
- if (linkType != PDFLink.EXTERNAL) {
- String pdfdest = "/FitR " + dest;
- resourceContext.addAnnotation(
- pdfDoc.getFactory().makeLink(rect, getPageReference(), pdfdest));
- } else {
- resourceContext.addAnnotation(
- pdfDoc.getFactory().makeLink(rect, dest, linkType, 0));
- }
- }
- }
-
- /**
- * Add a natively handled image directly to the PDF document.
- * This is used by the PDFImageElementBridge to draw a natively handled image
- * (like JPEG or CCITT images)
- * directly into the PDF document rather than converting the image into
- * a bitmap and increasing the size.
- *
- * @param image the image to draw
- * @param x the x position
- * @param y the y position
- * @param width the width to draw the image
- * @param height the height to draw the image
- */
- public void addNativeImage(org.apache.xmlgraphics.image.loader.Image image, float x, float y,
- float width, float height) {
- preparePainting();
- String key = image.getInfo().getOriginalURI();
- if (key == null) {
- // Need to include hash code as when invoked from FO you
- // may have several 'independent' PDFGraphics2D so the
- // count is not enough.
- key = "__AddNative_" + hashCode() + "_" + nativeCount;
- nativeCount++;
- }
-
- PDFImage pdfImage;
- if (image instanceof ImageRawJPEG) {
- pdfImage = new ImageRawJPEGAdapter((ImageRawJPEG)image, key);
- } else if (image instanceof ImageRawCCITTFax) {
- pdfImage = new ImageRawCCITTFaxAdapter((ImageRawCCITTFax)image, key);
- } else {
- throw new IllegalArgumentException(
- "Unsupported Image subclass: " + image.getClass().getName());
- }
-
- PDFXObject xObject = this.pdfDoc.addImage(resourceContext, pdfImage);
- flushPDFDocument();
-
- AffineTransform at = new AffineTransform();
- at.translate(x, y);
- useXObject(xObject, at, width, height);
- }
-
- private void flushPDFDocument() {
- if (outputStream != null) {
- try {
- this.pdfDoc.output(outputStream);
- } catch (IOException ioe) {
- // ignore exception, will be thrown again later
- }
- }
- }
-
- /**
- * Draws as much of the specified image as is currently available.
- * The image is drawn with its top-left corner at
- * (<i>x</i>, <i>y</i>) in this graphics context's coordinate
- * space. Transparent pixels in the image do not affect whatever
- * pixels are already there.
- * <p>
- * This method returns immediately in all cases, even if the
- * complete image has not yet been loaded, and it has not been dithered
- * and converted for the current output device.
- * <p>
- * If the image has not yet been completely loaded, then
- * <code>drawImage</code> returns <code>false</code>. As more of
- * the image becomes available, the process that draws the image notifies
- * the specified image observer.
- * @param img the specified image to be drawn.
- * @param x the <i>x</i> coordinate.
- * @param y the <i>y</i> coordinate.
- * @param observer object to be notified as more of
- * the image is converted.
- * @return true if the image was drawn
- * @see java.awt.Image
- * @see java.awt.image.ImageObserver
- * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
- */
- @Override
- public boolean drawImage(Image img, int x, int y,
- ImageObserver observer) {
- preparePainting();
-
- int width = img.getWidth(observer);
- int height = img.getHeight(observer);
-
- if (width == -1 || height == -1) {
- return false;
- }
-
- return drawImage(img, x, y, width, height, observer);
- }
-
- private BufferedImage buildBufferedImage(Dimension size) {
- return new BufferedImage(size.width, size.height,
- BufferedImage.TYPE_INT_ARGB);
- }
-
- /** {@inheritDoc} */
- @Override
- public boolean drawImage(Image img, int x, int y, int width, int height,
- ImageObserver observer) {
- preparePainting();
- // first we look to see if we've already added this image to
- // the pdf document. If so, we just reuse the reference;
- // otherwise we have to build a FopImage and add it to the pdf
- // document
- String key = "TempImage:" + img.toString();
- PDFXObject xObject = pdfDoc.getXObject(key);
- if (xObject == null) {
- // OK, have to build and add a PDF image
-
- Dimension size = new Dimension(width, height);
- BufferedImage buf = buildBufferedImage(size);
-
- java.awt.Graphics2D g = buf.createGraphics();
- g.setComposite(AlphaComposite.SrcOver);
- g.setBackground(new Color(1, 1, 1, 0));
- g.setPaint(new Color(1, 1, 1, 0));
- g.fillRect(0, 0, width, height);
-
- int imageWidth = buf.getWidth();
- int imageHeight = buf.getHeight();
- g.clip(new Rectangle(0, 0, imageWidth, imageHeight));
- g.setComposite(gc.getComposite());
-
- boolean drawn = g.drawImage(img, 0, 0, imageWidth, imageHeight, observer);
- if (!drawn) {
- return false;
- }
- g.dispose();
-
- xObject = addRenderedImage(key, buf);
- } else {
- resourceContext.getPDFResources().addXObject(xObject);
- }
-
- AffineTransform at = new AffineTransform();
- at.translate(x, y);
- useXObject(xObject, at, width, height);
- return true;
- }
-
- /**
- * Disposes of this graphics context and releases
- * any system resources that it is using.
- * A <code>Graphics</code> object cannot be used after
- * <code>dispose</code>has been called.
- * <p>
- * When a Java program runs, a large number of <code>Graphics</code>
- * objects can be created within a short time frame.
- * Although the finalization process of the garbage collector
- * also disposes of the same system resources, it is preferable
- * to manually free the associated resources by calling this
- * method rather than to rely on a finalization process which
- * may not run to completion for a long period of time.
- * <p>
- * Graphics objects which are provided as arguments to the
- * <code>paint</code> and <code>update</code> methods
- * of components are automatically released by the system when
- * those methods return. For efficiency, programmers should
- * call <code>dispose</code> when finished using
- * a <code>Graphics</code> object only if it was created
- * directly from a component or another <code>Graphics</code> object.
- * @see java.awt.Graphics#finalize
- * @see java.awt.Component#paint
- * @see java.awt.Component#update
- * @see java.awt.Component#getGraphics
- * @see java.awt.Graphics#create
- */
- @Override
- public void dispose() {
- pdfDoc = null;
- fontInfo = null;
- currentStream = null;
- currentFontName = null;
- }
-
- /**
- * Strokes the outline of a <code>Shape</code> using the settings of the
- * current <code>Graphics2D</code> context. The rendering attributes
- * applied include the <code>Clip</code>, <code>Transform</code>,
- * <code>Paint</code>, <code>Composite</code> and
- * <code>Stroke</code> attributes.
- * @param s the <code>Shape</code> to be rendered
- * @see #setStroke
- * @see #setPaint
- * @see java.awt.Graphics#setColor
- * @see #transform
- * @see #setTransform
- * @see #clip
- * @see #setClip
- * @see #setComposite
- */
- @Override
- public void draw(Shape s) {
- preparePainting();
-
- //Transparency shortcut
- Color c;
- c = getColor();
- if (c.getAlpha() == 0) {
- return;
- }
-
- AffineTransform trans = getTransform();
- double[] tranvals = new double[6];
- trans.getMatrix(tranvals);
-
- Shape imclip = getClip();
- boolean newClip = paintingState.checkClip(imclip);
- boolean newTransform = paintingState.checkTransform(trans)
- && !trans.isIdentity();
-
- if (newClip || newTransform) {
- saveGraphicsState();
- if (newTransform) {
- concatMatrix(tranvals);
- }
- if (newClip) {
- writeClip(imclip);
- }
- }
-
- applyAlpha(OPAQUE, c.getAlpha());
-
- c = getColor();
- applyColor(c, false);
- c = getBackground();
- applyColor(c, true);
-
- Paint paint = getPaint();
- if (paintingState.setPaint(paint)) {
- if (!applyPaint(paint, false)) {
- // Stroke the shape and use it to 'clip'
- // the paint contents.
- Shape ss = getStroke().createStrokedShape(s);
- applyUnknownPaint(paint, ss);
-
- if (newClip || newTransform) {
- restoreGraphicsState();
- }
- return;
- }
- }
- applyStroke(getStroke());
-
- PathIterator iter = s.getPathIterator(IDENTITY_TRANSFORM);
- processPathIterator(iter);
- doDrawing(false, true, false);
- if (newClip || newTransform) {
- restoreGraphicsState();
- }
- }
-
- /*
- // in theory we could set the clip using these methods
- // it doesn't seem to improve the file sizes much
- // and makes everything more complicated
-
- Shape lastClip = null;
-
- public void clip(Shape cl) {
- super.clip(cl);
- Shape newClip = getClip();
- if (newClip == null || lastClip == null
- || !(new Area(newClip).equals(new Area(lastClip)))) {
- graphicsState.setClip(newClip);
- writeClip(newClip);
- }
-
- lastClip = newClip;
- }
-
- public void setClip(Shape cl) {
- super.setClip(cl);
- Shape newClip = getClip();
- if (newClip == null || lastClip == null
- || !(new Area(newClip).equals(new Area(lastClip)))) {
- for (int count = graphicsState.getStackLevel(); count > baseLevel; count--) {
- currentStream.write("Q\n");
- }
- graphicsState.restoreLevel(baseLevel);
- currentStream.write("q\n");
- graphicsState.push();
- if (newClip != null) {
- graphicsState.setClip(newClip);
- }
- writeClip(newClip);
- }
-
- lastClip = newClip;
- }
- */
-
- /**
- * Set the clipping shape for future PDF drawing in the current graphics state.
- * This sets creates and writes a clipping shape that will apply
- * to future drawings in the current graphics state.
- *
- * @param s the clipping shape
- */
- protected void writeClip(Shape s) {
- if (s == null) {
- return;
- }
- PathIterator iter = s.getPathIterator(IDENTITY_TRANSFORM);
- if (iter.isDone()) {
- // no segments available. Not worth doing anything
- return;
- }
- preparePainting();
-
- processPathIterator(iter);
- // clip area
- currentStream.write("W\n");
- currentStream.write("n\n");
- }
-
- /**
- * Apply the java Color to PDF.
- * This converts the java colour to a PDF colour and
- * sets it for the next drawing.
- *
- * @param col the java colour
- * @param fill true if the colour will be used for filling
- */
- protected void applyColor(Color col, boolean fill) {
- preparePainting();
-
- //TODO Handle this in PDFColorHandler by automatically converting the color.
- //This won't work properly anyway after the redesign of ColorExt
- if (col.getColorSpace().getType() == ColorSpace.TYPE_CMYK) {
- if (pdfDoc.getProfile().getPDFAMode().isPDFA1LevelB()) {
- //See PDF/A-1, ISO 19005:1:2005(E), 6.2.3.3
- //FOP is currently restricted to DeviceRGB if PDF/A-1 is active.
- throw new PDFConformanceException(
- "PDF/A-1 does not allow mixing DeviceRGB and DeviceCMYK.");
- }
- }
-
- boolean doWrite = false;
- if (fill) {
- if (paintingState.setBackColor(col)) {
- doWrite = true;
- }
- } else {
- if (paintingState.setColor(col)) {
- doWrite = true;
- }
- }
- if (doWrite) {
- StringBuffer sb = new StringBuffer();
- colorHandler.establishColor(sb, col, fill);
- currentStream.write(sb.toString());
- }
- }
-
- /**
- * Apply the java paint to the PDF.
- * This takes the java paint sets up the appropraite PDF commands
- * for the drawing with that paint.
- * Currently this supports the gradients and patterns from batik.
- *
- * @param paint the paint to convert to PDF
- * @param fill true if the paint should be set for filling
- * @return true if the paint is handled natively, false if the paint should be rasterized
- */
- protected boolean applyPaint(Paint paint, boolean fill) { // CSOK: MethodLength
- preparePainting();
-
- if (paint instanceof Color) {
- return true;
- }
- // convert java.awt.GradientPaint to LinearGradientPaint to avoid rasterization
- if (paint instanceof GradientPaint) {
- GradientPaint gpaint = (GradientPaint) paint;
- paint = new LinearGradientPaint(
- (float) gpaint.getPoint1().getX(),
- (float) gpaint.getPoint1().getY(),
- (float) gpaint.getPoint2().getX(),
- (float) gpaint.getPoint2().getY(),
- new float[] {0, 1},
- new Color[] {gpaint.getColor1(), gpaint.getColor2()},
- gpaint.isCyclic() ? LinearGradientPaint.REPEAT : LinearGradientPaint.NO_CYCLE);
- }
- if (paint instanceof LinearGradientPaint) {
- LinearGradientPaint gp = (LinearGradientPaint)paint;
-
- // This code currently doesn't support 'repeat'.
- // For linear gradients it is possible to construct
- // a 'tile' that is repeated with a PDF pattern, but
- // it would be very tricky as you would have to rotate
- // the coordinate system so the repeat was axially
- // aligned. At this point I'm just going to rasterize it.
- MultipleGradientPaint.CycleMethodEnum cycle = gp.getCycleMethod();
- if (cycle != MultipleGradientPaint.NO_CYCLE) {
- return false;
- }
-
- Color[] cols = gp.getColors();
- float[] fractions = gp.getFractions();
-
- // Build proper transform from gradient space to page space
- // ('Patterns' don't get userspace transform).
- AffineTransform transform;
- transform = new AffineTransform(getBaseTransform());
- transform.concatenate(getTransform());
- transform.concatenate(gp.getTransform());
-
- List<Double> theMatrix = new java.util.ArrayList<Double>();
- double [] mat = new double[6];
- transform.getMatrix(mat);
- for (int idx = 0; idx < mat.length; idx++) {
- theMatrix.add(new Double(mat[idx]));
- }
-
- Point2D p1 = gp.getStartPoint();
- Point2D p2 = gp.getEndPoint();
- List<Double> theCoords = new java.util.ArrayList<Double>();
- theCoords.add(new Double(p1.getX()));
- theCoords.add(new Double(p1.getY()));
- theCoords.add(new Double(p2.getX()));
- theCoords.add(new Double(p2.getY()));
-
- List<Boolean> theExtend = new java.util.ArrayList<Boolean>();
- theExtend.add(Boolean.TRUE);
- theExtend.add(Boolean.TRUE);
-
- List<Double> theDomain = new java.util.ArrayList<Double>();
- theDomain.add(new Double(0));
- theDomain.add(new Double(1));
-
- List<Double> theEncode = new java.util.ArrayList<Double>();
- theEncode.add(new Double(0));
- theEncode.add(new Double(1));
- theEncode.add(new Double(0));
- theEncode.add(new Double(1));
-
- List<Double> theBounds = new java.util.ArrayList<Double>();
-
- List<Color> someColors = new java.util.ArrayList<Color>();
-
- for (int count = 0; count < cols.length; count++) {
- Color c1 = cols[count];
- if (c1.getAlpha() != 255) {
- return false; // PDF can't do alpha
- }
-
- //PDFColor color1 = new PDFColor(c1.getRed(), c1.getGreen(),
- // c1.getBlue());
- someColors.add(c1);
- if (count > 0 && count < cols.length - 1) {
- theBounds.add(new Double(fractions[count]));
- }
- }
-
- //Gradients are currently restricted to sRGB
- PDFDeviceColorSpace aColorSpace;
- aColorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
- PDFPattern myPat = pdfDoc.getFactory().makeGradient(
- resourceContext, false, aColorSpace,
- someColors, theBounds, theCoords, theMatrix);
- currentStream.write(myPat.getColorSpaceOut(fill));
-
- return true;
- }
- if (paint instanceof RadialGradientPaint) {
- RadialGradientPaint rgp = (RadialGradientPaint)paint;
-
- // There is essentially no way to support repeats
- // in PDF for radial gradients (the one option would
- // be to 'grow' the outer circle until it fully covered
- // the bounds and then grow the stops accordingly, the
- // problem is that this may require an extremely large
- // number of stops for cases where the focus is near
- // the edge of the outer circle). so we rasterize.
- MultipleGradientPaint.CycleMethodEnum cycle = rgp.getCycleMethod();
- if (cycle != MultipleGradientPaint.NO_CYCLE) {
- return false;
- }
-
- AffineTransform transform;
- transform = new AffineTransform(getBaseTransform());
- transform.concatenate(getTransform());
- transform.concatenate(rgp.getTransform());
-
- List<Double> theMatrix = new java.util.ArrayList<Double>();
- double [] mat = new double[6];
- transform.getMatrix(mat);
- for (int idx = 0; idx < mat.length; idx++) {
- theMatrix.add(new Double(mat[idx]));
- }
-
- double ar = rgp.getRadius();
- Point2D ac = rgp.getCenterPoint();
- Point2D af = rgp.getFocusPoint();
-
- List<Double> theCoords = new java.util.ArrayList<Double>();
- double dx = af.getX() - ac.getX();
- double dy = af.getY() - ac.getY();
- double d = Math.sqrt(dx * dx + dy * dy);
- if (d > ar) {
- // the center point af must be within the circle with
- // radius ar centered at ac so limit it to that.
- double scale = (ar * .9999) / d;
- dx = dx * scale;
- dy = dy * scale;
- }
-
- theCoords.add(new Double(ac.getX() + dx)); // Fx
- theCoords.add(new Double(ac.getY() + dy)); // Fy
- theCoords.add(new Double(0));
- theCoords.add(new Double(ac.getX()));
- theCoords.add(new Double(ac.getY()));
- theCoords.add(new Double(ar));
-
- Color[] cols = rgp.getColors();
- List<Color> someColors = new java.util.ArrayList<Color>();
- for (int count = 0; count < cols.length; count++) {
- Color cc = cols[count];
- if (cc.getAlpha() != 255) {
- return false; // PDF can't do alpha
- }
-
- someColors.add(cc);
- }
-
- float[] fractions = rgp.getFractions();
- List<Double> theBounds = new java.util.ArrayList<Double>();
- for (int count = 1; count < fractions.length - 1; count++) {
- float offset = fractions[count];
- theBounds.add(new Double(offset));
- }
- PDFDeviceColorSpace colSpace;
- colSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
-
- PDFPattern myPat = pdfDoc.getFactory().makeGradient
- (resourceContext, true, colSpace,
- someColors, theBounds, theCoords, theMatrix);
-
- currentStream.write(myPat.getColorSpaceOut(fill));
-
- return true;
- }
- if (paint instanceof PatternPaint) {
- PatternPaint pp = (PatternPaint)paint;
- return createPattern(pp, fill);
- }
- return false; // unknown paint
- }
-
- private boolean createPattern(PatternPaint pp, boolean fill) {
- preparePainting();
-
- FontInfo specialFontInfo = new FontInfo();
- boolean base14Kerning = false;
- FontSetup.setup(specialFontInfo, base14Kerning);
-
- PDFResources res = pdfDoc.getFactory().makeResources();
- PDFResourceContext context = new PDFResourceContext(res);
- PDFGraphics2D pattGraphic = new PDFGraphics2D(textAsShapes, specialFontInfo,
- pdfDoc, context, getPageReference(),
- "", 0);
- pattGraphic.setGraphicContext(new GraphicContext());
- pattGraphic.gc.validateTransformStack();
- pattGraphic.setRenderingHints(this.getRenderingHints());
- pattGraphic.setOutputStream(outputStream);
-
- GraphicsNode gn = pp.getGraphicsNode();
- //Rectangle2D gnBBox = gn.getBounds();
- Rectangle2D rect = pp.getPatternRect();
-
- // if (!pp.getOverflow()) {
- gn.paint(pattGraphic);
- // } else {
- // /* Commented out until SVN version of Batik is included */
- // // For overflow we need to paint the content from
- // // all the tiles who's overflow will intersect one
- // // tile (left->right, top->bottom). Then we can
- // // simply replicate that tile as normal.
- // double gnMinX = gnBBox.getX();
- // double gnMaxX = gnBBox.getX() + gnBBox.getWidth();
- // double gnMinY = gnBBox.getY();
- // double gnMaxY = gnBBox.getY() + gnBBox.getHeight();
- // double patMaxX = rect.getX() + rect.getWidth();
- // double patMaxY = rect.getY() + rect.getHeight();
- // double stepX = rect.getWidth();
- // double stepY = rect.getHeight();
- //
- // int startX = (int)((rect.getX() - gnMaxX)/stepX);
- // int startY = (int)((rect.getY() - gnMaxY)/stepY);
- //
- // int endX = (int)((patMaxX - gnMinX)/stepX);
- // int endY = (int)((patMaxY - gnMinY)/stepY);
- //
- // pattGraphic.translate(startX*stepX, startY*stepY);
- // for (int yIdx=startY; yIdx<=endY; yIdx++) {
- // for (int xIdx=startX; xIdx<=endX; xIdx++) {
- // gn.paint(pattGraphic);
- // pattGraphic.translate(stepX,0);
- // }
- // pattGraphic.translate(-(endX-startX+1)*stepX, stepY);
- // }
- // }
-
- List<Double> bbox = new java.util.ArrayList<Double>();
- bbox.add(new Double(rect.getX()));
- bbox.add(new Double(rect.getHeight() + rect.getY()));
- bbox.add(new Double(rect.getWidth() + rect.getX()));
- bbox.add(new Double(rect.getY()));
-
- AffineTransform transform;
- transform = new AffineTransform(getBaseTransform());
- transform.concatenate(getTransform());
- transform.concatenate(pp.getPatternTransform());
-
- List<Double> theMatrix = new java.util.ArrayList<Double>();
- double [] mat = new double[6];
- transform.getMatrix(mat);
- for (int idx = 0; idx < mat.length; idx++) {
- theMatrix.add(new Double(mat[idx]));
- }
-
- /** @todo see if pdfDoc and res can be linked here,
- (currently res <> PDFDocument's resources) so addFonts()
- can be moved to PDFDocument class */
- res.addFonts(pdfDoc, specialFontInfo);
-
- PDFPattern myPat = pdfDoc.getFactory().makePattern(
- resourceContext, 1, res, 1, 1, bbox,
- rect.getWidth(), rect.getHeight(),
- theMatrix, null,
- pattGraphic.getBuffer());
-
- currentStream.write(myPat.getColorSpaceOut(fill));
-
- PDFAnnotList annots = context.getAnnotations();
- if (annots != null) {
- this.pdfDoc.addObject(annots);
- }
-
- flushPDFDocument();
- return true;
- }
-
- /**
- * @param paint some paint
- * @param shape a shape
- * @return true (always)
- */
- protected boolean applyUnknownPaint(Paint paint, Shape shape) {
- preparePainting();
-
- Shape clip = getClip();
- Rectangle2D usrClipBounds;
- Rectangle2D usrBounds;
- usrBounds = shape.getBounds2D();
- if (clip != null) {
- usrClipBounds = clip.getBounds2D();
- if (!usrClipBounds.intersects(usrBounds)) {
- return true;
- }
- Rectangle2D.intersect(usrBounds, usrClipBounds, usrBounds);
- }
- double usrX = usrBounds.getX();
- double usrY = usrBounds.getY();
- double usrW = usrBounds.getWidth();
- double usrH = usrBounds.getHeight();
-
- Rectangle devShapeBounds;
- Rectangle devClipBounds;
- Rectangle devBounds;
- AffineTransform at = getTransform();
- devShapeBounds = at.createTransformedShape(shape).getBounds();
- if (clip != null) {
- devClipBounds = at.createTransformedShape(clip).getBounds();
- if (!devClipBounds.intersects(devShapeBounds)) {
- return true;
- }
- devBounds = devShapeBounds.intersection(devClipBounds);
- } else {
- devBounds = devShapeBounds;
- }
- int devX = devBounds.x;
- int devY = devBounds.y;
- int devW = devBounds.width;
- int devH = devBounds.height;
-
- ColorSpace rgbCS = ColorSpace.getInstance(ColorSpace.CS_sRGB);
- ColorModel rgbCM = new DirectColorModel
- (rgbCS, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000,
- false, DataBuffer.TYPE_BYTE);
-
- PaintContext pctx = paint.createContext(rgbCM, devBounds, usrBounds,
- at, getRenderingHints());
- PDFXObject imageInfo = pdfDoc.getXObject
- ("TempImage:" + pctx.toString());
- if (imageInfo != null) {
- resourceContext.getPDFResources().addXObject(imageInfo);
- } else {
- Raster r = pctx.getRaster(devX, devY, devW, devH);
- WritableRaster wr = (WritableRaster)r;
- wr = wr.createWritableTranslatedChild(0, 0);
-
- ColorModel pcm = pctx.getColorModel();
- BufferedImage bi = new BufferedImage
- (pcm, wr, pcm.isAlphaPremultiplied(), null);
- final byte[] rgb = new byte[devW * devH * 3];
- final int[] line = new int[devW];
- final byte[] mask;
- int x;
- int y;
- int val;
- int rgbIdx = 0;
-
- if (pcm.hasAlpha()) {
- mask = new byte[devW * devH];
- int maskIdx = 0;
- for (y = 0; y < devH; y++) {
- bi.getRGB(0, y, devW, 1, line, 0, devW);
- for (x = 0; x < devW; x++) {
- val = line[x];
- mask[maskIdx++] = (byte)(val >>> 24);
- rgb[rgbIdx++] = (byte)((val >> 16) & 0x0FF);
- rgb[rgbIdx++] = (byte)((val >> 8 ) & 0x0FF);
- rgb[rgbIdx++] = (byte)((val ) & 0x0FF);
- }
- }
- } else {
- mask = null;
- for (y = 0; y < devH; y++) {
- bi.getRGB(0, y, devW, 1, line, 0, devW);
- for (x = 0; x < devW; x++) {
- val = line[x];
- rgb[rgbIdx++] = (byte)((val >> 16) & 0x0FF);
- rgb[rgbIdx++] = (byte)((val >> 8 ) & 0x0FF);
- rgb[rgbIdx++] = (byte)((val ) & 0x0FF);
- }
- }
- }
-
- String maskRef = null;
- if (mask != null) {
- BitmapImage fopimg = new BitmapImage
- ("TempImageMask:" + pctx.toString(), devW, devH, mask, null);
- fopimg.setColorSpace(new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_GRAY));
- PDFImageXObject xobj = pdfDoc.addImage(resourceContext, fopimg);
- maskRef = xobj.referencePDF();
-
- flushPDFDocument();
- }
- BitmapImage fopimg;
- fopimg = new BitmapImage("TempImage:" + pctx.toString(),
- devW, devH, rgb, maskRef);
- fopimg.setTransparent(new PDFColor(255, 255, 255));
- imageInfo = pdfDoc.addImage(resourceContext, fopimg);
- flushPDFDocument();
- }
-
- currentStream.write("q\n");
- writeClip(shape);
- currentStream.write("" + PDFNumber.doubleOut(usrW) + " 0 0 " + PDFNumber.doubleOut(-usrH) + " "
- + PDFNumber.doubleOut(usrX) + " " + PDFNumber.doubleOut(usrY + usrH) + " cm\n"
- + imageInfo.getName() + " Do\nQ\n");
- return true;
- }
-
- /**
- * Apply the stroke to the PDF.
- * This takes the java stroke and outputs the appropriate settings
- * to the PDF so that the stroke attributes are handled.
- *
- * @param stroke the java stroke
- */
- protected void applyStroke(Stroke stroke) {
- preparePainting();
- if (stroke instanceof BasicStroke) {
- BasicStroke bs = (BasicStroke)stroke;
-
- float[] da = bs.getDashArray();
- if (da != null) {
- currentStream.write("[");
- for (int count = 0; count < da.length; count++) {
- currentStream.write(PDFNumber.doubleOut(da[count]));
- if (count < da.length - 1) {
- currentStream.write(" ");
- }
- }
- currentStream.write("] ");
- float offset = bs.getDashPhase();
- currentStream.write(PDFNumber.doubleOut(offset) + " d\n");
- } else {
- currentStream.write("[] 0 d\n");
- }
- int ec = bs.getEndCap();
- switch (ec) {
- case BasicStroke.CAP_BUTT:
- currentStream.write(0 + " J\n");
- break;
- case BasicStroke.CAP_ROUND:
- currentStream.write(1 + " J\n");
- break;
- case BasicStroke.CAP_SQUARE:
- currentStream.write(2 + " J\n");
- break;
- default:
- break;
- }
-
- int lj = bs.getLineJoin();
- switch (lj) {
- case BasicStroke.JOIN_MITER:
- currentStream.write(0 + " j\n");
- break;
- case BasicStroke.JOIN_ROUND:
- currentStream.write(1 + " j\n");
- break;
- case BasicStroke.JOIN_BEVEL:
- currentStream.write(2 + " j\n");
- break;
- default:
- break;
- }
- float lw = bs.getLineWidth();
- currentStream.write(PDFNumber.doubleOut(lw) + " w\n");
-
- float ml = bs.getMiterLimit();
- currentStream.write(PDFNumber.doubleOut(ml) + " M\n");
- }
- }
-
- /** {@inheritDoc} */
- @Override
- public void drawRenderedImage(RenderedImage img, AffineTransform xform) {
- String key = "TempImage:" + img.toString();
- drawInnerRenderedImage(key, img, xform);
- }
-
- /**
- * @param key a key
- * @param img an image
- * @param xform a transform
- */
- public void drawInnerRenderedImage(String key, RenderedImage img, AffineTransform xform) {
- preparePainting();
- PDFXObject xObject = pdfDoc.getXObject(key);
- if (xObject == null) {
- xObject = addRenderedImage(key, img);
- } else {
- resourceContext.getPDFResources().addXObject(xObject);
- }
-
- useXObject(xObject, xform, img.getWidth(), img.getHeight());
- }
-
- private void useXObject(PDFXObject xObject, AffineTransform xform, float width, float height) {
- // now do any transformation required and add the actual image
- // placement instance
- currentStream.write("q\n");
- concatMatrix(getTransform());
- Shape imclip = getClip();
- writeClip(imclip);
- concatMatrix(xform);
- String w = PDFNumber.doubleOut(width, DEC);
- String h = PDFNumber.doubleOut(height, DEC);
- currentStream.write("" + w + " 0 0 -" + h + " 0 " + h + " cm\n"
- + xObject.getName() + " Do\nQ\n");
- }
-
- private PDFXObject addRenderedImage(String key, RenderedImage img) {
- ImageInfo info = new ImageInfo(null, "image/unknown");
- ImageSize size = new ImageSize(img.getWidth(), img.getHeight(),
- GraphicsConstants.DEFAULT_DPI);
- info.setSize(size);
- ImageRendered imgRend = new ImageRendered(info, img, null);
- ImageRenderedAdapter adapter = new ImageRenderedAdapter(imgRend, key);
- PDFXObject xObject = pdfDoc.addImage(resourceContext, adapter);
- flushPDFDocument();
- return xObject;
- }
-
- /** {@inheritDoc} */
- @Override
- public void drawRenderableImage(RenderableImage img,
- AffineTransform xform) {
- //TODO Check if this is good enough
- drawRenderedImage(img.createDefaultRendering(), xform);
- }
-
- /**
- * Renders the text specified by the specified <code>String</code>,
- * using the current <code>Font</code> and <code>Paint</code> attributes
- * in the <code>Graphics2D</code> context.
- * The baseline of the first character is at position
- * (<i>x</i>, <i>y</i>) in the User Space.
- * The rendering attributes applied include the <code>Clip</code>,
- * <code>Transform</code>, <code>Paint</code>, <code>Font</code> and
- * <code>Composite</code> attributes. For characters in script systems
- * such as Hebrew and Arabic, the glyphs can be rendered from right to
- * left, in which case the coordinate supplied is the location of the
- * leftmost character on the baseline.
- * @param s the <code>String</code> to be rendered
- * @param x the coordinate where the <code>String</code>
- * should be rendered
- * @param y the coordinate where the <code>String</code>
- * should be rendered
- * @see #setPaint
- * @see java.awt.Graphics#setColor
- * @see java.awt.Graphics#setFont
- * @see #setTransform
- * @see #setComposite
- * @see #setClip
- */
- @Override
- public void drawString(String s, float x, float y) {
- preparePainting();
-
- Font fontState;
- AffineTransform fontTransform = null;
- if (ovFontState == null) {
- java.awt.Font gFont = getFont();
- fontTransform = gFont.getTransform();
- fontState = fontInfo.getFontInstanceForAWTFont(gFont);
- } else {
- fontState = fontInfo.getFontInstance(
- ovFontState.getFontTriplet(), ovFontState.getFontSize());
- ovFontState = null;
- }
- updateCurrentFont(fontState);
-
- saveGraphicsState();
-
- Color c = getColor();
- applyColor(c, true);
- applyPaint(getPaint(), true);
- applyAlpha(c.getAlpha(), OPAQUE);
-
- Map<Integer, Map<Integer, Integer>> kerning = fontState.getKerning();
- boolean kerningAvailable = (kerning != null && !kerning.isEmpty());
-
- boolean useMultiByte = isMultiByteFont(currentFontName);
-
- // String startText = useMultiByte ? "<FEFF" : "(";
- String startText = useMultiByte ? "<" : "(";
- String endText = useMultiByte ? "> " : ") ";
-
- AffineTransform trans = getTransform();
- //trans.translate(x, y);
- double[] vals = new double[6];
- trans.getMatrix(vals);
-
- concatMatrix(vals);
- Shape imclip = getClip();
- writeClip(imclip);
-
- currentStream.write("BT\n");
-
- AffineTransform localTransform = new AffineTransform();
- localTransform.translate(x, y);
- if (fontTransform != null) {
- localTransform.concatenate(fontTransform);
- }
- localTransform.scale(1, -1);
- double[] lt = new double[6];
- localTransform.getMatrix(lt);
- currentStream.write(PDFNumber.doubleOut(lt[0]) + " "
- + PDFNumber.doubleOut(lt[1]) + " " + PDFNumber.doubleOut(lt[2]) + " "
- + PDFNumber.doubleOut(lt[3]) + " " + PDFNumber.doubleOut(lt[4]) + " "
- + PDFNumber.doubleOut(lt[5]) + " Tm [" + startText);
-
- int l = s.length();
-
- for (int i = 0; i < l; i++) {
- char ch = fontState.mapChar(s.charAt(i));
-
- if (!useMultiByte) {
- if (ch > 127) {
- currentStream.write("\\");
- currentStream.write(Integer.toOctalString(ch));
- } else {
- switch (ch) {
- case '(':
- case ')':
- case '\\':
- currentStream.write("\\");
- break;
- default:
- }
- currentStream.write(ch);
- }
- } else {
- currentStream.write(PDFText.toUnicodeHex(ch));
- }
-
- if (kerningAvailable && (i + 1) < l) {
- addKerning(currentStream, (Integer.valueOf(ch)),
- (Integer.valueOf(fontState.mapChar(s.charAt(i + 1)))),
- kerning, startText, endText);
- }
-
- }
- currentStream.write(endText);
-
- currentStream.write("] TJ\n");
- currentStream.write("ET\n");
- restoreGraphicsState();
- }
-
- /**
- * Applies the given alpha values for filling and stroking.
- * @param fillAlpha A value between 0 and 255 (=OPAQUE) for filling
- * @param strokeAlpha A value between 0 and 255 (=OPAQUE) for stroking
- */
- protected void applyAlpha(int fillAlpha, int strokeAlpha) {
- if (fillAlpha != OPAQUE || strokeAlpha != OPAQUE) {
- checkTransparencyAllowed();
- Map<String, Float> vals = new java.util.HashMap<String, Float>();
- if (fillAlpha != OPAQUE) {
- vals.put(PDFGState.GSTATE_ALPHA_NONSTROKE, new Float(fillAlpha / 255f));
- }
- if (strokeAlpha != OPAQUE) {
- vals.put(PDFGState.GSTATE_ALPHA_STROKE, new Float(strokeAlpha / 255f));
- }
- PDFGState gstate = pdfDoc.getFactory().makeGState(
- vals, paintingState.getGState());
- resourceContext.addGState(gstate);
- currentStream.write("/" + gstate.getName() + " gs\n");
- }
- }
-
- /**
- * Updates the currently selected font.
- * @param font the new font to use
- */
- protected void updateCurrentFont(Font font) {
- String name = font.getFontName();
- float size = font.getFontSize() / 1000f;
-
- //Only update if necessary
- if ((!name.equals(this.currentFontName))
- || (size != this.currentFontSize)) {
- this.currentFontName = name;
- this.currentFontSize = size;
- currentStream.write("/" + name + " " + size + " Tf\n");
- }
- }
-
- /**
- * Returns a suitable internal font given an AWT Font instance.
- * @param awtFont the AWT font
- * @return the internal Font
- * @deprecated use FontInfo.getFontInstanceForAWTFont(java.awt.Font awtFont) instead
- */
- @Deprecated
- protected Font getInternalFontForAWTFont(java.awt.Font awtFont) {
- return fontInfo.getFontInstanceForAWTFont(awtFont);
- }
-
- /**
- * Determines whether the font with the given name is a multi-byte font.
- * @param name the name of the font
- * @return true if it's a multi-byte font
- */
- protected boolean isMultiByteFont(String name) {
- // This assumes that *all* CIDFonts use a /ToUnicode mapping
- org.apache.fop.fonts.Typeface f
- = fontInfo.getFonts().get(name);
- return f.isMultiByte();
- }
-
- private void addKerning(StringWriter buf, Integer ch1, Integer ch2,
- Map<Integer, Map<Integer, Integer>> kerning, String startText,
- String endText) {
- preparePainting();
- Map<Integer, Integer> kernPair = kerning.get(ch1);
-
- if (kernPair != null) {
- Integer width = kernPair.get(ch2);
- if (width != null) {
- currentStream.write(endText + (-width.intValue()) + " " + startText);
- }
- }
- }
-
- /**
- * Renders the text of the specified iterator, using the
- * <code>Graphics2D</code> context's current <code>Paint</code>. The
- * iterator must specify a font
- * for each character. The baseline of the
- * first character is at position (<i>x</i>, <i>y</i>) in the
- * User Space.
- * The rendering attributes applied include the <code>Clip</code>,
- * <code>Transform</code>, <code>Paint</code>, and
- * <code>Composite</code> attributes.
- * For characters in script systems such as Hebrew and Arabic,
- * the glyphs can be rendered from right to left, in which case the
- * coordinate supplied is the location of the leftmost character
- * on the baseline.
- * @param iterator the iterator whose text is to be rendered
- * @param x the coordinate where the iterator's text is to be
- * rendered
- * @param y the coordinate where the iterator's text is to be
- * rendered
- * @see #setPaint
- * @see java.awt.Graphics#setColor
- * @see #setTransform
- * @see #setComposite
- * @see #setClip
- *//* TODO Reimplement for higher efficiency similar to the way it was done in PDFTextPainter
- public void drawString(AttributedCharacterIterator iterator, float x,
- float y) {
- preparePainting();
-
- Font fontState = null;
-
- Shape imclip = getClip();
- writeClip(imclip);
- Color c = getColor();
- applyColor(c, true);
- applyPaint(getPaint(), true);
-
- boolean fill = true;
- boolean stroke = false;
- if (true) {
- Stroke currentStroke = getStroke();
- stroke = true;
- applyStroke(currentStroke);
- applyColor(c, false);
- applyPaint(getPaint(), false);
- }
-
- currentStream.write("BT\n");
-
- // set text rendering mode:
- // 0 - fill, 1 - stroke, 2 - fill then stroke
- int textr = 0;
- if (fill && stroke) {
- textr = 2;
- } else if (stroke) {
- textr = 1;
- }
- currentStream.write(textr + " Tr\n");
-
- AffineTransform trans = getTransform();
- trans.translate(x, y);
- double[] vals = new double[6];
- trans.getMatrix(vals);
-
- for (char ch = iterator.first(); ch != CharacterIterator.DONE;
- ch = iterator.next()) {
- //Map attr = iterator.getAttributes();
-
- String name = fontState.getFontName();
- int size = fontState.getFontSize();
- if ((!name.equals(this.currentFontName))
- || (size != this.currentFontSize)) {
- this.currentFontName = name;
- this.currentFontSize = size;
- currentStream.write("/" + name + " " + (size / 1000)
- + " Tf\n");
-
- }
-
- currentStream.write(PDFNumber.doubleOut(vals[0], DEC) + " "
- + PDFNumber.doubleOut(vals[1], DEC) + " "
- + PDFNumber.doubleOut(vals[2], DEC) + " "
- + PDFNumber.doubleOut(vals[3], DEC) + " "
- + PDFNumber.doubleOut(vals[4], DEC) + " "
- + PDFNumber.doubleOut(vals[5], DEC) + " Tm (" + ch
- + ") Tj\n");
- }
-
- currentStream.write("ET\n");
- }*/
-
- /**
- * Fills the interior of a <code>Shape</code> using the settings of the
- * <code>Graphics2D</code> context. The rendering attributes applied
- * include the <code>Clip</code>, <code>Transform</code>,
- * <code>Paint</code>, and <code>Composite</code>.
- * @param s the <code>Shape</code> to be filled
- * @see #setPaint
- * @see java.awt.Graphics#setColor
- * @see #transform
- * @see #setTransform
- * @see #setComposite
- * @see #clip
- * @see #setClip
- */
- @Override
- public void fill(Shape s) {
- preparePainting();
-
- //Transparency shortcut
- Color c;
- c = getBackground();
- if (c.getAlpha() == 0) {
- c = getColor();
- if (c.getAlpha() == 0) {
- return;
- }
- }
-
- AffineTransform trans = getTransform();
- double[] tranvals = new double[6];
- trans.getMatrix(tranvals);
-
- Shape imclip = getClip();
- boolean newClip = paintingState.checkClip(imclip);
- boolean newTransform = paintingState.checkTransform(trans)
- && !trans.isIdentity();
-
- if (newClip || newTransform) {
- saveGraphicsState();
- if (newTransform) {
- concatMatrix(tranvals);
- }
- if (newClip) {
- writeClip(imclip);
- }
- }
-
- applyAlpha(c.getAlpha(), OPAQUE);
-
- c = getColor();
- applyColor(c, true);
- c = getBackground();
- applyColor(c, false);
-
- Paint paint = getPaint();
- if (paintingState.setPaint(paint)) {
- if (!applyPaint(paint, true)) {
- // Use the shape to 'clip' the paint contents.
- applyUnknownPaint(paint, s);
-
- if (newClip || newTransform) {
- restoreGraphicsState();
- }
- return;
- }
- }
-
- if (s instanceof Rectangle2D) {
- Rectangle2D rect = (Rectangle2D)s;
- currentStream.write(PDFNumber.doubleOut(rect.getMinX(), DEC) + " "
- + PDFNumber.doubleOut(rect.getMinY(), DEC) + " ");
- currentStream.write(PDFNumber.doubleOut(rect.getWidth(), DEC) + " "
- + PDFNumber.doubleOut(rect.getHeight(), DEC) + " re ");
- doDrawing(true, false, false);
- } else {
- PathIterator iter = s.getPathIterator(IDENTITY_TRANSFORM);
- processPathIterator(iter);
- doDrawing(true, false,
- iter.getWindingRule() == PathIterator.WIND_EVEN_ODD);
- }
- if (newClip || newTransform) {
- restoreGraphicsState();
- }
- }
-
- void saveGraphicsState() {
- currentStream.write("q\n");
- paintingState.save();
- }
-
- void restoreGraphicsState() {
- currentStream.write("Q\n");
- paintingState.restore();
- }
-
- /** Checks whether the use of transparency is allowed. */
- protected void checkTransparencyAllowed() {
- pdfDoc.getProfile().verifyTransparencyAllowed("Java2D graphics");
- }
-
- /**
- * Processes a path iterator generating the necessary painting operations.
- * @param iter PathIterator to process
- */
- public void processPathIterator(PathIterator iter) {
- while (!iter.isDone()) {
- double[] vals = new double[6];
- int type = iter.currentSegment(vals);
- switch (type) {
- case PathIterator.SEG_CUBICTO:
- currentStream.write(PDFNumber.doubleOut(vals[0], DEC) + " "
- + PDFNumber.doubleOut(vals[1], DEC) + " "
- + PDFNumber.doubleOut(vals[2], DEC) + " "
- + PDFNumber.doubleOut(vals[3], DEC) + " "
- + PDFNumber.doubleOut(vals[4], DEC) + " "
- + PDFNumber.doubleOut(vals[5], DEC) + " c\n");
- break;
- case PathIterator.SEG_LINETO:
- currentStream.write(PDFNumber.doubleOut(vals[0], DEC) + " "
- + PDFNumber.doubleOut(vals[1], DEC) + " l\n");
- break;
- case PathIterator.SEG_MOVETO:
- currentStream.write(PDFNumber.doubleOut(vals[0], DEC) + " "
- + PDFNumber.doubleOut(vals[1], DEC) + " m\n");
- break;
- case PathIterator.SEG_QUADTO:
- currentStream.write(PDFNumber.doubleOut(vals[0], DEC) + " "
- + PDFNumber.doubleOut(vals[1], DEC) + " "
- + PDFNumber.doubleOut(vals[2], DEC) + " "
- + PDFNumber.doubleOut(vals[3], DEC) + " y\n");
- break;
- case PathIterator.SEG_CLOSE:
- currentStream.write("h\n");
- break;
- default:
- break;
- }
- iter.next();
- }
- }
-
- /**
- * Do the PDF drawing command.
- * This does the PDF drawing command according to fill
- * stroke and winding rule.
- *
- * @param fill true if filling the path
- * @param stroke true if stroking the path
- * @param nonzero true if using the non-zero winding rule
- */
- protected void doDrawing(boolean fill, boolean stroke, boolean nonzero) {
- preparePainting();
- if (fill) {
- if (stroke) {
- if (nonzero) {
- currentStream.write("B*\n");
- } else {
- currentStream.write("B\n");
- }
- } else {
- if (nonzero) {
- currentStream.write("f*\n");
- } else {
- currentStream.write("f\n");
- }
- }
- } else {
- // if (stroke)
- currentStream.write("S\n");
- }
- }
-
- /**
- * Returns the device configuration associated with this
- * <code>Graphics2D</code>.
- *
- * @return the PDF graphics configuration
- */
- @Override
- public GraphicsConfiguration getDeviceConfiguration() {
- return new PDFGraphicsConfiguration();
- }
-
- /**
- * Used to create proper font metrics
- */
- private Graphics2D fmg;
-
- {
- BufferedImage bi = new BufferedImage(1, 1,
- BufferedImage.TYPE_INT_ARGB);
-
- fmg = bi.createGraphics();
- }
-
- /**
- * Gets the font metrics for the specified font.
- * @return the font metrics for the specified font.
- * @param f the specified font
- * @see java.awt.Graphics#getFont
- * @see java.awt.FontMetrics
- * @see java.awt.Graphics#getFontMetrics()
- */
- @Override
- public java.awt.FontMetrics getFontMetrics(java.awt.Font f) {
- return fmg.getFontMetrics(f);
- }
-
- /**
- * Sets the paint mode of this graphics context to alternate between
- * this graphics context's current color and the new specified color.
- * This specifies that logical pixel operations are performed in the
- * XOR mode, which alternates pixels between the current color and
- * a specified XOR color.
- * <p>
- * When drawing operations are performed, pixels which are the
- * current color are changed to the specified color, and vice versa.
- * <p>
- * Pixels that are of colors other than those two colors are changed
- * in an unpredictable but reversible manner; if the same figure is
- * drawn twice, then all pixels are restored to their original values.
- * @param c1 the XOR alternation color
- */
- @Override
- public void setXORMode(Color c1) {
- //NYI
- }
-
-
- /**
- * Copies an area of the component by a distance specified by
- * <code>dx</code> and <code>dy</code>. From the point specified
- * by <code>x</code> and <code>y</code>, this method
- * copies downwards and to the right. To copy an area of the
- * component to the left or upwards, specify a negative value for
- * <code>dx</code> or <code>dy</code>.
- * If a portion of the source rectangle lies outside the bounds
- * of the component, or is obscured by another window or component,
- * <code>copyArea</code> will be unable to copy the associated
- * pixels. The area that is omitted can be refreshed by calling
- * the component's <code>paint</code> method.
- * @param x the <i>x</i> coordinate of the source rectangle.
- * @param y the <i>y</i> coordinate of the source rectangle.
- * @param width the width of the source rectangle.
- * @param height the height of the source rectangle.
- * @param dx the horizontal distance to copy the pixels.
- * @param dy the vertical distance to copy the pixels.
- */
- @Override
- public void copyArea(int x, int y, int width, int height, int dx,
- int dy) {
- //NYI
- }
-
- }
|