aboutsummaryrefslogtreecommitdiffstats
path: root/src/java/org/apache/fop/svg
diff options
context:
space:
mode:
Diffstat (limited to 'src/java/org/apache/fop/svg')
-rw-r--r--src/java/org/apache/fop/svg/AbstractFOPBridgeContext.java142
-rw-r--r--src/java/org/apache/fop/svg/AbstractFOPImageElementBridge.java284
-rw-r--r--src/java/org/apache/fop/svg/AbstractFOPTextElementBridge.java113
-rw-r--r--src/java/org/apache/fop/svg/AbstractFOPTextPainter.java528
-rw-r--r--src/java/org/apache/fop/svg/FOPTextHandler.java27
-rw-r--r--src/java/org/apache/fop/svg/GraphicsConfiguration.java1
-rw-r--r--src/java/org/apache/fop/svg/NativeImageHandler.java40
-rw-r--r--src/java/org/apache/fop/svg/PDFBridgeContext.java106
-rw-r--r--src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java40
-rw-r--r--src/java/org/apache/fop/svg/PDFGraphics2D.java97
-rw-r--r--src/java/org/apache/fop/svg/PDFGraphicsConfiguration.java1
-rw-r--r--src/java/org/apache/fop/svg/PDFImageElementBridge.java233
-rw-r--r--src/java/org/apache/fop/svg/PDFTextElementBridge.java42
-rw-r--r--src/java/org/apache/fop/svg/PDFTextPainter.java24
14 files changed, 1255 insertions, 423 deletions
diff --git a/src/java/org/apache/fop/svg/AbstractFOPBridgeContext.java b/src/java/org/apache/fop/svg/AbstractFOPBridgeContext.java
new file mode 100644
index 000000000..ae4d67516
--- /dev/null
+++ b/src/java/org/apache/fop/svg/AbstractFOPBridgeContext.java
@@ -0,0 +1,142 @@
+/*
+ * 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.geom.AffineTransform;
+import java.lang.reflect.Constructor;
+
+import org.apache.batik.bridge.Bridge;
+import org.apache.batik.bridge.BridgeContext;
+import org.apache.batik.bridge.DocumentLoader;
+import org.apache.batik.bridge.UserAgent;
+import org.apache.fop.fonts.FontInfo;
+import org.apache.xmlgraphics.image.loader.ImageManager;
+import org.apache.xmlgraphics.image.loader.ImageSessionContext;
+
+/**
+ * A FOP base implementation of a Batik BridgeContext.
+ */
+public abstract class AbstractFOPBridgeContext extends BridgeContext {
+
+ /** The font list. */
+ protected final FontInfo fontInfo;
+
+ protected final ImageManager imageManager;
+ protected final ImageSessionContext imageSessionContext;
+
+ protected final AffineTransform linkTransform;
+
+ /**
+ * Constructs a new bridge context.
+ * @param userAgent the user agent
+ * @param loader the Document Loader to use for referenced documents.
+ * @param fontInfo the font list for the text painter, may be null
+ * in which case text is painted as shapes
+ * @param linkTransform AffineTransform to properly place links,
+ * may be null
+ * @param imageManager an image manager
+ * @param imageSessionContext an image session context
+ * @param linkTransform AffineTransform to properly place links,
+ * may be null
+ */
+ public AbstractFOPBridgeContext(UserAgent userAgent,
+ DocumentLoader loader,
+ FontInfo fontInfo,
+ ImageManager imageManager,
+ ImageSessionContext imageSessionContext,
+ AffineTransform linkTransform) {
+ super(userAgent, loader);
+ this.fontInfo = fontInfo;
+ this.imageManager = imageManager;
+ this.imageSessionContext = imageSessionContext;
+ this.linkTransform = linkTransform;
+ }
+
+ /**
+ * Constructs a new bridge context.
+ * @param userAgent the user agent
+ * @param fontInfo the font list for the text painter, may be null
+ * in which case text is painted as shapes
+ * @param imageManager an image manager
+ * @param imageSessionContext an image session context
+ * @param linkTransform AffineTransform to properly place links,
+ * may be null
+ */
+ public AbstractFOPBridgeContext(UserAgent userAgent,
+ FontInfo fontInfo,
+ ImageManager imageManager,
+ ImageSessionContext imageSessionContext,
+ AffineTransform linkTransform) {
+ super(userAgent);
+ this.fontInfo = fontInfo;
+ this.imageManager = imageManager;
+ this.imageSessionContext = imageSessionContext;
+ this.linkTransform = linkTransform;
+ }
+
+ /**
+ * Constructs a new bridge context.
+ * @param userAgent the user agent
+ * @param fontInfo the font list for the text painter, may be null
+ * in which case text is painted as shapes
+ * @param imageManager an image manager
+ * @param imageSessionContext an image session context
+ */
+ public AbstractFOPBridgeContext(UserAgent userAgent,
+ FontInfo fontInfo,
+ ImageManager imageManager,
+ ImageSessionContext imageSessionContext) {
+ this(userAgent, fontInfo, imageManager, imageSessionContext, null);
+ }
+
+ /**
+ * Returns the ImageManager to be used by the ImageElementBridge.
+ * @return the image manager
+ */
+ public ImageManager getImageManager() {
+ return this.imageManager;
+ }
+
+ /**
+ * Returns the ImageSessionContext to be used by the ImageElementBridge.
+ * @return the image session context
+ */
+ public ImageSessionContext getImageSessionContext() {
+ return this.imageSessionContext;
+ }
+
+ protected void putElementBridgeConditional(String className, String testFor) {
+ try {
+ Class.forName(testFor);
+ //if we get here the test class is available
+
+ Class clazz = Class.forName(className);
+ Constructor constructor = clazz.getConstructor(new Class[] {FontInfo.class});
+ putBridge((Bridge)constructor.newInstance(new Object[] {fontInfo}));
+ } catch (Throwable t) {
+ //simply ignore (bridges instantiated over this method are optional)
+ }
+ }
+
+ // Make sure any 'sub bridge contexts' also have our bridges.
+ //TODO There's no matching method in the super-class here
+ public abstract BridgeContext createBridgeContext();
+
+}
diff --git a/src/java/org/apache/fop/svg/AbstractFOPImageElementBridge.java b/src/java/org/apache/fop/svg/AbstractFOPImageElementBridge.java
new file mode 100644
index 000000000..31895cebe
--- /dev/null
+++ b/src/java/org/apache/fop/svg/AbstractFOPImageElementBridge.java
@@ -0,0 +1,284 @@
+/*
+ * 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.Graphics2D;
+import java.awt.Shape;
+import java.awt.geom.Rectangle2D;
+
+import org.apache.batik.bridge.BridgeContext;
+import org.apache.batik.bridge.SVGImageElementBridge;
+import org.apache.batik.gvt.AbstractGraphicsNode;
+import org.apache.batik.gvt.GraphicsNode;
+import org.apache.batik.util.ParsedURL;
+import org.apache.xmlgraphics.image.loader.Image;
+import org.apache.xmlgraphics.image.loader.ImageException;
+import org.apache.xmlgraphics.image.loader.ImageFlavor;
+import org.apache.xmlgraphics.image.loader.ImageInfo;
+import org.apache.xmlgraphics.image.loader.ImageManager;
+import org.apache.xmlgraphics.image.loader.ImageSessionContext;
+import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawCCITTFax;
+import org.apache.xmlgraphics.image.loader.impl.ImageRawJPEG;
+import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM;
+import org.apache.xmlgraphics.java2d.Graphics2DImagePainter;
+import org.w3c.dom.Element;
+import org.w3c.dom.svg.SVGDocument;
+
+/**
+ * Bridge class for the <image> element when jpeg images.
+ *
+ * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
+ */
+public abstract class AbstractFOPImageElementBridge extends SVGImageElementBridge {
+
+ /**
+ * Constructs a new bridge for the &lt;image> element.
+ */
+ public AbstractFOPImageElementBridge() { }
+
+ /**
+ * Create the raster image node.
+ * THis checks if it is a jpeg file and creates a jpeg node
+ * so the jpeg can be inserted directly into the pdf document.
+ * @param ctx the bridge context
+ * @param imageElement the svg element for the image
+ * @param purl the parsed url for the image resource
+ * @return a new graphics node
+ */
+ protected GraphicsNode createImageGraphicsNode
+ (BridgeContext ctx, Element imageElement, ParsedURL purl) {
+ AbstractFOPBridgeContext bridgeCtx = (AbstractFOPBridgeContext)ctx;
+
+ ImageManager manager = bridgeCtx.getImageManager();
+ ImageSessionContext sessionContext = bridgeCtx.getImageSessionContext();
+ try {
+ ImageInfo info = manager.getImageInfo(purl.toString(), sessionContext);
+ ImageFlavor[] supportedFlavors = getSupportedFlavours();
+ Image image = manager.getImage(info, supportedFlavors, sessionContext);
+
+ //TODO color profile overrides aren't handled, yet!
+ //ICCColorSpaceExt colorspaceOverride = extractColorSpace(e, ctx);
+ AbstractGraphicsNode specializedNode = null;
+ if (image instanceof ImageXMLDOM) {
+ ImageXMLDOM xmlImage = (ImageXMLDOM)image;
+ if (xmlImage.getDocument() instanceof SVGDocument) {
+ return createSVGImageNode(ctx, imageElement,
+ (SVGDocument)xmlImage.getDocument());
+ } else {
+ //Convert image to Graphics2D
+ image = manager.convertImage(xmlImage,
+ new ImageFlavor[] {ImageFlavor.GRAPHICS2D});
+ }
+ }
+ if (image instanceof ImageRawJPEG) {
+ specializedNode = createLoaderImageNode(image, ctx, imageElement, purl);
+ } else if (image instanceof ImageRawCCITTFax) {
+ specializedNode = createLoaderImageNode(image, ctx, imageElement, purl);
+ } else if (image instanceof ImageGraphics2D) {
+ ImageGraphics2D g2dImage = (ImageGraphics2D)image;
+ specializedNode = new Graphics2DNode(g2dImage);
+ } else {
+ ctx.getUserAgent().displayError(
+ new ImageException("Cannot convert an image to a usable format: " + purl));
+ }
+
+ Rectangle2D imgBounds = getImageBounds(ctx, imageElement);
+ Rectangle2D bounds = specializedNode.getPrimitiveBounds();
+ float [] vb = new float[4];
+ vb[0] = 0; // x
+ vb[1] = 0; // y
+ vb[2] = (float) bounds.getWidth(); // width
+ vb[3] = (float) bounds.getHeight(); // height
+
+ // handles the 'preserveAspectRatio', 'overflow' and 'clip'
+ // and sets the appropriate AffineTransform to the image node
+ initializeViewport(ctx, imageElement, specializedNode, vb, imgBounds);
+ return specializedNode;
+ } catch (Exception e) {
+ ctx.getUserAgent().displayError(e);
+ }
+
+ return superCreateGraphicsNode(ctx, imageElement, purl);
+ }
+
+ /**
+ * Calls the superclass' createImageGraphicNode() method to create the normal GraphicsNode.
+ * @param ctx the bridge context
+ * @param imageElement the image element
+ * @param purl the parsed URL
+ * @return the newly created graphics node
+ * @see org.apache.batik.bridge.SVGImageElementBridge#createGraphicsNode(BridgeContext, Element)
+ */
+ protected GraphicsNode superCreateGraphicsNode
+ (BridgeContext ctx, Element imageElement, ParsedURL purl) {
+ return super.createImageGraphicsNode(ctx, imageElement, purl);
+ }
+
+ /**
+ * Returns an array of supported image flavours
+ *
+ * @return an array of supported image flavours
+ */
+ protected abstract ImageFlavor[] getSupportedFlavours();
+
+ /**
+ * Creates a loader image node implementation
+ * @param purl the parsed url
+ * @param imageElement the image element
+ * @param ctx the batik bridge context
+ * @param image the image
+ *
+ * @return a loader image node implementation
+ */
+ protected LoaderImageNode createLoaderImageNode(
+ Image image, BridgeContext ctx, Element imageElement, ParsedURL purl) {
+ return new LoaderImageNode(image, ctx, imageElement, purl);
+ }
+
+ /**
+ * An image node for natively handled Image instance.
+ * This holds a natively handled image so that it can be drawn into
+ * the PDFGraphics2D.
+ */
+ public class LoaderImageNode extends AbstractGraphicsNode {
+
+ protected final Image image;
+ protected final BridgeContext ctx;
+ protected final Element imageElement;
+ protected final ParsedURL purl;
+ protected GraphicsNode origGraphicsNode = null;
+
+ /**
+ * Create a new image node for drawing natively handled images
+ * into PDF graphics.
+ * @param image the JPEG image
+ * @param ctx the bridge context
+ * @param imageElement the SVG image element
+ * @param purl the URL to the image
+ */
+ public LoaderImageNode(Image image, BridgeContext ctx,
+ Element imageElement, ParsedURL purl) {
+ this.image = image;
+ this.ctx = ctx;
+ this.imageElement = imageElement;
+ this.purl = purl;
+ }
+
+ /** {@inheritDoc} */
+ public Shape getOutline() {
+ return getPrimitiveBounds();
+ }
+
+ /** {@inheritDoc} */
+ public void primitivePaint(Graphics2D g2d) {
+ if (g2d instanceof NativeImageHandler) {
+ NativeImageHandler nativeImageHandler = (NativeImageHandler) g2d;
+ float x = 0;
+ float y = 0;
+ try {
+ float width = image.getSize().getWidthPx();
+ float height = image.getSize().getHeightPx();
+ nativeImageHandler.addNativeImage(image, x, y, width, height);
+ } catch (Exception e) {
+ ctx.getUserAgent().displayError(e);
+ }
+ } else {
+ // Not going directly into PDF so use
+ // original implementation so filters etc work.
+ if (origGraphicsNode == null) {
+ // Haven't constructed base class Graphics Node,
+ // so do so now.
+ origGraphicsNode
+ = superCreateGraphicsNode(ctx, imageElement, purl);
+ }
+ origGraphicsNode.primitivePaint(g2d);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public Rectangle2D getGeometryBounds() {
+ return getPrimitiveBounds();
+ }
+
+ /** {@inheritDoc} */
+ public Rectangle2D getPrimitiveBounds() {
+ return new Rectangle2D.Double(0, 0,
+ image.getSize().getWidthPx(),
+ image.getSize().getHeightPx());
+ }
+
+ /** {@inheritDoc} */
+ public Rectangle2D getSensitiveBounds() {
+ //No interactive features, just return primitive bounds
+ return getPrimitiveBounds();
+ }
+
+ }
+
+ /**
+ * A node that holds a Graphics2D image.
+ */
+ public class Graphics2DNode extends AbstractGraphicsNode {
+
+ private final ImageGraphics2D image;
+
+ /**
+ * Create a new Graphics2D node.
+ * @param g2d the Graphics2D image
+ */
+ public Graphics2DNode(ImageGraphics2D g2d) {
+ this.image = g2d;
+ }
+
+ /** {@inheritDoc} */
+ public Shape getOutline() {
+ return getPrimitiveBounds();
+ }
+
+ /** {@inheritDoc} */
+ public void primitivePaint(Graphics2D g2d) {
+ int width = image.getSize().getWidthPx();
+ int height = image.getSize().getHeightPx();
+ Rectangle2D area = new Rectangle2D.Double(0, 0, width, height);
+ Graphics2DImagePainter painter = image.getGraphics2DImagePainter();
+ painter.paint(g2d, area);
+ }
+
+ /** {@inheritDoc} */
+ public Rectangle2D getGeometryBounds() {
+ return getPrimitiveBounds();
+ }
+
+ /** {@inheritDoc} */
+ public Rectangle2D getPrimitiveBounds() {
+ return new Rectangle2D.Double(0, 0,
+ image.getSize().getWidthPx(),
+ image.getSize().getHeightPx());
+ }
+
+ /** {@inheritDoc} */
+ public Rectangle2D getSensitiveBounds() {
+ //No interactive features, just return primitive bounds
+ return getPrimitiveBounds();
+ }
+
+ }
+}
diff --git a/src/java/org/apache/fop/svg/AbstractFOPTextElementBridge.java b/src/java/org/apache/fop/svg/AbstractFOPTextElementBridge.java
new file mode 100644
index 000000000..53b8e2ad5
--- /dev/null
+++ b/src/java/org/apache/fop/svg/AbstractFOPTextElementBridge.java
@@ -0,0 +1,113 @@
+/*
+ * 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 org.apache.batik.bridge.BridgeContext;
+import org.apache.batik.bridge.SVGTextElementBridge;
+import org.apache.batik.gvt.GraphicsNode;
+import org.apache.batik.gvt.TextNode;
+import org.apache.batik.gvt.TextPainter;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * Bridge class for the &lt;text> element.
+ * This bridge will use the direct text painter if the text
+ * for the element is simple.
+ *
+ * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
+ */
+public abstract class AbstractFOPTextElementBridge extends SVGTextElementBridge {
+
+ /** text painter */
+ protected TextPainter textPainter;
+
+ /**
+ * Main constructor
+ *
+ * @param textPainter the text painter
+ */
+ public AbstractFOPTextElementBridge(TextPainter textPainter) {
+ this.textPainter = textPainter;
+ }
+
+ /**
+ * Create a text element bridge.
+ *
+ * This set the text painter on the node if the text is simple.
+ * @param ctx the bridge context
+ * @param e the svg element
+ * @return the text graphics node created by the super class
+ */
+ public GraphicsNode createGraphicsNode(BridgeContext ctx, Element e) {
+ GraphicsNode node = super.createGraphicsNode(ctx, e);
+ if (node != null) {
+ //Set our own text painter
+ ((TextNode)node).setTextPainter(textPainter);
+ }
+ return node;
+ }
+
+ /**
+ * Check if text element contains simple text.
+ * This checks the children of the text element to determine
+ * if the text is simple. The text is simple if it can be rendered
+ * with basic text drawing algorithms. This means there are no
+ * alternate characters, the font is known and there are no effects
+ * applied to the text.
+ *
+ * @param ctx the bridge context
+ * @param element the svg text element
+ * @param node the graphics node
+ * @return true if this text is simple of false if it cannot be
+ * easily rendered using normal drawString on the Graphics2D
+ */
+ protected boolean isSimple(BridgeContext ctx, Element element, GraphicsNode node) {
+ for (Node n = element.getFirstChild();
+ n != null;
+ n = n.getNextSibling()) {
+
+ switch (n.getNodeType()) {
+ case Node.ELEMENT_NODE:
+
+ if (n.getLocalName().equals(SVG_TSPAN_TAG)
+ || n.getLocalName().equals(SVG_ALT_GLYPH_TAG)) {
+ return false;
+ } else if (n.getLocalName().equals(SVG_TEXT_PATH_TAG)) {
+ return false;
+ } else if (n.getLocalName().equals(SVG_TREF_TAG)) {
+ return false;
+ }
+ break;
+ case Node.TEXT_NODE:
+ case Node.CDATA_SECTION_NODE:
+ default:
+ }
+ }
+
+ /*if (CSSUtilities.convertFilter(element, node, ctx) != null) {
+ return false;
+ }*/
+
+ return true;
+ }
+
+}
+
diff --git a/src/java/org/apache/fop/svg/AbstractFOPTextPainter.java b/src/java/org/apache/fop/svg/AbstractFOPTextPainter.java
new file mode 100644
index 000000000..560c76cb5
--- /dev/null
+++ b/src/java/org/apache/fop/svg/AbstractFOPTextPainter.java
@@ -0,0 +1,528 @@
+/*
+ * 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.Color;
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.Shape;
+import java.awt.font.TextAttribute;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.io.IOException;
+import java.text.AttributedCharacterIterator;
+import java.text.CharacterIterator;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.batik.dom.svg.SVGOMTextElement;
+import org.apache.batik.gvt.TextNode;
+import org.apache.batik.gvt.TextPainter;
+import org.apache.batik.gvt.font.GVTFontFamily;
+import org.apache.batik.gvt.renderer.StrokingTextPainter;
+import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
+import org.apache.batik.gvt.text.Mark;
+import org.apache.batik.gvt.text.TextPaintInfo;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.fop.afp.AFPGraphics2D;
+import org.apache.fop.fonts.Font;
+import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.fonts.FontTriplet;
+
+
+/**
+ * Renders the attributed character iterator of a <tt>TextNode</tt>.
+ * This class draws the text directly into the Graphics2D so that
+ * the text is not drawn using shapes.
+ * If the text is simple enough to draw then it sets the font and calls
+ * drawString. If the text is complex or the cannot be translated
+ * into a simple drawString the StrokingTextPainter is used instead.
+ */
+public abstract class AbstractFOPTextPainter implements TextPainter {
+
+ /** the logger for this class */
+ protected Log log = LogFactory.getLog(AbstractFOPTextPainter.class);
+
+ private final FOPTextHandler nativeTextHandler;
+
+ /**
+ * Use the stroking text painter to get the bounds and shape.
+ * Also used as a fallback to draw the string with strokes.
+ */
+ protected static final TextPainter
+ PROXY_PAINTER = StrokingTextPainter.getInstance();
+
+ /**
+ * Create a new PS text painter with the given font information.
+ * @param nativeTextHandler the NativeTextHandler instance used for text painting
+ */
+ public AbstractFOPTextPainter(FOPTextHandler nativeTextHandler) {
+ this.nativeTextHandler = nativeTextHandler;
+ }
+
+ /**
+ * Paints the specified attributed character iterator using the
+ * specified Graphics2D and context and font context.
+ *
+ * @param node the TextNode to paint
+ * @param g2d the Graphics2D to use
+ */
+ public void paint(TextNode node, Graphics2D g2d) {
+ Point2D loc = node.getLocation();
+ log.debug("painting text node " + node);
+ if (hasUnsupportedAttributes(node)) {
+ log.debug("hasUnsuportedAttributes");
+ PROXY_PAINTER.paint(node, g2d);
+ } else {
+ log.debug("allAttributesSupported");
+ paintTextRuns(node.getTextRuns(), g2d, loc);
+ }
+ }
+
+ private boolean hasUnsupportedAttributes(TextNode node) {
+ Iterator iter = node.getTextRuns().iterator();
+ while (iter.hasNext()) {
+ StrokingTextPainter.TextRun
+ run = (StrokingTextPainter.TextRun)iter.next();
+ AttributedCharacterIterator aci = run.getACI();
+ boolean hasUnsupported = hasUnsupportedAttributes(aci);
+ if (hasUnsupported) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean hasUnsupportedAttributes(AttributedCharacterIterator aci) {
+ boolean hasUnsupported = false;
+
+ Font font = getFont(aci);
+ String text = getText(aci);
+ if (hasUnsupportedGlyphs(text, font)) {
+ log.trace("-> Unsupported glyphs found");
+ hasUnsupported = true;
+ }
+
+ TextPaintInfo tpi = (TextPaintInfo) aci.getAttribute(
+ GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO);
+ if ((tpi != null)
+ && ((tpi.strokeStroke != null && tpi.strokePaint != null)
+ || (tpi.strikethroughStroke != null)
+ || (tpi.underlineStroke != null)
+ || (tpi.overlineStroke != null))) {
+ log.trace("-> under/overlines etc. found");
+ hasUnsupported = true;
+ }
+
+ //Alpha is not supported
+ Paint foreground = (Paint) aci.getAttribute(TextAttribute.FOREGROUND);
+ if (foreground instanceof Color) {
+ Color col = (Color)foreground;
+ if (col.getAlpha() != 255) {
+ log.trace("-> transparency found");
+ hasUnsupported = true;
+ }
+ }
+
+ Object letSpace = aci.getAttribute(
+ GVTAttributedCharacterIterator.TextAttribute.LETTER_SPACING);
+ if (letSpace != null) {
+ log.trace("-> letter spacing found");
+ hasUnsupported = true;
+ }
+
+ Object wordSpace = aci.getAttribute(
+ GVTAttributedCharacterIterator.TextAttribute.WORD_SPACING);
+ if (wordSpace != null) {
+ log.trace("-> word spacing found");
+ hasUnsupported = true;
+ }
+
+ Object lengthAdjust = aci.getAttribute(
+ GVTAttributedCharacterIterator.TextAttribute.LENGTH_ADJUST);
+ if (lengthAdjust != null) {
+ log.trace("-> length adjustments found");
+ hasUnsupported = true;
+ }
+
+ Object writeMod = aci.getAttribute(
+ GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE);
+ if (writeMod != null
+ && !GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE_LTR.equals(
+ writeMod)) {
+ log.trace("-> Unsupported writing modes found");
+ hasUnsupported = true;
+ }
+
+ Object vertOr = aci.getAttribute(
+ GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION);
+ if (GVTAttributedCharacterIterator.TextAttribute.ORIENTATION_ANGLE.equals(
+ vertOr)) {
+ log.trace("-> vertical orientation found");
+ hasUnsupported = true;
+ }
+
+ Object rcDel = aci.getAttribute(
+ GVTAttributedCharacterIterator.TextAttribute.TEXT_COMPOUND_DELIMITER);
+ //Batik 1.6 returns null here which makes it impossible to determine whether this can
+ //be painted or not, i.e. fall back to stroking. :-(
+ if (rcDel != null && !(rcDel instanceof SVGOMTextElement)) {
+ log.trace("-> spans found");
+ hasUnsupported = true; //Filter spans
+ }
+
+ if (hasUnsupported) {
+ log.trace("Unsupported attributes found in ACI, using StrokingTextPainter");
+ }
+ return hasUnsupported;
+ }
+
+ /**
+ * Paint a list of text runs on the Graphics2D at a given location.
+ * @param textRuns the list of text runs
+ * @param g2d the Graphics2D to paint to
+ * @param loc the current location of the "cursor"
+ */
+ protected void paintTextRuns(List textRuns, Graphics2D g2d, Point2D loc) {
+ Point2D currentloc = loc;
+ Iterator i = textRuns.iterator();
+ while (i.hasNext()) {
+ StrokingTextPainter.TextRun
+ run = (StrokingTextPainter.TextRun)i.next();
+ currentloc = paintTextRun(run, g2d, currentloc);
+ }
+ }
+
+ /**
+ * Paint a single text run on the Graphics2D at a given location.
+ * @param run the text run to paint
+ * @param g2d the Graphics2D to paint to
+ * @param loc the current location of the "cursor"
+ * @return the new location of the "cursor" after painting the text run
+ */
+ protected Point2D paintTextRun(StrokingTextPainter.TextRun run, Graphics2D g2d, Point2D loc) {
+ AttributedCharacterIterator aci = run.getACI();
+ aci.first();
+
+ updateLocationFromACI(aci, loc);
+ AffineTransform at = g2d.getTransform();
+ loc = at.transform(loc, null);
+
+ // font
+ Font font = getFont(aci);
+ if (font != null) {
+ nativeTextHandler.setOverrideFont(font);
+ }
+
+ // color
+ TextPaintInfo tpi = (TextPaintInfo) aci.getAttribute(
+ GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO);
+ if (tpi == null) {
+ return loc;
+ }
+ Paint foreground = tpi.fillPaint;
+ if (foreground instanceof Color) {
+ Color col = (Color)foreground;
+ g2d.setColor(col);
+ }
+ g2d.setPaint(foreground);
+
+ // text
+ String txt = getText(aci);
+ float advance = getStringWidth(txt, font);
+ float tx = 0;
+ TextNode.Anchor anchor = (TextNode.Anchor)aci.getAttribute(
+ GVTAttributedCharacterIterator.TextAttribute.ANCHOR_TYPE);
+ if (anchor != null) {
+ switch (anchor.getType()) {
+ case TextNode.Anchor.ANCHOR_MIDDLE:
+ tx = -advance / 2;
+ break;
+ case TextNode.Anchor.ANCHOR_END:
+ tx = -advance;
+ break;
+ default: //nop
+ }
+ }
+
+ // draw string
+ double x = loc.getX();
+ double y = loc.getY();
+ try {
+ try {
+ nativeTextHandler.drawString(g2d, txt, (float)x + tx, (float)y);
+ } catch (IOException ioe) {
+ if (g2d instanceof AFPGraphics2D) {
+ ((AFPGraphics2D)g2d).handleIOException(ioe);
+ }
+ }
+ } finally {
+ nativeTextHandler.setOverrideFont(null);
+ }
+ loc.setLocation(loc.getX() + advance, loc.getY());
+ return loc;
+ }
+
+ /**
+ * Extract the raw text from an ACI.
+ * @param aci ACI to inspect
+ * @return the extracted text
+ */
+ protected String getText(AttributedCharacterIterator aci) {
+ StringBuffer sb = new StringBuffer(aci.getEndIndex() - aci.getBeginIndex());
+ for (char c = aci.first(); c != CharacterIterator.DONE; c = aci.next()) {
+ sb.append(c);
+ }
+ return sb.toString();
+ }
+
+ private void updateLocationFromACI(
+ AttributedCharacterIterator aci,
+ Point2D loc) {
+ //Adjust position of span
+ Float xpos = (Float)aci.getAttribute(
+ GVTAttributedCharacterIterator.TextAttribute.X);
+ Float ypos = (Float)aci.getAttribute(
+ GVTAttributedCharacterIterator.TextAttribute.Y);
+ Float dxpos = (Float)aci.getAttribute(
+ GVTAttributedCharacterIterator.TextAttribute.DX);
+ Float dypos = (Float)aci.getAttribute(
+ GVTAttributedCharacterIterator.TextAttribute.DY);
+ if (xpos != null) {
+ loc.setLocation(xpos.doubleValue(), loc.getY());
+ }
+ if (ypos != null) {
+ loc.setLocation(loc.getX(), ypos.doubleValue());
+ }
+ if (dxpos != null) {
+ loc.setLocation(loc.getX() + dxpos.doubleValue(), loc.getY());
+ }
+ if (dypos != null) {
+ loc.setLocation(loc.getX(), loc.getY() + dypos.doubleValue());
+ }
+ }
+
+ private String getStyle(AttributedCharacterIterator aci) {
+ Float posture = (Float)aci.getAttribute(TextAttribute.POSTURE);
+ return ((posture != null) && (posture.floatValue() > 0.0))
+ ? Font.STYLE_ITALIC
+ : Font.STYLE_NORMAL;
+ }
+
+ private int getWeight(AttributedCharacterIterator aci) {
+ Float taWeight = (Float)aci.getAttribute(TextAttribute.WEIGHT);
+ return ((taWeight != null) && (taWeight.floatValue() > 1.0))
+ ? Font.WEIGHT_BOLD
+ : Font.WEIGHT_NORMAL;
+ }
+
+ private Font getFont(AttributedCharacterIterator aci) {
+ Float fontSize = (Float)aci.getAttribute(TextAttribute.SIZE);
+ String style = getStyle(aci);
+ int weight = getWeight(aci);
+
+ FontInfo fontInfo = nativeTextHandler.getFontInfo();
+ String fontFamily = null;
+ List gvtFonts = (List) aci.getAttribute(
+ GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES);
+ if (gvtFonts != null) {
+ Iterator i = gvtFonts.iterator();
+ while (i.hasNext()) {
+ GVTFontFamily fam = (GVTFontFamily) i.next();
+ /* (todo) Enable SVG Font painting
+ if (fam instanceof SVGFontFamily) {
+ PROXY_PAINTER.paint(node, g2d);
+ return;
+ }*/
+ fontFamily = fam.getFamilyName();
+ if (fontInfo.hasFont(fontFamily, style, weight)) {
+ FontTriplet triplet = fontInfo.fontLookup(
+ fontFamily, style, weight);
+ int fsize = (int)(fontSize.floatValue() * 1000);
+ return fontInfo.getFontInstance(triplet, fsize);
+ }
+ }
+ }
+ FontTriplet triplet = fontInfo.fontLookup("any", style, Font.WEIGHT_NORMAL);
+ int fsize = (int)(fontSize.floatValue() * 1000);
+ return fontInfo.getFontInstance(triplet, fsize);
+ }
+
+ private float getStringWidth(String str, Font font) {
+ float wordWidth = 0;
+ float whitespaceWidth = font.getWidth(font.mapChar(' '));
+
+ for (int i = 0; i < str.length(); i++) {
+ float charWidth;
+ char c = str.charAt(i);
+ if (!((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t'))) {
+ charWidth = font.getWidth(font.mapChar(c));
+ if (charWidth <= 0) {
+ charWidth = whitespaceWidth;
+ }
+ } else {
+ charWidth = whitespaceWidth;
+ }
+ wordWidth += charWidth;
+ }
+ return wordWidth / 1000f;
+ }
+
+ private boolean hasUnsupportedGlyphs(String str, Font font) {
+ for (int i = 0; i < str.length(); i++) {
+ char c = str.charAt(i);
+ if (!((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t'))) {
+ if (!font.hasChar(c)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get the outline shape of the text characters.
+ * This uses the StrokingTextPainter to get the outline
+ * shape since in theory it should be the same.
+ *
+ * @param node the text node
+ * @return the outline shape of the text characters
+ */
+ public Shape getOutline(TextNode node) {
+ return PROXY_PAINTER.getOutline(node);
+ }
+
+ /**
+ * Get the bounds.
+ * This uses the StrokingTextPainter to get the bounds
+ * since in theory it should be the same.
+ *
+ * @param node the text node
+ * @return the bounds of the text
+ */
+ public Rectangle2D getBounds2D(TextNode node) {
+ /* (todo) getBounds2D() is too slow
+ * because it uses the StrokingTextPainter. We should implement this
+ * method ourselves. */
+ return PROXY_PAINTER.getBounds2D(node);
+ }
+
+ /**
+ * Get the geometry bounds.
+ * This uses the StrokingTextPainter to get the bounds
+ * since in theory it should be the same.
+ *
+ * @param node the text node
+ * @return the bounds of the text
+ */
+ public Rectangle2D getGeometryBounds(TextNode node) {
+ return PROXY_PAINTER.getGeometryBounds(node);
+ }
+
+ // Methods that have no purpose for PS
+
+ /**
+ * Get the mark.
+ * This does nothing since the output is AFP and not interactive.
+ *
+ * @param node the text node
+ * @param pos the position
+ * @param all select all
+ * @return null
+ */
+ public Mark getMark(TextNode node, int pos, boolean all) {
+ return null;
+ }
+
+ /**
+ * Select at.
+ * This does nothing since the output is AFP and not interactive.
+ *
+ * @param x the x position
+ * @param y the y position
+ * @param node the text node
+ * @return null
+ */
+ public Mark selectAt(double x, double y, TextNode node) {
+ return null;
+ }
+
+ /**
+ * Select to.
+ * This does nothing since the output is AFP and not interactive.
+ *
+ * @param x the x position
+ * @param y the y position
+ * @param beginMark the start mark
+ * @return null
+ */
+ public Mark selectTo(double x, double y, Mark beginMark) {
+ return null;
+ }
+
+ /**
+ * Selec first.
+ * This does nothing since the output is AFP and not interactive.
+ *
+ * @param node the text node
+ * @return null
+ */
+ public Mark selectFirst(TextNode node) {
+ return null;
+ }
+
+ /**
+ * Select last.
+ * This does nothing since the output is AFP and not interactive.
+ *
+ * @param node the text node
+ * @return null
+ */
+ public Mark selectLast(TextNode node) {
+ return null;
+ }
+
+ /**
+ * Get selected.
+ * This does nothing since the output is AFP and not interactive.
+ *
+ * @param start the start mark
+ * @param finish the finish mark
+ * @return null
+ */
+ public int[] getSelected(Mark start, Mark finish) {
+ return null;
+ }
+
+ /**
+ * Get the highlighted shape.
+ * This does nothing since the output is AFP and not interactive.
+ *
+ * @param beginMark the start mark
+ * @param endMark the end mark
+ * @return null
+ */
+ public Shape getHighlightShape(Mark beginMark, Mark endMark) {
+ return null;
+ }
+
+}
diff --git a/src/java/org/apache/fop/svg/FOPTextHandler.java b/src/java/org/apache/fop/svg/FOPTextHandler.java
new file mode 100644
index 000000000..8fa9eeedd
--- /dev/null
+++ b/src/java/org/apache/fop/svg/FOPTextHandler.java
@@ -0,0 +1,27 @@
+/*
+ * 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;
+
+public interface FOPTextHandler extends org.apache.xmlgraphics.java2d.TextHandler {
+
+ void setOverrideFont(org.apache.fop.fonts.Font font);
+
+ org.apache.fop.fonts.FontInfo getFontInfo();
+}
diff --git a/src/java/org/apache/fop/svg/GraphicsConfiguration.java b/src/java/org/apache/fop/svg/GraphicsConfiguration.java
index ca3b3363c..881096b9a 100644
--- a/src/java/org/apache/fop/svg/GraphicsConfiguration.java
+++ b/src/java/org/apache/fop/svg/GraphicsConfiguration.java
@@ -17,7 +17,6 @@
/* $Id$ */
-
package org.apache.fop.svg;
import java.awt.image.VolatileImage;
diff --git a/src/java/org/apache/fop/svg/NativeImageHandler.java b/src/java/org/apache/fop/svg/NativeImageHandler.java
new file mode 100644
index 000000000..8e74cba1d
--- /dev/null
+++ b/src/java/org/apache/fop/svg/NativeImageHandler.java
@@ -0,0 +1,40 @@
+/*
+ * 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;
+
+public interface NativeImageHandler {
+
+ /**
+ * Add a natively handled image directly to the document.
+ * This is used by the ImageElementBridge to draw a natively handled image
+ * (like JPEG or CCITT images)
+ * directly into the 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
+ */
+ void addNativeImage(org.apache.xmlgraphics.image.loader.Image image, float x, float y,
+ float width, float height);
+
+}
diff --git a/src/java/org/apache/fop/svg/PDFBridgeContext.java b/src/java/org/apache/fop/svg/PDFBridgeContext.java
index fdf83784f..364c7a6f3 100644
--- a/src/java/org/apache/fop/svg/PDFBridgeContext.java
+++ b/src/java/org/apache/fop/svg/PDFBridgeContext.java
@@ -20,30 +20,20 @@
package org.apache.fop.svg;
import java.awt.geom.AffineTransform;
-import java.lang.reflect.Constructor;
-import org.apache.batik.bridge.Bridge;
import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.DocumentLoader;
+import org.apache.batik.bridge.SVGTextElementBridge;
import org.apache.batik.bridge.UserAgent;
-
+import org.apache.batik.gvt.TextPainter;
+import org.apache.fop.fonts.FontInfo;
import org.apache.xmlgraphics.image.loader.ImageManager;
import org.apache.xmlgraphics.image.loader.ImageSessionContext;
-import org.apache.fop.fonts.FontInfo;
-
/**
* BridgeContext which registers the custom bridges for PDF output.
*/
-public class PDFBridgeContext extends BridgeContext {
-
- /** The font list. */
- private final FontInfo fontInfo;
-
- private final ImageManager imageManager;
- private final ImageSessionContext imageSessionContext;
-
- private AffineTransform linkTransform;
+public class PDFBridgeContext extends AbstractFOPBridgeContext {
/**
* Constructs a new bridge context.
@@ -53,18 +43,16 @@ public class PDFBridgeContext extends BridgeContext {
* in which case text is painted as shapes
* @param linkTransform AffineTransform to properly place links,
* may be null
+ * @param imageManager an image manager
+ * @param imageSessionContext an image session context
+ * @param linkTransform AffineTransform to properly place links,
+ * may be null
*/
- public PDFBridgeContext(UserAgent userAgent,
- DocumentLoader loader,
- FontInfo fontInfo,
- ImageManager imageManager,
- ImageSessionContext imageSessionContext,
- AffineTransform linkTransform) {
- super(userAgent, loader);
- this.fontInfo = fontInfo;
- this.imageManager = imageManager;
- this.imageSessionContext = imageSessionContext;
- this.linkTransform = linkTransform;
+ public PDFBridgeContext(UserAgent userAgent, DocumentLoader documentLoader,
+ FontInfo fontInfo, ImageManager imageManager,
+ ImageSessionContext imageSessionContext,
+ AffineTransform linkTransform) {
+ super(userAgent, documentLoader, fontInfo, imageManager, imageSessionContext, linkTransform);
}
/**
@@ -72,19 +60,12 @@ public class PDFBridgeContext extends BridgeContext {
* @param userAgent the user agent
* @param fontInfo the font list for the text painter, may be null
* in which case text is painted as shapes
- * @param linkTransform AffineTransform to properly place links,
- * may be null
+ * @param imageManager an image manager
+ * @param imageSessionContext an image session context
*/
- public PDFBridgeContext(UserAgent userAgent,
- FontInfo fontInfo,
- ImageManager imageManager,
- ImageSessionContext imageSessionContext,
- AffineTransform linkTransform) {
- super(userAgent);
- this.fontInfo = fontInfo;
- this.imageManager = imageManager;
- this.imageSessionContext = imageSessionContext;
- this.linkTransform = linkTransform;
+ public PDFBridgeContext(UserAgent userAgent, FontInfo fontInfo,
+ ImageManager imageManager, ImageSessionContext imageSessionContext) {
+ super(userAgent, fontInfo, imageManager, imageSessionContext);
}
/**
@@ -92,41 +73,15 @@ public class PDFBridgeContext extends BridgeContext {
* @param userAgent the user agent
* @param fontInfo the font list for the text painter, may be null
* in which case text is painted as shapes
+ * @param imageManager an image manager
+ * @param imageSessionContext an image session context
+ * @param linkTransform AffineTransform to properly place links,
+ * may be null
*/
- public PDFBridgeContext(UserAgent userAgent,
- FontInfo fontInfo,
- ImageManager imageManager,
- ImageSessionContext imageSessionContext) {
- this(userAgent, fontInfo, imageManager, imageSessionContext, null);
- }
-
- /**
- * Returns the ImageManager to be used by the ImageElementBridge.
- * @return the image manager
- */
- public ImageManager getImageManager() {
- return this.imageManager;
- }
-
- /**
- * Returns the ImageSessionContext to be used by the ImageElementBridge.
- * @return the image session context
- */
- public ImageSessionContext getImageSessionContext() {
- return this.imageSessionContext;
- }
-
- private void putPDFElementBridgeConditional(String className, String testFor) {
- try {
- Class.forName(testFor);
- //if we get here the test class is available
-
- Class clazz = Class.forName(className);
- Constructor constructor = clazz.getConstructor(new Class[] {FontInfo.class});
- putBridge((Bridge)constructor.newInstance(new Object[] {fontInfo}));
- } catch (Throwable t) {
- //simply ignore (bridges instantiated over this method are optional)
- }
+ public PDFBridgeContext(SVGUserAgent userAgent, FontInfo fontInfo,
+ ImageManager imageManager, ImageSessionContext imageSessionContext,
+ AffineTransform linkTransform) {
+ super(userAgent, fontInfo, imageManager, imageSessionContext, linkTransform);
}
/** {@inheritDoc} */
@@ -134,23 +89,24 @@ public class PDFBridgeContext extends BridgeContext {
super.registerSVGBridges();
if (fontInfo != null) {
- PDFTextElementBridge textElementBridge = new PDFTextElementBridge(fontInfo);
+ TextPainter textPainter = new PDFTextPainter(fontInfo);
+ SVGTextElementBridge textElementBridge = new PDFTextElementBridge(textPainter);
putBridge(textElementBridge);
//Batik flow text extension (may not always be available)
//putBridge(new PDFBatikFlowTextElementBridge(fontInfo);
- putPDFElementBridgeConditional(
+ putElementBridgeConditional(
"org.apache.fop.svg.PDFBatikFlowTextElementBridge",
"org.apache.batik.extension.svg.BatikFlowTextElementBridge");
//SVG 1.2 flow text support
//putBridge(new PDFSVG12TextElementBridge(fontInfo)); //-->Batik 1.7
- putPDFElementBridgeConditional(
+ putElementBridgeConditional(
"org.apache.fop.svg.PDFSVG12TextElementBridge",
"org.apache.batik.bridge.svg12.SVG12TextElementBridge");
//putBridge(new PDFSVGFlowRootElementBridge(fontInfo));
- putPDFElementBridgeConditional(
+ putElementBridgeConditional(
"org.apache.fop.svg.PDFSVGFlowRootElementBridge",
"org.apache.batik.bridge.svg12.SVGFlowRootElementBridge");
}
diff --git a/src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java b/src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java
index dd13df1c4..cc6e06978 100644
--- a/src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java
+++ b/src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java
@@ -19,30 +19,30 @@
package org.apache.fop.svg;
-import org.apache.fop.Version;
-import org.apache.fop.pdf.PDFDocument;
-import org.apache.fop.pdf.PDFFilterList;
-import org.apache.fop.pdf.PDFPage;
-import org.apache.fop.pdf.PDFStream;
-import org.apache.fop.pdf.PDFState;
-import org.apache.fop.pdf.PDFNumber;
-import org.apache.fop.pdf.PDFResources;
-import org.apache.fop.pdf.PDFColor;
-import org.apache.fop.pdf.PDFAnnotList;
-import org.apache.fop.fonts.FontInfo;
-import org.apache.fop.fonts.FontSetup;
-
-import java.awt.Graphics;
-import java.awt.Font;
import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.geom.AffineTransform;
-import java.io.OutputStream;
import java.io.IOException;
+import java.io.OutputStream;
import java.io.StringWriter;
+import org.apache.fop.Version;
+import org.apache.fop.fonts.FontInfo;
+import org.apache.fop.fonts.FontSetup;
+import org.apache.fop.pdf.PDFAnnotList;
+import org.apache.fop.pdf.PDFColor;
+import org.apache.fop.pdf.PDFDocument;
+import org.apache.fop.pdf.PDFFilterList;
+import org.apache.fop.pdf.PDFNumber;
+import org.apache.fop.pdf.PDFPage;
+import org.apache.fop.pdf.PDFPaintingState;
+import org.apache.fop.pdf.PDFResources;
+import org.apache.fop.pdf.PDFStream;
+
/**
* This class is a wrapper for the <tt>PDFGraphics2D</tt> that
* is used to create a full document around the pdf rendering from
@@ -52,7 +52,7 @@ import java.io.StringWriter;
*/
public class PDFDocumentGraphics2D extends PDFGraphics2D {
- private PDFContext pdfContext;
+ private final PDFContext pdfContext;
private int width;
private int height;
@@ -296,7 +296,7 @@ public class PDFDocumentGraphics2D extends PDFGraphics2D {
throw new IllegalStateException("Close page first before starting another");
}
//Start page
- graphicsState = new PDFState();
+ paintingState = new PDFPaintingState();
if (this.initialTransform == null) {
//Save initial transformation matrix
this.initialTransform = getTransform();
@@ -322,7 +322,7 @@ public class PDFDocumentGraphics2D extends PDFGraphics2D {
pageRef = page.referencePDF();
AffineTransform at = new AffineTransform(1.0, 0.0, 0.0, -1.0,
- 0.0, (double)height);
+ 0.0, height);
currentStream.write("1 0 0 -1 0 " + height + " cm\n");
if (svgWidth != 0) {
double scaleX = width / svgWidth;
@@ -340,7 +340,7 @@ public class PDFDocumentGraphics2D extends PDFGraphics2D {
scale(1 / s, 1 / s);
}
// Remember the transform we installed.
- graphicsState.concatenate(at);
+ paintingState.concatenate(at);
pdfContext.increasePageCount();
}
diff --git a/src/java/org/apache/fop/svg/PDFGraphics2D.java b/src/java/org/apache/fop/svg/PDFGraphics2D.java
index 08655fd47..554d26798 100644
--- a/src/java/org/apache/fop/svg/PDFGraphics2D.java
+++ b/src/java/org/apache/fop/svg/PDFGraphics2D.java
@@ -59,19 +59,9 @@ 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.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.fonts.FontTriplet;
import org.apache.fop.pdf.BitmapImage;
import org.apache.fop.pdf.PDFAnnotList;
import org.apache.fop.pdf.PDFColor;
@@ -83,16 +73,23 @@ 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.PDFState;
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;
import org.apache.fop.util.ColorExt;
+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;
/**
* PDF Graphics 2D.
@@ -103,8 +100,7 @@ import org.apache.fop.util.ColorExt;
* @version $Id$
* @see org.apache.batik.ext.awt.g2d.AbstractGraphics2D
*/
-public class PDFGraphics2D extends AbstractGraphics2D {
-
+public class PDFGraphics2D extends AbstractGraphics2D implements NativeImageHandler {
private static final AffineTransform IDENTITY_TRANSFORM = new AffineTransform();
/** The number of decimal places. */
@@ -129,9 +125,9 @@ public class PDFGraphics2D extends AbstractGraphics2D {
protected String pageRef;
/**
- * the current state of the pdf graphics
+ * The PDF painting state
*/
- protected PDFState graphicsState;
+ protected PDFPaintingState paintingState;
/**
* The PDF graphics state level that this svg is being drawn into.
@@ -200,7 +196,7 @@ public class PDFGraphics2D extends AbstractGraphics2D {
currentFontSize = size;
fontInfo = fi;
pageRef = pref;
- graphicsState = new PDFState();
+ paintingState = new PDFPaintingState();
}
/**
@@ -226,7 +222,7 @@ public class PDFGraphics2D extends AbstractGraphics2D {
this.currentFontSize = g.currentFontSize;
this.fontInfo = g.fontInfo;
this.pageRef = g.pageRef;
- this.graphicsState = g.graphicsState;
+ this.paintingState = g.paintingState;
this.currentStream = g.currentStream;
this.nativeCount = g.nativeCount;
this.outputStream = g.outputStream;
@@ -266,9 +262,9 @@ public class PDFGraphics2D extends AbstractGraphics2D {
*
* @param state the PDF state
*/
- public void setPDFState(PDFState state) {
- graphicsState = state;
- baseLevel = graphicsState.getStackLevel();
+ public void setPaintingState(PDFPaintingState state) {
+ paintingState = state;
+ baseLevel = paintingState.getStackLevel();
}
/**
@@ -369,7 +365,7 @@ public class PDFGraphics2D extends AbstractGraphics2D {
* @return the transformation matrix that established the basic user space for this document
*/
protected AffineTransform getBaseTransform() {
- AffineTransform at = new AffineTransform(graphicsState.getTransform());
+ AffineTransform at = new AffineTransform(paintingState.getTransform());
return at;
}
@@ -417,7 +413,7 @@ public class PDFGraphics2D extends AbstractGraphics2D {
* @param width the width to draw the image
* @param height the height to draw the image
*/
- void addNativeImage(org.apache.xmlgraphics.image.loader.Image image, float x, float y,
+ public void addNativeImage(org.apache.xmlgraphics.image.loader.Image image, float x, float y,
float width, float height) {
preparePainting();
String key = image.getInfo().getOriginalURI();
@@ -522,10 +518,14 @@ public class PDFGraphics2D extends AbstractGraphics2D {
g.setBackground(new Color(1, 1, 1, 0));
g.setPaint(new Color(1, 1, 1, 0));
g.fillRect(0, 0, width, height);
- g.clip(new Rectangle(0, 0, buf.getWidth(), buf.getHeight()));
+
+ int imageWidth = buf.getWidth();
+ int imageHeight = buf.getHeight();
+ g.clip(new Rectangle(0, 0, imageWidth, imageHeight));
g.setComposite(gc.getComposite());
- if (!g.drawImage(img, 0, 0, buf.getWidth(), buf.getHeight(), observer)) {
+ boolean drawn = g.drawImage(img, 0, 0, imageWidth, imageHeight, observer);
+ if (!drawn) {
return false;
}
g.dispose();
@@ -606,13 +606,13 @@ public class PDFGraphics2D extends AbstractGraphics2D {
trans.getMatrix(tranvals);
Shape imclip = getClip();
- boolean newClip = graphicsState.checkClip(imclip);
- boolean newTransform = graphicsState.checkTransform(trans)
+ boolean newClip = paintingState.checkClip(imclip);
+ boolean newTransform = paintingState.checkTransform(trans)
&& !trans.isIdentity();
if (newClip || newTransform) {
currentStream.write("q\n");
- graphicsState.push();
+ paintingState.save();
if (newTransform) {
concatMatrix(tranvals);
}
@@ -629,7 +629,7 @@ public class PDFGraphics2D extends AbstractGraphics2D {
applyColor(c, true);
Paint paint = getPaint();
- if (graphicsState.setPaint(paint)) {
+ if (paintingState.setPaint(paint)) {
if (!applyPaint(paint, false)) {
// Stroke the shape and use it to 'clip'
// the paint contents.
@@ -638,7 +638,7 @@ public class PDFGraphics2D extends AbstractGraphics2D {
if (newClip || newTransform) {
currentStream.write("Q\n");
- graphicsState.pop();
+ paintingState.restore();
}
return;
}
@@ -650,7 +650,7 @@ public class PDFGraphics2D extends AbstractGraphics2D {
doDrawing(false, true, false);
if (newClip || newTransform) {
currentStream.write("Q\n");
- graphicsState.pop();
+ paintingState.restore();
}
}
@@ -1304,7 +1304,7 @@ public class PDFGraphics2D extends AbstractGraphics2D {
if (ovFontState == null) {
java.awt.Font gFont = getFont();
fontTransform = gFont.getTransform();
- fontState = getInternalFontForAWTFont(gFont);
+ fontState = fontInfo.getFontInstanceForAWTFont(gFont);
} else {
fontState = fontInfo.getFontInstance(
ovFontState.getFontTriplet(), ovFontState.getFontSize());
@@ -1360,7 +1360,7 @@ public class PDFGraphics2D extends AbstractGraphics2D {
if (!useMultiByte) {
if (ch > 127) {
currentStream.write("\\");
- currentStream.write(Integer.toOctalString((int)ch));
+ currentStream.write(Integer.toOctalString(ch));
} else {
switch (ch) {
case '(':
@@ -1377,8 +1377,8 @@ public class PDFGraphics2D extends AbstractGraphics2D {
}
if (kerningAvailable && (i + 1) < l) {
- addKerning(currentStream, (new Integer((int)ch)),
- (new Integer((int)fontState.mapChar(s.charAt(i + 1)))),
+ addKerning(currentStream, (new Integer(ch)),
+ (new Integer(fontState.mapChar(s.charAt(i + 1)))),
kerning, startText, endText);
}
@@ -1406,7 +1406,7 @@ public class PDFGraphics2D extends AbstractGraphics2D {
vals.put(PDFGState.GSTATE_ALPHA_STROKE, new Float(strokeAlpha / 255f));
}
PDFGState gstate = pdfDoc.getFactory().makeGState(
- vals, graphicsState.getGState());
+ vals, paintingState.getGState());
resourceContext.addGState(gstate);
currentStream.write("/" + gstate.getName() + " gs\n");
}
@@ -1418,7 +1418,7 @@ public class PDFGraphics2D extends AbstractGraphics2D {
*/
protected void updateCurrentFont(Font font) {
String name = font.getFontName();
- float size = (float)font.getFontSize() / 1000f;
+ float size = font.getFontSize() / 1000f;
//Only update if necessary
if ((!name.equals(this.currentFontName))
@@ -1433,19 +1433,10 @@ public class PDFGraphics2D extends AbstractGraphics2D {
* 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
*/
protected Font getInternalFontForAWTFont(java.awt.Font awtFont) {
- Font fontState;
- String n = awtFont.getFamily();
- if (n.equals("sanserif")) {
- n = "sans-serif";
- }
- float siz = awtFont.getSize2D();
- String style = awtFont.isItalic() ? "italic" : "normal";
- int weight = awtFont.isBold() ? Font.WEIGHT_BOLD : Font.WEIGHT_NORMAL;
- FontTriplet triplet = fontInfo.fontLookup(n, style, weight);
- fontState = fontInfo.getFontInstance(triplet, (int)(siz * 1000 + 0.5));
- return fontState;
+ return fontInfo.getFontInstanceForAWTFont(awtFont);
}
/**
@@ -1597,13 +1588,13 @@ public class PDFGraphics2D extends AbstractGraphics2D {
trans.getMatrix(tranvals);
Shape imclip = getClip();
- boolean newClip = graphicsState.checkClip(imclip);
- boolean newTransform = graphicsState.checkTransform(trans)
+ boolean newClip = paintingState.checkClip(imclip);
+ boolean newTransform = paintingState.checkTransform(trans)
&& !trans.isIdentity();
if (newClip || newTransform) {
currentStream.write("q\n");
- graphicsState.push();
+ paintingState.save();
if (newTransform) {
concatMatrix(tranvals);
}
@@ -1620,14 +1611,14 @@ public class PDFGraphics2D extends AbstractGraphics2D {
applyColor(c, false);
Paint paint = getPaint();
- if (graphicsState.setPaint(paint)) {
+ if (paintingState.setPaint(paint)) {
if (!applyPaint(paint, true)) {
// Use the shape to 'clip' the paint contents.
applyUnknownPaint(paint, s);
if (newClip || newTransform) {
currentStream.write("Q\n");
- graphicsState.pop();
+ paintingState.restore();
}
return;
}
@@ -1640,7 +1631,7 @@ public class PDFGraphics2D extends AbstractGraphics2D {
iter.getWindingRule() == PathIterator.WIND_EVEN_ODD);
if (newClip || newTransform) {
currentStream.write("Q\n");
- graphicsState.pop();
+ paintingState.restore();
}
}
diff --git a/src/java/org/apache/fop/svg/PDFGraphicsConfiguration.java b/src/java/org/apache/fop/svg/PDFGraphicsConfiguration.java
index 83a431d5e..0204a2756 100644
--- a/src/java/org/apache/fop/svg/PDFGraphicsConfiguration.java
+++ b/src/java/org/apache/fop/svg/PDFGraphicsConfiguration.java
@@ -26,6 +26,7 @@ import java.awt.image.ColorModel;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
+
/**
* Our implementation of the class that returns information about
* roughly what we can handle and want to see (alpha for example).
diff --git a/src/java/org/apache/fop/svg/PDFImageElementBridge.java b/src/java/org/apache/fop/svg/PDFImageElementBridge.java
index 7eb89d2b1..1a17aa410 100644
--- a/src/java/org/apache/fop/svg/PDFImageElementBridge.java
+++ b/src/java/org/apache/fop/svg/PDFImageElementBridge.java
@@ -19,36 +19,14 @@
package org.apache.fop.svg;
-import java.awt.Graphics2D;
-import java.awt.Shape;
-import java.awt.geom.Rectangle2D;
-
-import org.w3c.dom.Element;
-import org.w3c.dom.svg.SVGDocument;
-
-import org.apache.batik.bridge.BridgeContext;
-import org.apache.batik.bridge.SVGImageElementBridge;
-import org.apache.batik.gvt.AbstractGraphicsNode;
-import org.apache.batik.gvt.GraphicsNode;
-import org.apache.batik.util.ParsedURL;
-
-import org.apache.xmlgraphics.image.loader.Image;
-import org.apache.xmlgraphics.image.loader.ImageException;
import org.apache.xmlgraphics.image.loader.ImageFlavor;
-import org.apache.xmlgraphics.image.loader.ImageInfo;
-import org.apache.xmlgraphics.image.loader.ImageManager;
-import org.apache.xmlgraphics.image.loader.ImageSessionContext;
-import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D;
-import org.apache.xmlgraphics.image.loader.impl.ImageRawCCITTFax;
-import org.apache.xmlgraphics.image.loader.impl.ImageRawJPEG;
-import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM;
/**
- * Bridge class for the &lt;image> element when jpeg images.
+ * PDF Image Element Bridge class for the &lt;image> element when jpeg images.
*
* @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
*/
-public class PDFImageElementBridge extends SVGImageElementBridge {
+public class PDFImageElementBridge extends AbstractFOPImageElementBridge {
/**
* Constructs a new bridge for the &lt;image> element.
@@ -60,210 +38,9 @@ public class PDFImageElementBridge extends SVGImageElementBridge {
ImageFlavor.RAW_CCITTFAX,
ImageFlavor.GRAPHICS2D,
ImageFlavor.XML_DOM};
- /**
- * Create the raster image node.
- * THis checks if it is a jpeg file and creates a jpeg node
- * so the jpeg can be inserted directly into the pdf document.
- * @param ctx the bridge context
- * @param imageElement the svg element for the image
- * @param purl the parsed url for the image resource
- * @return a new graphics node
- */
- protected GraphicsNode createImageGraphicsNode
- (BridgeContext ctx, Element imageElement, ParsedURL purl) {
- PDFBridgeContext pdfCtx = (PDFBridgeContext)ctx;
-
- ImageManager manager = pdfCtx.getImageManager();
- ImageSessionContext sessionContext = pdfCtx.getImageSessionContext();
- try {
- ImageInfo info = manager.getImageInfo(purl.toString(), sessionContext);
- Image image = manager.getImage(info, supportedFlavors, sessionContext);
-
- //TODO color profile overrides aren't handled, yet!
- //ICCColorSpaceExt colorspaceOverride = extractColorSpace(e, ctx);
- AbstractGraphicsNode specializedNode = null;
- if (image instanceof ImageXMLDOM) {
- ImageXMLDOM xmlImage = (ImageXMLDOM)image;
- if (xmlImage.getDocument() instanceof SVGDocument) {
- return createSVGImageNode(ctx, imageElement,
- (SVGDocument)xmlImage.getDocument());
- } else {
- //Convert image to Graphics2D
- image = manager.convertImage(xmlImage,
- new ImageFlavor[] {ImageFlavor.GRAPHICS2D});
- }
- }
- if (image instanceof ImageRawJPEG) {
- specializedNode = new LoaderImageNode(image, ctx, imageElement, purl);
- } else if (image instanceof ImageRawCCITTFax) {
- specializedNode = new LoaderImageNode(image, ctx, imageElement, purl);
- } else if (image instanceof ImageGraphics2D) {
- ImageGraphics2D g2dImage = (ImageGraphics2D)image;
- specializedNode = new Graphics2DNode(g2dImage);
- } else {
- ctx.getUserAgent().displayError(
- new ImageException("Cannot convert an image to a usable format: " + purl));
- }
-
- Rectangle2D imgBounds = getImageBounds(ctx, imageElement);
- Rectangle2D bounds = specializedNode.getPrimitiveBounds();
- float [] vb = new float[4];
- vb[0] = 0; // x
- vb[1] = 0; // y
- vb[2] = (float) bounds.getWidth(); // width
- vb[3] = (float) bounds.getHeight(); // height
-
- // handles the 'preserveAspectRatio', 'overflow' and 'clip'
- // and sets the appropriate AffineTransform to the image node
- initializeViewport(ctx, imageElement, specializedNode, vb, imgBounds);
- return specializedNode;
- } catch (Exception e) {
- ctx.getUserAgent().displayError(e);
- }
-
- return superCreateGraphicsNode(ctx, imageElement, purl);
- }
-
- /**
- * Calls the superclass' createImageGraphicNode() method to create the normal GraphicsNode.
- * @param ctx the bridge context
- * @param imageElement the image element
- * @param purl the parsed URL
- * @return the newly created graphics node
- * @see org.apache.batik.bridge.SVGImageElementBridge#createGraphicsNode(BridgeContext, Element)
- */
- protected GraphicsNode superCreateGraphicsNode
- (BridgeContext ctx, Element imageElement, ParsedURL purl) {
- return super.createImageGraphicsNode(ctx, imageElement, purl);
- }
-
-
- /**
- * An image node for natively handled Image instance.
- * This holds a natively handled image so that it can be drawn into
- * the PDFGraphics2D.
- */
- public class LoaderImageNode extends AbstractGraphicsNode {
-
- private Image image;
- private BridgeContext ctx;
- private Element imageElement;
- private ParsedURL purl;
- private GraphicsNode origGraphicsNode = null;
-
- /**
- * Create a new image node for drawing natively handled images
- * into PDF graphics.
- * @param image the JPEG image
- * @param ctx the bridge context
- * @param imageElement the SVG image element
- * @param purl the URL to the image
- */
- public LoaderImageNode(Image image, BridgeContext ctx,
- Element imageElement, ParsedURL purl) {
- this.image = image;
- this.ctx = ctx;
- this.imageElement = imageElement;
- this.purl = purl;
- }
-
- /** {@inheritDoc} */
- public Shape getOutline() {
- return getPrimitiveBounds();
- }
-
- /** {@inheritDoc} */
- public void primitivePaint(Graphics2D g2d) {
- if (g2d instanceof PDFGraphics2D) {
- PDFGraphics2D pdfg = (PDFGraphics2D) g2d;
- float x = 0;
- float y = 0;
- try {
- float width = image.getSize().getWidthPx();
- float height = image.getSize().getHeightPx();
- pdfg.addNativeImage(image, x, y, width, height);
- } catch (Exception e) {
- ctx.getUserAgent().displayError(e);
- }
- } else {
- // Not going directly into PDF so use
- // original implementation so filters etc work.
- if (origGraphicsNode == null) {
- // Haven't constructed baseclass Graphics Node,
- // so do so now.
- origGraphicsNode
- = PDFImageElementBridge.this.superCreateGraphicsNode
- (ctx, imageElement, purl);
- }
- origGraphicsNode.primitivePaint(g2d);
- }
- }
-
- /** {@inheritDoc} */
- public Rectangle2D getGeometryBounds() {
- return getPrimitiveBounds();
- }
-
- /** {@inheritDoc} */
- public Rectangle2D getPrimitiveBounds() {
- return new Rectangle2D.Double(0, 0,
- image.getSize().getWidthPx(),
- image.getSize().getHeightPx());
- }
-
- /** {@inheritDoc} */
- public Rectangle2D getSensitiveBounds() {
- //No interactive features, just return primitive bounds
- return getPrimitiveBounds();
- }
-
- }
-
- /**
- * A node that holds a Graphics2D image.
- */
- public class Graphics2DNode extends AbstractGraphicsNode {
-
- private ImageGraphics2D image;
-
- /**
- * Create a new Graphics2D node.
- * @param g2d the Graphics2D image
- */
- public Graphics2DNode(ImageGraphics2D g2d) {
- this.image = g2d;
- }
-
- /** {@inheritDoc} */
- public Shape getOutline() {
- return getPrimitiveBounds();
- }
-
- /** {@inheritDoc} */
- public void primitivePaint(Graphics2D g2d) {
- int width = image.getSize().getWidthPx();
- int height = image.getSize().getHeightPx();
- Rectangle2D area = new Rectangle2D.Double(0, 0, width, height);
- image.getGraphics2DImagePainter().paint(g2d, area);
- }
-
- /** {@inheritDoc} */
- public Rectangle2D getGeometryBounds() {
- return getPrimitiveBounds();
- }
-
- /** {@inheritDoc} */
- public Rectangle2D getPrimitiveBounds() {
- return new Rectangle2D.Double(0, 0,
- image.getSize().getWidthPx(),
- image.getSize().getHeightPx());
- }
-
- /** {@inheritDoc} */
- public Rectangle2D getSensitiveBounds() {
- //No interactive features, just return primitive bounds
- return getPrimitiveBounds();
- }
+ /** {@inheritDoc} */
+ protected ImageFlavor[] getSupportedFlavours() {
+ return supportedFlavors;
}
}
diff --git a/src/java/org/apache/fop/svg/PDFTextElementBridge.java b/src/java/org/apache/fop/svg/PDFTextElementBridge.java
index 4c11aa97e..c983d2b45 100644
--- a/src/java/org/apache/fop/svg/PDFTextElementBridge.java
+++ b/src/java/org/apache/fop/svg/PDFTextElementBridge.java
@@ -19,13 +19,7 @@
package org.apache.fop.svg;
-import org.apache.batik.bridge.BridgeContext;
-import org.apache.batik.bridge.SVGTextElementBridge;
-import org.apache.batik.gvt.GraphicsNode;
-import org.apache.batik.gvt.TextNode;
import org.apache.batik.gvt.TextPainter;
-import org.apache.fop.fonts.FontInfo;
-import org.w3c.dom.Element;
/**
* Bridge class for the &lt;text> element.
@@ -34,41 +28,15 @@ import org.w3c.dom.Element;
*
* @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
*/
-public class PDFTextElementBridge extends SVGTextElementBridge {
-
- private PDFTextPainter pdfTextPainter;
+public class PDFTextElementBridge extends AbstractFOPTextElementBridge {
/**
* Constructs a new bridge for the &lt;text> element.
- * @param fi the font information
- */
- public PDFTextElementBridge(FontInfo fi) {
- pdfTextPainter = new PDFTextPainter(fi);
- }
-
- /**
- * Create a text element bridge.
- * This set the text painter on the node if the text is simple.
- * @param ctx the bridge context
- * @param e the svg element
- * @return the text graphics node created by the super class
+ *
+ * @param textPainter the text painter to use
*/
- public GraphicsNode createGraphicsNode(BridgeContext ctx, Element e) {
- GraphicsNode node = super.createGraphicsNode(ctx, e);
- if (node != null) {
- //Set our own text painter
- ((TextNode)node).setTextPainter(getTextPainter());
- }
- return node;
+ public PDFTextElementBridge(TextPainter textPainter) {
+ super(textPainter);
}
-
- /**
- * Returns the TextPainter instance used by this bridge.
- * @return the text painter
- */
- public TextPainter getTextPainter() {
- return pdfTextPainter;
- }
-
}
diff --git a/src/java/org/apache/fop/svg/PDFTextPainter.java b/src/java/org/apache/fop/svg/PDFTextPainter.java
index 06fea54cc..85447a4f9 100644
--- a/src/java/org/apache/fop/svg/PDFTextPainter.java
+++ b/src/java/org/apache/fop/svg/PDFTextPainter.java
@@ -63,8 +63,8 @@ public class PDFTextPainter extends StrokingTextPainter {
private static final boolean DEBUG = false;
- private boolean strokeText = false;
- private FontInfo fontInfo;
+ private final boolean strokeText = false;
+ private final FontInfo fontInfo;
/**
* Create a new PDF text painter with the given font information.
@@ -280,12 +280,12 @@ public class PDFTextPainter extends StrokingTextPainter {
Float fontSize = (Float) aci.getAttribute(TextAttribute.SIZE);
String style = ((posture != null) && (posture.floatValue() > 0.0))
- ? "italic" : "normal";
+ ? Font.STYLE_ITALIC : Font.STYLE_NORMAL;
int weight = ((taWeight != null)
&& (taWeight.floatValue() > 1.0)) ? Font.WEIGHT_BOLD
: Font.WEIGHT_NORMAL;
- String fontFamily = null;
+ String firstFontFamily = null;
//GVT_FONT can sometimes be different from the fonts in GVT_FONT_FAMILIES
//or GVT_FONT_FAMILIES can even be empty and only GVT_FONT is set
@@ -307,6 +307,7 @@ public class PDFTextPainter extends StrokingTextPainter {
int fsize = (int)(fontSize.floatValue() * 1000);
fonts.add(fontInfo.getFontInstance(triplet, fsize));
}
+ firstFontFamily = gvtFontFamily;
} catch (Exception e) {
//Most likely NoSuchMethodError here when using Batik 1.6
//Just skip this section in this case
@@ -320,7 +321,7 @@ public class PDFTextPainter extends StrokingTextPainter {
if (fam instanceof SVGFontFamily) {
return null; //Let Batik paint this text!
}
- fontFamily = fam.getFamilyName();
+ String fontFamily = fam.getFamilyName();
if (DEBUG) {
System.out.print(fontFamily + ", ");
}
@@ -330,15 +331,20 @@ public class PDFTextPainter extends StrokingTextPainter {
int fsize = (int)(fontSize.floatValue() * 1000);
fonts.add(fontInfo.getFontInstance(triplet, fsize));
}
+ if (firstFontFamily == null) {
+ firstFontFamily = fontFamily;
+ }
}
}
if (fonts.size() == 0) {
- FontTriplet triplet = fontInfo.fontLookup("any", style, Font.WEIGHT_NORMAL);
+ if (firstFontFamily == null) {
+ //This will probably never happen. Just to be on the safe side.
+ firstFontFamily = "any";
+ }
+ //lookup with fallback possibility (incl. substitution notification)
+ FontTriplet triplet = fontInfo.fontLookup(firstFontFamily, style, weight);
int fsize = (int)(fontSize.floatValue() * 1000);
fonts.add(fontInfo.getFontInstance(triplet, fsize));
- if (DEBUG) {
- System.out.print("fallback to 'any' font");
- }
}
if (DEBUG) {
System.out.println();