All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openpdf.renderer.PDFTextFormat Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */
package org.openpdf.renderer;

import java.awt.Color;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.util.Iterator;
import java.util.List;

import org.openpdf.renderer.font.PDFFont;
import org.openpdf.renderer.font.PDFGlyph;

/**
* a class encapsulating the text state
*
* @author Mike Wessler
*/
public class PDFTextFormat implements Cloneable {
    /** character spacing */
    private float tc = 0;
    /** word spacing */
    private float tw = 0;
    /** horizontal scaling */
    private float th = 1;
    /** leading */
    private float tl = 0;
    /** rise amount */
    private float tr = 0;
    /** text mode */
    private int tm = PDFShapeCmd.FILL;
    /** text knockout */
    private float tk = 0;
    /** current matrix transform */
    private final AffineTransform cur;
    /** matrix transform at start of line */
    private AffineTransform line;
    /** font */
    private PDFFont font;
    /** font size */
    private float fsize = 1;
    /** are we between BT and ET? */
    private boolean inuse = false;
    // private Object array[]= new Object[1];
    /** build text rep of word */
    private final StringBuffer word = new StringBuffer();
    // this is where we build and keep the word list for this page.
    /** start location of the hunk of text */
    private final Point2D.Float wordStart;
    /** location of the end of the previous hunk of text */
    private final Point2D.Float prevEnd;

    /**
    * create a new PDFTextFormat, with initial values
    */
    public PDFTextFormat() {
        this.cur = new AffineTransform();
        this.line = new AffineTransform();
        this.wordStart = new Point2D.Float(-100, -100);
        this.prevEnd = new Point2D.Float(-100, -100);
        this.tc = this.tw = this.tr = this.tk = 0;
        this.tm = PDFShapeCmd.FILL;
        this.th = 1;
    }

    /**
    * reset the PDFTextFormat for a new run
    */
    public void reset() {
        this.cur.setToIdentity();
        this.line.setToIdentity();
        this.inuse = true;
        this.word.setLength(0);
    }

    /**
    * end a span of text
    */
    public void end() {
        this.inuse = false;
    }

    /** get the char spacing */
    public float getCharSpacing() {
        return this.tc;
    }

    /** set the character spacing */
    public void setCharSpacing(float spc) {
        this.tc = spc;
    }

    /** get the word spacing */
    public float getWordSpacing() {
        return this.tw;
    }

    /** set the word spacing */
    public void setWordSpacing(float spc) {
        this.tw = spc;
    }

    /**
    * Get the horizontal scale
    *
    * @return the horizontal scale, in percent
    */
    public float getHorizontalScale() {
        return this.th * 100;
    }

    /**
    * set the horizontal scale.
    *
    * @param scl
    * the horizontal scale, in percent (100=normal)
    */
    public void setHorizontalScale(float scl) {
        this.th = scl / 100;
    }

    /** get the leading */
    public float getLeading() {
        return this.tl;
    }

    /** set the leading */
    public void setLeading(float spc) {
        this.tl = spc;
    }

    /** get the font */
    public PDFFont getFont() {
        return this.font;
    }

    /** get the font size */
    public float getFontSize() {
        return this.fsize;
    }

    /** set the font and size */
    public void setFont(PDFFont f, float size) {
        this.font = f;
        this.fsize = size;
    }

    /**
    * Get the mode of the text
    */
    public int getMode() {
        return this.tm;
    }

    /**
    * set the mode of the text. The correspondence of m to mode is
    * show in the following table. m is a value from 0-7 in binary:
    *
    * 000 Fill
    * 001 Stroke
    * 010 Fill + Stroke
    * 011 Nothing
    * 100 Fill + Clip
    * 101 Stroke + Clip
    * 110 Fill + Stroke + Clip
    * 111 Clip
    *
    * Therefore: Fill corresponds to the low bit being 0; Clip
    * corresponds to the hight bit being 1; and Stroke corresponds
    * to the middle xor low bit being 1.
    */
    public void setMode(int m) {
        int mode = 0;
        if ((m & 0x1) == 0) {
            mode |= PDFShapeCmd.FILL;
        }
        if ((m & 0x4) != 0) {
            mode |= PDFShapeCmd.CLIP;
        }
        if (((m & 0x1) ^ ((m & 0x2) >> 1)) != 0) {
            mode |= PDFShapeCmd.STROKE;
        }
        this.tm = mode;
    }

    /**
    * Set the mode from another text format mode
    *
    * @param mode
    * the text render mode using the
    * codes from PDFShapeCmd and not the wacky PDF codes
    */
    public void setTextFormatMode(int mode) {
        this.tm = mode;
    }

    /**
    * Get the rise
    */
    public float getRise() {
        return this.tr;
    }

    /**
    * set the rise
    */
    public void setRise(float spc) {
        this.tr = spc;
    }

    /**
    * perform a carriage return
    */
    public void carriageReturn() {
        carriageReturn(0, -this.tl);
    }

    /**
    * perform a carriage return by translating by x and y. The next
    * carriage return will be relative to the new location.
    */
    public void carriageReturn(float x, float y) {
        this.line.concatenate(AffineTransform.getTranslateInstance(x, y));
        this.cur.setTransform(this.line);
    }

