View | Details | Raw Unified | Return to bug 47000
Collapse All | Expand All

(-)status.xml (+4 lines)
Lines 58-63 Link Here
58
      documents. Example: the fix of marks layering will be such a case when it's done.
58
      documents. Example: the fix of marks layering will be such a case when it's done.
59
    -->
59
    -->
60
    <release version="FOP Trunk" date="TBD">
60
    <release version="FOP Trunk" date="TBD">
61
      <action context="Renderers" dev="JM" type="add">
62
        Added a custom text painter for rendering SVG text using text operators when rendering
63
        to PostScript or EPS. Text is no longer painted as shapes, thus creating much smaller files. 
64
      </action>
61
      <action context="Renderers" dev="JM" type="fix">
65
      <action context="Renderers" dev="JM" type="fix">
62
        Fixed a bug that left the PrintRenderer unconfigured even if a configuration was
66
        Fixed a bug that left the PrintRenderer unconfigured even if a configuration was
63
        specified for "application/X-fop-print". 
67
        specified for "application/X-fop-print". 
(-)src/java/org/apache/fop/svg/PDFTranscoder.java (-89 / +3 lines)
Lines 23-57 Link Here
23
import java.io.BufferedOutputStream;
23
import java.io.BufferedOutputStream;
24
import java.io.IOException;
24
import java.io.IOException;
25
import java.io.OutputStream;
25
import java.io.OutputStream;
26
import java.io.InputStream;
27
26
28
import javax.xml.transform.Source;
29
import javax.xml.transform.stream.StreamSource;
30
31
import org.w3c.dom.Document;
27
import org.w3c.dom.Document;
32
import org.w3c.dom.svg.SVGLength;
28
import org.w3c.dom.svg.SVGLength;
33
29
34
import org.apache.avalon.framework.configuration.Configurable;
30
import org.apache.avalon.framework.configuration.Configurable;
35
import org.apache.avalon.framework.configuration.Configuration;
31
import org.apache.avalon.framework.configuration.Configuration;
36
import org.apache.avalon.framework.configuration.ConfigurationException;
37
import org.apache.avalon.framework.configuration.DefaultConfiguration;
38
import org.apache.batik.bridge.BridgeContext;
32
import org.apache.batik.bridge.BridgeContext;
39
import org.apache.batik.bridge.UnitProcessor;
33
import org.apache.batik.bridge.UnitProcessor;
40
import org.apache.batik.bridge.UserAgent;
34
import org.apache.batik.bridge.UserAgent;
41
import org.apache.batik.ext.awt.RenderingHintsKeyExt;
35
import org.apache.batik.ext.awt.RenderingHintsKeyExt;
42
import org.apache.batik.transcoder.TranscoderException;
36
import org.apache.batik.transcoder.TranscoderException;
43
import org.apache.batik.transcoder.TranscoderOutput;
37
import org.apache.batik.transcoder.TranscoderOutput;
44
import org.apache.batik.transcoder.TranscodingHints;
45
import org.apache.batik.transcoder.image.ImageTranscoder;
38
import org.apache.batik.transcoder.image.ImageTranscoder;
46
import org.apache.batik.transcoder.keys.BooleanKey;
47
import org.apache.batik.transcoder.keys.FloatKey;
48
import org.apache.batik.util.ParsedURL;
49
39
50
import org.apache.xmlgraphics.image.loader.ImageContext;
51
import org.apache.xmlgraphics.image.loader.ImageManager;
52
import org.apache.xmlgraphics.image.loader.ImageSessionContext;
53
import org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext;
54
55
import org.apache.fop.Version;
40
import org.apache.fop.Version;
56
import org.apache.fop.fonts.FontInfo;
41
import org.apache.fop.fonts.FontInfo;
57
42
Lines 91-117 Link Here
91
public class PDFTranscoder extends AbstractFOPTranscoder
76
public class PDFTranscoder extends AbstractFOPTranscoder
92
        implements Configurable {
77
        implements Configurable {
93
78
94
    /**
95
     * The key is used to specify the resolution for on-the-fly images generated
96
     * due to complex effects like gradients and filters.
97
     */
98
    public static final TranscodingHints.Key KEY_DEVICE_RESOLUTION = new FloatKey();
99
100
    /**
101
     * The key is used to specify whether the available fonts should be automatically
102
     * detected. The alternative is to configure the transcoder manually using a configuration
103
     * file.
104
     */
105
    public static final TranscodingHints.Key KEY_AUTO_FONTS = new BooleanKey();
106
107
    private Configuration cfg = null;
108
109
    /** Graphics2D instance that is used to paint to */
79
    /** Graphics2D instance that is used to paint to */
110
    protected PDFDocumentGraphics2D graphics = null;
80
    protected PDFDocumentGraphics2D graphics = null;
111
81
112
    private ImageManager imageManager;
113
    private ImageSessionContext imageSessionContext;
114
115
    /**
82
    /**
116
     * Constructs a new <tt>PDFTranscoder</tt>.
83
     * Constructs a new <tt>PDFTranscoder</tt>.
117
     */
84
     */
Lines 133-143 Link Here
133
        };
100
        };
134
    }
101
    }
135
102
136
    /** {@inheritDoc} */
137
    public void configure(Configuration cfg) throws ConfigurationException {
138
        this.cfg = cfg;
139
    }
140
141
    /**
103
    /**
142
     * Transcodes the specified Document as an image in the specified output.
104
     * Transcodes the specified Document as an image in the specified output.
143
     *
105
     *
Lines 155-182 Link Here
155
                + Version.getVersion()
117
                + Version.getVersion()
156
                + ": PDF Transcoder for Batik");
118
                + ": PDF Transcoder for Batik");
157
        if (hints.containsKey(KEY_DEVICE_RESOLUTION)) {
119
        if (hints.containsKey(KEY_DEVICE_RESOLUTION)) {
158
            graphics.setDeviceDPI(((Float)hints.get(KEY_DEVICE_RESOLUTION)).floatValue());
120
            graphics.setDeviceDPI(getDeviceResolution());
159
        }
121
        }
160
122
161
        setupImageInfrastructure(uri);
123
        setupImageInfrastructure(uri);
162
124
163
        try {
125
        try {
164
            Configuration effCfg = this.cfg;
126
            Configuration effCfg = getEffectiveConfiguration();
165
            if (effCfg == null) {
166
                //By default, enable font auto-detection if no cfg is given
167
                boolean autoFonts = true;
168
                if (hints.containsKey(KEY_AUTO_FONTS)) {
169
                    autoFonts = ((Boolean)hints.get(KEY_AUTO_FONTS)).booleanValue();
170
                }
171
                if (autoFonts) {
172
                    DefaultConfiguration c = new DefaultConfiguration("pdf-transcoder");
173
                    DefaultConfiguration fonts = new DefaultConfiguration("fonts");
174
                    c.addChild(fonts);
175
                    DefaultConfiguration autodetect = new DefaultConfiguration("auto-detect");
176
                    fonts.addChild(autodetect);
177
                    effCfg = c;
178
                }
179
            }
180
127
181
            if (effCfg != null) {
128
            if (effCfg != null) {
182
                PDFDocumentGraphics2DConfigurator configurator
129
                PDFDocumentGraphics2DConfigurator configurator
Lines 242-280 Link Here
242
        }
189
        }
243
    }
190
    }
244
191
245
    private void setupImageInfrastructure(final String baseURI) {
246
        final ImageContext imageContext = new ImageContext() {
247
            public float getSourceResolution() {
248
                return 25.4f / userAgent.getPixelUnitToMillimeter();
249
            }
250
        };
251
        this.imageManager = new ImageManager(imageContext);
252
        this.imageSessionContext = new AbstractImageSessionContext() {
253
254
            public ImageContext getParentContext() {
255
                return imageContext;
256
            }
257
258
            public float getTargetResolution() {
259
                return graphics.getDeviceDPI();
260
            }
261
262
            public Source resolveURI(String uri) {
263
                System.out.println("resolve " + uri);
264
                try {
265
                    ParsedURL url = new ParsedURL(baseURI, uri);
266
                    InputStream in = url.openStream();
267
                    StreamSource source = new StreamSource(in, url.toString());
268
                    return source;
269
                } catch (IOException ioe) {
270
                    userAgent.displayError(ioe);
271
                    return null;
272
                }
273
            }
274
275
        };
276
    }
277
278
    /** {@inheritDoc} */
192
    /** {@inheritDoc} */
279
    protected BridgeContext createBridgeContext() {
193
    protected BridgeContext createBridgeContext() {
280
        //For compatibility with Batik 1.6
194
        //For compatibility with Batik 1.6
Lines 288-294 Link Here
288
            fontInfo = null;
202
            fontInfo = null;
289
        }
203
        }
290
        BridgeContext ctx = new PDFBridgeContext(userAgent, fontInfo,
204
        BridgeContext ctx = new PDFBridgeContext(userAgent, fontInfo,
291
                this.imageManager, this.imageSessionContext);
205
                getImageManager(), getImageSessionContext());
292
        return ctx;
206
        return ctx;
293
    }
207
    }
294
208
(-)src/java/org/apache/fop/svg/PDFTextPainter.java (-243 / +118 lines)
Lines 25-52 Link Here
25
import java.awt.Paint;
25
import java.awt.Paint;
26
import java.awt.Shape;
26
import java.awt.Shape;
27
import java.awt.Stroke;
27
import java.awt.Stroke;
28
import java.awt.font.TextAttribute;
29
import java.awt.geom.AffineTransform;
28
import java.awt.geom.AffineTransform;
30
import java.awt.geom.Ellipse2D;
29
import java.awt.geom.Ellipse2D;
31
import java.awt.geom.GeneralPath;
30
import java.awt.geom.GeneralPath;
32
import java.awt.geom.Point2D;
31
import java.awt.geom.Point2D;
33
import java.lang.reflect.Method;
34
import java.text.AttributedCharacterIterator;
32
import java.text.AttributedCharacterIterator;
35
import java.util.Iterator;
36
import java.util.List;
37
33
38
import org.apache.batik.bridge.SVGFontFamily;
39
import org.apache.batik.gvt.font.GVTFont;
40
import org.apache.batik.gvt.font.GVTFontFamily;
41
import org.apache.batik.gvt.font.GVTGlyphVector;
34
import org.apache.batik.gvt.font.GVTGlyphVector;
42
import org.apache.batik.gvt.renderer.StrokingTextPainter;
43
import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
44
import org.apache.batik.gvt.text.TextPaintInfo;
35
import org.apache.batik.gvt.text.TextPaintInfo;
45
import org.apache.batik.gvt.text.TextSpanLayout;
36
import org.apache.batik.gvt.text.TextSpanLayout;
46
37
47
import org.apache.fop.fonts.Font;
38
import org.apache.fop.fonts.Font;
48
import org.apache.fop.fonts.FontInfo;
39
import org.apache.fop.fonts.FontInfo;
49
import org.apache.fop.fonts.FontTriplet;
50
import org.apache.fop.util.CharUtilities;
40
import org.apache.fop.util.CharUtilities;
51
41
52
/**
42
/**
Lines 59-252 Link Here
59
 *
49
 *
60
 * @version $Id$
50
 * @version $Id$
61
 */
51
 */
