You are viewing a plain text version of this content. The canonical link for it is here.
Posted to fop-commits@xmlgraphics.apache.org by je...@apache.org on 2007/11/03 11:59:10 UTC

svn commit: r591587 - in /xmlgraphics/fop/trunk: ./ src/java/org/apache/fop/render/ps/ src/java/org/apache/fop/svg/

Author: jeremias
Date: Sat Nov  3 03:59:09 2007
New Revision: 591587

URL: http://svn.apache.org/viewvc?rev=591587&view=rev
Log:
Completely reimplemented the PDFTextPainter. All SVG text (including flow text, but excluding special cases with filters) is now painted in PDF text primitives.
The whole thing compiles against and runs with Batik 1.6 but was developed against Batik Trunk (1.7). The TextBridge for SVG 1.2 text is omitted because we're still on Batik 1.6 and FOP wouldn't compile with it. The full feature set is only available with Batik 1.7, of course. With Batik 1.6, font selection may not work as expected.

Added:
    xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFBatikFlowTextElementBridge.java   (with props)
    xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFFlowExtTextPainter.java   (with props)
    xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFFlowTextPainter.java   (with props)
    xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFSVGFlowRootElementBridge.java   (with props)
    xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFTextUtil.java   (with props)
Modified:
    xmlgraphics/fop/trunk/build.xml
    xmlgraphics/fop/trunk/src/java/org/apache/fop/render/ps/AbstractPSTranscoder.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/AbstractFOPTranscoder.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFBridgeContext.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFGraphics2D.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFTextElementBridge.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFTextPainter.java
    xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFTranscoder.java
    xmlgraphics/fop/trunk/status.xml

Modified: xmlgraphics/fop/trunk/build.xml
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/build.xml?rev=591587&r1=591586&r2=591587&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/build.xml (original)
+++ xmlgraphics/fop/trunk/build.xml Sat Nov  3 03:59:09 2007
@@ -614,12 +614,14 @@
       <include name="org/apache/fop/util/*OutputStream.class"/>
       <include name="org/apache/fop/util/SubInputStream.class"/>
       <include name="org/apache/fop/util/Finalizable.class"/>
+      <include name="org/apache/fop/util/CharUtilities.class"/>
     </patternset>
     <!-- PDF transcoder -->
     <patternset>
       <include name="org/apache/fop/render/pdf/**"/>
       <exclude name="org/apache/fop/render/pdf/PDFRenderer.class"/>
       <exclude name="org/apache/fop/render/pdf/PDFXMLHandler*"/>
+      <include name="org/apache/fop/render/*RendererConfigurator**"/>
       <include name="org/apache/fop/pdf/**"/>
     </patternset>
     <!-- PS transcoder -->
@@ -679,11 +681,9 @@
         <include name="org/apache/avalon/framework/configuration/*"/>
         <include name="org/apache/avalon/framework/container/*"/>
         <include name="org/apache/commons/logging/**"/>
-        <include name="org/apache/commons/io/CopyUtils.class"/>
-        <include name="org/apache/commons/io/IOUtils.class"/>
-        <include name="org/apache/commons/io/output/ProxyOutputStream.class"/>
-        <include name="org/apache/commons/io/output/ByteArrayOutputStream.class"/>
-        <include name="org/apache/commons/io/output/CountingOutputStream.class"/>
+        <include name="org/apache/commons/io/*.class"/>
+        <include name="org/apache/commons/io/filefilter/*.class"/>
+        <include name="org/apache/commons/io/output/*.class"/>
         <!-- TODO Remove the following lines once Batik switches over to using XML Graphics Commons -->
         <include name="org/apache/xmlgraphics/java2d/**"/>
         <include name="org/apache/xmlgraphics/ps/**"/>

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/render/ps/AbstractPSTranscoder.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/render/ps/AbstractPSTranscoder.java?rev=591587&r1=591586&r2=591587&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/render/ps/AbstractPSTranscoder.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/render/ps/AbstractPSTranscoder.java Sat Nov  3 03:59:09 2007
@@ -21,7 +21,6 @@
 
 
 import java.awt.Color;
-
 import java.io.IOException;
 
 import org.apache.avalon.framework.configuration.Configuration;
@@ -29,15 +28,12 @@
 import org.apache.batik.bridge.UnitProcessor;
 import org.apache.batik.transcoder.TranscoderException;
 import org.apache.batik.transcoder.TranscoderOutput;
-
 import org.apache.batik.transcoder.image.ImageTranscoder;
-
 import org.apache.fop.fonts.FontInfo;
 import org.apache.fop.fonts.FontSetup;
 import org.apache.fop.svg.AbstractFOPTranscoder;
 import org.apache.xmlgraphics.java2d.ps.AbstractPSDocumentGraphics2D;
 import org.apache.xmlgraphics.java2d.ps.TextHandler;
-
 import org.w3c.dom.Document;
 import org.w3c.dom.svg.SVGLength;
 
@@ -137,15 +133,6 @@
         }
     }
     
-    /** @return true if text should be stroked rather than painted using text operators */
-    protected boolean isTextStroked() {
-        boolean stroke = false;
-        if (hints.containsKey(KEY_STROKE_TEXT)) {
-            stroke = ((Boolean)hints.get(KEY_STROKE_TEXT)).booleanValue();
-        }
-        return stroke;
-    }
-
     /** {@inheritDoc} */
     protected BridgeContext createBridgeContext() {
 

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/AbstractFOPTranscoder.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/AbstractFOPTranscoder.java?rev=591587&r1=591586&r2=591587&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/AbstractFOPTranscoder.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/AbstractFOPTranscoder.java Sat Nov  3 03:59:09 2007
@@ -19,27 +19,25 @@
  
 package org.apache.fop.svg;
 
-import org.xml.sax.EntityResolver;
-
-import org.apache.commons.logging.impl.SimpleLog;
-import org.apache.commons.logging.Log;
 import org.apache.batik.bridge.UserAgent;
 import org.apache.batik.dom.svg.SVGDOMImplementation;
 import org.apache.batik.dom.util.DocumentFactory;
 import org.apache.batik.transcoder.ErrorHandler;
+import org.apache.batik.transcoder.SVGAbstractTranscoder;
 import org.apache.batik.transcoder.TranscoderException;
 import org.apache.batik.transcoder.TranscodingHints;
-import org.apache.batik.transcoder.SVGAbstractTranscoder;
 import org.apache.batik.transcoder.image.ImageTranscoder;
 import org.apache.batik.transcoder.keys.BooleanKey;
 import org.apache.batik.util.SVGConstants;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.impl.SimpleLog;
 import org.w3c.dom.DOMImplementation;
+import org.xml.sax.EntityResolver;
 
 /**
  * This is the common base class of all of FOP's transcoders.
  */
-public abstract class AbstractFOPTranscoder extends SVGAbstractTranscoder
-            {
+public abstract class AbstractFOPTranscoder extends SVGAbstractTranscoder {
 
     /**
      * The key to specify whether to stroke text instead of using text 
@@ -81,6 +79,9 @@
         return new FOPTranscoderUserAgent();
     }
     
+    /**
+     * @param logger
+     */
     public void setLogger(Log logger) {
         this.logger = logger;
     }
@@ -125,6 +126,22 @@
         return factory;
     }
 
+    /**
+     * Indicates whether text should be stroked rather than painted using text operators. Stroking
+     * text (also referred to as "painting as shapes") can used in situations where the quality of
+     * text output is not satisfying. The downside of the work-around: The generated file will
+     * likely become bigger and you will lose copy/paste functionality for certain output formats
+     * such as PDF.
+     * @return true if text should be stroked rather than painted using text operators
+     */
+    protected boolean isTextStroked() {
+        boolean stroke = false;
+        if (hints.containsKey(KEY_STROKE_TEXT)) {
+            stroke = ((Boolean)hints.get(KEY_STROKE_TEXT)).booleanValue();
+        }
+        return stroke;
+    }
+    
     // --------------------------------------------------------------------
     // FOP's default error handler (for transcoders)
     // --------------------------------------------------------------------

Added: xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFBatikFlowTextElementBridge.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFBatikFlowTextElementBridge.java?rev=591587&view=auto
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFBatikFlowTextElementBridge.java (added)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFBatikFlowTextElementBridge.java Sat Nov  3 03:59:09 2007
@@ -0,0 +1,62 @@
+/*
+ * 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.extension.svg.BatikFlowTextElementBridge;
+import org.apache.batik.gvt.GraphicsNode;
+import org.apache.batik.gvt.TextNode;
+import org.apache.batik.gvt.TextPainter;
+import org.apache.fop.fonts.FontInfo;
+
+/**
+ * Element Bridge for Batik's flow text extension, so those texts can be painted using
+ * PDF primitives.
+ */
+public class PDFBatikFlowTextElementBridge extends BatikFlowTextElementBridge {
+
+    private PDFTextPainter textPainter;
+    
+    /**
+     * Main Constructor.
+     * @param fontInfo the font directory
+     */
+    public PDFBatikFlowTextElementBridge(FontInfo fontInfo) {
+        this.textPainter = new PDFFlowExtTextPainter(fontInfo);
+    }
+    
+    /** {@inheritDoc} */
+    protected GraphicsNode instantiateGraphicsNode() {
+        GraphicsNode node = super.instantiateGraphicsNode();
+        if (node != null) {
+            //Set our own text painter
+            ((TextNode)node).setTextPainter(getTextPainter());
+        }
+        return node;
+    }
+
+    /**
+     * Returns the text painter used by this bridge.
+     * @return the text painter
+     */
+    public TextPainter getTextPainter() {
+        return this.textPainter;
+    }
+    
+}

Propchange: xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFBatikFlowTextElementBridge.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFBatikFlowTextElementBridge.java
------------------------------------------------------------------------------
    svn:keywords = Id

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFBridgeContext.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFBridgeContext.java?rev=591587&r1=591586&r2=591587&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFBridgeContext.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFBridgeContext.java Sat Nov  3 03:59:09 2007
@@ -20,7 +20,9 @@
 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;
@@ -62,8 +64,9 @@
      * @param linkTransform AffineTransform to properly place links,
      *                      may be null
      */
-    public PDFBridgeContext(UserAgent userAgent, FontInfo fontInfo, 
-                AffineTransform linkTransform) {
+    public PDFBridgeContext(UserAgent userAgent, 
+                            FontInfo fontInfo, 
+                            AffineTransform linkTransform) {
         super(userAgent);
         this.fontInfo = fontInfo;
         this.linkTransform = linkTransform;
@@ -79,12 +82,43 @@
         this(userAgent, fontInfo, null);
     }
 
+    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)
+        }
+    }
+    
     /** {@inheritDoc} */
     public void registerSVGBridges() {
         super.registerSVGBridges();
 
         if (fontInfo != null) {
-            putBridge(new PDFTextElementBridge(fontInfo));
+            PDFTextElementBridge textElementBridge = new PDFTextElementBridge(fontInfo);
+            putBridge(textElementBridge);
+            
+            //Batik flow text extension (may not always be available)
+            //putBridge(new PDFBatikFlowTextElementBridge(fontInfo);
+            putPDFElementBridgeConditional(
+                    "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(
+                    "org.apache.fop.svg.PDFSVG12TextElementBridge",
+                    "org.apache.batik.bridge.svg12.SVG12TextElementBridge");
+            
+            //putBridge(new PDFSVGFlowRootElementBridge(fontInfo));
+            putPDFElementBridgeConditional(
+                    "org.apache.fop.svg.PDFSVGFlowRootElementBridge",
+                    "org.apache.batik.bridge.svg12.SVGFlowRootElementBridge");
         }
 
         PDFAElementBridge pdfAElementBridge = new PDFAElementBridge();
@@ -99,8 +133,10 @@
     }
 
     // Make sure any 'sub bridge contexts' also have our bridges.
+    //TODO There's no matching method in the super-class here
     public BridgeContext createBridgeContext() {
         return new PDFBridgeContext(getUserAgent(), getDocumentLoader(),
                                     fontInfo, linkTransform);
     }
+    
 }

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java?rev=591587&r1=591586&r2=591587&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java Sat Nov  3 03:59:09 2007
@@ -24,6 +24,7 @@
 import org.apache.avalon.framework.configuration.Configuration;
 import org.apache.avalon.framework.configuration.ConfigurationException;
 import org.apache.fop.apps.FOPException;
+import org.apache.fop.fonts.FontCache;
 import org.apache.fop.fonts.FontInfo;
 import org.apache.fop.fonts.FontResolver;
 import org.apache.fop.fonts.FontSetup;
@@ -53,8 +54,14 @@
         //Fonts
         try {
             FontResolver fontResolver = FontSetup.createMinimalFontResolver();
+            //TODO The following could be optimized by retaining the FontCache somewhere
+            FontCache fontCache = FontCache.load();
+            if (fontCache == null) {
+                fontCache = new FontCache();
+            }
             List fontList = PrintRendererConfigurator.buildFontListFromConfiguration(
-                    cfg, null, fontResolver, false, null);
+                    cfg, null, fontResolver, false, fontCache);
+            fontCache.save();
             FontInfo fontInfo = new FontInfo();
             FontSetup.setup(fontInfo, fontList, fontResolver);
             graphics.setFontInfo(fontInfo);

Added: xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFFlowExtTextPainter.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFFlowExtTextPainter.java?rev=591587&view=auto
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFFlowExtTextPainter.java (added)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFFlowExtTextPainter.java Sat Nov  3 03:59:09 2007
@@ -0,0 +1,49 @@
+/*
+ * 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.text.AttributedCharacterIterator;
+import java.util.List;
+
+import org.apache.batik.extension.svg.FlowExtTextPainter;
+import org.apache.batik.gvt.TextNode;
+import org.apache.fop.fonts.FontInfo;
+
+/**
+ * Text Painter for Batik's flow text extension.
+ */
+public class PDFFlowExtTextPainter extends PDFTextPainter {
+
+    /**
+     * Main constructor
+     * @param fontInfo the font directory
+     */
+    public PDFFlowExtTextPainter(FontInfo fontInfo) {
+        super(fontInfo);
+    }
+
+    /** {@inheritDoc} */
+    public List getTextRuns(TextNode node, AttributedCharacterIterator aci) {
+        //Text runs are delegated to the normal FlowExtTextPainter, we just paint the text.
+        FlowExtTextPainter delegate = (FlowExtTextPainter)FlowExtTextPainter.getInstance();
+        return delegate.getTextRuns(node, aci);
+    }
+    
+}

Propchange: xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFFlowExtTextPainter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFFlowExtTextPainter.java
------------------------------------------------------------------------------
    svn:keywords = Id

Added: xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFFlowTextPainter.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFFlowTextPainter.java?rev=591587&view=auto
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFFlowTextPainter.java (added)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFFlowTextPainter.java Sat Nov  3 03:59:09 2007
@@ -0,0 +1,49 @@
+/*
+ * 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.text.AttributedCharacterIterator;
+import java.util.List;
+
+import org.apache.batik.gvt.TextNode;
+import org.apache.batik.gvt.flow.FlowTextPainter;
+import org.apache.fop.fonts.FontInfo;
+
+/**
+ * Text Painter for SVG 1.2 (flow) text.
+ */
+public class PDFFlowTextPainter extends PDFTextPainter {
+    
+    /**
+     * Main constructor
+     * @param fontInfo the font directory
+     */
+    public PDFFlowTextPainter(FontInfo fontInfo) {
+        super(fontInfo);
+    }
+
+    /** {@inheritDoc} */
+    public List getTextRuns(TextNode node, AttributedCharacterIterator aci) {
+        //Text runs are delegated to the normal FlowTextPainter, we just paint the text.
+        FlowTextPainter delegate = (FlowTextPainter)FlowTextPainter.getInstance();
+        return delegate.getTextRuns(node, aci);
+    }
+    
+}

Propchange: xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFFlowTextPainter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFFlowTextPainter.java
------------------------------------------------------------------------------
    svn:keywords = Id

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFGraphics2D.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFGraphics2D.java?rev=591587&r1=591586&r2=591587&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFGraphics2D.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFGraphics2D.java Sat Nov  3 03:59:09 2007
@@ -106,6 +106,9 @@
     
     /** 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
@@ -619,7 +622,7 @@
         Shape imclip = getClip();
         writeClip(imclip);
         currentStream.write("" + width + " 0 0 " + (-height) + " " + x
-                            + " " + (y + height) + " cm\n" + "/Im"
+                            + " " + (y + height) + " cm\n"
                             + imageInfo.getName() + " Do\nQ\n");
         return true;
     }
@@ -704,16 +707,7 @@
             }
         }
 
-        if (c.getAlpha() != 255) {
-            checkTransparencyAllowed();
-            Map vals = new java.util.HashMap();
-            vals.put(PDFGState.GSTATE_ALPHA_STROKE, 
-                    new Float(c.getAlpha() / 255f));
-            PDFGState gstate = pdfDoc.getFactory().makeGState(
-                    vals, graphicsState.getGState());
-            resourceContext.addGState(gstate);
-            currentStream.write("/" + gstate.getName() + " gs\n");
-        }
+        applyAlpha(OPAQUE, c.getAlpha());
 
         c = getColor();
         applyColor(c, false);
@@ -1054,12 +1048,12 @@
     private boolean createPattern(PatternPaint pp, boolean fill) {
         preparePainting();
 
-        FontInfo fontInfo = new FontInfo();
-        FontSetup.setup(fontInfo, null, null);
+        FontInfo specialFontInfo = new FontInfo();
+        FontSetup.setup(specialFontInfo, null, null);
 
         PDFResources res = pdfDoc.getFactory().makeResources();
         PDFResourceContext context = new PDFResourceContext(res);
-        PDFGraphics2D pattGraphic = new PDFGraphics2D(textAsShapes, fontInfo,
+        PDFGraphics2D pattGraphic = new PDFGraphics2D(textAsShapes, specialFontInfo,
                                         pdfDoc, context, pageRef,
                                         "", 0);
         pattGraphic.setGraphicContext(new GraphicContext());
@@ -1125,7 +1119,7 @@
         /** @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, fontInfo);
+        res.addFonts(pdfDoc, specialFontInfo);
 
         PDFPattern myPat = pdfDoc.getFactory().makePattern(
                                 resourceContext, 1, res, 1, 1, bbox,
@@ -1156,11 +1150,13 @@
         Shape clip = getClip();
         Rectangle2D usrClipBounds, usrBounds;
         usrBounds = shape.getBounds2D();
-        usrClipBounds  = clip.getBounds2D();
-        if (!usrClipBounds.intersects(usrBounds)) {
-            return true;
+        if (clip != null) {
+            usrClipBounds  = clip.getBounds2D();
+            if (!usrClipBounds.intersects(usrBounds)) {
+                return true;
+            }
+            Rectangle2D.intersect(usrBounds, usrClipBounds, usrBounds);
         }
-        Rectangle2D.intersect(usrBounds, usrClipBounds, usrBounds);
         double usrX = usrBounds.getX();
         double usrY = usrBounds.getY();
         double usrW = usrBounds.getWidth();
@@ -1169,11 +1165,15 @@
         Rectangle devShapeBounds, devClipBounds, devBounds;
         AffineTransform at = getTransform();
         devShapeBounds = at.createTransformedShape(shape).getBounds();
-        devClipBounds  = at.createTransformedShape(clip).getBounds();
-        if (!devClipBounds.intersects(devShapeBounds)) {
-            return true;
+        if (clip != null) {
+            devClipBounds  = at.createTransformedShape(clip).getBounds();
+            if (!devClipBounds.intersects(devShapeBounds)) {
+                return true;
+            }
+            devBounds = devShapeBounds.intersection(devClipBounds);
+        } else {
+            devBounds = devShapeBounds;
         }
-        devBounds = devShapeBounds.intersection(devClipBounds);
         int devX = devBounds.x;
         int devY = devBounds.y;
         int devW = devBounds.width;
@@ -1416,69 +1416,25 @@
         if (ovFontState == null) {
             java.awt.Font gFont = getFont();
             fontTransform = gFont.getTransform();
-            String n = gFont.getFamily();
-            if (n.equals("sanserif")) {
-                n = "sans-serif";
-            }
-            float siz = gFont.getSize2D();
-            String style = gFont.isItalic() ? "italic" : "normal";
-            int weight = gFont.isBold() ? Font.WEIGHT_BOLD : Font.WEIGHT_NORMAL;
-            FontTriplet triplet = fontInfo.fontLookup(n, style, weight);
-            fontState = fontInfo.getFontInstance(triplet, (int)(siz * 1000 + 0.5));
+            fontState = getInternalFontForAWTFont(gFont);
         } else {
             fontState = fontInfo.getFontInstance(
                     ovFontState.getFontTriplet(), ovFontState.getFontSize());
             ovFontState = null;
         }
-        String name;
-        float size;
-        name = fontState.getFontName();
-        size = (float)fontState.getFontSize() / 1000f;
-
-        if ((!name.equals(this.currentFontName))
-                || (size != this.currentFontSize)) {
-            this.currentFontName = name;
-            this.currentFontSize = size;
-            currentStream.write("/" + name + " " + size + " Tf\n");
-
-        }
+        updateCurrentFont(fontState);
 
         currentStream.write("q\n");
 
         Color c = getColor();
         applyColor(c, true);
         applyPaint(getPaint(), true);
-        int salpha = c.getAlpha();
-
-        if (salpha != 255) {
-            checkTransparencyAllowed();
-            Map vals = new java.util.HashMap();
-            vals.put(PDFGState.GSTATE_ALPHA_NONSTROKE, new Float(salpha / 255f));
-            PDFGState gstate = pdfDoc.getFactory().makeGState(
-                    vals, graphicsState.getGState());
-            resourceContext.addGState(gstate);
-            currentStream.write("/" + gstate.getName() + " gs\n");
-        }
-
-        Map kerning = null;
-        boolean kerningAvailable = false;
+        applyAlpha(c.getAlpha(), OPAQUE);
 
-        kerning = fontState.getKerning();
-        if (kerning != null && !kerning.isEmpty()) {
-            kerningAvailable = true;
-        }
+        Map kerning = fontState.getKerning();
+        boolean kerningAvailable = (kerning != null && !kerning.isEmpty());
 
-        // This assumes that *all* CIDFonts use a /ToUnicode mapping
-        boolean useMultiByte = false;
-        org.apache.fop.fonts.Typeface f =
-            (org.apache.fop.fonts.Typeface)fontInfo.getFonts().get(name);
-        if (f instanceof LazyFont) {
-            if (((LazyFont) f).getRealFont() instanceof CIDFont) {
-                useMultiByte = true;
-            }
-        } else if (f instanceof CIDFont) {
-            useMultiByte = true;
-        }
+        boolean useMultiByte = isMultiByteFont(currentFontName);
 
         // String startText = useMultiByte ? "<FEFF" : "(";
         String startText = useMultiByte ? "<" : "(";
@@ -1524,6 +1480,7 @@
                     case '\\':
                         currentStream.write("\\");
                         break;
+                    default:
                     }
                     currentStream.write(ch);
                 }
@@ -1540,13 +1497,88 @@
         }
         currentStream.write(endText);
 
-
         currentStream.write("] TJ\n");
-
         currentStream.write("ET\n");
         currentStream.write("Q\n");
     }
 
+    /**
+     * 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 vals = new java.util.HashMap();
+            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, graphicsState.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 = (float)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
+     */
+    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;
+    }
+
+    /**
+     * 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
+            = (org.apache.fop.fonts.Typeface)fontInfo.getFonts().get(name);
+        if (f instanceof LazyFont) {
+            if (((LazyFont) f).getRealFont() instanceof CIDFont) {
+                return true;
+            }
+        } else if (f instanceof CIDFont) {
+            return true;
+        }
+        return false;
+    }
+
     private void addKerning(StringWriter buf, Integer ch1, Integer ch2,
                             Map kerning, String startText,
                             String endText) {
@@ -1699,16 +1731,7 @@
             }
         }
 
-        if (c.getAlpha() != 255) {
-            checkTransparencyAllowed();
-            Map vals = new java.util.HashMap();
-            vals.put(PDFGState.GSTATE_ALPHA_NONSTROKE, 
-                    new Float(c.getAlpha() / 255f));
-            PDFGState gstate = pdfDoc.getFactory().makeGState(
-                    vals, graphicsState.getGState());
-            resourceContext.addGState(gstate);
-            currentStream.write("/" + gstate.getName() + " gs\n");
-        }
+        applyAlpha(c.getAlpha(), OPAQUE);
 
         c = getColor();
         applyColor(c, true);

Added: xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFSVGFlowRootElementBridge.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFSVGFlowRootElementBridge.java?rev=591587&view=auto
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFSVGFlowRootElementBridge.java (added)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFSVGFlowRootElementBridge.java Sat Nov  3 03:59:09 2007
@@ -0,0 +1,62 @@
+/*
+ * 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.svg12.SVGFlowRootElementBridge;
+import org.apache.batik.gvt.GraphicsNode;
+import org.apache.batik.gvt.TextNode;
+import org.apache.batik.gvt.TextPainter;
+import org.apache.fop.fonts.FontInfo;
+
+/**
+ * Element Bridge for SVG 1.2 flow text, so those texts can be painted using
+ * PDF primitives.
+ */
+public class PDFSVGFlowRootElementBridge extends SVGFlowRootElementBridge {
+
+    private PDFTextPainter textPainter;
+    
+    /**
+     * Main Constructor.
+     * @param fontInfo the font directory
+     */
+    public PDFSVGFlowRootElementBridge(FontInfo fontInfo) {
+        this.textPainter = new PDFFlowTextPainter(fontInfo);
+    }
+    
+    /** {@inheritDoc} */
+    protected GraphicsNode instantiateGraphicsNode() {
+        GraphicsNode node = super.instantiateGraphicsNode();
+        if (node != null) {
+            //Set our own text painter
+            ((TextNode)node).setTextPainter(getTextPainter());
+        }
+        return node;
+    }
+
+    /**
+     * Returns the text painter used by this bridge.
+     * @return the text painter
+     */
+    public TextPainter getTextPainter() {
+        return this.textPainter;
+    }
+    
+}

Propchange: xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFSVGFlowRootElementBridge.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFSVGFlowRootElementBridge.java
------------------------------------------------------------------------------
    svn:keywords = Id

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFTextElementBridge.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFTextElementBridge.java?rev=591587&r1=591586&r2=591587&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFTextElementBridge.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFTextElementBridge.java Sat Nov  3 03:59:09 2007
@@ -19,15 +19,13 @@
 
 package org.apache.fop.svg;
 
-import org.apache.batik.gvt.TextNode;
-import org.apache.batik.bridge.SVGTextElementBridge;
 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;
-import org.w3c.dom.Node;
 
 /**
  * Bridge class for the &lt;text> element.
@@ -37,11 +35,12 @@
  * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
  */
 public class PDFTextElementBridge extends SVGTextElementBridge {
+    
     private PDFTextPainter pdfTextPainter;
 
     /**
      * Constructs a new bridge for the &lt;text> element.
-     * @param fi the font infomration
+     * @param fi the font information
      */
     public PDFTextElementBridge(FontInfo fi) {
         pdfTextPainter = new PDFTextPainter(fi);
@@ -56,71 +55,20 @@
      */
     public GraphicsNode createGraphicsNode(BridgeContext ctx, Element e) {
         GraphicsNode node = super.createGraphicsNode(ctx, e);
-        if (node != null && isSimple(ctx, e, node)) {
+        if (node != null) {
+            //Set our own text painter
             ((TextNode)node).setTextPainter(getTextPainter());
         }
         return node;
     }
 
-    private PDFTextPainter getTextPainter() {
-        return pdfTextPainter;
-    }
-
     /**
-     * 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 PDFGraphics2D
+     * Returns the TextPainter instance used by this bridge.
+     * @return the text painter
      */
-    private boolean isSimple(BridgeContext ctx, Element element, GraphicsNode node) {
-        /* I cannot find any reference that 36pt is the maximum font size in PDF. Tests show
-         * no such restriction (jeremias, 28.5.2007)
-         * 
-        // Font size, in user space units.
-        float fs = TextUtilities.convertFontSize(element).floatValue();
-        // PDF cannot display fonts over 36pt
-        if (fs > 36) {
-            return false;
-        }
-        */
-
-        Element nodeElement;
-        for (Node n = element.getFirstChild();
-             n != null;
-             n = n.getNextSibling()) {
-
-            switch (n.getNodeType()) {
-            case Node.ELEMENT_NODE:
-
-                nodeElement = (Element)n;
-
-                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:
-            }
-        }
-
-        /*if (CSSUtilities.convertFilter(element, node, ctx) != null) {
-            return false;
-        }*/
-
-        return true;
+    public TextPainter getTextPainter() {
+        return pdfTextPainter;
     }
+
 }
 

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFTextPainter.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFTextPainter.java?rev=591587&r1=591586&r2=591587&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFTextPainter.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFTextPainter.java Sat Nov  3 03:59:09 2007
@@ -19,32 +19,34 @@
 
 package org.apache.fop.svg;
 
+import java.awt.BasicStroke;
+import java.awt.Color;
 import java.awt.Graphics2D;
-import java.awt.geom.Point2D;
-import java.awt.geom.Rectangle2D;
-/* java.awt.Font is not imported to avoid confusion with
-   org.apache.fop.fonts.Font */
-import java.text.AttributedCharacterIterator;
-import java.awt.font.TextAttribute;
-import java.awt.Shape;
 import java.awt.Paint;
+import java.awt.Shape;
 import java.awt.Stroke;
-import java.awt.Color;
-import java.util.List;
+import java.awt.font.TextAttribute;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Point2D;
+import java.lang.reflect.Method;
+import java.text.AttributedCharacterIterator;
 import java.util.Iterator;
+import java.util.List;
 
-import org.apache.batik.gvt.text.Mark;
-import org.apache.batik.gvt.TextPainter;
-import org.apache.batik.gvt.TextNode;
-import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
-import org.apache.batik.gvt.text.TextPaintInfo;
-import org.apache.batik.gvt.font.GVTFontFamily;
 import org.apache.batik.bridge.SVGFontFamily;
+import org.apache.batik.gvt.font.GVTFont;
+import org.apache.batik.gvt.font.GVTFontFamily;
+import org.apache.batik.gvt.font.GVTGlyphVector;
 import org.apache.batik.gvt.renderer.StrokingTextPainter;
-
+import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
+import org.apache.batik.gvt.text.TextPaintInfo;
+import org.apache.batik.gvt.text.TextSpanLayout;
 import org.apache.fop.fonts.Font;
 import org.apache.fop.fonts.FontInfo;
 import org.apache.fop.fonts.FontTriplet;
+import org.apache.fop.util.CharUtilities;
 
 /**
  * Renders the attributed character iterator of a <tt>TextNode</tt>.
@@ -54,108 +56,221 @@
  * drawString. If the text is complex or the cannot be translated
  * into a simple drawString the StrokingTextPainter is used instead.
  *
- * (todo) handle underline, overline and strikethrough
- * (todo) use drawString(AttributedCharacterIterator iterator...) for some
- *
- * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
  * @version $Id$
  */
-public class PDFTextPainter implements TextPainter {
-    private FontInfo fontInfo;
+public class PDFTextPainter extends StrokingTextPainter {
 
-    /**
-     * 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();
+    private static final boolean DEBUG = true;
+    
+    private boolean strokeText = false;
+    private FontInfo fontInfo;
 
     /**
      * Create a new PDF text painter with the given font information.
-     * @param fi the fint info
+     * @param fi the font info
      */
     public PDFTextPainter(FontInfo fi) {
         fontInfo = fi;
     }
 
-    /**
-     * 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) {
-        String txt = node.getText();
-        Point2D loc = node.getLocation();
-        
-        AttributedCharacterIterator aci =
-          node.getAttributedCharacterIterator();
-        // reset position to start of char iterator
-        if (aci.getBeginIndex() == aci.getEndIndex()) {
-            return;
-        }
-        char ch = aci.first();
-        if (ch == AttributedCharacterIterator.DONE) {
-            return;
+    /** {@inheritDoc} */
+    protected void paintTextRuns(List textRuns, Graphics2D g2d) {
+        if (DEBUG) {
+            System.out.println("paintTextRuns: count = " + textRuns.size());
+            //fontInfo.dumpAllTripletsToSystemOut();
         }
-        TextNode.Anchor anchor;
-        anchor = (TextNode.Anchor) aci.getAttribute(
-                      GVTAttributedCharacterIterator.TextAttribute.ANCHOR_TYPE);
-
-        List gvtFonts;
-        gvtFonts = (List) aci.getAttribute(
-            GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES);
-
-        TextPaintInfo tpi = (TextPaintInfo) aci.getAttribute(
-            GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO);
-        
-        if (tpi == null) {
-            return;
-        }        
-        
-        Paint forg = tpi.fillPaint;
-        Paint strokePaint = tpi.strokePaint;
-        Float size = (Float) aci.getAttribute(TextAttribute.SIZE);
-        if (size == null) {
+        if (!(g2d instanceof PDFGraphics2D) || strokeText) {
+            super.paintTextRuns(textRuns, g2d);
             return;
         }
-        Stroke stroke = tpi.strokeStroke;
-        /*
-        Float xpos = (Float) aci.getAttribute(
-                       GVTAttributedCharacterIterator.TextAttribute.X);
-        Float ypos = (Float) aci.getAttribute(
-                       GVTAttributedCharacterIterator.TextAttribute.Y);
-        */
-
-        Float posture = (Float) aci.getAttribute(TextAttribute.POSTURE);
-        Float taWeight = (Float) aci.getAttribute(TextAttribute.WEIGHT);
+        PDFGraphics2D pdf = (PDFGraphics2D)g2d;
+        PDFTextUtil textUtil = new PDFTextUtil(pdf);
+        for (int i = 0; i < textRuns.size(); i++) {
+            TextRun textRun = (TextRun)textRuns.get(i);
+            AttributedCharacterIterator runaci = textRun.getACI();
+            runaci.first();
+
+            TextPaintInfo tpi = (TextPaintInfo)runaci.getAttribute(PAINT_INFO);
+            if (tpi == null || !tpi.visible) {
+                continue;
+            }
+            if ((tpi != null) && (tpi.composite != null)) {
+                g2d.setComposite(tpi.composite);
+            }
+            
+            //------------------------------------
+            TextSpanLayout layout = textRun.getLayout();
+            if (DEBUG) {
+                int charCount = runaci.getEndIndex() - runaci.getBeginIndex();
+                System.out.println("================================================");
+                System.out.println("New text run:");
+                System.out.println("char count: " + charCount);
+                System.out.println("range: " 
+                        + runaci.getBeginIndex() + " - " + runaci.getEndIndex());
+                System.out.println("glyph count: " + layout.getGlyphCount()); //=getNumGlyphs()
+            }
+            //Gather all characters of the run
+            StringBuffer chars = new StringBuffer();
+            for (runaci.first(); runaci.getIndex() < runaci.getEndIndex();) {
+                chars.append(runaci.current());
+                runaci.next();
+            }
+            runaci.first();
+            if (DEBUG) {
+                System.out.println("Text: " + chars);
+                pdf.currentStream.write("%Text: " + chars + "\n");
+            }
+            
+            GeneralPath debugShapes = null;
+            if (DEBUG) {
+                debugShapes = new GeneralPath();
+            }
+            
+            Font[] fonts = findFonts(runaci);
+            if (fonts == null || fonts.length == 0) {
+                //Draw using Java2D
+                textRun.getLayout().draw(g2d);
+                continue;
+            }
+            
+            textUtil.saveGraphicsState();
+            textUtil.concatMatrixCurrentTransform();
+            Shape imclip = g2d.getClip();
+            pdf.writeClip(imclip);
+            
+            applyColorAndPaint(tpi, pdf);
+            
+            textUtil.beginTextObject();
+            textUtil.setFonts(fonts);
+            textUtil.setTextRenderingMode(tpi.fillPaint != null, tpi.strokePaint != null, false);
+
+            AffineTransform localTransform = new AffineTransform();
+            Point2D prevPos = null;
+            double prevVisibleCharWidth = 0.0;
+            GVTGlyphVector gv = layout.getGlyphVector();
+            for (int index = 0, c = gv.getNumGlyphs(); index < c; index++) {
+                char ch = chars.charAt(index);
+                boolean visibleChar = gv.isGlyphVisible(index)
+                    || (CharUtilities.isAnySpace(ch) && !CharUtilities.isZeroWidthSpace(ch));
+                if (DEBUG) {
+                    System.out.println("glyph " + index 
+                            + " -> " + layout.getGlyphIndex(index) + " => " + ch);
+                    if (CharUtilities.isAnySpace(ch) && ch != 32) {
+                        System.out.println("Space found: " + Integer.toHexString(ch));
+                    }
+                    if (ch == CharUtilities.ZERO_WIDTH_JOINER) {
+                        System.out.println("ZWJ found: " + Integer.toHexString(ch));
+                    }
+                    if (ch == CharUtilities.SOFT_HYPHEN) {
+                        System.out.println("Soft hyphen found: " + Integer.toHexString(ch));
+                    }
+                    if (!visibleChar) {
+                        System.out.println("Invisible glyph found: " + Integer.toHexString(ch));
+                    }
+                }
+                if (!visibleChar) {
+                    continue;
+                }
+                Point2D p = gv.getGlyphPosition(index);
 
-        boolean useStrokePainter = false;
+                AffineTransform glyphTransform = gv.getGlyphTransform(index);
+                //TODO Glyph transforms could be refined so not every char has to be painted
+                //with its own TJ command (stretch/squeeze case could be optimized)
+                if (DEBUG) {
+                    System.out.println("pos " + p + ", transform " + glyphTransform);
+                    Shape sh;
+                    sh = gv.getGlyphLogicalBounds(index);
+                    if (sh == null) {
+                        sh = new Ellipse2D.Double(p.getX(), p.getY(), 2, 2);
+                    }
+                    debugShapes.append(sh, false);
+                }
 
-        if (forg instanceof Color) {
-            Color col = (Color) forg;
-            if (col.getAlpha() != 255) {
-                useStrokePainter = true;
+                //Exact position of the glyph
+                localTransform.setToIdentity();
+                localTransform.translate(p.getX(), p.getY());
+                if (glyphTransform != null) {
+                    localTransform.concatenate(glyphTransform);
+                }
+                localTransform.scale(1, -1);
+                
+                boolean yPosChanged = (prevPos == null 
+                        || prevPos.getY() != p.getY() 
+                        || glyphTransform != null);
+                if (yPosChanged) {
+                    if (index > 0) {
+                        textUtil.writeTJ();
+                        textUtil.writeTextMatrix(localTransform);
+                    }
+                } else {
+                    double xdiff = p.getX() - prevPos.getX();
+                    //Width of previous character
+                    Font font = textUtil.getCurrentFont();
+                    double cw = prevVisibleCharWidth;
+                    double effxdiff = (1000 * xdiff) - cw;
+                    if (effxdiff != 0) {
+                        double adjust = (-effxdiff / font.getFontSize());
+                        textUtil.adjustGlyphTJ(adjust * 1000);
+                    }
+                    if (DEBUG) {
+                        System.out.println("==> x diff: " + xdiff + ", " + effxdiff 
+                                + ", charWidth: " + cw);
+                    }
+                }
+                Font f = textUtil.selectFontForChar(ch);
+                if (f != textUtil.getCurrentFont()) {
+                    textUtil.writeTJ();
+                    textUtil.setCurrentFont(f);
+                    textUtil.writeTf(f);
+                    textUtil.writeTextMatrix(localTransform);
+                }
+                char paintChar = (CharUtilities.isAnySpace(ch) ? ' ' : ch);
+                textUtil.writeTJChar(paintChar);
+                
+                //Update last position
+                prevPos = p;
+                prevVisibleCharWidth = textUtil.getCurrentFont().getCharWidth(chars.charAt(index));
+            }
+            textUtil.writeTJ();
+            textUtil.endTextObject();
+            textUtil.restoreGraphicsState();
+            if (DEBUG) {
+                g2d.setStroke(new BasicStroke(0));
+                g2d.setColor(Color.LIGHT_GRAY);
+                g2d.draw(debugShapes);
             }
-            g2d.setColor(col);
-        }
-        g2d.setPaint(forg);
-        g2d.setStroke(stroke);
-
-        if (strokePaint != null) {
-            // need to draw using AttributedCharacterIterator
-            useStrokePainter = true;
         }
+    }
 
-        if (hasUnsupportedAttributes(aci)) {
-            useStrokePainter = true;
+    private void applyColorAndPaint(TextPaintInfo tpi, PDFGraphics2D pdf) {
+        Paint fillPaint = tpi.fillPaint;
+        Paint strokePaint = tpi.strokePaint;
+        Stroke stroke = tpi.strokeStroke;
+        int fillAlpha = PDFGraphics2D.OPAQUE;
+        if (fillPaint instanceof Color) {
+            Color col = (Color)fillPaint;
+            pdf.applyColor(col, true);
+            fillAlpha = col.getAlpha();
+        }
+        if (strokePaint instanceof Color) {
+            Color col = (Color)strokePaint;
+            pdf.applyColor(col, false);
         }
-
-        // text contains unsupported information
-        if (useStrokePainter) {
-            PROXY_PAINTER.paint(node, g2d);
-            return;
+        pdf.applyPaint(fillPaint, true);
+        pdf.applyStroke(stroke);
+        if (strokePaint != null) {
+            pdf.applyPaint(strokePaint, false);
         }
+        pdf.applyAlpha(fillAlpha, PDFGraphics2D.OPAQUE);
+    }
+    
+    private Font[] findFonts(AttributedCharacterIterator aci) {
+        List fonts = new java.util.ArrayList();
+        List gvtFonts = (List) aci.getAttribute(
+                GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES);
+        Float posture = (Float) aci.getAttribute(TextAttribute.POSTURE);
+        Float taWeight = (Float) aci.getAttribute(TextAttribute.WEIGHT);
+        Float fontSize = (Float) aci.getAttribute(TextAttribute.SIZE);
 
         String style = ((posture != null) && (posture.floatValue() > 0.0))
                        ? "italic" : "normal";
@@ -163,236 +278,65 @@
                        &&  (taWeight.floatValue() > 1.0)) ? Font.WEIGHT_BOLD
                        : Font.WEIGHT_NORMAL;
 
-        Font fontState = null;
-        FontInfo fi = fontInfo;
-        boolean found = false;
         String fontFamily = 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
+        /* The following code section is not available until Batik 1.7 is released. */
+        GVTFont gvtFont = (GVTFont)aci.getAttribute(
+                GVTAttributedCharacterIterator.TextAttribute.GVT_FONT);
+        if (gvtFont != null) {
+            try {
+                Method method = gvtFont.getClass().getMethod("getFamilyName", null);
+                String gvtFontFamily = (String)method.invoke(gvtFont, null);
+                //TODO Uncomment the following line when Batik 1.7 is shipped with FOP
+                //String gvtFontFamily = gvtFont.getFamilyName(); //Not available in Batik 1.6
+                if (DEBUG) {
+                    System.out.print(gvtFontFamily + ", ");
+                }
+                if (fontInfo.hasFont(gvtFontFamily, style, weight)) {
+                    FontTriplet triplet = fontInfo.fontLookup(gvtFontFamily, style,
+                                                       weight);
+                    int fsize = (int)(fontSize.floatValue() * 1000);
+                    fonts.add(fontInfo.getFontInstance(triplet, fsize));
+                }
+            } catch (Exception e) {
+                //Most likely NoSuchMethodError here when using Batik 1.6
+                //Just skip this section in this case
+            }
+        }
+
         if (gvtFonts != null) {
             Iterator i = gvtFonts.iterator();
             while (i.hasNext()) {
                 GVTFontFamily fam = (GVTFontFamily) i.next();
                 if (fam instanceof SVGFontFamily) {
-                    PROXY_PAINTER.paint(node, g2d);
-                    return;
+                    return null; //Let Batik paint this text!
                 }
                 fontFamily = fam.getFamilyName();
-                if (fi.hasFont(fontFamily, style, weight)) {
+                if (DEBUG) {
+                    System.out.print(fontFamily + ", ");
+                }
+                if (fontInfo.hasFont(fontFamily, style, weight)) {
                     FontTriplet triplet = fontInfo.fontLookup(fontFamily, style,
                                                        weight);
-                    int fsize = (int)(size.floatValue() * 1000);
-                    fontState = fontInfo.getFontInstance(triplet, fsize);
-                    found = true;
-                    break;
+                    int fsize = (int)(fontSize.floatValue() * 1000);
+                    fonts.add(fontInfo.getFontInstance(triplet, fsize));
                 }
             }
         }
-        if (!found) {
+        if (fonts.size() == 0) {
             FontTriplet triplet = fontInfo.fontLookup("any", style, Font.WEIGHT_NORMAL);
-            int fsize = (int)(size.floatValue() * 1000);
-            fontState = fontInfo.getFontInstance(triplet, fsize);
-        } else {
-            if (g2d instanceof PDFGraphics2D) {
-                ((PDFGraphics2D) g2d).setOverrideFontState(fontState);
-            }
-        }
-        int fStyle = java.awt.Font.PLAIN;
-        if (weight == Font.WEIGHT_BOLD) {
-            if (style.equals("italic")) {
-                fStyle = java.awt.Font.BOLD | java.awt.Font.ITALIC;
-            } else {
-                fStyle = java.awt.Font.BOLD;
-            }
-        } else {
-            if (style.equals("italic")) {
-                fStyle = java.awt.Font.ITALIC;
-            } else {
-                fStyle = java.awt.Font.PLAIN;
-            }
-        }
-        java.awt.Font font = new java.awt.Font(fontFamily, fStyle,
-                             (int)(fontState.getFontSize() / 1000));
-
-        g2d.setFont(font);
-
-        float advance = getStringWidth(txt, fontState);
-        float tx = 0;
-        if (anchor != null) {
-            switch (anchor.getType()) {
-                case TextNode.Anchor.ANCHOR_MIDDLE:
-                    tx = -advance / 2;
-                    break;
-                case TextNode.Anchor.ANCHOR_END:
-                    tx = -advance;
+            int fsize = (int)(fontSize.floatValue() * 1000);
+            fonts.add(fontInfo.getFontInstance(triplet, fsize));
+            if (DEBUG) {
+                System.out.print("fallback to 'any' font");
             }
         }
-        g2d.drawString(txt, (float)(loc.getX() + tx), (float)(loc.getY()));
-    }
-
-    private boolean hasUnsupportedAttributes(AttributedCharacterIterator aci) {
-        boolean hasunsupported = false;
-        Object letSpace = aci.getAttribute(
-                            GVTAttributedCharacterIterator.TextAttribute.LETTER_SPACING);
-        if (letSpace != null) {
-            hasunsupported = true;
-        }
-
-        Object wordSpace = aci.getAttribute(
-                             GVTAttributedCharacterIterator.TextAttribute.WORD_SPACING);
-        if (wordSpace != null) {
-            hasunsupported = true;
-        }
-
-        AttributedCharacterIterator.Attribute key;
-        key = GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE;
-        Object writeMod = aci.getAttribute(key);
-        if (!GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE_LTR.equals(
-                  writeMod)) {
-            hasunsupported = true;
-        }
-
-        Object vertOr = aci.getAttribute(
-                          GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION);
-        if (GVTAttributedCharacterIterator.TextAttribute.ORIENTATION_ANGLE.equals(
-                  vertOr)) {
-            hasunsupported = true;
+        if (DEBUG) {
+            System.out.println();
         }
-        return hasunsupported;
+        return (Font[])fonts.toArray(new Font[fonts.size()]);
     }
-
-    private float getStringWidth(String str, Font fontState) {
-        float wordWidth = 0;
-        float whitespaceWidth = fontState.getWidth(fontState.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 = fontState.getWidth(fontState.mapChar(c));
-                if (charWidth <= 0) {
-                    charWidth = whitespaceWidth;
-                }
-            } else {
-                charWidth = whitespaceWidth;
-            }
-            wordWidth += charWidth;
-        }
-        return wordWidth / 1000f;
-    }
-
-    /**
-     * 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) {
-        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 PDF
-
-    /**
-     * Get the mark.
-     * This does nothing since the output is pdf 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 pdf 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 pdf 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 pdf 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 pdf 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 pdf 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 pdf and not interactive.
-     * @param beginMark the start mark
-     * @param endMark the end mark
-     * @return null
-     */
-    public Shape getHighlightShape(Mark beginMark, Mark endMark) {
-        return null;
-    }
-
-}
-
+    
+}
\ No newline at end of file

Added: xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFTextUtil.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFTextUtil.java?rev=591587&view=auto
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFTextUtil.java (added)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFTextUtil.java Sat Nov  3 03:59:09 2007
@@ -0,0 +1,308 @@
+/*
+ * 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 org.apache.fop.fonts.Font;
+import org.apache.fop.pdf.PDFNumber;
+import org.apache.fop.pdf.PDFText;
+
+/**
+ * Utility class for generating PDF text objects.
+ */
+public class PDFTextUtil {
+
+    /** The number of decimal places. */ 
+    private static final int DEC = 8;
+    
+    /** PDF text rendering mode: Fill text */
+    public static final int TR_FILL = 0;
+    /** PDF text rendering mode: Stroke text */
+    public static final int TR_STROKE = 1;
+    /** PDF text rendering mode: Fill, then stroke text */
+    public static final int TR_FILL_STROKE = 2;
+    /** PDF text rendering mode: Neither fill nor stroke text (invisible) */
+    public static final int TR_INVISIBLE = 3;
+    /** PDF text rendering mode: Fill text and add to path for clipping */
+    public static final int TR_FILL_CLIP = 4;
+    /** PDF text rendering mode: Stroke text and add to path for clipping */
+    public static final int TR_STROKE_CLIP = 5;
+    /** PDF text rendering mode: Fill, then stroke text and add to path for clipping */
+    public static final int TR_FILL_STROKE_CLIP = 6;
+    /** PDF text rendering mode: Add text to path for clipping */
+    public static final int TR_CLIP = 7;
+    
+    
+    private PDFGraphics2D g2d;
+    private boolean inTextObject = false;
+    private Font[] fonts;
+    private Font font;
+    private String startText;
+    private String endText;
+    private boolean useMultiByte;
+    private StringBuffer bufTJ;
+    private int textRenderingMode = 0;
+    
+    /**
+     * Main constructor.
+     * @param g2d the PDFGraphics2D instance to work with
+     */
+    public PDFTextUtil(PDFGraphics2D g2d) {
+        this.g2d = g2d;
+    }
+    
+    private void writeAffineTransform(AffineTransform at, StringBuffer sb) {
+        double[] lt = new double[6];
+        at.getMatrix(lt);
+        sb.append(PDFNumber.doubleOut(lt[0], DEC)).append(" ");
+        sb.append(PDFNumber.doubleOut(lt[1], DEC)).append(" ");
+        sb.append(PDFNumber.doubleOut(lt[2], DEC)).append(" ");
+        sb.append(PDFNumber.doubleOut(lt[3], DEC)).append(" ");
+        sb.append(PDFNumber.doubleOut(lt[4], DEC)).append(" ");
+        sb.append(PDFNumber.doubleOut(lt[5], DEC));
+    }
+
+    private void writeChar(char ch, StringBuffer sb) {
+        if (!useMultiByte) {
+            if (ch > 127) {
+                sb.append("\\").append(Integer.toOctalString((int)ch));
+            } else {
+                switch (ch) {
+                case '(':
+                case ')':
+                case '\\':
+                    sb.append("\\");
+                    break;
+                default:
+                }
+                sb.append(ch);
+            }
+        } else {
+            sb.append(PDFText.toUnicodeHex(ch));
+        }
+    }
+    
+    private void checkInTextObject() {
+        if (!inTextObject) {
+            throw new IllegalStateException("Not in text object");
+        }
+    }
+    
+    /**
+     * Called when a new text object should be started. Be sure to call setFont() before
+     * issuing any text painting commands.
+     */
+    public void beginTextObject() {
+        if (inTextObject) {
+            throw new IllegalStateException("Already in text object");
+        }
+        g2d.currentStream.write("BT\n");
+        this.inTextObject = true;
+    }
+    
+    /**
+     * Called when a text object should be ended.
+     */
+    public void endTextObject() {
+        checkInTextObject();
+        g2d.currentStream.write("ET\n");
+        this.inTextObject = false;
+        initValues();
+    }
+    
+    private void initValues() {
+        this.font = null;
+        this.textRenderingMode = TR_FILL;
+    }
+    
+    /**
+     * Creates a "q" command, pushing a copy of the entire graphics state onto the stack.
+     */
+    public void saveGraphicsState() {
+        g2d.currentStream.write("q\n");
+    }
+    
+    /**
+     * Creates a "Q" command, restoring the entire graphics state to its former value by popping
+     * it from the stack.
+     */
+    public void restoreGraphicsState() {
+        g2d.currentStream.write("Q\n");
+    }
+    
+    /**
+     * Creates a "cm" command using the current transformation as the matrix.
+     */
+    public void concatMatrixCurrentTransform() {
+        StringBuffer sb = new StringBuffer();
+        if (!g2d.getTransform().isIdentity()) {
+            writeAffineTransform(g2d.getTransform(), sb);
+            sb.append(" cm\n");
+        }
+        g2d.currentStream.write(sb.toString());
+    }
+    
+    /**
+     * Sets the current fonts for the text object. For every character, the suitable font will
+     * be selected.
+     * @param fonts the new fonts
+     */
+    public void setFonts(Font[] fonts) {
+        this.fonts = fonts;
+    }
+    
+    /**
+     * Sets the current font for the text object.
+     * @param font the new font
+     */
+    public void setFont(Font font) {
+        setFonts(new Font[] {font});
+    }
+    
+    /**
+     * Returns the current font in use.
+     * @return the current font or null if no font is currently active.
+     */
+    public Font getCurrentFont() {
+        return this.font;
+    }
+    
+    /**
+     * Sets the current font.
+     * @param f the new font to use
+     */
+    public void setCurrentFont(Font f) {
+        this.font = f;
+    }
+    
+    /**
+     * Writes a "Tf" command, setting a new current font.
+     * @param f the font to select
+     */
+    public void writeTf(Font f) {
+        checkInTextObject();
+        String fontName = f.getFontName();
+        float fontSize = (float)f.getFontSize() / 1000f;
+        g2d.currentStream.write("/" + fontName + " " + PDFNumber.doubleOut(fontSize) + " Tf\n");
+        
+        this.useMultiByte = g2d.isMultiByteFont(fontName);
+        this.startText = useMultiByte ? "<" : "(";
+        this.endText = useMultiByte ? ">" : ")";
+    }
+
+    /**
+     * Sets the text rendering mode.
+     * @param mode the rendering mode (value 0 to 7, see PDF Spec, constants: TR_*)
+     */
+    public void setTextRenderingMode(int mode) {
+        if (mode < 0 || mode > 7) {
+            throw new IllegalArgumentException(
+                    "Illegal value for text rendering mode. Expected: 0-7");
+        }
+        if (mode != this.textRenderingMode) {
+            this.textRenderingMode = mode;
+            g2d.currentStream.write(this.textRenderingMode + " Tr\n");
+        }
+    }
+    
+    /**
+     * Sets the text rendering mode.
+     * @param fill true if the text should be filled
+     * @param stroke true if the text should be stroked
+     * @param addToClip true if the path should be added for clipping
+     */
+    public void setTextRenderingMode(boolean fill, boolean stroke, boolean addToClip) {
+        int mode;
+        if (fill) {
+            mode = (stroke ? 2 : 0);
+        } else {
+            mode = (stroke ? 1 : 3);
+        }
+        if (addToClip) {
+            mode += 4;
+        }
+        setTextRenderingMode(mode);
+    }
+    
+    /**
+     * Writes a "Tm" command, setting a new text transformation matrix.
+     * @param localTransform the new text transformation matrix
+     */
+    public void writeTextMatrix(AffineTransform localTransform) {
+        StringBuffer sb = new StringBuffer();
+        writeAffineTransform(localTransform, sb);
+        sb.append(" Tm\n");
+        g2d.currentStream.write(sb.toString());
+    }
+
+    /**
+     * Selects a font from the font list suitable to display the given character.
+     * @param ch the character
+     * @return the recommended Font to use
+     */
+    public Font selectFontForChar(char ch) {
+        for (int i = 0, c = fonts.length; i < c; i++) {
+            if (fonts[i].hasChar(ch)) {
+                return fonts[i];
+            }
+        }
+        return fonts[0]; //TODO Maybe fall back to painting with shapes
+    }
+    
+    /**
+     * Writes a char to the "TJ-Buffer".
+     * @param ch the unmapped character
+     */
+    public void writeTJChar(char ch) {
+        if (bufTJ == null) {
+            bufTJ = new StringBuffer();
+        }
+        if (bufTJ.length() == 0) {
+            bufTJ.append("[").append(startText);
+        }
+        char mappedChar = font.mapChar(ch);
+        writeChar(mappedChar, bufTJ);
+    }
+
+    /**
+     * Writes a glyph adjust value to the "TJ-Buffer".
+     * @param adjust the glyph adjust value in thousands of text unit space.
+     */
+    public void adjustGlyphTJ(double adjust) {
+        bufTJ.append(endText).append(" ");
+        bufTJ.append(PDFNumber.doubleOut(adjust, DEC - 4));
+        bufTJ.append(" ");
+        bufTJ.append(startText);
+    }
+
+    /**
+     * Writes a "TJ" command, writing out the accumulated buffer with the characters and glyph
+     * positioning values. The buffer is reset afterwards.
+     */
+    public void writeTJ() {
+        if (bufTJ != null && bufTJ.length() > 0) {
+            bufTJ.append(endText).append("] TJ\n");
+            g2d.currentStream.write(bufTJ.toString());
+            bufTJ.setLength(0);
+        }
+    }
+
+}

Propchange: xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFTextUtil.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFTextUtil.java
------------------------------------------------------------------------------
    svn:keywords = Id

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFTranscoder.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFTranscoder.java?rev=591587&r1=591586&r2=591587&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFTranscoder.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/svg/PDFTranscoder.java Sat Nov  3 03:59:09 2007
@@ -25,6 +25,7 @@
 import org.apache.avalon.framework.configuration.Configurable;
 import org.apache.avalon.framework.configuration.Configuration;
 import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.avalon.framework.configuration.DefaultConfiguration;
 import org.apache.batik.bridge.BridgeContext;
 import org.apache.batik.bridge.UnitProcessor;
 import org.apache.batik.bridge.UserAgent;
@@ -33,10 +34,10 @@
 import org.apache.batik.transcoder.TranscoderOutput;
 import org.apache.batik.transcoder.TranscodingHints;
 import org.apache.batik.transcoder.image.ImageTranscoder;
+import org.apache.batik.transcoder.keys.BooleanKey;
 import org.apache.batik.transcoder.keys.FloatKey;
 import org.apache.fop.Version;
 import org.apache.fop.fonts.FontInfo;
-import org.apache.fop.fonts.FontSetup;
 import org.w3c.dom.Document;
 import org.w3c.dom.svg.SVGLength;
 
@@ -63,6 +64,12 @@
  * <tt>KEY_USER_STYLESHEET_URI</tt> to fix the URI of a user
  * stylesheet, and <tt>KEY_PIXEL_TO_MM</tt> to specify the pixel to
  * millimeter conversion factor.
+ * 
+ * <p><tt>KEY_AUTO_FONTS</tt> to disable the auto-detection of fonts installed in the system.
+ * The PDF Transcoder cannot use AWT's font subsystem and that's why the fonts have to be
+ * configured differently. By default, font auto-detection is enabled to match the behaviour
+ * of the other transcoders, but this may be associated with a price in the form of a small
+ * performance penalty. If font auto-detection is not desired, it can be disable using this key.
  *
  * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
  * @version $Id$
@@ -76,13 +83,20 @@
      */
     public static final TranscodingHints.Key KEY_DEVICE_RESOLUTION = new FloatKey();
 
+    /**
+     * The key is used to specify whether the available fonts should be automatically
+     * detected. The alternative is to configure the transcoder manually using a configuration
+     * file. 
+     */
+    public static final TranscodingHints.Key KEY_AUTO_FONTS = new BooleanKey();
+
     private Configuration cfg = null;
     
     /** Graphics2D instance that is used to paint to */
     protected PDFDocumentGraphics2D graphics = null;
 
     /**
-     * Constructs a new <tt>ImageTranscoder</tt>.
+     * Constructs a new <tt>PDFTranscoder</tt>.
      */
     public PDFTranscoder() {
         super();
@@ -102,9 +116,7 @@
         };
     }
     
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     public void configure(Configuration cfg) throws ConfigurationException {
         this.cfg = cfg;
     }
@@ -121,16 +133,35 @@
                              TranscoderOutput output) 
         throws TranscoderException {
 
-        graphics = new PDFDocumentGraphics2D();
+        graphics = new PDFDocumentGraphics2D(isTextStroked());
         graphics.getPDFDocument().getInfo().setProducer("Apache FOP Version " 
                 + Version.getVersion() 
                 + ": PDF Transcoder for Batik");
         
         try {
-            if (this.cfg != null) {
+            Configuration effCfg = this.cfg; 
+            if (effCfg == null) {
+                //By default, enable font auto-detection if no cfg is given
+                boolean autoFonts = true;
+                if (hints.containsKey(KEY_AUTO_FONTS)) {
+                    autoFonts = ((Boolean)hints.get(KEY_AUTO_FONTS)).booleanValue();
+                }
+                if (autoFonts) {
+                    DefaultConfiguration c = new DefaultConfiguration("pdf-transcoder");
+                    DefaultConfiguration fonts = new DefaultConfiguration("fonts");
+                    c.addChild(fonts);
+                    DefaultConfiguration autodetect = new DefaultConfiguration("auto-detect");
+                    fonts.addChild(autodetect);
+                    effCfg = c;
+                }
+            }
+            
+            if (effCfg != null) {
                 PDFDocumentGraphics2DConfigurator configurator
                         = new PDFDocumentGraphics2DConfigurator();
-                configurator.configure(graphics, this.cfg);
+                configurator.configure(graphics, effCfg);
+            } else {
+                graphics.setupDefaultFontInfo();
             }
         } catch (Exception e) {
             throw new TranscoderException(
@@ -190,8 +221,18 @@
 
     /** {@inheritDoc} */
     protected BridgeContext createBridgeContext() {
-        BridgeContext ctx = new PDFBridgeContext(userAgent, graphics.getFontInfo());
-        return ctx;
+        //For compatibility with Batik 1.6
+        return createBridgeContext("1.x");
     }
 
+    /** {@inheritDoc} */
+    public BridgeContext createBridgeContext(String version) {
+        FontInfo fontInfo = graphics.getFontInfo();
+        if (isTextStroked()) {
+            fontInfo = null;
+        }
+        BridgeContext ctx = new PDFBridgeContext(userAgent, fontInfo);
+        return ctx;
+    }
+    
 }

Modified: xmlgraphics/fop/trunk/status.xml
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/status.xml?rev=591587&r1=591586&r2=591587&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/status.xml (original)
+++ xmlgraphics/fop/trunk/status.xml Sat Nov  3 03:59:09 2007
@@ -28,6 +28,11 @@
 
   <changes>
     <release version="FOP Trunk">
+      <action context="Code" dev="JM" type="update">
+        PDF Transcoder (SVG) text painting has been completely rewritten.
+        Except for some special cases (with filters for example), all text
+        (including flow text) is now painted using PDF text operators.
+      </action>
       <action context="Code" dev="AD" type="add" fixes-bug="42748">
         Added support for ids on empty fo:inlines.
       </action>



---------------------------------------------------------------------
To unsubscribe, e-mail: fop-commits-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: fop-commits-help@xmlgraphics.apache.org


Re: svn commit: r591587 - in /xmlgraphics/fop/trunk: ./ src/java/org/apache/fop/render/ps/ src/java/org/apache/fop/svg/

Posted by Jeremias Maerki <de...@jeremias-maerki.ch>.
On 05.11.2007 15:37:29 Vincent Hennebert wrote:
<snip/>
> >> - in PDFTextPainter: now that the code is working, you probably want to 
> >>   remove the DEBUG variable and all the statements displaying stuff to 
> >>   System.out; or replace them with proper log.debug statements ;-)
> > 
> > No, I don't. The debug code is most probably optimized away by the
> > compiler so it doesn't bother us at runtime. If any bugs arise, it's
> 
> Except if the DEBUG variable is left to true...

Oh, that was a mistake. I thought I switched it to false. Thanks for
correcting it.

<snip/>

Jeremias Maerki

Re: svn commit: r591587 - in /xmlgraphics/fop/trunk: ./ src/java/org/apache/fop/render/ps/ src/java/org/apache/fop/svg/

Posted by Vincent Hennebert <vi...@anyware-tech.com>.
Jeremias Maerki wrote:
<snip/>
>>   - it seems to me that a bit more of the internal state of 
>>     a PDFTextUtil object could be controlled by the object itself, 
>>     especially in the following piece of code (PDFTextPainter, l.220):
>>         Font f = textUtil.selectFontForChar(ch);
>>         if (f != textUtil.getCurrentFont()) {
>>             textUtil.writeTJ();
>>             textUtil.setCurrentFont(f);
>>             textUtil.writeTf(f);
>>             textUtil.writeTextMatrix(localTransform);
>>         }
>>     A signalFontUsed(f) method might make sense. Basically doing the 
>>     same test as above, but inside PDFTextUtil. The code would be better 
>>     encapsulated, so more maintainable. WDYT?
> 
> Again, feel free to change and improve.

Ok. It’s just that I’m reluctant to modify code I’m not familiar with, 
and from which I may have missed the logic.


>> - in PDFTextPainter: now that the code is working, you probably want to 
>>   remove the DEBUG variable and all the statements displaying stuff to 
>>   System.out; or replace them with proper log.debug statements ;-)
> 
> No, I don't. The debug code is most probably optimized away by the
> compiler so it doesn't bother us at runtime. If any bugs arise, it's

Except if the DEBUG variable is left to true...

> convenient to switch on. Most importantly, though, the Batik guys have
> indicated that they don't want to work with a logging framework. Since
> this code is destined to go to Batik I try not to introduce more Commons
> Logging in this area.


Vincent

Re: svn commit: r591587 - in /xmlgraphics/fop/trunk: ./ src/java/org/apache/fop/render/ps/ src/java/org/apache/fop/svg/

Posted by Jeremias Maerki <de...@jeremias-maerki.ch>.
On 05.11.2007 13:05:29 Vincent Hennebert wrote:
> Hi Jeremias,
> 
> I’d have a couple of comments regarding your changes. I’ve only had 
> a quick look and don’t understand this part of the code well, but 
> following the principle of pair review, here they are:
> 
> - at several places there is code like the following:
>     private void checkInTextObject() {
>         if (!inTextObject) {
>             throw new IllegalStateException("Not in text object");
>         }
>     }
>   I understand and don’t criticise the need for safety checks, 
>   especially if the code is new or complex. Only, since we now have 
>   Java 1.4 as a minimal requirement, why not use assert statements? They 
>   were created exactly for that.

I could do a lot of stuff. I'm so used to working pre-Java-1.4 that it
didn't occur to me. Feel free to change. There's no code-ownership in
Apache projects.

> - in PDFTextUtil:
>   - the setTextRenderingMode(int) method is only used within 
>     PDFTextUtil; why then not making it private? The other 
>     setTextRenderingMode method will still be available and seems to be 
>     much safer to use anyway.

PDFTextUtil is designed to be useful for classes other than
PDFTextPainter. Someone might prefer working more closely to the PDF
spec and therefore prefer the int variant.

>   - it seems to me that a bit more of the internal state of 
>     a PDFTextUtil object could be controlled by the object itself, 
>     especially in the following piece of code (PDFTextPainter, l.220):
>         Font f = textUtil.selectFontForChar(ch);
>         if (f != textUtil.getCurrentFont()) {
>             textUtil.writeTJ();
>             textUtil.setCurrentFont(f);
>             textUtil.writeTf(f);
>             textUtil.writeTextMatrix(localTransform);
>         }
>     A signalFontUsed(f) method might make sense. Basically doing the 
>     same test as above, but inside PDFTextUtil. The code would be better 
>     encapsulated, so more maintainable. WDYT?

Again, feel free to change and improve.

> - in PDFTextPainter: now that the code is working, you probably want to 
>   remove the DEBUG variable and all the statements displaying stuff to 
>   System.out; or replace them with proper log.debug statements ;-)

No, I don't. The debug code is most probably optimized away by the
compiler so it doesn't bother us at runtime. If any bugs arise, it's
convenient to switch on. Most importantly, though, the Batik guys have
indicated that they don't want to work with a logging framework. Since
this code is destined to go to Batik I try not to introduce more Commons
Logging in this area.

> For the rest I believe when you say it’s working... Congrats!
> 
> Vincent


Jeremias Maerki

Re: svn commit: r591587 - in /xmlgraphics/fop/trunk: ./ src/java/org/apache/fop/render/ps/ src/java/org/apache/fop/svg/

Posted by Vincent Hennebert <vi...@anyware-tech.com>.
Hi Jeremias,

I’d have a couple of comments regarding your changes. I’ve only had 
a quick look and don’t understand this part of the code well, but 
following the principle of pair review, here they are:

- at several places there is code like the following:
    private void checkInTextObject() {
        if (!inTextObject) {
            throw new IllegalStateException("Not in text object");
        }
    }
  I understand and don’t criticise the need for safety checks, 
  especially if the code is new or complex. Only, since we now have 
  Java 1.4 as a minimal requirement, why not use assert statements? They 
  were created exactly for that.

- in PDFTextUtil:
  - the setTextRenderingMode(int) method is only used within 
    PDFTextUtil; why then not making it private? The other 
    setTextRenderingMode method will still be available and seems to be 
    much safer to use anyway.
  - it seems to me that a bit more of the internal state of 
    a PDFTextUtil object could be controlled by the object itself, 
    especially in the following piece of code (PDFTextPainter, l.220):
        Font f = textUtil.selectFontForChar(ch);
        if (f != textUtil.getCurrentFont()) {
            textUtil.writeTJ();
            textUtil.setCurrentFont(f);
            textUtil.writeTf(f);
            textUtil.writeTextMatrix(localTransform);
        }
    A signalFontUsed(f) method might make sense. Basically doing the 
    same test as above, but inside PDFTextUtil. The code would be better 
    encapsulated, so more maintainable. WDYT?

- in PDFTextPainter: now that the code is working, you probably want to 
  remove the DEBUG variable and all the statements displaying stuff to 
  System.out; or replace them with proper log.debug statements ;-)

For the rest I believe when you say it’s working... Congrats!

Vincent