001: /*
002: * $Id: PDFTextFormat.java,v 1.2 2007/12/20 18:17:41 rbair Exp $
003: *
004: * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005: * Santa Clara, California 95054, U.S.A. All rights reserved.
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
020: */
021:
022: package com.sun.pdfview;
023:
024: import java.awt.geom.AffineTransform;
025: import java.awt.geom.Point2D;
026: import java.util.Iterator;
027: import java.util.List;
028:
029: import com.sun.pdfview.font.PDFFont;
030: import com.sun.pdfview.font.PDFGlyph;
031:
032: /**
033: * a class encapsulating the text state
034: * @author Mike Wessler
035: */
036: public class PDFTextFormat implements Cloneable {
037: /** character spacing */
038: private float tc = 0;
039:
040: /** word spacing */
041: private float tw = 0;
042:
043: /** horizontal scaling */
044: private float th = 1;
045:
046: /** leading */
047: private float tl = 0;
048:
049: /** rise amount */
050: private float tr = 0;
051:
052: /** text mode */
053: private int tm = PDFShapeCmd.FILL;
054:
055: /** text knockout */
056: private float tk = 0;
057:
058: /** current matrix transform */
059: private AffineTransform cur;
060:
061: /** matrix transform at start of line */
062: private AffineTransform line;
063:
064: /** font */
065: private PDFFont font;
066:
067: /** font size */
068: private float fsize = 1;
069:
070: /** are we between BT and ET? */
071: private boolean inuse = false;
072:
073: // private Object array[]= new Object[1];
074:
075: /** build text rep of word */
076: private StringBuffer word = new StringBuffer();
077:
078: // this is where we build and keep the word list for this page.
079: /** start location of the hunk of text */
080: private Point2D.Float wordStart;
081:
082: /** location of the end of the previous hunk of text */
083: private Point2D.Float prevEnd;
084:
085: /**
086: * create a new PDFTextFormat, with initial values
087: */
088: public PDFTextFormat() {
089: cur = new AffineTransform();
090: line = new AffineTransform();
091: wordStart = new Point2D.Float(-100, -100);
092: prevEnd = new Point2D.Float(-100, -100);
093:
094: tc = tw = tr = tk = 0;
095: tm = PDFShapeCmd.FILL;
096: th = 1;
097: }
098:
099: /**
100: * reset the PDFTextFormat for a new run
101: */
102: public void reset() {
103: cur.setToIdentity();
104: line.setToIdentity();
105: inuse = true;
106: word.setLength(0);
107: }
108:
109: /**
110: * end a span of text
111: */
112: public void end() {
113: inuse = false;
114: }
115:
116: /** get the char spacing */
117: public float getCharSpacing() {
118: return tc;
119: }
120:
121: /** set the character spacing */
122: public void setCharSpacing(float spc) {
123: this .tc = spc;
124: }
125:
126: /** get the word spacing */
127: public float getWordSpacing() {
128: return tw;
129: }
130:
131: /** set the word spacing */
132: public void setWordSpacing(float spc) {
133: this .tw = spc;
134: }
135:
136: /**
137: * Get the horizontal scale
138: * @return the horizontal scale, in percent
139: */
140: public float getHorizontalScale() {
141: return th * 100;
142: }
143:
144: /**
145: * set the horizontal scale.
146: * @param scl the horizontal scale, in percent (100=normal)
147: */
148: public void setHorizontalScale(float scl) {
149: this .th = scl / 100;
150: }
151:
152: /** get the leading */
153: public float getLeading() {
154: return tl;
155: }
156:
157: /** set the leading */
158: public void setLeading(float spc) {
159: this .tl = spc;
160: }
161:
162: /** get the font */
163: public PDFFont getFont() {
164: return font;
165: }
166:
167: /** get the font size */
168: public float getFontSize() {
169: return fsize;
170: }
171:
172: /** set the font and size */
173: public void setFont(PDFFont f, float size) {
174: this .font = f;
175: this .fsize = size;
176: }
177:
178: /**
179: * Get the mode of the text
180: */
181: public int getMode() {
182: return tm;
183: }
184:
185: /**
186: * set the mode of the text. The correspondence of m to mode is
187: * show in the following table. m is a value from 0-7 in binary:
188: *
189: * 000 Fill
190: * 001 Stroke
191: * 010 Fill + Stroke
192: * 011 Nothing
193: * 100 Fill + Clip
194: * 101 Stroke + Clip
195: * 110 Fill + Stroke + Clip
196: * 111 Clip
197: *
198: * Therefore: Fill corresponds to the low bit being 0; Clip
199: * corresponds to the hight bit being 1; and Stroke corresponds
200: * to the middle xor low bit being 1.
201: */
202: public void setMode(int m) {
203: int mode = 0;
204:
205: if ((m & 0x1) == 0) {
206: mode |= PDFShapeCmd.FILL;
207: }
208: if ((m & 0x4) != 0) {
209: mode |= PDFShapeCmd.CLIP;
210: }
211: if (((m & 0x1) ^ ((m & 0x2) >> 1)) != 0) {
212: mode |= PDFShapeCmd.STROKE;
213: }
214:
215: this .tm = mode;
216: }
217:
218: /**
219: * Set the mode from another text format mode
220: *
221: * @param mode the text render mode using the
222: * codes from PDFShapeCmd and not the wacky PDF codes
223: */
224: public void setTextFormatMode(int mode) {
225: this .tm = mode;
226: }
227:
228: /**
229: * Get the rise
230: */
231: public float getRise() {
232: return tr;
233: }
234:
235: /**
236: * set the rise
237: */
238: public void setRise(float spc) {
239: this .tr = spc;
240: }
241:
242: /**
243: * perform a carriage return
244: */
245: public void carriageReturn() {
246: carriageReturn(0, -tl);
247: }
248:
249: /**
250: * perform a carriage return by translating by x and y. The next
251: * carriage return will be relative to the new location.
252: */
253: public void carriageReturn(float x, float y) {
254: line.concatenate(AffineTransform.getTranslateInstance(x, y));
255: cur.setTransform(line);
256: }
257:
258: /**
259: * Get the current transform
260: */
261: public AffineTransform getTransform() {
262: return cur;
263: }
264:
265: /**
266: * set the transform matrix directly
267: */
268: public void setMatrix(float[] matrix) {
269: line = new AffineTransform(matrix);
270: cur.setTransform(line);
271: }
272:
273: /**
274: * add some text to the page.
275: * @param cmds the PDFPage to add the commands to
276: * @param text the text to add
277: */
278: public void doText(PDFPage cmds, String text) {
279: Point2D.Float zero = new Point2D.Float();
280: AffineTransform scale = new AffineTransform(fsize, 0, 0, fsize
281: * th, 0, tr);
282: AffineTransform at = new AffineTransform();
283:
284: List l = font.getGlyphs(text);
285:
286: for (Iterator i = l.iterator(); i.hasNext();) {
287: PDFGlyph glyph = (PDFGlyph) i.next();
288:
289: at.setTransform(cur);
290: at.concatenate(scale);
291:
292: Point2D advance = glyph.addCommands(cmds, at, tm);
293:
294: double advanceX = (advance.getX() * fsize) + tc;
295: if (glyph.getChar() == ' ') {
296: advanceX += tw;
297: }
298: advanceX *= th;
299:
300: cur.translate(advanceX, advance.getY());
301: }
302:
303: cur.transform(zero, prevEnd);
304: }
305:
306: /**
307: * add some text to the page.
308: * @param cmds the PDFPage to add the commands to
309: * @param ary an array of Strings and Doubles, where the Strings
310: * represent text to be added, and the Doubles represent kerning
311: * amounts.
312: */
313: public void doText(PDFPage cmds, Object ary[])
314: throws PDFParseException {
315: for (int i = 0; i < ary.length; i++) {
316: if (ary[i] instanceof String) {
317: doText(cmds, (String) ary[i]);
318: } else if (ary[i] instanceof Double) {
319: float val = ((Double) ary[i]).floatValue() / 1000f;
320: cur.translate(-val * fsize * th, 0);
321: } else {
322: throw new PDFParseException("Bad element in TJ array");
323: }
324: }
325: }
326:
327: /**
328: * finish any unfinished words. TODO: write this!
329: */
330: public void flush() {
331: // TODO: finish any unfinished words
332: }
333:
334: /**
335: * Clone the text format
336: */
337: @Override
338: public Object clone() {
339: PDFTextFormat newFormat = new PDFTextFormat();
340:
341: // copy values
342: newFormat.setCharSpacing(getCharSpacing());
343: newFormat.setWordSpacing(getWordSpacing());
344: newFormat.setHorizontalScale(getHorizontalScale());
345: newFormat.setLeading(getLeading());
346: newFormat.setTextFormatMode(getMode());
347: newFormat.setRise(getRise());
348:
349: // copy immutable fields
350: newFormat.setFont(getFont(), getFontSize());
351:
352: // clone transform (mutable)
353: // newFormat.getTransform().setTransform(getTransform());
354:
355: return newFormat;
356: }
357: }
|