62
public class PDFTextPainter extends StrokingTextPainter {
52
class PDFTextPainter extends NativeTextPainter {
63
53
64
    private static final boolean DEBUG = false;
54
    private static final boolean DEBUG = false;
65
55
66
    private final boolean strokeText = false;
67
    private final FontInfo fontInfo;
68
69
    /**
56
    /**
70
     * Create a new PDF text painter with the given font information.
57
     * Create a new PDF text painter with the given font information.
71
     * @param fi the font info
58
     * @param fi the font info
72
     */
59
     */
73
    public PDFTextPainter(FontInfo fi) {
60
    public PDFTextPainter(FontInfo fi) {
74
        fontInfo = fi;
61
        super(fi);
75
    }
62
    }
76
63
77
    /** {@inheritDoc} */
64
    /** {@inheritDoc} */
78
    protected void paintTextRuns(List textRuns, Graphics2D g2d) {
65
    protected boolean isSupported(Graphics2D g2d) {
79
        if (DEBUG) {
66
        return g2d instanceof PDFGraphics2D;
80
            System.out.println("paintTextRuns: count = " + textRuns.size());
67
    }
81
            //fontInfo.dumpAllTripletsToSystemOut();
68
82
        }
69
    /** {@inheritDoc} */
83
        if (!(g2d instanceof PDFGraphics2D) || strokeText) {
70
    protected void paintTextRun(TextRun textRun, Graphics2D g2d) {
84
            super.paintTextRuns(textRuns, g2d);
71
        AttributedCharacterIterator runaci = textRun.getACI();
72
        runaci.first();
73
74
        TextPaintInfo tpi = (TextPaintInfo)runaci.getAttribute(PAINT_INFO);
75
        if (tpi == null || !tpi.visible) {
85
            return;
76
            return;
86
        }
77
        }
78
        if ((tpi != null) && (tpi.composite != null)) {
79
            g2d.setComposite(tpi.composite);
80
        }
81
82
        //------------------------------------
83
        TextSpanLayout layout = textRun.getLayout();
84
        logTextRun(runaci, layout);
85
        CharSequence chars = collectCharacters(runaci);
86
        runaci.first(); //Reset ACI
87
87
        final PDFGraphics2D pdf = (PDFGraphics2D)g2d;
88
        final PDFGraphics2D pdf = (PDFGraphics2D)g2d;
88
        PDFTextUtil textUtil = new PDFTextUtil(pdf.fontInfo) {
89
        PDFTextUtil textUtil = new PDFTextUtil(pdf.fontInfo) {
89
            protected void write(String code) {
90
            protected void write(String code) {
90
                pdf.currentStream.write(code);
91
                pdf.currentStream.write(code);
91
            }
92
            }
92
        };
93
        };
93
        for (int i = 0; i < textRuns.size(); i++) {
94
            TextRun textRun = (TextRun)textRuns.get(i);
95
            AttributedCharacterIterator runaci = textRun.getACI();
96
            runaci.first();
97
94
98
            TextPaintInfo tpi = (TextPaintInfo)runaci.getAttribute(PAINT_INFO);
95
        if (DEBUG) {
99
            if (tpi == null || !tpi.visible) {
96
            log.debug("Text: " + chars);
100
                continue;
97
            pdf.currentStream.write("%Text: " + chars + "\n");
101
            }
98
        }
102
            if ((tpi != null) && (tpi.composite != null)) {
103
                g2d.setComposite(tpi.composite);
104
            }
105
99
106
            //------------------------------------
100
        GeneralPath debugShapes = null;
107
            TextSpanLayout layout = textRun.getLayout();
101
        if (DEBUG) {
108
            if (DEBUG) {
102
            debugShapes = new GeneralPath();
109
                int charCount = runaci.getEndIndex() - runaci.getBeginIndex();
103
        }
110
                System.out.println("================================================");
111
                System.out.println("New text run:");
112
                System.out.println("char count: " + charCount);
113
                System.out.println("range: "
114
                        + runaci.getBeginIndex() + " - " + runaci.getEndIndex());
115
                System.out.println("glyph count: " + layout.getGlyphCount()); //=getNumGlyphs()
116
            }
117
            //Gather all characters of the run
118
            StringBuffer chars = new StringBuffer();
119
            for (runaci.first(); runaci.getIndex() < runaci.getEndIndex();) {
120
                chars.append(runaci.current());
121
                runaci.next();
122
            }
123
            runaci.first();
124
            if (DEBUG) {
125
                System.out.println("Text: " + chars);
126
                pdf.currentStream.write("%Text: " + chars + "\n");
127
            }
128
104
129
            GeneralPath debugShapes = null;
105
        Font[] fonts = findFonts(runaci);
130
            if (DEBUG) {
106
        if (fonts == null || fonts.length == 0) {
131
                debugShapes = new GeneralPath();
107
            //Draw using Java2D when no native fonts are available
132
            }
108
            textRun.getLayout().draw(g2d);
109
            return;
110
        }
133
111
134
            Font[] fonts = findFonts(runaci);
112
        textUtil.saveGraphicsState();
135
            if (fonts == null || fonts.length == 0) {
113
        textUtil.concatMatrix(g2d.getTransform());
136
                //Draw using Java2D
114
        Shape imclip = g2d.getClip();
137
                textRun.getLayout().draw(g2d);
115
        pdf.writeClip(imclip);
138
                continue;
139
            }
140
116
141
            textUtil.saveGraphicsState();
117
        applyColorAndPaint(tpi, pdf);
142
            textUtil.concatMatrix(g2d.getTransform());
143
            Shape imclip = g2d.getClip();
144
            pdf.writeClip(imclip);
145
118
146
            applyColorAndPaint(tpi, pdf);
119
        textUtil.beginTextObject();
120
        textUtil.setFonts(fonts);
121
        boolean stroke = (tpi.strokePaint != null)
122
            && (tpi.strokeStroke != null);
123
        textUtil.setTextRenderingMode(tpi.fillPaint != null, stroke, false);
147
124
148
            textUtil.beginTextObject();
125
        AffineTransform localTransform = new AffineTransform();
149
            textUtil.setFonts(fonts);
126
        Point2D prevPos = null;
150
            boolean stroke = (tpi.strokePaint != null)
127
        double prevVisibleCharWidth = 0.0;
151
                && (tpi.strokeStroke != null);
128
        GVTGlyphVector gv = layout.getGlyphVector();
152
            textUtil.setTextRenderingMode(tpi.fillPaint != null, stroke, false);
129
        for (int index = 0, c = gv.getNumGlyphs(); index < c; index++) {
130
            char ch = chars.charAt(index);
131
            boolean visibleChar = gv.isGlyphVisible(index)
132
                || (CharUtilities.isAnySpace(ch) && !CharUtilities.isZeroWidthSpace(ch));
133
            logCharacter(ch, layout, index, visibleChar);
134
            if (!visibleChar) {
135
                continue;
136
            }
137
            Point2D glyphPos = gv.getGlyphPosition(index);
153
138
154
            AffineTransform localTransform = new AffineTransform();
139
            AffineTransform glyphTransform = gv.getGlyphTransform(index);
155
            Point2D prevPos = null;
140
            //TODO Glyph transforms could be refined so not every char has to be painted
156
            double prevVisibleCharWidth = 0.0;
141
            //with its own TJ command (stretch/squeeze case could be optimized)
157
            GVTGlyphVector gv = layout.getGlyphVector();
142
            if (log.isTraceEnabled()) {
158
            for (int index = 0, c = gv.getNumGlyphs(); index < c; index++) {
143
                log.trace("pos " + glyphPos + ", transform " + glyphTransform);
159
                char ch = chars.charAt(index);
144
            }
160
                boolean visibleChar = gv.isGlyphVisible(index)
145
            if (DEBUG) {
161
                    || (CharUtilities.isAnySpace(ch) && !CharUtilities.isZeroWidthSpace(ch));
146
                Shape sh = gv.getGlyphLogicalBounds(index);
162
                if (DEBUG) {
147
                if (sh == null) {
163
                    System.out.println("glyph " + index
148
                    sh = new Ellipse2D.Double(glyphPos.getX(), glyphPos.getY(), 2, 2);
164
                            + " -> " + layout.getGlyphIndex(index) + " => " + ch);
165
                    if (CharUtilities.isAnySpace(ch) && ch != 32) {
166
                        System.out.println("Space found: " + Integer.toHexString(ch));
167
                    }
168
                    if (ch == CharUtilities.ZERO_WIDTH_JOINER) {
169
                        System.out.println("ZWJ found: " + Integer.toHexString(ch));
170
                    }
171
                    if (ch == CharUtilities.SOFT_HYPHEN) {
172
                        System.out.println("Soft hyphen found: " + Integer.toHexString(ch));
173
                    }
174
                    if (!visibleChar) {
175
                        System.out.println("Invisible glyph found: " + Integer.toHexString(ch));
176
                    }
177
                }
149
                }
178
                if (!visibleChar) {
150
                debugShapes.append(sh, false);
179
                    continue;
151
            }
180
                }
181
                Point2D p = gv.getGlyphPosition(index);
182
152
183
                AffineTransform glyphTransform = gv.getGlyphTransform(index);
153
            //Exact position of the glyph
184
                //TODO Glyph transforms could be refined so not every char has to be painted
154
            localTransform.setToIdentity();
185
                //with its own TJ command (stretch/squeeze case could be optimized)
155
            localTransform.translate(glyphPos.getX(), glyphPos.getY());
186
                if (DEBUG) {
156
            if (glyphTransform != null) {
187
                    System.out.println("pos " + p + ", transform " + glyphTransform);
157
                localTransform.concatenate(glyphTransform);
188
                    Shape sh;
158
            }
189
                    sh = gv.getGlyphLogicalBounds(index);
159
            localTransform.scale(1, -1);
190
                    if (sh == null) {
191
                        sh = new Ellipse2D.Double(p.getX(), p.getY(), 2, 2);
192
                    }
193
                    debugShapes.append(sh, false);
194
                }
195
160
196
                //Exact position of the glyph
161
            boolean yPosChanged = (prevPos == null
197
                localTransform.setToIdentity();
162
                    || prevPos.getY() != glyphPos.getY()
198
                localTransform.translate(p.getX(), p.getY());
163
                    || glyphTransform != null);
199
                if (glyphTransform != null) {
164
            if (yPosChanged) {
200
                    localTransform.concatenate(glyphTransform);
165
                if (index > 0) {
201
                }
202
                localTransform.scale(1, -1);
203
204
                boolean yPosChanged = (prevPos == null
205
                        || prevPos.getY() != p.getY()
206
                        || glyphTransform != null);
207
                if (yPosChanged) {
208
                    if (index > 0) {
209
                        textUtil.writeTJ();
210
                        textUtil.writeTextMatrix(localTransform);
211
                    }
212
                } else {
213
                    double xdiff = p.getX() - prevPos.getX();
214
                    //Width of previous character
215
                    Font font = textUtil.getCurrentFont();
216
                    double cw = prevVisibleCharWidth;
217
                    double effxdiff = (1000 * xdiff) - cw;
218
                    if (effxdiff != 0) {
219
                        double adjust = (-effxdiff / font.getFontSize());
220
                        textUtil.adjustGlyphTJ(adjust * 1000);
221
                    }
222
                    if (DEBUG) {
223
                        System.out.println("==> x diff: " + xdiff + ", " + effxdiff
224
                                + ", charWidth: " + cw);
225
                    }
226
                }
227
                Font f = textUtil.selectFontForChar(ch);
228
                if (f != textUtil.getCurrentFont()) {
229
                    textUtil.writeTJ();
166
                    textUtil.writeTJ();
230
                    textUtil.setCurrentFont(f);
231
                    textUtil.writeTf(f);
232
                    textUtil.writeTextMatrix(localTransform);
167
                    textUtil.writeTextMatrix(localTransform);
233
                }
168
                }
234
                char paintChar = (CharUtilities.isAnySpace(ch) ? ' ' : ch);
169
            } else {
235
                textUtil.writeTJChar(paintChar);
170
                double xdiff = glyphPos.getX() - prevPos.getX();
236
171
                //Width of previous character
237
                //Update last position
172
                Font font = textUtil.getCurrentFont();
238
                prevPos = p;
173
                double cw = prevVisibleCharWidth;
239
                prevVisibleCharWidth = textUtil.getCurrentFont().getCharWidth(chars.charAt(index));
174
                double effxdiff = (1000 * xdiff) - cw;
175
                if (effxdiff != 0) {
176
                    double adjust = (-effxdiff / font.getFontSize());
177
                    textUtil.adjustGlyphTJ(adjust * 1000);
178
                }
179
                if (log.isTraceEnabled()) {
180
                    log.trace("==> x diff: " + xdiff + ", " + effxdiff
181
                            + ", charWidth: " + cw);
182
                }
240
            }
183
            }
241
            textUtil.writeTJ();
184
            Font f = textUtil.selectFontForChar(ch);
242
            textUtil.endTextObject();
185
            if (f != textUtil.getCurrentFont()) {
243
            textUtil.restoreGraphicsState();
186
                textUtil.writeTJ();
244
            if (DEBUG) {
187
                textUtil.setCurrentFont(f);
245
                g2d.setStroke(new BasicStroke(0));
188
                textUtil.writeTf(f);
246
                g2d.setColor(Color.LIGHT_GRAY);
189
                textUtil.writeTextMatrix(localTransform);
247
                g2d.draw(debugShapes);
248
            }
190
            }
191
            char paintChar = (CharUtilities.isAnySpace(ch) ? ' ' : ch);
192
            textUtil.writeTJChar(paintChar);
193
194
            //Update last position
195
            prevPos = glyphPos;
196
            prevVisibleCharWidth = textUtil.getCurrentFont().getCharWidth(chars.charAt(index));
249
        }
197
        }
198
        textUtil.writeTJ();
199
        textUtil.endTextObject();
200
        textUtil.restoreGraphicsState();
201
        if (DEBUG) {
202
            g2d.setStroke(new BasicStroke(0));
203
            g2d.setColor(Color.LIGHT_GRAY);
204
            g2d.draw(debugShapes);
205
        }
250
    }
206
    }
251
207
252
    private void applyColorAndPaint(TextPaintInfo tpi, PDFGraphics2D pdf) {
208
    private void applyColorAndPaint(TextPaintInfo tpi, PDFGraphics2D pdf) {
Lines 271-355 Link Here
271
        pdf.applyAlpha(fillAlpha, PDFGraphics2D.OPAQUE);
227
        pdf.applyAlpha(fillAlpha, PDFGraphics2D.OPAQUE);
272
    }
228
    }
273
229
274
    private Font[] findFonts(AttributedCharacterIterator aci) {
275
        List fonts = new java.util.ArrayList();
276
        List gvtFonts = (List) aci.getAttribute(
277
                GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES);
278
        Float posture = (Float) aci.getAttribute(TextAttribute.POSTURE);
279
        Float taWeight = (Float) aci.getAttribute(TextAttribute.WEIGHT);
280
        Float fontSize = (Float) aci.getAttribute(TextAttribute.SIZE);
281
282
        String style = ((posture != null) && (posture.floatValue() > 0.0))
283
                       ? Font.STYLE_ITALIC : Font.STYLE_NORMAL;
284
        int weight = ((taWeight != null)
285
                       &&  (taWeight.floatValue() > 1.0)) ? Font.WEIGHT_BOLD
286
                       : Font.WEIGHT_NORMAL;
287
288
        String firstFontFamily = null;
289
290
        //GVT_FONT can sometimes be different from the fonts in GVT_FONT_FAMILIES
291
        //or GVT_FONT_FAMILIES can even be empty and only GVT_FONT is set
292
        /* The following code section is not available until Batik 1.7 is released. */
293
        GVTFont gvtFont = (GVTFont)aci.getAttribute(
294
                GVTAttributedCharacterIterator.TextAttribute.GVT_FONT);
295
        if (gvtFont != null) {
296
            try {
297
                Method method = gvtFont.getClass().getMethod("getFamilyName", null);
298
                String gvtFontFamily = (String)method.invoke(gvtFont, null);
299
                //TODO Uncomment the following line when Batik 1.7 is shipped with FOP
300
                //String gvtFontFamily = gvtFont.getFamilyName(); //Not available in Batik 1.6
301
                if (DEBUG) {
302
                    System.out.print(gvtFontFamily + ", ");
303
                }
304
                if (fontInfo.hasFont(gvtFontFamily, style, weight)) {
305
                    FontTriplet triplet = fontInfo.fontLookup(gvtFontFamily, style,
306
                                                       weight);
307
                    int fsize = (int)(fontSize.floatValue() * 1000);
308
                    fonts.add(fontInfo.getFontInstance(triplet, fsize));
309
                }
310
                firstFontFamily = gvtFontFamily;
311
            } catch (Exception e) {
312
                //Most likely NoSuchMethodError here when using Batik 1.6
313
                //Just skip this section in this case
314
            }
315
        }
316
317
        if (gvtFonts != null) {
318
            Iterator i = gvtFonts.iterator();
319
            while (i.hasNext()) {
320
                GVTFontFamily fam = (GVTFontFamily) i.next();
321
                if (fam instanceof SVGFontFamily) {
322
                    return null; //Let Batik paint this text!
323
                }
324
                String fontFamily = fam.getFamilyName();
325
                if (DEBUG) {
326
                    System.out.print(fontFamily + ", ");
327
                }
328
                if (fontInfo.hasFont(fontFamily, style, weight)) {
329
                    FontTriplet triplet = fontInfo.fontLookup(fontFamily, style,
330
                                                       weight);
331
                    int fsize = (int)(fontSize.floatValue() * 1000);
332
                    fonts.add(fontInfo.getFontInstance(triplet, fsize));
333
                }
334
                if (firstFontFamily == null) {
335
                    firstFontFamily = fontFamily;
336
                }
337
            }
338
        }
339
        if (fonts.size() == 0) {
340
            if (firstFontFamily == null) {
341
                //This will probably never happen. Just to be on the safe side.
342
                firstFontFamily = "any";
343
            }
344
            //lookup with fallback possibility (incl. substitution notification)
345
            FontTriplet triplet = fontInfo.fontLookup(firstFontFamily, style, weight);
346
            int fsize = (int)(fontSize.floatValue() * 1000);
347
            fonts.add(fontInfo.getFontInstance(triplet, fsize));
348
        }
349
        if (DEBUG) {
350
            System.out.println();
351
        }
352
        return (Font[])fonts.toArray(new Font[fonts.size()]);
353
    }
354
355
}
230
}
(-)src/java/org/apache/fop/svg/PDFDocumentGraphics2DConfigurator.java (-4 / +19 lines)
Lines 55-60 Link Here
55
55
56
        //Fonts
56
        //Fonts
57
        try {
57
        try {
58
            FontInfo fontInfo = createFontInfo(cfg);
59
            graphics.setFontInfo(fontInfo);
60
        } catch (FOPException e) {
61
            throw new ConfigurationException("Error while setting up fonts", e);
62
        }
63
    }
64
65
    /**
66
     * Creates the {@link FontInfo} instance for the given configuration.
67
     * @param cfg the configuration
68
     * @return the font collection
69
     * @throws FOPException if an error occurs while setting up the fonts
70
     */
71
    public static FontInfo createFontInfo(Configuration cfg) throws FOPException {
72
        FontInfo fontInfo = new FontInfo();
73
        if (cfg != null) {
58
            FontResolver fontResolver = FontManager.createMinimalFontResolver();
74
            FontResolver fontResolver = FontManager.createMinimalFontResolver();
59
            //TODO The following could be optimized by retaining the FontManager somewhere
75
            //TODO The following could be optimized by retaining the FontManager somewhere
60
            FontManager fontManager = new FontManager();
76
            FontManager fontManager = new FontManager();
Lines 73-84 Link Here
73
            if (fontManager.useCache()) {
89
            if (fontManager.useCache()) {
74
                fontManager.getFontCache().save();
90
                fontManager.getFontCache().save();
75
            }
91
            }
76
            FontInfo fontInfo = new FontInfo();
77
            FontSetup.setup(fontInfo, fontInfoList, fontResolver);
92
            FontSetup.setup(fontInfo, fontInfoList, fontResolver);
78
            graphics.setFontInfo(fontInfo);
93
        } else {
79
        } catch (FOPException e) {
94
            FontSetup.setup(fontInfo);
80
            throw new ConfigurationException("Error while setting up fonts", e);
81
        }
95
        }
96
        return fontInfo;
82
    }
97
    }
83
98
84
}
99
}
(-)src/java/org/apache/fop/svg/PDFBridgeContext.java (-5 / +6 lines)
Lines 26-35 Link Here
26
import org.apache.batik.bridge.SVGTextElementBridge;
26
import org.apache.batik.bridge.SVGTextElementBridge;
27
import org.apache.batik.bridge.UserAgent;
27
import org.apache.batik.bridge.UserAgent;
28
import org.apache.batik.gvt.TextPainter;
28
import org.apache.batik.gvt.TextPainter;
29
import org.apache.fop.fonts.FontInfo;
29
30
import org.apache.xmlgraphics.image.loader.ImageManager;
30
import org.apache.xmlgraphics.image.loader.ImageManager;
31
import org.apache.xmlgraphics.image.loader.ImageSessionContext;
31
import org.apache.xmlgraphics.image.loader.ImageSessionContext;
32
32
33
import org.apache.fop.fonts.FontInfo;
34
33
/**
35
/**
34
 * BridgeContext which registers the custom bridges for PDF output.
36
 * BridgeContext which registers the custom bridges for PDF output.
35
 */
37
 */
Lines 38-48 Link Here
38
    /**
40
    /**
39
     * Constructs a new bridge context.
41
     * Constructs a new bridge context.
40
     * @param userAgent the user agent
42
     * @param userAgent the user agent
41
     * @param loader the Document Loader to use for referenced documents.
43
     * @param documentLoader the Document Loader to use for referenced documents.
42
     * @param fontInfo the font list for the text painter, may be null
44
     * @param fontInfo the font list for the text painter, may be null
43
     *                 in which case text is painted as shapes
45
     *                 in which case text is painted as shapes
44
     * @param linkTransform AffineTransform to properly place links,
45
     *                      may be null
46
     * @param imageManager an image manager
46
     * @param imageManager an image manager
47
     * @param imageSessionContext an image session context
47
     * @param imageSessionContext an image session context
48
     * @param linkTransform AffineTransform to properly place links,
48
     * @param linkTransform AffineTransform to properly place links,
Lines 52-58 Link Here
52
            FontInfo fontInfo, ImageManager imageManager,
52
            FontInfo fontInfo, ImageManager imageManager,
53
            ImageSessionContext imageSessionContext,
53
            ImageSessionContext imageSessionContext,
54
            AffineTransform linkTransform) {
54
            AffineTransform linkTransform) {
55
        super(userAgent, documentLoader, fontInfo, imageManager, imageSessionContext, linkTransform);
55
        super(userAgent, documentLoader, fontInfo,
56
                imageManager, imageSessionContext, linkTransform);
56
    }
57
    }
57
58
58
    /**
59
    /**
(-)src/java/org/apache/fop/svg/NativeTextPainter.java (+224 lines)
Line 0 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
/* $Id$ */
19
20
package org.apache.fop.svg;
21
22
import java.awt.Graphics2D;
23
import java.awt.font.TextAttribute;
24
import java.io.IOException;
25
import java.text.AttributedCharacterIterator;
26
import java.util.Iterator;
27
import java.util.List;
28
29
import org.apache.batik.bridge.SVGFontFamily;
30
import org.apache.batik.gvt.font.GVTFont;
31
import org.apache.batik.gvt.font.GVTFontFamily;
32
import org.apache.batik.gvt.renderer.StrokingTextPainter;
33
import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
34
import org.apache.batik.gvt.text.TextSpanLayout;
35
import org.apache.commons.logging.Log;
36
import org.apache.commons.logging.LogFactory;
37
38
import org.apache.fop.fonts.Font;
39
import org.apache.fop.fonts.FontInfo;
40
import org.apache.fop.fonts.FontTriplet;
41
import org.apache.fop.util.CharUtilities;
42
43
/**
44
 * Abstract base class for text painters that use specialized text commands native to an output
45
 * format to render text.
46
 */
47
public abstract class NativeTextPainter extends StrokingTextPainter {
48
49
    /** the logger for this class */
50
    protected Log log = LogFactory.getLog(NativeTextPainter.class);
51
52
    /** the font collection */
53
    protected final FontInfo fontInfo;
54
55
    /**
56
     * Creates a new instance.
57
     * @param fontInfo the font collection
58
     */
59
    public NativeTextPainter(FontInfo fontInfo) {
60
        this.fontInfo = fontInfo;
61
    }
62
63
    /**
64
     * Indicates whether the given {@link Graphics2D} instance if compatible with this text painter
65
     * implementation.
66
     * @param g2d the instance to check
67
     * @return true if the instance is compatible.
68
     */
69
    protected abstract boolean isSupported(Graphics2D g2d);
70
71
    /**
72
     * Paints a single text run.
73
     * @param textRun the text run
74
     * @param g2d the target Graphics2D instance
75
     * @throws IOException if an I/O error occurs while rendering the text
76
     */
77
    protected abstract void paintTextRun(TextRun textRun, Graphics2D g2d) throws IOException;
78
79
    /** {@inheritDoc} */
80
    protected void paintTextRuns(List textRuns, Graphics2D g2d) {
81
        if (log.isTraceEnabled()) {
82
            log.trace("paintTextRuns: count = " + textRuns.size());
83
        }
84
        if (!isSupported(g2d)) {
85
            super.paintTextRuns(textRuns, g2d);
86
            return;
87
        }
88
        for (int i = 0; i < textRuns.size(); i++) {
89
            TextRun textRun = (TextRun)textRuns.get(i);
90
            try {
91
                paintTextRun(textRun, g2d);
92
            } catch (IOException ioe) {
93
                //No other possibility than to use a RuntimeException
94
                throw new RuntimeException(ioe);
95
            }
96
        }
97
    }
98
99
    /**
100
     * Finds an array of suitable fonts for a given AttributedCharacterIterator.
101
     * @param aci the character iterator
102
     * @return the array of fonts
103
     */
104
    protected Font[] findFonts(AttributedCharacterIterator aci) {
105
        List fonts = new java.util.ArrayList();
106
        List gvtFonts = (List) aci.getAttribute(
107
                GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES);
108
        Float posture = (Float) aci.getAttribute(TextAttribute.POSTURE);
109
        Float taWeight = (Float) aci.getAttribute(TextAttribute.WEIGHT);
110
        Float fontSize = (Float) aci.getAttribute(TextAttribute.SIZE);
111
112
        String style = ((posture != null) && (posture.floatValue() > 0.0))
113
                       ? Font.STYLE_ITALIC : Font.STYLE_NORMAL;
114
        int weight = ((taWeight != null)
115
                       &&  (taWeight.floatValue() > 1.0)) ? Font.WEIGHT_BOLD
116
                       : Font.WEIGHT_NORMAL;
117
118
        String firstFontFamily = null;
119
120
        //GVT_FONT can sometimes be different from the fonts in GVT_FONT_FAMILIES
121
        //or GVT_FONT_FAMILIES can even be empty and only GVT_FONT is set
122
        /* The following code section is not available until Batik 1.7 is released. */
123
        GVTFont gvtFont = (GVTFont)aci.getAttribute(
124
                GVTAttributedCharacterIterator.TextAttribute.GVT_FONT);
125
        if (gvtFont != null) {
126
            try {
127
                String gvtFontFamily = gvtFont.getFamilyName(); //Not available in Batik 1.6!
128
                if (log.isDebugEnabled()) {
129
                    log.debug("Matching font family: " + gvtFontFamily);
130
                }
131
                if (fontInfo.hasFont(gvtFontFamily, style, weight)) {
132
                    FontTriplet triplet = fontInfo.fontLookup(gvtFontFamily, style,
133
                                                       weight);
134
                    int fsize = (int)(fontSize.floatValue() * 1000);
135
                    fonts.add(fontInfo.getFontInstance(triplet, fsize));
136
                }
137
                firstFontFamily = gvtFontFamily;
138
            } catch (Exception e) {
139
                //Most likely NoSuchMethodError here when using Batik 1.6
140
                //Just skip this section in this case
141
            }
142
        }
143
144
        if (gvtFonts != null) {
145
            Iterator i = gvtFonts.iterator();
146
            while (i.hasNext()) {
147
                GVTFontFamily fam = (GVTFontFamily) i.next();
148
                if (fam instanceof SVGFontFamily) {
149
                    return null; //Let Batik paint this text!
150
                }
151
                String fontFamily = fam.getFamilyName();
152
                if (log.isDebugEnabled()) {
153
                    log.debug("Matching font family: " + fontFamily);
154
                }
155
                if (fontInfo.hasFont(fontFamily, style, weight)) {
156
                    FontTriplet triplet = fontInfo.fontLookup(fontFamily, style,
157
                                                       weight);
158
                    int fsize = (int)(fontSize.floatValue() * 1000);
159
                    fonts.add(fontInfo.getFontInstance(triplet, fsize));
160
                }
161
                if (firstFontFamily == null) {
162
                    firstFontFamily = fontFamily;
163
                }
164
            }
165
        }
166
        if (fonts.size() == 0) {
167
            if (firstFontFamily == null) {
168
                //This will probably never happen. Just to be on the safe side.
169
                firstFontFamily = "any";
170
            }
171
            //lookup with fallback possibility (incl. substitution notification)
172
            FontTriplet triplet = fontInfo.fontLookup(firstFontFamily, style, weight);
173
            int fsize = (int)(fontSize.floatValue() * 1000);
174
            fonts.add(fontInfo.getFontInstance(triplet, fsize));
175
        }
176
        return (Font[])fonts.toArray(new Font[fonts.size()]);
177
    }
178
179
    /**
180
     * Collects all characters from an {@link AttributedCharacterIterator}.
181
     * @param runaci the character iterator
182
     * @return the characters
183
     */
184
    protected CharSequence collectCharacters(AttributedCharacterIterator runaci) {
185
        StringBuffer chars = new StringBuffer();
186
        for (runaci.first(); runaci.getIndex() < runaci.getEndIndex();) {
187
            chars.append(runaci.current());
188
            runaci.next();
189
        }
190
        return chars;
191
    }
192
193
    protected final void logTextRun(AttributedCharacterIterator runaci, TextSpanLayout layout) {
194
        if (log.isTraceEnabled()) {
195
            int charCount = runaci.getEndIndex() - runaci.getBeginIndex();
196
            log.trace("================================================");
197
            log.trace("New text run:");
198
            log.trace("char count: " + charCount);
199
            log.trace("range: "
200
                    + runaci.getBeginIndex() + " - " + runaci.getEndIndex());
201
            log.trace("glyph count: " + layout.getGlyphCount()); //=getNumGlyphs()
202
        }
203
    }
204
205
    protected final void logCharacter(char ch, TextSpanLayout layout, int index,
206
            boolean visibleChar) {
207
        if (log.isTraceEnabled()) {
208
            log.trace("glyph " + index
209
                    + " -> " + layout.getGlyphIndex(index) + " => " + ch);
210
            if (CharUtilities.isAnySpace(ch) && ch != 32) {
211
                log.trace("Space found: " + Integer.toHexString(ch));
212
            } else if (ch == CharUtilities.ZERO_WIDTH_JOINER) {
213
                log.trace("ZWJ found: " + Integer.toHexString(ch));
214
            } else if (ch == CharUtilities.SOFT_HYPHEN) {
215
                log.trace("Soft hyphen found: " + Integer.toHexString(ch));
216
            }
217
            if (!visibleChar) {
218
                log.trace("Invisible glyph found: " + Integer.toHexString(ch));
219
            }
220
        }
221
    }
222
223
224
}
0
  + Id
225
  + Id
1
  + native
226
  + native
(-)src/java/org/apache/fop/svg/AbstractFOPTranscoder.java (-3 / +132 lines)
Lines 19-24 Link Here
19
19
20
package org.apache.fop.svg;
20
package org.apache.fop.svg;
21
21
22
import java.io.IOException;
23
import java.io.InputStream;
24
25
import javax.xml.transform.Source;
26
import javax.xml.transform.stream.StreamSource;
27
28
import org.w3c.dom.DOMImplementation;
29
30
import org.xml.sax.EntityResolver;
31
32
import org.apache.avalon.framework.configuration.Configuration;
33
import org.apache.avalon.framework.configuration.ConfigurationException;
34
import org.apache.avalon.framework.configuration.DefaultConfiguration;
22
import org.apache.batik.bridge.UserAgent;
35
import org.apache.batik.bridge.UserAgent;
23
import org.apache.batik.dom.svg.SVGDOMImplementation;
36
import org.apache.batik.dom.svg.SVGDOMImplementation;
24
import org.apache.batik.dom.util.DocumentFactory;
37
import org.apache.batik.dom.util.DocumentFactory;
Lines 28-50 Link Here
28
import org.apache.batik.transcoder.TranscodingHints;
41
import org.apache.batik.transcoder.TranscodingHints;
29
import org.apache.batik.transcoder.image.ImageTranscoder;
42
import org.apache.batik.transcoder.image.ImageTranscoder;
30
import org.apache.batik.transcoder.keys.BooleanKey;
43
import org.apache.batik.transcoder.keys.BooleanKey;
44
import org.apache.batik.transcoder.keys.FloatKey;
45
import org.apache.batik.util.ParsedURL;
31
import org.apache.batik.util.SVGConstants;
46
import org.apache.batik.util.SVGConstants;
32
import org.apache.commons.logging.Log;
47
import org.apache.commons.logging.Log;
33
import org.apache.commons.logging.impl.SimpleLog;
48
import org.apache.commons.logging.impl.SimpleLog;
34
import org.w3c.dom.DOMImplementation;
35
import org.xml.sax.EntityResolver;
36
49
50
import org.apache.xmlgraphics.image.loader.ImageContext;
51
import org.apache.xmlgraphics.image.loader.ImageManager;
52
import org.apache.xmlgraphics.image.loader.ImageSessionContext;
53
import org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext;
54
37
/**
55
/**
38
 * This is the common base class of all of FOP's transcoders.
56
 * This is the common base class of all of FOP's transcoders.
39
 */
57
 */
40
public abstract class AbstractFOPTranscoder extends SVGAbstractTranscoder {
58
public abstract class AbstractFOPTranscoder extends SVGAbstractTranscoder {
41
59
42
    /**
60
    /**
61
     * The key is used to specify the resolution for on-the-fly images generated
62
     * due to complex effects like gradients and filters.
63
     */
64
    public static final TranscodingHints.Key KEY_DEVICE_RESOLUTION = new FloatKey();
65
66
    /**
43
     * The key to specify whether to stroke text instead of using text
67
     * The key to specify whether to stroke text instead of using text
44
     * operations.
68
     * operations.
45
     */
69
     */
46
    public static final TranscodingHints.Key KEY_STROKE_TEXT = new BooleanKey();
70
    public static final TranscodingHints.Key KEY_STROKE_TEXT = new BooleanKey();
47
71
72
    /**
73
     * The key is used to specify whether the available fonts should be automatically
74
     * detected. The alternative is to configure the transcoder manually using a configuration
75
     * file.
76
     */
77
    public static final TranscodingHints.Key KEY_AUTO_FONTS = new BooleanKey();
78
48
    /** The value to turn on text stroking. */
79
    /** The value to turn on text stroking. */
49
    public static final Boolean VALUE_FORMAT_ON = Boolean.TRUE;
80
    public static final Boolean VALUE_FORMAT_ON = Boolean.TRUE;
50
81
Lines 58-63 Link Here
58
89
59
    private Log logger;
90
    private Log logger;
60
    private EntityResolver resolver;
91
    private EntityResolver resolver;
92
    private Configuration cfg = null;
93
    private ImageManager imageManager;
94
    private ImageSessionContext imageSessionContext;
61
95
62
    /**
96
    /**
63
     * Constructs a new FOP-style transcoder.
97
     * Constructs a new FOP-style transcoder.
Lines 80-86 Link Here
80
    }
114
    }
81
115
82
    /**
116
    /**
83
     * @param logger
117
     * Sets the logger.
118
     * @param logger the logger
84
     */
119
     */
85
    public void setLogger(Log logger) {
120
    public void setLogger(Log logger) {
86
        this.logger = logger;
121
        this.logger = logger;
Lines 94-100 Link Here
94
        this.resolver = resolver;
129
        this.resolver = resolver;
95
    }
130
    }
96
131
132
    /** {@inheritDoc} */
133
    public void configure(Configuration cfg) throws ConfigurationException {
134
        this.cfg = cfg;
135
    }
136
97
    /**
137
    /**
138
     * Returns the effective configuration for the transcoder.
139
     * @return the effective configuration
140
     */
141
    protected Configuration getEffectiveConfiguration() {
142
        Configuration effCfg = this.cfg;
143
        if (effCfg == null) {
144
            //By default, enable font auto-detection if no cfg is given
145
            boolean autoFonts = true;
146
            if (hints.containsKey(KEY_AUTO_FONTS)) {
147
                autoFonts = ((Boolean)hints.get(KEY_AUTO_FONTS)).booleanValue();
148
            }
149
            if (autoFonts) {
150
                DefaultConfiguration c = new DefaultConfiguration("cfg");
151
                DefaultConfiguration fonts = new DefaultConfiguration("fonts");
152
                c.addChild(fonts);
153
                DefaultConfiguration autodetect = new DefaultConfiguration("auto-detect");
154
                fonts.addChild(autodetect);
155
                effCfg = c;
156
            }
157
        }
158
        return effCfg;
159
    }
160
161
    /**
98
     * Returns the logger associated with this transcoder. It returns a
162
     * Returns the logger associated with this transcoder. It returns a
99
     * SimpleLog if no logger has been explicitly set.
163
     * SimpleLog if no logger has been explicitly set.
100
     * @return Logger the logger for the transcoder.
164
     * @return Logger the logger for the transcoder.
Lines 142-147 Link Here
142
        return stroke;
206
        return stroke;
143
    }
207
    }
144
208
209
    /**
210
     * Returns the device resolution that has been set up.
211
     * @return the device resolution (in dpi)
212
     */
213
    protected float getDeviceResolution() {
214
        if (hints.containsKey(KEY_DEVICE_RESOLUTION)) {
215
            return ((Float)hints.get(KEY_DEVICE_RESOLUTION)).floatValue();
216
        } else {
217
            return 72;
218
        }
219
    }
220
221
    /**
222
     * Returns the ImageManager to be used by the transcoder.
223
     * @return the image manager
224
     */
225
    protected ImageManager getImageManager() {
226
        return this.imageManager;
227
    }
228
229
    /**
230
     * Returns the ImageSessionContext to be used by the transcoder.
231
     * @return the image session context
232
     */
233
    protected ImageSessionContext getImageSessionContext() {
234
        return this.imageSessionContext;
235
    }
236
237
    /**
238
     * Sets up the image infrastructure (the image loading framework).
239
     * @param baseURI the base URI of the current document
240
     */
241
    protected void setupImageInfrastructure(final String baseURI) {
242
        final ImageContext imageContext = new ImageContext() {
243
            public float getSourceResolution() {
244
                return 25.4f / userAgent.getPixelUnitToMillimeter();
245
            }
246
        };
247
        this.imageManager = new ImageManager(imageContext);
248
        this.imageSessionContext = new AbstractImageSessionContext() {
249
250
            public ImageContext getParentContext() {
251
                return imageContext;
252
            }
253
254
            public float getTargetResolution() {
255
                return getDeviceResolution();
256
            }
257
258
            public Source resolveURI(String uri) {
259
                System.out.println("resolve " + uri);
260
                try {
261
                    ParsedURL url = new ParsedURL(baseURI, uri);
262
                    InputStream in = url.openStream();
263
                    StreamSource source = new StreamSource(in, url.toString());
264
                    return source;
265
                } catch (IOException ioe) {
266
                    userAgent.displayError(ioe);
267
                    return null;
268
                }
269
            }
270
271
        };
272
    }
273
145
    // --------------------------------------------------------------------
274
    // --------------------------------------------------------------------
146
    // FOP's default error handler (for transcoders)
275
    // FOP's default error handler (for transcoders)
147
    // --------------------------------------------------------------------
276
    // --------------------------------------------------------------------
(-)src/java/org/apache/fop/svg/AbstractFOPTextElementBridge.java (-46 / +2 lines)
Lines 19-31 Link Here
19
19
20
package org.apache.fop.svg;
20
package org.apache.fop.svg;
21
21
22
import org.w3c.dom.Element;
23
22
import org.apache.batik.bridge.BridgeContext;
24
import org.apache.batik.bridge.BridgeContext;
23
import org.apache.batik.bridge.SVGTextElementBridge;
25
import org.apache.batik.bridge.SVGTextElementBridge;
24
import org.apache.batik.gvt.GraphicsNode;
26
import org.apache.batik.gvt.GraphicsNode;
25
import org.apache.batik.gvt.TextNode;
27
import org.apache.batik.gvt.TextNode;
26
import org.apache.batik.gvt.TextPainter;
28
import org.apache.batik.gvt.TextPainter;
27
import org.w3c.dom.Element;
28
import org.w3c.dom.Node;
29
29
30
/**
30
/**
31
 * Bridge class for the &lt;text> element.
31
 * Bridge class for the &lt;text> element.
Lines 65-113 Link Here
65
        return node;
65
        return node;
66
    }
66
    }
67
67
68
    /**
69
     * Check if text element contains simple text.
70
     * This checks the children of the text element to determine
71
     * if the text is simple. The text is simple if it can be rendered
72
     * with basic text drawing algorithms. This means there are no
73
     * alternate characters, the font is known and there are no effects
74
     * applied to the text.
75
     *
76
     * @param ctx the bridge context
77
     * @param element the svg text element
78
     * @param node the graphics node
79
     * @return true if this text is simple of false if it cannot be
80
     *         easily rendered using normal drawString on the Graphics2D
81
     */
82
    protected boolean isSimple(BridgeContext ctx, Element element, GraphicsNode node) {
83
        for (Node n = element.getFirstChild();
84
                n != null;
85
                n = n.getNextSibling()) {
86
87
            switch (n.getNodeType()) {
88
            case Node.ELEMENT_NODE:
89
90
                if (n.getLocalName().equals(SVG_TSPAN_TAG)
91
                        || n.getLocalName().equals(SVG_ALT_GLYPH_TAG)) {
92
                    return false;
93
                } else if (n.getLocalName().equals(SVG_TEXT_PATH_TAG)) {
94
                    return false;
95
                } else if (n.getLocalName().equals(SVG_TREF_TAG)) {
96
                    return false;
97
                }
98
                break;
99
            case Node.TEXT_NODE:
100
            case Node.CDATA_SECTION_NODE:
101
            default:
102
            }
103
        }
104
105
        /*if (CSSUtilities.convertFilter(element, node, ctx) != null) {
106
            return false;
107
        }*/
108
109
        return true;
110
    }
111
112
}
68
}
113
69
(-)src/java/org/apache/fop/svg/AbstractFOPBridgeContext.java (-3 / +3 lines)
Lines 26-35 Link Here
26
import org.apache.batik.bridge.BridgeContext;
26
import org.apache.batik.bridge.BridgeContext;
27
import org.apache.batik.bridge.DocumentLoader;
27
import org.apache.batik.bridge.DocumentLoader;
28
import org.apache.batik.bridge.UserAgent;
28
import org.apache.batik.bridge.UserAgent;
29
import org.apache.fop.fonts.FontInfo;
29
30
import org.apache.xmlgraphics.image.loader.ImageManager;
30
import org.apache.xmlgraphics.image.loader.ImageManager;
31
import org.apache.xmlgraphics.image.loader.ImageSessionContext;
31
import org.apache.xmlgraphics.image.loader.ImageSessionContext;
32
32
33
import org.apache.fop.fonts.FontInfo;
34
33
/**
35
/**
34
 * A FOP base implementation of a Batik BridgeContext.
36
 * A FOP base implementation of a Batik BridgeContext.
35
 */
37
 */
Lines 49-56 Link Here
49
     * @param loader the Document Loader to use for referenced documents.
51
     * @param loader the Document Loader to use for referenced documents.
50
     * @param fontInfo the font list for the text painter, may be null
52
     * @param fontInfo the font list for the text painter, may be null
51
     *                 in which case text is painted as shapes
53
     *                 in which case text is painted as shapes
52
     * @param linkTransform AffineTransform to properly place links,
53
     *                      may be null
54
     * @param imageManager an image manager
54
     * @param imageManager an image manager
55
     * @param imageSessionContext an image session context
55
     * @param imageSessionContext an image session context
56
     * @param linkTransform AffineTransform to properly place links,
56
     * @param linkTransform AffineTransform to properly place links,
(-)src/java/org/apache/fop/render/ps/PSTextPainter.java (-445 / +406 lines)
Lines 19-573 Link Here
19
19
20
package org.apache.fop.render.ps;
20
package org.apache.fop.render.ps;
21
21
22
import java.awt.BasicStroke;
22
import java.awt.Color;
23
import java.awt.Color;
23
import java.awt.Graphics2D;
24
import java.awt.Graphics2D;
24
import java.awt.Paint;
25
import java.awt.Paint;
25
import java.awt.Shape;
26
import java.awt.Shape;
26
import java.awt.Stroke;
27
import java.awt.Stroke;
27
import java.awt.font.TextAttribute;
28
import java.awt.geom.AffineTransform;
29
import java.awt.geom.Ellipse2D;
30
import java.awt.geom.GeneralPath;
31
import java.awt.geom.PathIterator;
28
import java.awt.geom.Point2D;
32
import java.awt.geom.Point2D;
29
import java.awt.geom.Rectangle2D;
30
import java.io.IOException;
33
import java.io.IOException;
31
import java.text.AttributedCharacterIterator;
34
import java.text.AttributedCharacterIterator;
32
import java.text.CharacterIterator;
33
import java.util.Iterator;
35
import java.util.Iterator;
34
import java.util.List;
36
import java.util.List;
35
37
36
import org.apache.batik.dom.svg.SVGOMTextElement;
38
import org.apache.batik.gvt.font.GVTGlyphVector;
37
import org.apache.batik.gvt.TextNode;
38
import org.apache.batik.gvt.TextPainter;
39
import org.apache.batik.gvt.font.GVTFontFamily;
40
import org.apache.batik.gvt.renderer.StrokingTextPainter;
41
import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
42
import org.apache.batik.gvt.text.Mark;
43
import org.apache.batik.gvt.text.TextPaintInfo;
39
import org.apache.batik.gvt.text.TextPaintInfo;
44
import org.apache.commons.logging.Log;
40
import org.apache.batik.gvt.text.TextSpanLayout;
45
import org.apache.commons.logging.LogFactory;
41
42
import org.apache.xmlgraphics.java2d.ps.PSGraphics2D;
43
import org.apache.xmlgraphics.ps.PSGenerator;
44
import org.apache.xmlgraphics.ps.PSResource;
45
46
import org.apache.fop.fonts.Font;
46
import org.apache.fop.fonts.Font;
47
import org.apache.fop.fonts.FontInfo;
47
import org.apache.fop.fonts.FontInfo;
48
import org.apache.fop.fonts.FontTriplet;
48
import org.apache.fop.svg.NativeTextPainter;
49
import org.apache.xmlgraphics.java2d.ps.PSGraphics2D;
49
import org.apache.fop.util.CharUtilities;
50
50
51
52
/**
51
/**
53
 * Renders the attributed character iterator of a <tt>TextNode</tt>.
52
 * Renders the attributed character iterator of a <tt>TextNode</tt>.
54
 * This class draws the text directly into the PSGraphics2D so that
53
 * This class draws the text directly using PostScript text operators so
55
 * the text is not drawn using shapes which makes the PS files larger.
54
 * the text is not drawn using shapes which makes the PS files larger.
56
 * If the text is simple enough to draw then it sets the font and calls
55
 * <p>
57
 * drawString. If the text is complex or the cannot be translated
56
 * The text runs are split into smaller text runs that can be bundles in single
58
 * into a simple drawString the StrokingTextPainter is used instead.
57
 * calls of the xshow, yshow or xyshow operators. For outline text, the charpath
59
 *
58
 * operator is used.
60
 * (todo) handle underline, overline and strikethrough
61
 * (todo) use drawString(AttributedCharacterIterator iterator...) for some
62
 *
63
 * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
64
 * @version $Id$
65
 */
59
 */
66
public class PSTextPainter implements TextPainter {
60
public class PSTextPainter extends NativeTextPainter {
67
61
68
    /** the logger for this class */
62
    private static final boolean DEBUG = false;
69
    protected Log log = LogFactory.getLog(PSTextPainter.class);
70
63
71
    private final NativeTextHandler nativeTextHandler;
64
    private FontResourceCache fontResources;
72
    private final FontInfo fontInfo;
73
65
74
    /**
66
    private static final AffineTransform IDENTITY_TRANSFORM = new AffineTransform();
75
     * Use the stroking text painter to get the bounds and shape.
76
     * Also used as a fallback to draw the string with strokes.
77
     */
78
    protected static final TextPainter
79
        PROXY_PAINTER = StrokingTextPainter.getInstance();
80
67
81
    /**
68
    /**
82
     * Create a new PS text painter with the given font information.
69
     * Create a new PS text painter with the given font information.
83
     * @param nativeTextHandler the NativeTextHandler instance used for text painting
70
     * @param fontInfo the font collection
84
     */
71
     */
85
    public PSTextPainter(NativeTextHandler nativeTextHandler) {
72
    public PSTextPainter(FontInfo fontInfo) {
86
        this.nativeTextHandler = nativeTextHandler;
73
        super(fontInfo);
87
        this.fontInfo = nativeTextHandler.getFontInfo();
74
        this.fontResources = new FontResourceCache(fontInfo);
88
    }
75
    }
89
76
90
    /**
77
    /** {@inheritDoc} */
91
     * Paints the specified attributed character iterator using the
78
    protected boolean isSupported(Graphics2D g2d) {
92
     * specified Graphics2D and context and font context.
79
        return g2d instanceof PSGraphics2D;
93
     * @param node the TextNode to paint
94
     * @param g2d the Graphics2D to use
95
     */
96
    public void paint(TextNode node, Graphics2D g2d) {
97
        String txt = node.getText();
98
        Point2D loc = node.getLocation();
99
100
        if (hasUnsupportedAttributes(node)) {
101
            PROXY_PAINTER.paint(node, g2d);
102
        } else {
103
            paintTextRuns(node.getTextRuns(), g2d, loc);
104
        }
105
    }
80
    }
106
81
82
    /** {@inheritDoc} */
83
    protected void paintTextRun(TextRun textRun, Graphics2D g2d) throws IOException {
84
        AttributedCharacterIterator runaci = textRun.getACI();
85
        runaci.first();
107
86
108
    private boolean hasUnsupportedAttributes(TextNode node) {
87
        TextPaintInfo tpi = (TextPaintInfo)runaci.getAttribute(PAINT_INFO);
109
        Iterator i = node.getTextRuns().iterator();
88
        if (tpi == null || !tpi.visible) {
110
        while (i.hasNext()) {
89
            return;
111
            StrokingTextPainter.TextRun
112
                    run = (StrokingTextPainter.TextRun)i.next();
113
            AttributedCharacterIterator aci = run.getACI();
114
            boolean hasUnsupported = hasUnsupportedAttributes(aci);
115
            if (hasUnsupported) {
116
                return true;
117
            }
118
        }
90
        }
119
        return false;
91
        if ((tpi != null) && (tpi.composite != null)) {
120
    }
92
            g2d.setComposite(tpi.composite);
93
        }
121
94
122
    private boolean hasUnsupportedAttributes(AttributedCharacterIterator aci) {
95
        //------------------------------------
123
        boolean hasunsupported = false;
96
        TextSpanLayout layout = textRun.getLayout();
97
        logTextRun(runaci, layout);
98
        CharSequence chars = collectCharacters(runaci);
99
        runaci.first(); //Reset ACI
124
100
125
        String text = getText(aci);
101
        final PSGraphics2D ps = (PSGraphics2D)g2d;
126
        Font font = makeFont(aci);
102
        final PSGenerator gen = ps.getPSGenerator();
127
        if (hasUnsupportedGlyphs(text, font)) {
103
        ps.preparePainting();
128
            log.trace("-> Unsupported glyphs found");
129
            hasunsupported = true;
130
        }
131
104
132
        TextPaintInfo tpi = (TextPaintInfo) aci.getAttribute(
105
        if (DEBUG) {
133
            GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO);
106
            log.debug("Text: " + chars);
134
        if ((tpi != null)
107
            gen.commentln("%Text: " + chars);
135
                && ((tpi.strokeStroke != null && tpi.strokePaint != null)
136
                    || (tpi.strikethroughStroke != null)
137
                    || (tpi.underlineStroke != null)
138
                    || (tpi.overlineStroke != null))) {
139
                        log.trace("-> under/overlines etc. found");
140
            hasunsupported = true;
141
        }
108
        }
142
109
143
        //Alpha is not supported
110
        GeneralPath debugShapes = null;
144
        Paint foreground = (Paint) aci.getAttribute(TextAttribute.FOREGROUND);
111
        if (DEBUG) {
145
        if (foreground instanceof Color) {
112
            debugShapes = new GeneralPath();
146
            Color col = (Color)foreground;
147
            if (col.getAlpha() != 255) {
148
                log.trace("-> transparency found");
149
                hasunsupported = true;
150
            }
151
        }
113
        }
152
114
153
        Object letSpace = aci.getAttribute(
115
        TextUtil textUtil = new TextUtil(gen);
154
                            GVTAttributedCharacterIterator.TextAttribute.LETTER_SPACING);
116
        textUtil.setupFonts(runaci);
155
        if (letSpace != null) {
117
        if (!textUtil.hasFonts()) {
156
            log.trace("-> letter spacing found");
118
            //Draw using Java2D when no native fonts are available
157
            hasunsupported = true;
119
            textRun.getLayout().draw(g2d);
120
            return;
158
        }
121
        }
159
122
160
        Object wordSpace = aci.getAttribute(
123
        gen.saveGraphicsState();
161
                             GVTAttributedCharacterIterator.TextAttribute.WORD_SPACING);
124
        gen.concatMatrix(g2d.getTransform());
162
        if (wordSpace != null) {
125
        Shape imclip = g2d.getClip();
163
            log.trace("-> word spacing found");
126
        clip(ps, imclip);
164
            hasunsupported = true;
165
        }
166
127
167
        Object lengthAdjust = aci.getAttribute(
128
        gen.writeln("BT"); //beginTextObject()
168
                            GVTAttributedCharacterIterator.TextAttribute.LENGTH_ADJUST);
169
        if (lengthAdjust != null) {
170
            log.trace("-> length adjustments found");
171
            hasunsupported = true;
172
        }
173
129
174
        Object writeMod = aci.getAttribute(
130
        AffineTransform localTransform = new AffineTransform();
175
                GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE);
131
        Point2D prevPos = null;
176
        if (writeMod != null
132
        GVTGlyphVector gv = layout.getGlyphVector();
177
            && !GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE_LTR.equals(
133
        PSTextRun psRun = new PSTextRun(); //Used to split a text run into smaller runs
178
                  writeMod)) {
134
        for (int index = 0, c = gv.getNumGlyphs(); index < c; index++) {
179
            log.trace("-> Unsupported writing modes found");
135
            char ch = chars.charAt(index);
180
            hasunsupported = true;
136
            boolean visibleChar = gv.isGlyphVisible(index)
181
        }
137
                || (CharUtilities.isAnySpace(ch) && !CharUtilities.isZeroWidthSpace(ch));
138
            logCharacter(ch, layout, index, visibleChar);
139
            if (!visibleChar) {
140
                continue;
141
            }
142
            Point2D glyphPos = gv.getGlyphPosition(index);
182
143
183
        Object vertOr = aci.getAttribute(
144
            AffineTransform glyphTransform = gv.getGlyphTransform(index);
184
                GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION);
145
            if (log.isTraceEnabled()) {
185
        if (GVTAttributedCharacterIterator.TextAttribute.ORIENTATION_ANGLE.equals(
146
                log.trace("pos " + glyphPos + ", transform " + glyphTransform);
186
                  vertOr)) {
147
            }
187
            log.trace("-> vertical orientation found");
148
            if (DEBUG) {
188
            hasunsupported = true;
149
                Shape sh = gv.getGlyphLogicalBounds(index);
189
        }
150
                if (sh == null) {
151
                    sh = new Ellipse2D.Double(glyphPos.getX(), glyphPos.getY(), 2, 2);
152
                }
153
                debugShapes.append(sh, false);
154
            }
190
155
191
        Object rcDel = aci.getAttribute(
156
            //Exact position of the glyph
192
                GVTAttributedCharacterIterator.TextAttribute.TEXT_COMPOUND_DELIMITER);
157
            localTransform.setToIdentity();
193
        //Batik 1.6 returns null here which makes it impossible to determine whether this can
158
            localTransform.translate(glyphPos.getX(), glyphPos.getY());
194
        //be painted or not, i.e. fall back to stroking. :-(
159
            if (glyphTransform != null) {
195
        if (/*rcDel != null &&*/ !(rcDel instanceof SVGOMTextElement)) {
160
                localTransform.concatenate(glyphTransform);
196
            log.trace("-> spans found");
161
            }
197
            hasunsupported = true; //Filter spans
162
            localTransform.scale(1, -1);
163
164
            boolean flushCurrentRun = false;
165
            //Try to optimize by combining characters using the same font and on the same line.
166
            if (glyphTransform != null) {
167
                //Happens for text-on-a-path
168
                flushCurrentRun = true;
169
            }
170
            if (psRun.getRunLength() >= 128) {
171
                //Don't let a run get too long
172
                flushCurrentRun = true;
173
            }
174
175
            //Note the position of the glyph relative to the previous one
176
            Point2D relPos;
177
            if (prevPos == null) {
178
                relPos = new Point2D.Double(0, 0);
179
            } else {
180
                relPos = new Point2D.Double(
181
                        glyphPos.getX() - prevPos.getX(),
182
                        glyphPos.getY() - prevPos.getY());
183
            }
184
            if (psRun.vertChanges == 0
185
                    && psRun.getHorizRunLength() > 2
186
                    && relPos.getY() != 0) {
187
                //new line
188
                flushCurrentRun = true;
189
            }
190
191
            //Select the actual character to paint
192
            char paintChar = (CharUtilities.isAnySpace(ch) ? ' ' : ch);
193
194
            //Select (sub)font for character
195
            Font f = textUtil.selectFontForChar(paintChar);
196
            char mapped = f.mapChar(ch);
197
            boolean fontChanging = textUtil.isFontChanging(f, mapped);
198
            if (fontChanging) {
199
                flushCurrentRun = true;
200
            }
201
202
            if (flushCurrentRun) {
203
                //Paint the current run and reset for the next run
204
                psRun.paint(ps, textUtil, tpi);
205
                psRun.reset();
206
            }
207
208
            //Track current run
209
            psRun.addCharacter(paintChar, relPos);
210
            psRun.noteStartingTransformation(localTransform);
211
212
            //Change font if necessary
213
            if (fontChanging) {
214
                textUtil.setCurrentFont(f, mapped);
215
            }
216
217
            //Update last position
218
            prevPos = glyphPos;
198
        }
219
        }
220
        psRun.paint(ps, textUtil, tpi);
221
        gen.writeln("ET"); //endTextObject()
222
        gen.restoreGraphicsState();
199
223
200
        if (hasunsupported) {
224
        if (DEBUG) {
201
            log.trace("Unsupported attributes found in ACI, using StrokingTextPainter");
225
            //Paint debug shapes
226
            g2d.setStroke(new BasicStroke(0));
227
            g2d.setColor(Color.LIGHT_GRAY);
228
            g2d.draw(debugShapes);
202
        }
229
        }
203
        return hasunsupported;
204
    }
230
    }
205
231
206
    /**
232
    private void applyColor(Paint paint, final PSGenerator gen) throws IOException {
207
     * Paint a list of text runs on the Graphics2D at a given location.
233
        if (paint == null) {
208
     * @param textRuns the list of text runs
234
            return;
209
     * @param g2d the Graphics2D to paint to
235
        } else if (paint instanceof Color) {
210
     * @param loc the current location of the "cursor"
236
            Color col = (Color)paint;
211
     */
237
            gen.useColor(col);
212
    protected void paintTextRuns(List textRuns, Graphics2D g2d, Point2D loc) {
238
        } else {
213
        Point2D currentloc = loc;
239
            log.warn("Paint not supported: " + paint.toString());
214
        Iterator i = textRuns.iterator();
215
        while (i.hasNext()) {
216
            StrokingTextPainter.TextRun
217
                    run = (StrokingTextPainter.TextRun)i.next();
218
            currentloc = paintTextRun(run, g2d, currentloc);
219
        }
240
        }
220
    }
241
    }
221
242
222
    /**
243
    private PSResource getResourceForFont(Font f, String postfix) {
223
     * Paint a single text run on the Graphics2D at a given location.
244
        String key = (postfix != null ? f.getFontName() + '_' + postfix : f.getFontName());
224
     * @param run the text run to paint
245
        return this.fontResources.getPSResourceForFontKey(key);
225
     * @param g2d the Graphics2D to paint to
226
     * @param loc the current location of the "cursor"
227
     * @return the new location of the "cursor" after painting the text run
228
     */
229
    protected Point2D paintTextRun(StrokingTextPainter.TextRun run, Graphics2D g2d, Point2D loc) {
230
        AttributedCharacterIterator aci = run.getACI();
231
        return paintACI(aci, g2d, loc);
232
    }
246
    }
233
247
234
    /**
248
    private void clip(PSGraphics2D ps, Shape shape) throws IOException {
235
     * Extract the raw text from an ACI.
249
        if (shape == null) {
236
     * @param aci ACI to inspect
250
            return;
237
     * @return the extracted text
238
     */
239
    protected String getText(AttributedCharacterIterator aci) {
240
        StringBuffer sb = new StringBuffer(aci.getEndIndex() - aci.getBeginIndex());
241
        for (char c = aci.first(); c != CharacterIterator.DONE; c = aci.next()) {
242
            sb.append(c);
243
        }
251
        }
244
        return sb.toString();
252
        ps.getPSGenerator().writeln("newpath");
253
        PathIterator iter = shape.getPathIterator(IDENTITY_TRANSFORM);
254
        ps.processPathIterator(iter);
255
        ps.getPSGenerator().writeln("clip");
245
    }
256
    }
246
257
247
    /**
258
    private class TextUtil {
248
     * Paint an ACI on a Graphics2D at a given location. The method has to
249
     * update the location after painting.
250
     * @param aci ACI to paint
251
     * @param g2d Graphics2D to paint on
252
     * @param loc start location
253
     * @return new current location
254
     */
255
    protected Point2D paintACI(AttributedCharacterIterator aci, Graphics2D g2d, Point2D loc) {
256
        //ACIUtils.dumpAttrs(aci);
257
259
258
        aci.first();
260
        private PSGenerator gen;
261
        private Font[] fonts;
262
        private Font currentFont;
263
        private int currentEncoding = -1;
259
264
260
        updateLocationFromACI(aci, loc);
265
        public TextUtil(PSGenerator gen) {
261
266
            this.gen = gen;
262
        TextPaintInfo tpi = (TextPaintInfo) aci.getAttribute(
263
            GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO);
264
265
        if (tpi == null) {
266
            return loc;
267
        }
267
        }
268
268
269
        TextNode.Anchor anchor = (TextNode.Anchor)aci.getAttribute(
269
        public Font selectFontForChar(char ch) {
270
                GVTAttributedCharacterIterator.TextAttribute.ANCHOR_TYPE);
270
            for (int i = 0, c = fonts.length; i < c; i++) {
271
271
                if (fonts[i].hasChar(ch)) {
272
        //Set up font
272
                    return fonts[i];
273
        List gvtFonts = (List)aci.getAttribute(
273
                }
274
                GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES);
274
            }
275
        Paint foreground = tpi.fillPaint;
275
            return fonts[0]; //TODO Maybe fall back to painting with shapes
276
        Paint strokePaint = tpi.strokePaint;
277
        Stroke stroke = tpi.strokeStroke;
278
279
        Float fontSize = (Float)aci.getAttribute(TextAttribute.SIZE);
280
        if (fontSize == null) {
281
            return loc;
282
        }
276
        }
283
        Float posture = (Float)aci.getAttribute(TextAttribute.POSTURE);
284
        Float taWeight = (Float)aci.getAttribute(TextAttribute.WEIGHT);
285
277
286
        if (foreground instanceof Color) {
278
        public void writeTextMatrix(AffineTransform transform) throws IOException {
287
            Color col = (Color)foreground;
279
            double[] matrix = new double[6];
288
            g2d.setColor(col);
280
            transform.getMatrix(matrix);
281
            gen.writeln(gen.formatDouble5(matrix[0]) + " "
282
                + gen.formatDouble5(matrix[1]) + " "
283
                + gen.formatDouble5(matrix[2]) + " "
284
                + gen.formatDouble5(matrix[3]) + " "
285
                + gen.formatDouble5(matrix[4]) + " "
286
                + gen.formatDouble5(matrix[5]) + " Tm");
289
        }
287
        }
290
        g2d.setPaint(foreground);
291
        g2d.setStroke(stroke);
292
288
293
        Font font = makeFont(aci);
289
        public boolean isFontChanging(Font f, char mapped) {
294
        java.awt.Font awtFont = makeAWTFont(aci, font);
290
            if (f != getCurrentFont()) {
295
291
                int encoding = mapped / 256;
296
        g2d.setFont(awtFont);
292
                if (encoding != getCurrentFontEncoding()) {
297
293
                    return true; //Font is changing
298
        String txt = getText(aci);
294
                }
299
        float advance = getStringWidth(txt, font);
300
        float tx = 0;
301
        if (anchor != null) {
302
            switch (anchor.getType()) {
303
                case TextNode.Anchor.ANCHOR_MIDDLE:
304
                    tx = -advance / 2;
305
                    break;
306
                case TextNode.Anchor.ANCHOR_END:
307
                    tx = -advance;
308
                    break;
309
                default: //nop
310
            }
295
            }
296
            return false; //Font is the same
311
        }
297
        }
312
298
313
        drawPrimitiveString(g2d, loc, font, txt, tx);
299
        public void selectFont(Font f, char mapped) throws IOException {
314
        loc.setLocation(loc.getX() + advance, loc.getY());
300
            int encoding = mapped / 256;
315
        return loc;
301
            String postfix = (encoding == 0 ? null : Integer.toString(encoding));
316
    }
302
            PSResource res = getResourceForFont(f, postfix);
303
            gen.useFont("/" + res.getName(), f.getFontSize() / 1000f);
304
            gen.getResourceTracker().notifyResourceUsageOnPage(res);
305
        }
317
306
318
    protected void drawPrimitiveString(Graphics2D g2d, Point2D loc, Font font, String txt, float tx) {
307
        public Font getCurrentFont() {
319
        //Finally draw text
308
            return this.currentFont;
320
        nativeTextHandler.setOverrideFont(font);
321
        try {
322
            try {
323
                nativeTextHandler.drawString(txt, (float)(loc.getX() + tx), (float)(loc.getY()));
324
            } catch (IOException ioe) {
325
                if (g2d instanceof PSGraphics2D) {
326
                    ((PSGraphics2D)g2d).handleIOException(ioe);
327
                }
328
            }
329
        } finally {
330
            nativeTextHandler.setOverrideFont(null);
331
        }
309
        }
332
    }
333
310
334
    private void updateLocationFromACI(
311
        public int getCurrentFontEncoding() {
335
                AttributedCharacterIterator aci,
312
            return this.currentEncoding;
336
                Point2D loc) {
337
        //Adjust position of span
338
        Float xpos = (Float)aci.getAttribute(
339
                GVTAttributedCharacterIterator.TextAttribute.X);
340
        Float ypos = (Float)aci.getAttribute(
341
                GVTAttributedCharacterIterator.TextAttribute.Y);
342
        Float dxpos = (Float)aci.getAttribute(
343
                GVTAttributedCharacterIterator.TextAttribute.DX);
344
        Float dypos = (Float)aci.getAttribute(
345
                GVTAttributedCharacterIterator.TextAttribute.DY);
346
        if (xpos != null) {
347
            loc.setLocation(xpos.doubleValue(), loc.getY());
348
        }
313
        }
349
        if (ypos != null) {
314
350
            loc.setLocation(loc.getX(), ypos.doubleValue());
315
        public void setCurrentFont(Font font, int encoding) {
316
            this.currentFont = font;
317
            this.currentEncoding = encoding;
351
        }
318
        }
352
        if (dxpos != null) {
319
353
            loc.setLocation(loc.getX() + dxpos.doubleValue(), loc.getY());
320
        public void setCurrentFont(Font font, char mapped) {
321
            int encoding = mapped / 256;
322
            setCurrentFont(font, encoding);
354
        }
323
        }
355
        if (dypos != null) {
324
356
            loc.setLocation(loc.getX(), loc.getY() + dypos.doubleValue());
325
        public void setupFonts(AttributedCharacterIterator runaci) {
326
            this.fonts = findFonts(runaci);
357
        }
327
        }
358
    }
359
328
360
    private String getStyle(AttributedCharacterIterator aci) {
329
        public boolean hasFonts() {
361
        Float posture = (Float)aci.getAttribute(TextAttribute.POSTURE);
330
            return (fonts != null) && (fonts.length > 0);
362
        return ((posture != null) && (posture.floatValue() > 0.0))
331
        }
363
                       ? "italic"
364
                       : "normal";
365
    }
366
332
367
    private int getWeight(AttributedCharacterIterator aci) {
368
        Float taWeight = (Float)aci.getAttribute(TextAttribute.WEIGHT);
369
        return ((taWeight != null) &&  (taWeight.floatValue() > 1.0))
370
                       ? Font.WEIGHT_BOLD
371
                       : Font.WEIGHT_NORMAL;
372
    }
333
    }
373
334
374
    private Font makeFont(AttributedCharacterIterator aci) {
335
    private class PSTextRun {
375
        Float fontSize = (Float)aci.getAttribute(TextAttribute.SIZE);
336
376
        if (fontSize == null) {
337
        private AffineTransform textTransform;
377
            fontSize = new Float(10.0f);
338
        private List relativePositions = new java.util.LinkedList();
339
        private StringBuffer currentChars = new StringBuffer();
340
        private int horizChanges = 0;
341
        private int vertChanges = 0;
342
343
        public void reset() {
344
            textTransform = null;
345
            currentChars.setLength(0);
346
            horizChanges = 0;
347
            vertChanges = 0;
348
            relativePositions.clear();
378
        }
349
        }
379
        String style = getStyle(aci);
380
        int weight = getWeight(aci);
381
350
382
        String fontFamily = null;
351
        public int getHorizRunLength() {
383
        List gvtFonts = (List) aci.getAttribute(
352
            if (this.vertChanges == 0
384
                      GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES);
353
                    && getRunLength() > 0) {
385
        if (gvtFonts != null) {
354
                return getRunLength();
386
            Iterator i = gvtFonts.iterator();
387
            while (i.hasNext()) {
388
                GVTFontFamily fam = (GVTFontFamily) i.next();
389
                /* (todo) Enable SVG Font painting
390
                if (fam instanceof SVGFontFamily) {
391
                    PROXY_PAINTER.paint(node, g2d);
392
                    return;
393
                }*/
394
                fontFamily = fam.getFamilyName();
395
                if (fontInfo.hasFont(fontFamily, style, weight)) {
396
                    FontTriplet triplet = fontInfo.fontLookup(
397
                            fontFamily, style, weight);
398
                    int fsize = (int)(fontSize.floatValue() * 1000);
399
                    return fontInfo.getFontInstance(triplet, fsize);
400
                }
401
            }
355
            }
356
            return 0;
402
        }
357
        }
403
        FontTriplet triplet = fontInfo.fontLookup("any", style, Font.WEIGHT_NORMAL);
404
        int fsize = (int)(fontSize.floatValue() * 1000);
405
        return fontInfo.getFontInstance(triplet, fsize);
406
    }
407
358
408
    private java.awt.Font makeAWTFont(AttributedCharacterIterator aci, Font font) {
359
        public void addCharacter(char paintChar, Point2D relPos) {
409
        final String style = getStyle(aci);
360
            addRelativePosition(relPos);
410
        final int weight = getWeight(aci);
361
            currentChars.append(paintChar);
411
        int fStyle = java.awt.Font.PLAIN;
412
        if (weight == Font.WEIGHT_BOLD) {
413
            fStyle |= java.awt.Font.BOLD;
414
        }
362
        }
415
        if ("italic".equals(style)) {
416
            fStyle |= java.awt.Font.ITALIC;
417
        }
418
        return new java.awt.Font(font.getFontName(), fStyle,
419
                             (font.getFontSize() / 1000));
420
    }
421
363
422
    private float getStringWidth(String str, Font font) {
364
        private void addRelativePosition(Point2D relPos) {
423
        float wordWidth = 0;
365
            if (getRunLength() > 0) {
424
        float whitespaceWidth = font.getWidth(font.mapChar(' '));
366
                if (relPos.getX() != 0) {
425
367
                    horizChanges++;
426
        for (int i = 0; i < str.length(); i++) {
427
            float charWidth;
428
            char c = str.charAt(i);
429
            if (!((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t'))) {
430
                charWidth = font.getWidth(font.mapChar(c));
431
                if (charWidth <= 0) {
432
                    charWidth = whitespaceWidth;
433
                }
368
                }
434
            } else {
369
                if (relPos.getY() != 0) {
435
                charWidth = whitespaceWidth;
370
                    vertChanges++;
371
                }
436
            }
372
            }
437
            wordWidth += charWidth;
373
            relativePositions.add(relPos);
438
        }
374
        }
439
        return wordWidth / 1000f;
440
    }
441
375
442
    private boolean hasUnsupportedGlyphs(String str, Font font) {
376
        public void noteStartingTransformation(AffineTransform transform) {
443
        for (int i = 0; i < str.length(); i++) {
377
            if (textTransform == null) {
444
            float charWidth;
378
                this.textTransform = new AffineTransform(transform);
445
            char c = str.charAt(i);
446
            if (!((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t'))) {
447
                if (!font.hasChar(c)) {
448
                    return true;
449
                }
450
            }
379
            }
451
        }
380
        }
452
        return false;
453
    }
454
381
455
    /**
382
        public int getRunLength() {
456
     * Get the outline shape of the text characters.
383
            return currentChars.length();
457
     * This uses the StrokingTextPainter to get the outline
384
        }
458
     * shape since in theory it should be the same.
459
     *
460
     * @param node the text node
461
     * @return the outline shape of the text characters
462
     */
463
    public Shape getOutline(TextNode node) {
464
        return PROXY_PAINTER.getOutline(node);
465
    }
466
385
467
    /**
386
        private boolean isXShow() {
468
     * Get the bounds.
387
            return vertChanges == 0;
469
     * This uses the StrokingTextPainter to get the bounds
388
        }
470
     * since in theory it should be the same.
471
     *
472
     * @param node the text node
473
     * @return the bounds of the text
474
     */
475
    public Rectangle2D getBounds2D(TextNode node) {
476
        /* (todo) getBounds2D() is too slow
477
         * because it uses the StrokingTextPainter. We should implement this
478
         * method ourselves. */
479
        return PROXY_PAINTER.getBounds2D(node);
480
    }
481
389
482
    /**
390
        private boolean isYShow() {
483
     * Get the geometry bounds.
391
            return horizChanges == 0;
484
     * This uses the StrokingTextPainter to get the bounds
392
        }
485
     * since in theory it should be the same.
486
     * @param node the text node
487
     * @return the bounds of the text
488
     */
489
    public Rectangle2D getGeometryBounds(TextNode node) {
490
        return PROXY_PAINTER.getGeometryBounds(node);
491
    }
492
393
493
    // Methods that have no purpose for PS
394
        public void paint(PSGraphics2D g2d, TextUtil textUtil, TextPaintInfo tpi)
395
                    throws IOException {
396
            if (getRunLength() > 0) {
397
                if (log.isDebugEnabled()) {
398
                    log.debug("Text run: " + currentChars);
399
                }
400
                textUtil.writeTextMatrix(this.textTransform);
401
                if (isXShow()) {
402
                    log.debug("Horizontal text: xshow");
403
                    paintXYShow(g2d, textUtil, tpi.fillPaint, true, false);
404
                } else if (isYShow()) {
405
                    log.debug("Vertical text: yshow");
406
                    paintXYShow(g2d, textUtil, tpi.fillPaint, false, true);
407
                } else {
408
                    log.debug("Arbitrary text: xyshow");
409
                    paintXYShow(g2d, textUtil, tpi.fillPaint, true, true);
410
                }
411
                boolean stroke = (tpi.strokePaint != null) && (tpi.strokeStroke != null);
412
                if (stroke) {
413
                    log.debug("Stroked glyph outlines");
414
                    paintStrokedGlyphs(g2d, textUtil, tpi.strokePaint, tpi.strokeStroke);
415
                }
416
            }
417
        }
494
418
495
    /**
419
        private void paintXYShow(PSGraphics2D g2d, TextUtil textUtil, Paint paint,
496
     * Get the mark.
420
                boolean x, boolean y) throws IOException {
497
     * This does nothing since the output is pdf and not interactive.
421
            PSGenerator gen = textUtil.gen;
498
     * @param node the text node
422
            char firstChar = this.currentChars.charAt(0);
499
     * @param pos the position
423
            //Font only has to be setup up before the first character
500
     * @param all select all
424
            Font f = textUtil.selectFontForChar(firstChar);
501
     * @return null
425
            char mapped = f.mapChar(firstChar);
502
     */
426
            textUtil.selectFont(f, mapped);
503
    public Mark getMark(TextNode node, int pos, boolean all) {
427
            textUtil.setCurrentFont(f, mapped);
504
        return null;
428
            applyColor(paint, gen);
505
    }
506
429
507
    /**
430
            StringBuffer sb = new StringBuffer();
508
     * Select at.
431
            sb.append('(');
509
     * This does nothing since the output is pdf and not interactive.
432
            for (int i = 0, c = this.currentChars.length(); i < c; i++) {
510
     * @param x the x position
433
                char ch = this.currentChars.charAt(i);
511
     * @param y the y position
434
                mapped = f.mapChar(ch);
512
     * @param node the text node
435
                PSGenerator.escapeChar(mapped, sb);
513
     * @return null
436
            }
514
     */
437
            sb.append(')');
515
    public Mark selectAt(double x, double y, TextNode node) {
438
            if (x || y) {
516
        return null;
439
                sb.append("\n[");
517
    }
440
                int idx = 0;
441
                Iterator iter = this.relativePositions.iterator();
442
                while (iter.hasNext()) {
443
                    Point2D pt = (Point2D)iter.next();
444
                    if (idx > 0) {
445
                        if (x) {
446
                            sb.append(format(gen, pt.getX()));
447
                        }
448
                        if (y) {
449
                            if (x) {
450
                                sb.append(' ');
451
                            }
452
                            sb.append(format(gen, -pt.getY()));
453
                        }
454
                        if (idx % 8 == 0) {
455
                            sb.append('\n');
456
                        } else {
457
                            sb.append(' ');
458
                        }
459
                    }
460
                    idx++;
461
                }
462
                if (x) {
463
                    sb.append('0');
464
                }
465
                if (y) {
466
                    if (x) {
467
                        sb.append(' ');
468
                    }
469
                    sb.append('0');
470
                }
471
                sb.append(']');
472
            }
473
            sb.append(' ');
474
            if (x) {
475
                sb.append('x');
476
            }
477
            if (y) {
478
                sb.append('y');
479
            }
480
            sb.append("show"); // --> xshow, yshow or xyshow
481
            gen.writeln(sb.toString());
482
        }
518
483
519
    /**
484
        private String format(PSGenerator gen, double coord) {
520
     * Select to.
485
            if (Math.abs(coord) < 0.00001) {
521
     * This does nothing since the output is pdf and not interactive.
486
                return "0";
522
     * @param x the x position
487
            } else {
523
     * @param y the y position
488
                return gen.formatDouble5(coord);
524
     * @param beginMark the start mark
489
            }
525
     * @return null
490
        }
526
     */
527
    public Mark selectTo(double x, double y, Mark beginMark) {
528
        return null;
529
    }
530
491
531
    /**
492
        private void paintStrokedGlyphs(PSGraphics2D g2d, TextUtil textUtil,
532
     * Selec first.
493
                Paint strokePaint, Stroke stroke) throws IOException {
533
     * This does nothing since the output is pdf and not interactive.
494
            PSGenerator gen = textUtil.gen;
534
     * @param node the text node
535
     * @return null
536
     */
537
    public Mark selectFirst(TextNode node) {
538
        return null;
539
    }
540
495
541
    /**
496
            applyColor(strokePaint, gen);
542
     * Select last.
497
            PSGraphics2D.applyStroke(stroke, gen);
543
     * This does nothing since the output is pdf and not interactive.
544
     * @param node the text node
545
     * @return null
546
     */
547
    public Mark selectLast(TextNode node) {
548
        return null;
549
    }
550
498
551
    /**
499
            Font f = null;
552
     * Get selected.
500
            Iterator iter = this.relativePositions.iterator();
553
     * This does nothing since the output is pdf and not interactive.
501
            iter.next();
554
     * @param start the start mark
502
            Point2D pos = new Point2D.Double(0, 0);
555
     * @param finish the finish mark
503
            gen.writeln("0 0 M");
556
     * @return null
504
            for (int i = 0, c = this.currentChars.length(); i < c; i++) {
557
     */
505
                char ch = this.currentChars.charAt(0);
558
    public int[] getSelected(Mark start, Mark finish) {
506
                if (i == 0) {
559
        return null;
507
                    //Font only has to be setup up before the first character
560
    }
508
                    f = textUtil.selectFontForChar(ch);
509
                }
510
                char mapped = f.mapChar(ch);
511
                if (i == 0) {
512
                    textUtil.selectFont(f, mapped);
513
                    textUtil.setCurrentFont(f, mapped);
514
                }
515
                mapped = f.mapChar(this.currentChars.charAt(i));
516
                //add glyph outlines to current path
517
                char codepoint = (char)(mapped % 256);
518
                gen.write("(" + codepoint + ")");
519
                gen.writeln(" false charpath");
561
520
562
    /**
521
                if (iter.hasNext()) {
563
     * Get the highlighted shape.
522
                    //Position for the next character
564
     * This does nothing since the output is pdf and not interactive.
523
                    Point2D pt = (Point2D)iter.next();
565
     * @param beginMark the start mark
524
                    pos.setLocation(pos.getX() + pt.getX(), pos.getY() - pt.getY());
566
     * @param endMark the end mark
525
                    gen.writeln(gen.formatDouble5(pos.getX()) + " "
567
     * @return null
526
                            + gen.formatDouble5(pos.getY()) + " M");
568
     */
527
                }
569
    public Shape getHighlightShape(Mark beginMark, Mark endMark) {
528
            }
570
        return null;
529
            gen.writeln("stroke"); //paints all accumulated glyph outlines
530
        }
531
571
    }
532
    }