    /**
    * Get the current transform
    */
    public AffineTransform getTransform() {
        return this.cur;
    }

    /**
    * set the transform matrix directly
    */
    public void setMatrix(float[] matrix) {
        this.line = new AffineTransform(matrix);
        this.cur.setTransform(this.line);
    }

    /**
    * add some text to the page.
    *
    * @param cmds
    * the PDFPage to add the commands to
    * @param text
    * the text to add
    */
    public void doText(PDFPage cmds, String text, boolean autoAdjustStroke) {
        Point2D.Float zero = new Point2D.Float();
        AffineTransform scale = new AffineTransform(this.fsize * this.th, 0, /* 0 */
        0, this.fsize, /* 0 */
        0, this.tr /* 1 */);
        AffineTransform at = new AffineTransform();
        List l = this.font.getGlyphs(text);
        if (PDFDebugger.SHOW_TEXT_ANCHOR) {
            if (PDFDebugger.DEBUG_TEXT) {
                PDFDebugger.debug("POINT count: " + l.size());
            }
        }
        for (Iterator i = l.iterator(); i.hasNext();) {
            PDFGlyph glyph = i.next();
            at.setTransform(this.cur);
            at.concatenate(scale);
            if (PDFDebugger.SHOW_TEXT_REGIONS) {
                GeneralPath path = new GeneralPath();
                path.moveTo(0, 0);
                path.lineTo(1, 0);
                path.lineTo(1, 1);
                path.lineTo(0, 1);
                path.lineTo(0, 0);
                path.closePath();
                path = (GeneralPath) path.createTransformedShape(at);
                if (PDFDebugger.DEBUG_TEXT) {
                    PDFDebugger.debug("BOX " + path.getBounds());
                }
                PDFCmd lastColor = cmds.findLastCommand(PDFFillPaintCmd.class);
                if (PDFDebugger.DEBUG_TEXT) {
                    PDFDebugger.debug("BOX " + lastColor);
                }
                cmds.addFillPaint(PDFPaint.getColorPaint(new Color(160, 160, 255)));
                cmds.addPath(path, PDFShapeCmd.FILL, autoAdjustStroke);
                if (lastColor != null) {
                    cmds.addCommand(lastColor);
                }
            }
            Point2D advance = glyph.getAdvance();
            if (!PDFDebugger.DISABLE_TEXT) {
                advance = glyph.addCommands(cmds, at, this.tm);
            }
            double advanceX = (advance.getX() * this.fsize) + this.tc;
            double advanceY = advance.getY() + this.fsize;
            if (glyph.getChar() == ' ') {
                advanceX += this.tw;
            }
            advanceX *= this.th;
            if (PDFDebugger.SHOW_TEXT_ANCHOR) {
                AffineTransform at2 = new AffineTransform();
                at2.setTransform(this.cur);
                GeneralPath path = new GeneralPath();
                path.moveTo(0, 0);
                path.lineTo(6, 0);
                path.lineTo(6, 6);
                path.lineTo(0, 6);
                path.lineTo(0, 0);
                path.closePath();
                path = (GeneralPath) path.createTransformedShape(at2);
                if (PDFDebugger.DEBUG_TEXT) {
                    PDFDebugger.debug("POINT " + advance);
                }
                PDFCmd lastColor = cmds.findLastCommand(PDFFillPaintCmd.class);
                cmds.addFillPaint(PDFPaint.getColorPaint(new Color(255, 0, 0)));
                cmds.addPath(path, PDFShapeCmd.FILL, autoAdjustStroke);
                if (lastColor != null) {
                    cmds.addCommand(lastColor);
                }
            }
            this.cur.translate(advanceX, advance.getY());
        }
        this.cur.transform(zero, this.prevEnd);
    }

    /**
    * add some text to the page.
    *
    * @param cmds
    * the PDFPage to add the commands to
    * @param ary
    * an array of Strings and Doubles, where the Strings
    * represent text to be added, and the Doubles represent kerning
    * amounts.
    */
    public void doText(PDFPage cmds, Object ary[], boolean autoAdjustStroke) throws PDFParseException {
        for (int i = 0; i < ary.length; i++) {
            if (ary[i] instanceof String) {
                doText(cmds, (String) ary[i], autoAdjustStroke);
            } else if (ary[i] instanceof Double) {
                float val = ((Double) ary[i]).floatValue() / 1000f;
                this.cur.translate(-val * this.fsize * this.th, 0);
            } else {
                throw new PDFParseException("Bad element in TJ array");
            }
        }
    }

    /**
    * finish any unfinished words. TODO: write this!
    */
    public void flush() {
        // TODO: finish any unfinished words
    }

    /**
    * Clone the text format
    */
    @Override
    public Object clone() {
        PDFTextFormat newFormat = new PDFTextFormat();
        // copy values
        newFormat.setCharSpacing(getCharSpacing());
        newFormat.setWordSpacing(getWordSpacing());
        newFormat.setHorizontalScale(getHorizontalScale());
        newFormat.setLeading(getLeading());
        newFormat.setTextFormatMode(getMode());
        newFormat.setRise(getRise());
        // copy immutable fields
        newFormat.setFont(getFont(), getFontSize());
        // clone transform (mutable)
        // newFormat.getTransform().setTransform(getTransform());
        return newFormat;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy