Package org.apache.fop.svg

Source Code of org.apache.fop.svg.PDFTextPainter

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* $Id: PDFTextPainter.java 592016 2007-11-05 14:34:18Z vhennebert $ */

package org.apache.fop.svg;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.font.TextAttribute;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.lang.reflect.Method;
import java.text.AttributedCharacterIterator;
import java.util.Iterator;
import java.util.List;

import org.apache.batik.bridge.SVGFontFamily;
import org.apache.batik.gvt.font.GVTFont;
import org.apache.batik.gvt.font.GVTFontFamily;
import org.apache.batik.gvt.font.GVTGlyphVector;
import org.apache.batik.gvt.renderer.StrokingTextPainter;
import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
import org.apache.batik.gvt.text.TextPaintInfo;
import org.apache.batik.gvt.text.TextSpanLayout;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.fonts.FontTriplet;
import org.apache.fop.util.CharUtilities;

/**
* Renders the attributed character iterator of a <tt>TextNode</tt>.
* This class draws the text directly into the PDFGraphics2D so that
* the text is not drawn using shapes which makes the PDF files larger.
* If the text is simple enough to draw then it sets the font and calls
* drawString. If the text is complex or the cannot be translated
* into a simple drawString the StrokingTextPainter is used instead.
*
* @version $Id: PDFTextPainter.java 592016 2007-11-05 14:34:18Z vhennebert $
*/
public class PDFTextPainter extends StrokingTextPainter {

    private static final boolean DEBUG = false;
   
    private boolean strokeText = false;
    private FontInfo fontInfo;

    /**
     * Create a new PDF text painter with the given font information.
     * @param fi the font info
     */
    public PDFTextPainter(FontInfo fi) {
        fontInfo = fi;
    }

    /** {@inheritDoc} */
    protected void paintTextRuns(List textRuns, Graphics2D g2d) {
        if (DEBUG) {
            System.out.println("paintTextRuns: count = " + textRuns.size());
            //fontInfo.dumpAllTripletsToSystemOut();
        }
        if (!(g2d instanceof PDFGraphics2D) || strokeText) {
            super.paintTextRuns(textRuns, g2d);
            return;
        }
        PDFGraphics2D pdf = (PDFGraphics2D)g2d;
        PDFTextUtil textUtil = new PDFTextUtil(pdf);
        for (int i = 0; i < textRuns.size(); i++) {
            TextRun textRun = (TextRun)textRuns.get(i);
            AttributedCharacterIterator runaci = textRun.getACI();
            runaci.first();

            TextPaintInfo tpi = (TextPaintInfo)runaci.getAttribute(PAINT_INFO);
            if (tpi == null || !tpi.visible) {
                continue;
            }
            if ((tpi != null) && (tpi.composite != null)) {
                g2d.setComposite(tpi.composite);
            }
           
            //------------------------------------
            TextSpanLayout layout = textRun.getLayout();
            if (DEBUG) {
                int charCount = runaci.getEndIndex() - runaci.getBeginIndex();
                System.out.println("================================================");
                System.out.println("New text run:");
                System.out.println("char count: " + charCount);
                System.out.println("range: "
                        + runaci.getBeginIndex() + " - " + runaci.getEndIndex());
                System.out.println("glyph count: " + layout.getGlyphCount()); //=getNumGlyphs()
            }
            //Gather all characters of the run
            StringBuffer chars = new StringBuffer();
            for (runaci.first(); runaci.getIndex() < runaci.getEndIndex();) {
                chars.append(runaci.current());
                runaci.next();
            }
            runaci.first();
            if (DEBUG) {
                System.out.println("Text: " + chars);
                pdf.currentStream.write("%Text: " + chars + "\n");
            }
           
            GeneralPath debugShapes = null;
            if (DEBUG) {
                debugShapes = new GeneralPath();
            }
           
            Font[] fonts = findFonts(runaci);
            if (fonts == null || fonts.length == 0) {
                //Draw using Java2D
                textRun.getLayout().draw(g2d);
                continue;
            }
           
            textUtil.saveGraphicsState();
            textUtil.concatMatrixCurrentTransform();
            Shape imclip = g2d.getClip();
            pdf.writeClip(imclip);
           
            applyColorAndPaint(tpi, pdf);
           
            textUtil.beginTextObject();
            textUtil.setFonts(fonts);
            textUtil.setTextRenderingMode(tpi.fillPaint != null, tpi.strokePaint != null, false);

            AffineTransform localTransform = new AffineTransform();
            Point2D prevPos = null;
            double prevVisibleCharWidth = 0.0;
            GVTGlyphVector gv = layout.getGlyphVector();
            for (int index = 0, c = gv.getNumGlyphs(); index < c; index++) {
                char ch = chars.charAt(index);
                boolean visibleChar = gv.isGlyphVisible(index)
                    || (CharUtilities.isAnySpace(ch) && !CharUtilities.isZeroWidthSpace(ch));
                if (DEBUG) {
                    System.out.println("glyph " + index
                            + " -> " + layout.getGlyphIndex(index) + " => " + ch);
                    if (CharUtilities.isAnySpace(ch) && ch != 32) {
                        System.out.println("Space found: " + Integer.toHexString(ch));
                    }
                    if (ch == CharUtilities.ZERO_WIDTH_JOINER) {
                        System.out.println("ZWJ found: " + Integer.toHexString(ch));
                    }
                    if (ch == CharUtilities.SOFT_HYPHEN) {
                        System.out.println("Soft hyphen found: " + Integer.toHexString(ch));
                    }
                    if (!visibleChar) {
                        System.out.println("Invisible glyph found: " + Integer.toHexString(ch));
                    }
                }
                if (!visibleChar) {
                    continue;
                }
                Point2D p = gv.getGlyphPosition(index);

                AffineTransform glyphTransform = gv.getGlyphTransform(index);
                //TODO Glyph transforms could be refined so not every char has to be painted
                //with its own TJ command (stretch/squeeze case could be optimized)
                if (DEBUG) {
                    System.out.println("pos " + p + ", transform " + glyphTransform);
                    Shape sh;
                    sh = gv.getGlyphLogicalBounds(index);
                    if (sh == null) {
                        sh = new Ellipse2D.Double(p.getX(), p.getY(), 2, 2);
                    }
                    debugShapes.append(sh, false);
                }

                //Exact position of the glyph
                localTransform.setToIdentity();
                localTransform.translate(p.getX(), p.getY());
                if (glyphTransform != null) {
                    localTransform.concatenate(glyphTransform);
                }
                localTransform.scale(1, -1);
               
                boolean yPosChanged = (prevPos == null
                        || prevPos.getY() != p.getY()
                        || glyphTransform != null);
                if (yPosChanged) {
                    if (index > 0) {
                        textUtil.writeTJ();
                        textUtil.writeTextMatrix(localTransform);
                    }
                } else {
                    double xdiff = p.getX() - prevPos.getX();
                    //Width of previous character
                    Font font = textUtil.getCurrentFont();
                    double cw = prevVisibleCharWidth;
                    double effxdiff = (1000 * xdiff) - cw;
                    if (effxdiff != 0) {
                        double adjust = (-effxdiff / font.getFontSize());
                        textUtil.adjustGlyphTJ(adjust * 1000);
                    }
                    if (DEBUG) {
                        System.out.println("==> x diff: " + xdiff + ", " + effxdiff
                                + ", charWidth: " + cw);
                    }
                }
                Font f = textUtil.selectFontForChar(ch);
                if (f != textUtil.getCurrentFont()) {
                    textUtil.writeTJ();
                    textUtil.setCurrentFont(f);
                    textUtil.writeTf(f);
                    textUtil.writeTextMatrix(localTransform);
                }
                char paintChar = (CharUtilities.isAnySpace(ch) ? ' ' : ch);
                textUtil.writeTJChar(paintChar);
               
                //Update last position
                prevPos = p;
                prevVisibleCharWidth = textUtil.getCurrentFont().getCharWidth(chars.charAt(index));
            }
            textUtil.writeTJ();
            textUtil.endTextObject();
            textUtil.restoreGraphicsState();
            if (DEBUG) {
                g2d.setStroke(new BasicStroke(0));
                g2d.setColor(Color.LIGHT_GRAY);
                g2d.draw(debugShapes);
            }
        }
    }

    private void applyColorAndPaint(TextPaintInfo tpi, PDFGraphics2D pdf) {
        Paint fillPaint = tpi.fillPaint;
        Paint strokePaint = tpi.strokePaint;
        Stroke stroke = tpi.strokeStroke;
        int fillAlpha = PDFGraphics2D.OPAQUE;
        if (fillPaint instanceof Color) {
            Color col = (Color)fillPaint;
            pdf.applyColor(col, true);
            fillAlpha = col.getAlpha();
        }
        if (strokePaint instanceof Color) {
            Color col = (Color)strokePaint;
            pdf.applyColor(col, false);
        }
        pdf.applyPaint(fillPaint, true);
        pdf.applyStroke(stroke);
        if (strokePaint != null) {
            pdf.applyPaint(strokePaint, false);
        }
        pdf.applyAlpha(fillAlpha, PDFGraphics2D.OPAQUE);
    }
   
    private Font[] findFonts(AttributedCharacterIterator aci) {
        List fonts = new java.util.ArrayList();
        List gvtFonts = (List) aci.getAttribute(
                GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES);
        Float posture = (Float) aci.getAttribute(TextAttribute.POSTURE);
        Float taWeight = (Float) aci.getAttribute(TextAttribute.WEIGHT);
        Float fontSize = (Float) aci.getAttribute(TextAttribute.SIZE);

        String style = ((posture != null) && (posture.floatValue() > 0.0))
                       ? "italic" : "normal";
        int weight = ((taWeight != null)
                       &&  (taWeight.floatValue() > 1.0)) ? Font.WEIGHT_BOLD
                       : Font.WEIGHT_NORMAL;

        String fontFamily = null;

        //GVT_FONT can sometimes be different from the fonts in GVT_FONT_FAMILIES
        //or GVT_FONT_FAMILIES can even be empty and only GVT_FONT is set
        /* The following code section is not available until Batik 1.7 is released. */
        GVTFont gvtFont = (GVTFont)aci.getAttribute(
                GVTAttributedCharacterIterator.TextAttribute.GVT_FONT);
        if (gvtFont != null) {
            try {
                Method method = gvtFont.getClass().getMethod("getFamilyName", null);
                String gvtFontFamily = (String)method.invoke(gvtFont, null);
                //TODO Uncomment the following line when Batik 1.7 is shipped with FOP
                //String gvtFontFamily = gvtFont.getFamilyName(); //Not available in Batik 1.6
                if (DEBUG) {
                    System.out.print(gvtFontFamily + ", ");
                }
                if (fontInfo.hasFont(gvtFontFamily, style, weight)) {
                    FontTriplet triplet = fontInfo.fontLookup(gvtFontFamily, style,
                                                       weight);
                    int fsize = (int)(fontSize.floatValue() * 1000);
                    fonts.add(fontInfo.getFontInstance(triplet, fsize));
                }
            } catch (Exception e) {
                //Most likely NoSuchMethodError here when using Batik 1.6
                //Just skip this section in this case
            }
        }

        if (gvtFonts != null) {
            Iterator i = gvtFonts.iterator();
            while (i.hasNext()) {
                GVTFontFamily fam = (GVTFontFamily) i.next();
                if (fam instanceof SVGFontFamily) {
                    return null; //Let Batik paint this text!
                }
                fontFamily = fam.getFamilyName();
                if (DEBUG) {
                    System.out.print(fontFamily + ", ");
                }
                if (fontInfo.hasFont(fontFamily, style, weight)) {
                    FontTriplet triplet = fontInfo.fontLookup(fontFamily, style,
                                                       weight);
                    int fsize = (int)(fontSize.floatValue() * 1000);
                    fonts.add(fontInfo.getFontInstance(triplet, fsize));
                }
            }
        }
        if (fonts.size() == 0) {
            FontTriplet triplet = fontInfo.fontLookup("any", style, Font.WEIGHT_NORMAL);
            int fsize = (int)(fontSize.floatValue() * 1000);
            fonts.add(fontInfo.getFontInstance(triplet, fsize));
            if (DEBUG) {
                System.out.print("fallback to 'any' font");
            }
        }
        if (DEBUG) {
            System.out.println();
        }
        return (Font[])fonts.toArray(new Font[fonts.size()]);
    }
   
}
TOP

Related Classes of org.apache.fop.svg.PDFTextPainter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.