572
533
573
}
534
}
(-)src/java/org/apache/fop/render/ps/PSTextElementBridge.java (-55 / +8 lines)
Lines 19-32 Link Here
19
19
20
package org.apache.fop.render.ps;
20
package org.apache.fop.render.ps;
21
21
22
import org.apache.batik.bridge.SVGTextElementBridge;
22
import org.w3c.dom.Element;
23
23
import org.apache.batik.bridge.BridgeContext;
24
import org.apache.batik.bridge.BridgeContext;
25
import org.apache.batik.bridge.SVGTextElementBridge;
24
import org.apache.batik.gvt.GraphicsNode;
26
import org.apache.batik.gvt.GraphicsNode;
25
import org.apache.batik.gvt.TextNode;
27
import org.apache.batik.gvt.TextNode;
28
import org.apache.batik.gvt.TextPainter;
26
29
27
import org.w3c.dom.Element;
28
import org.w3c.dom.Node;
29
30
/**
30
/**
31
 * Bridge class for the &lt;text> element.
31
 * Bridge class for the &lt;text> element.
32
 * This bridge will use the direct text painter if the text
32
 * This bridge will use the direct text painter if the text
Lines 37-49 Link Here
37
 */
37
 */
38
public class PSTextElementBridge extends SVGTextElementBridge {
38
public class PSTextElementBridge extends SVGTextElementBridge {
39
39
40
    private PSTextPainter textPainter;
40
    private TextPainter textPainter;
41
41
42
    /**
42
    /**
43
     * Constructs a new bridge for the &lt;text> element.
43
     * Constructs a new bridge for the &lt;text> element.
44
     * @param textPainter the text painter to use
44
     * @param textPainter the text painter to use
45
     */
45
     */
46
    public PSTextElementBridge(PSTextPainter textPainter) {
46
    public PSTextElementBridge(TextPainter textPainter) {
47
        this.textPainter = textPainter;
47
        this.textPainter = textPainter;
48
    }
48
    }
49
49
Lines 56-115 Link Here
56
     */
56
     */
57
    public GraphicsNode createGraphicsNode(BridgeContext ctx, Element e) {
57
    public GraphicsNode createGraphicsNode(BridgeContext ctx, Element e) {
58
        GraphicsNode node = super.createGraphicsNode(ctx, e);
58
        GraphicsNode node = super.createGraphicsNode(ctx, e);
59
        /* this code is worthless I think. PSTextPainter does a much better job
59
        ((TextNode)node).setTextPainter(getTextPainter());
60
         * at determining whether to stroke or not. */
61
        if (true/*node != null && isSimple(ctx, e, node)*/) {
62
            ((TextNode)node).setTextPainter(getTextPainter());
63
        }
64
        return node;
60
        return node;
65
    }
61
    }
66
62
67
    private PSTextPainter getTextPainter() {
63
    private TextPainter getTextPainter() {
68
        return this.textPainter;
64
        return this.textPainter;
69
    }
65
    }
70
66
71
    /**
72
     * Check if text element contains simple text.
73
     * This checks the children of the text element to determine
74
     * if the text is simple. The text is simple if it can be rendered
75
     * with basic text drawing algorithms. This means there are no
76
     * alternate characters, the font is known and there are no effects
77
     * applied to the text.
78
     *
79
     * @param ctx the bridge context
80
     * @param element the svg text element
81
     * @param node the graphics node
82
     * @return true if this text is simple of false if it cannot be
83
     *         easily rendered using normal drawString on the PDFGraphics2D
84
     */
85
    private boolean isSimple(BridgeContext ctx, Element element, GraphicsNode node) {
86
        for (Node n = element.getFirstChild();
87
                n != null;
88
                n = n.getNextSibling()) {
89
90
            switch (n.getNodeType()) {
91
            case Node.ELEMENT_NODE:
92
93
                if (n.getLocalName().equals(SVG_TSPAN_TAG)
94
                        || n.getLocalName().equals(SVG_ALT_GLYPH_TAG)) {
95
                    return false;
96
                } else if (n.getLocalName().equals(SVG_TEXT_PATH_TAG)) {
97
                    return false;
98
                } else if (n.getLocalName().equals(SVG_TREF_TAG)) {
99
                    return false;
100
                }
101
                break;
102
            case Node.TEXT_NODE:
103
            case Node.CDATA_SECTION_NODE:
104
            default:
105
            }
106
        }
107
108
        /*if (CSSUtilities.convertFilter(element, node, ctx) != null) {
109
            return false;
110
        }*/
111
112
        return true;
113
    }
114
}
67
}
115
68
(-)src/java/org/apache/fop/render/ps/PSSVGHandler.java (-11 / +4 lines)
Lines 259-275 Link Here
259
        PSGraphics2D graphics = new PSGraphics2D(strokeText, gen);
259
        PSGraphics2D graphics = new PSGraphics2D(strokeText, gen);
260
        graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext());
260
        graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext());
261
261
262
        NativeTextHandler nativeTextHandler = null;
262
        BridgeContext ctx = new PSBridgeContext(ua,
263
        BridgeContext ctx = new BridgeContext(ua);
263
                (strokeText ? null : psInfo.fontInfo),
264
        if (!strokeText) {
264
                context.getUserAgent().getFactory().getImageManager(),
265
            FontInfo fontInfo = psInfo.getFontInfo();
265
                context.getUserAgent().getImageSessionContext());
266
            nativeTextHandler = new NativeTextHandler(graphics, fontInfo);
267
            graphics.setCustomTextHandler(nativeTextHandler);
268
            PSTextPainter textPainter = new PSTextPainter(nativeTextHandler);
269
            ctx.setTextPainter(textPainter);
270
            PSTextElementBridge tBridge = new PSTextElementBridge(textPainter);
271
            ctx.putBridge(tBridge);
272
        }
273
266
274
        //Cloning SVG DOM as Batik attaches non-thread-safe facilities (like the CSS engine)
267
        //Cloning SVG DOM as Batik attaches non-thread-safe facilities (like the CSS engine)
275
        //to it.
268
        //to it.
(-)src/java/org/apache/fop/render/ps/PSSVGFlowRootElementBridge.java (+86 lines)
Line 0 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
/* $Id$ */
19
20
package org.apache.fop.render.ps;
21
22
import java.text.AttributedCharacterIterator;
23
import java.util.List;
24
25
import org.apache.batik.bridge.svg12.SVGFlowRootElementBridge;
26
import org.apache.batik.gvt.GraphicsNode;
27
import org.apache.batik.gvt.TextNode;
28
import org.apache.batik.gvt.TextPainter;
29
import org.apache.batik.gvt.flow.FlowTextPainter;
30
31
import org.apache.fop.fonts.FontInfo;
32
33
/**
34
 * Element Bridge for SVG 1.2 flow text, so those texts can be painted using
35
 * PDF primitives.
36
 */
37
public class PSSVGFlowRootElementBridge extends SVGFlowRootElementBridge {
38
39
    private PSTextPainter textPainter;
40
41
    /**
42
     * Main Constructor.
43
     * @param fontInfo the font directory
44
     */
45
    public PSSVGFlowRootElementBridge(FontInfo fontInfo) {
46
        this.textPainter = new PSFlowTextPainter(fontInfo);
47
    }
48
49
    /** {@inheritDoc} */
50
    protected GraphicsNode instantiateGraphicsNode() {
51
        GraphicsNode node = super.instantiateGraphicsNode();
52
        if (node != null) {
53
            //Set our own text painter
54
            ((TextNode)node).setTextPainter(getTextPainter());
55
        }
56
        return node;
57
    }
58
59
    /**
60
     * Returns the text painter used by this bridge.
61
     * @return the text painter
62
     */
63
    public TextPainter getTextPainter() {
64
        return this.textPainter;
65
    }
66
67
    private class PSFlowTextPainter extends PSTextPainter {
68
69
        /**
70
         * Main constructor
71
         * @param fontInfo the font directory
72
         */
73
        public PSFlowTextPainter(FontInfo fontInfo) {
74
            super(fontInfo);
75
        }
76
77
        /** {@inheritDoc} */
78
        public List getTextRuns(TextNode node, AttributedCharacterIterator aci) {
79
            //Text runs are delegated to the normal FlowTextPainter, we just paint the text.
80
            FlowTextPainter delegate = (FlowTextPainter)FlowTextPainter.getInstance();
81
            return delegate.getTextRuns(node, aci);
82
        }
83
84
    }
85
86
}
0
  + Id
87
  + Id
1
  + native
88
  + native
(-)src/java/org/apache/fop/render/ps/PSPainter.java (-4 / +1 lines)
Lines 350-356 Link Here
350
            //TODO Opportunity for font caching if font state is more heavily used
350
            //TODO Opportunity for font caching if font state is more heavily used
351
            String fontKey = getFontInfo().getInternalFontKey(triplet);
351
            String fontKey = getFontInfo().getInternalFontKey(triplet);
352
            int sizeMillipoints = state.getFontSize();
352
            int sizeMillipoints = state.getFontSize();
353
            float fontSize = sizeMillipoints / 1000f;
354
353
355
            // This assumes that *all* CIDFonts use a /ToUnicode mapping
354
            // This assumes that *all* CIDFonts use a /ToUnicode mapping
356
            Typeface tf = getTypeface(fontKey);
355
            Typeface tf = getTypeface(fontKey);
Lines 360-368 Link Here
360
            }
359
            }
361
            Font font = getFontInfo().getFontInstance(triplet, sizeMillipoints);
360
            Font font = getFontInfo().getFontInstance(triplet, sizeMillipoints);
362
361
363
            PSResource res = this.documentHandler.getPSResourceForFontKey(fontKey);
362
            useFont(fontKey, sizeMillipoints);
364
            generator.useFont("/" + res.getName(), fontSize);
365
            generator.getResourceTracker().notifyResourceUsageOnPage(res);
366
363
367
            generator.writeln("1 0 0 -1 " + formatMptAsPt(generator, x)
364
            generator.writeln("1 0 0 -1 " + formatMptAsPt(generator, x)
368
                    + " " + formatMptAsPt(generator, y) + " Tm");
365
                    + " " + formatMptAsPt(generator, y) + " Tm");
(-)src/java/org/apache/fop/render/ps/PSImageHandlerSVG.java (-10 / +4 lines)
Lines 65-80 Link Here
65
        PSGraphics2D graphics = new PSGraphics2D(strokeText, gen);
65
        PSGraphics2D graphics = new PSGraphics2D(strokeText, gen);
66
        graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext());
66
        graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext());
67
67
68
        NativeTextHandler nativeTextHandler = null;
68
        BridgeContext ctx = new PSBridgeContext(ua,
69
        BridgeContext ctx = new BridgeContext(ua);
69
                (strokeText ? null : psContext.getFontInfo()),
70
        if (!strokeText) {
70
                context.getUserAgent().getFactory().getImageManager(),
71
            nativeTextHandler = new NativeTextHandler(graphics, psContext.getFontInfo());
71
                context.getUserAgent().getImageSessionContext());
72
            graphics.setCustomTextHandler(nativeTextHandler);
73
            PSTextPainter textPainter = new PSTextPainter(nativeTextHandler);
74
            ctx.setTextPainter(textPainter);
75
            PSTextElementBridge tBridge = new PSTextElementBridge(textPainter);
76
            ctx.putBridge(tBridge);
77
        }
78
72
79
        GraphicsNode root;
73
        GraphicsNode root;
80
        try {
74
        try {
(-)src/java/org/apache/fop/render/ps/PSDocumentHandler.java (-38 / +5 lines)
Lines 50-57 Link Here
50
import org.apache.xmlgraphics.ps.dsc.events.DSCCommentHiResBoundingBox;
50
import org.apache.xmlgraphics.ps.dsc.events.DSCCommentHiResBoundingBox;
51
51
52
import org.apache.fop.apps.MimeConstants;
52
import org.apache.fop.apps.MimeConstants;
53
import org.apache.fop.fonts.LazyFont;
54
import org.apache.fop.fonts.Typeface;
55
import org.apache.fop.render.intermediate.AbstractBinaryWritingIFDocumentHandler;
53
import org.apache.fop.render.intermediate.AbstractBinaryWritingIFDocumentHandler;
56
import org.apache.fop.render.intermediate.IFContext;
54
import org.apache.fop.render.intermediate.IFContext;
57
import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator;
55
import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator;
Lines 91-98 Link Here
91
    /** Used to temporarily store PSSetupCode instance until they can be written. */
89
    /** Used to temporarily store PSSetupCode instance until they can be written. */
92
    private List setupCodeList;
90
    private List setupCodeList;
93
91
94
    /** This is a map of PSResource instances of all fonts defined (key: font key) */
92
    /** This is a cache of PSResource instances of all fonts defined */
95
    private Map fontResources;
93
    private FontResourceCache fontResources;
96
    /** This is a map of PSResource instances of all forms (key: uri) */
94
    /** This is a map of PSResource instances of all forms (key: uri) */
97
    private Map formResources;
95
    private Map formResources;
98
96
Lines 139-144 Link Here
139
    /** {@inheritDoc} */
137
    /** {@inheritDoc} */
140
    public void startDocument() throws IFException {
138
    public void startDocument() throws IFException {
141
        super.startDocument();
139
        super.startDocument();
140
        this.fontResources = new FontResourceCache(getFontInfo());
142
        try {
141
        try {
143
            OutputStream out;
142
            OutputStream out;
144
            if (psUtil.isOptimizeResources()) {
143
            if (psUtil.isOptimizeResources()) {
Lines 200-206 Link Here
200
        gen.writeDSCComment(DSCConstants.BEGIN_SETUP);
199
        gen.writeDSCComment(DSCConstants.BEGIN_SETUP);
201
        PSRenderingUtil.writeSetupCodeList(gen, setupCodeList, "SetupCode");
200
        PSRenderingUtil.writeSetupCodeList(gen, setupCodeList, "SetupCode");
202
        if (!psUtil.isOptimizeResources()) {
201
        if (!psUtil.isOptimizeResources()) {
203
            this.fontResources = PSFontUtils.writeFontDict(gen, fontInfo);
202
            this.fontResources.addAll(PSFontUtils.writeFontDict(gen, fontInfo));
204
        } else {
203
        } else {
205
            gen.commentln("%FOPFontSetup"); //Place-holder, will be replaced in the second pass
204
            gen.commentln("%FOPFontSetup"); //Place-holder, will be replaced in the second pass
206
        }
205
        }
Lines 534-578 Link Here
534
        }
533
        }
535
    }
534
    }
536
535
537
    private String getPostScriptNameForFontKey(String key) {
538
        int pos = key.indexOf('_');
539
        String postFix = null;
540
        if (pos > 0) {
541
            postFix = key.substring(pos);
542
            key = key.substring(0, pos);
543
        }
544
        Map fonts = fontInfo.getFonts();
545
        Typeface tf = (Typeface)fonts.get(key);
546
        if (tf instanceof LazyFont) {
547
            tf = ((LazyFont)tf).getRealFont();
548
        }
549
        if (tf == null) {
550
            throw new IllegalStateException("Font not available: " + key);
551
        }
552
        if (postFix == null) {
553
            return tf.getFontName();
554
        } else {
555
            return tf.getFontName() + postFix;
556
        }
557
    }
558
559
    /**
536
    /**
560
     * Returns the PSResource for the given font key.
537
     * Returns the PSResource for the given font key.
561
     * @param key the font key ("F*")
538
     * @param key the font key ("F*")
562
     * @return the matching PSResource
539
     * @return the matching PSResource
563
     */
540
     */
564
    protected PSResource getPSResourceForFontKey(String key) {
541
    protected PSResource getPSResourceForFontKey(String key) {
565
        PSResource res = null;
542
        return this.fontResources.getPSResourceForFontKey(key);
566
        if (this.fontResources != null) {
567
            res = (PSResource)this.fontResources.get(key);
568
        } else {
569
            this.fontResources = new java.util.HashMap();
570
        }
571
        if (res == null) {
572
            res = new PSResource(PSResource.TYPE_FONT, getPostScriptNameForFontKey(key));
573
            this.fontResources.put(key, res);
574
        }
575
        return res;
576
    }
543
    }
577
544
578
    /**
545
    /**
(-)src/java/org/apache/fop/render/ps/PSBridgeContext.java (+113 lines)
Line 0 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
/* $Id$ */
19
20
package org.apache.fop.render.ps;
21
22
import java.awt.geom.AffineTransform;
23
24
import org.apache.batik.bridge.BridgeContext;
25
import org.apache.batik.bridge.DocumentLoader;
26
import org.apache.batik.bridge.SVGTextElementBridge;
27
import org.apache.batik.bridge.UserAgent;
28
import org.apache.batik.gvt.TextPainter;
29
30
import org.apache.xmlgraphics.image.loader.ImageManager;
31
import org.apache.xmlgraphics.image.loader.ImageSessionContext;
32
33
import org.apache.fop.fonts.FontInfo;
34
import org.apache.fop.svg.AbstractFOPBridgeContext;
35
36
/**
37
 * BridgeContext which registers the custom bridges for PostScript output.
38
 */
39
public class PSBridgeContext extends AbstractFOPBridgeContext {
40
41
    /**
42
     * Constructs a new bridge context.
43
     * @param userAgent the user agent
44
     * @param documentLoader the Document Loader to use for referenced documents.
45
     * @param fontInfo the font list for the text painter, may be null
46
     *                 in which case text is painted as shapes
47
     * @param imageManager an image manager
48
     * @param imageSessionContext an image session context
49
     * @param linkTransform AffineTransform to properly place links,
50
     *                      may be null
51
     */
52
    public PSBridgeContext(UserAgent userAgent, DocumentLoader documentLoader,
53
            FontInfo fontInfo, ImageManager imageManager,
54
            ImageSessionContext imageSessionContext,
55
            AffineTransform linkTransform) {
56
        super(userAgent, documentLoader, fontInfo,
57
                imageManager, imageSessionContext, linkTransform);
58
    }
59
60
    /**
61
     * Constructs a new bridge context.
62
     * @param userAgent the user agent
63
     * @param fontInfo the font list for the text painter, may be null
64
     *                 in which case text is painted as shapes
65
     * @param imageManager an image manager
66
     * @param imageSessionContext an image session context
67
     */
68
    public PSBridgeContext(UserAgent userAgent, FontInfo fontInfo,
69
            ImageManager imageManager, ImageSessionContext imageSessionContext) {
70
        super(userAgent, fontInfo, imageManager, imageSessionContext);
71
    }
72
73
    /** {@inheritDoc} */
74
    public void registerSVGBridges() {
75
        super.registerSVGBridges();
76
77
        if (fontInfo != null) {
78
            TextPainter textPainter = new PSTextPainter(fontInfo);
79
            SVGTextElementBridge textElementBridge = new PSTextElementBridge(textPainter);
80
            putBridge(textElementBridge);
81
82
            //Batik flow text extension (may not always be available)
83
            //putBridge(new PDFBatikFlowTextElementBridge(fontInfo);
84
            putElementBridgeConditional(
85
                    "org.apache.fop.render.ps.PSBatikFlowTextElementBridge",
86
                    "org.apache.batik.extension.svg.BatikFlowTextElementBridge");
87
88
            //SVG 1.2 flow text support
89
            //putBridge(new PDFSVG12TextElementBridge(fontInfo)); //-->Batik 1.7
90
            putElementBridgeConditional(
91
                    "org.apache.fop.render.ps.PSSVG12TextElementBridge",
92
                    "org.apache.batik.bridge.svg12.SVG12TextElementBridge");
93
94
            //putBridge(new PDFSVGFlowRootElementBridge(fontInfo));
95
            putElementBridgeConditional(
96
                    "org.apache.fop.render.ps.PSSVGFlowRootElementBridge",
97
                    "org.apache.batik.bridge.svg12.SVGFlowRootElementBridge");
98
        }
99
100
        //putBridge(new PSImageElementBridge()); //TODO uncomment when implemented
101
    }
102
103
    // Make sure any 'sub bridge contexts' also have our bridges.
104
    //TODO There's no matching method in the super-class here
105
    public BridgeContext createBridgeContext() {
106
        return new PSBridgeContext(getUserAgent(), getDocumentLoader(),
107
                                    fontInfo,
108
                                    getImageManager(),
109
                                    getImageSessionContext(),
110
                                    linkTransform);
111
    }
112
113
}
0
  + Id
114
  + Id
1
  + native
115
  + native
(-)src/java/org/apache/fop/render/ps/PSBatikFlowTextElementBridge.java (+86 lines)
Line 0 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
/* $Id$ */
19
20
package org.apache.fop.render.ps;
21
22
import java.text.AttributedCharacterIterator;
23
import java.util.List;
24
25
import org.apache.batik.extension.svg.BatikFlowTextElementBridge;
26
import org.apache.batik.extension.svg.FlowExtTextPainter;
27
import org.apache.batik.gvt.GraphicsNode;
28
import org.apache.batik.gvt.TextNode;
29
import org.apache.batik.gvt.TextPainter;
30
31
import org.apache.fop.fonts.FontInfo;
32
33
/**
34
 * Element Bridge for Batik's flow text extension, so those texts can be painted using
35
 * PostScript primitives.
36
 */
37
public class PSBatikFlowTextElementBridge extends BatikFlowTextElementBridge {
38
39
    private PSTextPainter textPainter;
40
41
    /**
42
     * Main Constructor.
43
     * @param fontInfo the font directory
44
     */
45
    public PSBatikFlowTextElementBridge(FontInfo fontInfo) {
46
        this.textPainter = new PSFlowExtTextPainter(fontInfo);
47
    }
48
49
    /** {@inheritDoc} */
50
    protected GraphicsNode instantiateGraphicsNode() {
51
        GraphicsNode node = super.instantiateGraphicsNode();
52
        if (node != null) {
53
            //Set our own text painter
54
            ((TextNode)node).setTextPainter(getTextPainter());
55
        }
56
        return node;
57
    }
58
59
    /**
60
     * Returns the text painter used by this bridge.
61
     * @return the text painter
62
     */
63
    public TextPainter getTextPainter() {
64
        return this.textPainter;
65
    }
66
67
    private class PSFlowExtTextPainter extends PSTextPainter {
68
69
        /**
70
         * Main constructor
71
         * @param fontInfo the font directory
72
         */
73
        public PSFlowExtTextPainter(FontInfo fontInfo) {
74
            super(fontInfo);
75
        }
76
77
        /** {@inheritDoc} */
78
        public List getTextRuns(TextNode node, AttributedCharacterIterator aci) {
79
            //Text runs are delegated to the normal FlowExtTextPainter, we just paint the text.
80
            FlowExtTextPainter delegate = (FlowExtTextPainter)FlowExtTextPainter.getInstance();
81
            return delegate.getTextRuns(node, aci);
82
        }
83
84
    }
85
86
}
0
  + Id
87
  + Id
1
  + native
88
  + native
(-)src/java/org/apache/fop/render/ps/FontResourceCache.java (+93 lines)
Line 0 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
/* $Id$ */
19
20
package org.apache.fop.render.ps;
21
22
import java.util.Map;
23
24
import org.apache.xmlgraphics.ps.PSResource;
25
26
import org.apache.fop.fonts.FontInfo;
27
import org.apache.fop.fonts.LazyFont;
28
import org.apache.fop.fonts.Typeface;
29
30
/**
31
 * A cache for font resource objects.
32
 */
33
class FontResourceCache {
34
35
    private FontInfo fontInfo;
36
37
    /** This is a map of PSResource instances of all fonts defined (key: font key) */
38
    private Map fontResources = new java.util.HashMap();
39
40
    public FontResourceCache(FontInfo fontInfo) {
41
        this.fontInfo = fontInfo;
42
    }
43
44
    /**
45
     * Returns the PSResource for the given font key.
46
     * @param key the font key ("F*")
47
     * @return the matching PSResource
48
     */
49
    public PSResource getPSResourceForFontKey(String key) {
50
        PSResource res = null;
51
        if (this.fontResources != null) {
52
            res = (PSResource)this.fontResources.get(key);
53
        } else {
54
            this.fontResources = new java.util.HashMap();
55
        }
56
        if (res == null) {
57
            res = new PSResource(PSResource.TYPE_FONT, getPostScriptNameForFontKey(key));
58
            this.fontResources.put(key, res);
59
        }
60
        return res;
61
    }
62
63
    private String getPostScriptNameForFontKey(String key) {
64
        int pos = key.indexOf('_');
65
        String postFix = null;
66
        if (pos > 0) {
67
            postFix = key.substring(pos);
68
            key = key.substring(0, pos);
69
        }
70
        Map fonts = fontInfo.getFonts();
71
        Typeface tf = (Typeface)fonts.get(key);
72
        if (tf instanceof LazyFont) {
73
            tf = ((LazyFont)tf).getRealFont();
74
        }
75
        if (tf == null) {
76
            throw new IllegalStateException("Font not available: " + key);
77
        }
78
        if (postFix == null) {
79
            return tf.getFontName();
80
        } else {
81
            return tf.getFontName() + postFix;
82
        }
83
    }
84
85
    /**
86
     * Adds a number of fonts to the cache.
87
     * @param fontMap the font map
88
     */
89
    public void addAll(Map fontMap) {
90
        this.fontResources.putAll(fontMap);
91
    }
92
93
}
0
  + Id
94
  + Id
1
  + native
95
  + native
(-)src/java/org/apache/fop/render/ps/AbstractPSTranscoder.java (-23 / +23 lines)
Lines 28-47 Link Here
28
import org.w3c.dom.Document;
28
import org.w3c.dom.Document;
29
import org.w3c.dom.svg.SVGLength;
29
import org.w3c.dom.svg.SVGLength;
30
30
31
import org.apache.avalon.framework.configuration.Configuration;
32
import org.apache.batik.bridge.BridgeContext;
31
import org.apache.batik.bridge.BridgeContext;
33
import org.apache.batik.bridge.UnitProcessor;
32
import org.apache.batik.bridge.UnitProcessor;
34
import org.apache.batik.transcoder.TranscoderException;
33
import org.apache.batik.transcoder.TranscoderException;
35
import org.apache.batik.transcoder.TranscoderOutput;
34
import org.apache.batik.transcoder.TranscoderOutput;
36
import org.apache.batik.transcoder.image.ImageTranscoder;
35
import org.apache.batik.transcoder.image.ImageTranscoder;
37
36
38
import org.apache.xmlgraphics.java2d.TextHandler;
39
import org.apache.xmlgraphics.java2d.ps.AbstractPSDocumentGraphics2D;
37
import org.apache.xmlgraphics.java2d.ps.AbstractPSDocumentGraphics2D;
40
import org.apache.xmlgraphics.ps.PSGenerator;
41
38
39
import org.apache.fop.apps.FOPException;
42
import org.apache.fop.fonts.FontInfo;
40
import org.apache.fop.fonts.FontInfo;
43
import org.apache.fop.fonts.FontSetup;
44
import org.apache.fop.svg.AbstractFOPTranscoder;
41
import org.apache.fop.svg.AbstractFOPTranscoder;
42
import org.apache.fop.svg.PDFDocumentGraphics2DConfigurator;
45
43
46
/**
44
/**
47
 * This class enables to transcode an input to a PostScript document.
45
 * This class enables to transcode an input to a PostScript document.
Lines 72-80 Link Here
72
 */
70
 */
73
public abstract class AbstractPSTranscoder extends AbstractFOPTranscoder {
71
public abstract class AbstractPSTranscoder extends AbstractFOPTranscoder {
74
72
75
    private final   Configuration                cfg      = null;
73
    /** the root Graphics2D instance for generating PostScript */
76
    protected AbstractPSDocumentGraphics2D graphics = null;
74
    protected AbstractPSDocumentGraphics2D graphics = null;
77
75
76
    private FontInfo fontInfo;
77
78
    /**
78
    /**
79
     * Constructs a new <tt>AbstractPSTranscoder</tt>.
79
     * Constructs a new <tt>AbstractPSTranscoder</tt>.
80
     */
80
     */
Lines 82-87 Link Here
82
        super();
82
        super();
83
    }
83
    }
84
84
85
    /**
86
     * Creates the root Graphics2D instance for generating PostScript.
87
     * @return the root Graphics2D
88
     */
85
    protected abstract AbstractPSDocumentGraphics2D createDocumentGraphics2D();
89
    protected abstract AbstractPSDocumentGraphics2D createDocumentGraphics2D();
86
90
87
    /**
91
    /**
Lines 98-108 Link Here
98
102
99
        graphics = createDocumentGraphics2D();
103
        graphics = createDocumentGraphics2D();
100
        if (!isTextStroked()) {
104
        if (!isTextStroked()) {
101
            FontInfo fontInfo = new FontInfo();
105
            try {
102
            //TODO Do custom font configuration here somewhere/somehow
106
                this.fontInfo = PDFDocumentGraphics2DConfigurator.createFontInfo(
103
            FontSetup.setup(fontInfo);
107
                        getEffectiveConfiguration());
104
            PSGenerator generator = graphics.getPSGenerator();
108
                graphics.setCustomTextHandler(new NativeTextHandler(graphics, fontInfo));
105
            graphics.setCustomTextHandler(new NativeTextHandler(graphics, fontInfo));
109
            } catch (FOPException fe) {
110
                throw new TranscoderException(fe);
111
            }
106
        }
112
        }
107
113
108
        super.transcode(document, uri, output);
114
        super.transcode(document, uri, output);
Lines 146-166 Link Here
146
152
147
    /** {@inheritDoc} */
153
    /** {@inheritDoc} */
148
    protected BridgeContext createBridgeContext() {
154
    protected BridgeContext createBridgeContext() {
155
        //For compatibility with Batik 1.6
156
        return createBridgeContext("1.x");
157
    }
149
158
150
        BridgeContext ctx = new BridgeContext(userAgent);
159
    /** {@inheritDoc} */
151
        if (!isTextStroked()) {
160
    public BridgeContext createBridgeContext(String version) {
152
            TextHandler handler = graphics.getCustomTextHandler();
161
        BridgeContext ctx = new PSBridgeContext(userAgent, (isTextStroked() ? null : fontInfo),
153
            if (handler instanceof NativeTextHandler) {
162
                getImageManager(), getImageSessionContext());
154
                NativeTextHandler nativeTextHandler = (NativeTextHandler)handler;
155
                PSTextPainter textPainter = new PSTextPainter(nativeTextHandler);
156
                ctx.setTextPainter(textPainter);
157
                ctx.putBridge(new PSTextElementBridge(textPainter));
158
            }
159
        }
160
161
        //ctx.putBridge(new PSImageElementBridge());
162
        return ctx;
163
        return ctx;
163
    }
164
    }
164
165
165
166
}
166
}

Return to bug 47000