123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
- /* $Id$ */
-
- package org.apache.fop.pdf;
-
- import java.awt.geom.AffineTransform;
-
- /**
- * Utility class for generating PDF text objects. It needs to be subclassed to add writing
- * functionality (see {@link #write(String)}).
- */
- public abstract class PDFTextUtil {
-
- /** The number of decimal places. */
- private static final int DEC = 8;
-
- /** PDF text rendering mode: Fill text */
- public static final int TR_FILL = 0;
- /** PDF text rendering mode: Stroke text */
- public static final int TR_STROKE = 1;
- /** PDF text rendering mode: Fill, then stroke text */
- public static final int TR_FILL_STROKE = 2;
- /** PDF text rendering mode: Neither fill nor stroke text (invisible) */
- public static final int TR_INVISIBLE = 3;
- /** PDF text rendering mode: Fill text and add to path for clipping */
- public static final int TR_FILL_CLIP = 4;
- /** PDF text rendering mode: Stroke text and add to path for clipping */
- public static final int TR_STROKE_CLIP = 5;
- /** PDF text rendering mode: Fill, then stroke text and add to path for clipping */
- public static final int TR_FILL_STROKE_CLIP = 6;
- /** PDF text rendering mode: Add text to path for clipping */
- public static final int TR_CLIP = 7;
-
- private boolean inTextObject = false;
- private String startText;
- private String endText;
- private boolean useMultiByte;
- private StringBuffer bufTJ;
- private int textRenderingMode = TR_FILL;
-
- private String currentFontName;
- private double currentFontSize;
-
- /**
- * Main constructor.
- */
- public PDFTextUtil() {
- //nop
- }
-
- /**
- * Writes PDF code.
- * @param code the PDF code to write
- */
- protected abstract void write(String code);
-
- private void writeAffineTransform(AffineTransform at, StringBuffer sb) {
- double[] lt = new double[6];
- at.getMatrix(lt);
- sb.append(PDFNumber.doubleOut(lt[0], DEC)).append(" ");
- sb.append(PDFNumber.doubleOut(lt[1], DEC)).append(" ");
- sb.append(PDFNumber.doubleOut(lt[2], DEC)).append(" ");
- sb.append(PDFNumber.doubleOut(lt[3], DEC)).append(" ");
- sb.append(PDFNumber.doubleOut(lt[4], DEC)).append(" ");
- sb.append(PDFNumber.doubleOut(lt[5], DEC));
- }
-
- private void writeChar(char ch, StringBuffer sb) {
- if (!useMultiByte) {
- if (ch < 32 || ch > 127) {
- sb.append("\\").append(Integer.toOctalString((int)ch));
- } else {
- switch (ch) {
- case '(':
- case ')':
- case '\\':
- sb.append("\\");
- break;
- default:
- }
- sb.append(ch);
- }
- } else {
- sb.append(PDFText.toUnicodeHex(ch));
- }
- }
-
- private void checkInTextObject() {
- if (!inTextObject) {
- throw new IllegalStateException("Not in text object");
- }
- }
-
- /**
- * Indicates whether we are in a text object or not.
- * @return true if we are in a text object
- */
- public boolean isInTextObject() {
- return inTextObject;
- }
-
- /**
- * Called when a new text object should be started. Be sure to call setFont() before
- * issuing any text painting commands.
- */
- public void beginTextObject() {
- if (inTextObject) {
- throw new IllegalStateException("Already in text object");
- }
- write("BT\n");
- this.inTextObject = true;
- }
-
- /**
- * Called when a text object should be ended.
- */
- public void endTextObject() {
- checkInTextObject();
- write("ET\n");
- this.inTextObject = false;
- initValues();
- }
-
- /**
- * Resets the state fields.
- */
- protected void initValues() {
- this.currentFontName = null;
- this.currentFontSize = 0.0;
- this.textRenderingMode = TR_FILL;
- }
-
- /**
- * Creates a "q" command, pushing a copy of the entire graphics state onto the stack.
- */
- public void saveGraphicsState() {
- write("q\n");
- }
-
- /**
- * Creates a "Q" command, restoring the entire graphics state to its former value by popping
- * it from the stack.
- */
- public void restoreGraphicsState() {
- write("Q\n");
- }
-
- /**
- * Creates a "cm" command.
- * @param at the transformation matrix
- */
- public void concatMatrix(AffineTransform at) {
- if (!at.isIdentity()) {
- writeTJ();
- StringBuffer sb = new StringBuffer();
- writeAffineTransform(at, sb);
- sb.append(" cm\n");
- write(sb.toString());
- }
- }
-
- /**
- * Writes a "Tf" command, setting a new current font.
- * @param fontName the name of the font to select
- * @param fontSize the font size (in points)
- */
- public void writeTf(String fontName, double fontSize) {
- checkInTextObject();
- write("/" + fontName + " " + PDFNumber.doubleOut(fontSize) + " Tf\n");
-
- this.startText = useMultiByte ? "<" : "(";
- this.endText = useMultiByte ? ">" : ")";
- }
-
- /**
- * Updates the current font. This method only writes a "Tf" if the current font changes.
- * @param fontName the name of the font to select
- * @param fontSize the font size (in points)
- * @param multiByte true indicates the font is a multi-byte font, false means single-byte
- */
- public void updateTf(String fontName, double fontSize, boolean multiByte) {
- checkInTextObject();
- if (!fontName.equals(this.currentFontName) || (fontSize != this.currentFontSize)) {
- writeTJ();
- this.currentFontName = fontName;
- this.currentFontSize = fontSize;
- this.useMultiByte = multiByte;
- writeTf(fontName, fontSize);
- }
- }
-
- /**
- * Sets the text rendering mode.
- * @param mode the rendering mode (value 0 to 7, see PDF Spec, constants: TR_*)
- */
- public void setTextRenderingMode(int mode) {
- if (mode < 0 || mode > 7) {
- throw new IllegalArgumentException(
- "Illegal value for text rendering mode. Expected: 0-7");
- }
- if (mode != this.textRenderingMode) {
- writeTJ();
- this.textRenderingMode = mode;
- write(this.textRenderingMode + " Tr\n");
- }
- }
-
- /**
- * Sets the text rendering mode.
- * @param fill true if the text should be filled
- * @param stroke true if the text should be stroked
- * @param addToClip true if the path should be added for clipping
- */
- public void setTextRenderingMode(boolean fill, boolean stroke, boolean addToClip) {
- int mode;
- if (fill) {
- mode = (stroke ? 2 : 0);
- } else {
- mode = (stroke ? 1 : 3);
- }
- if (addToClip) {
- mode += 4;
- }
- setTextRenderingMode(mode);
- }
-
- /**
- * Writes a "Tm" command, setting a new text transformation matrix.
- * @param localTransform the new text transformation matrix
- */
- public void writeTextMatrix(AffineTransform localTransform) {
- StringBuffer sb = new StringBuffer();
- writeAffineTransform(localTransform, sb);
- sb.append(" Tm ");
- write(sb.toString());
- }
-
- /**
- * Writes a char to the "TJ-Buffer".
- * @param codepoint the mapped character (code point/character code)
- */
- public void writeTJMappedChar(char codepoint) {
- if (bufTJ == null) {
- bufTJ = new StringBuffer();
- }
- if (bufTJ.length() == 0) {
- bufTJ.append("[").append(startText);
- }
- writeChar(codepoint, bufTJ);
- }
-
- /**
- * Writes a glyph adjust value to the "TJ-Buffer".
- * @param adjust the glyph adjust value in thousands of text unit space.
- */
- public void adjustGlyphTJ(double adjust) {
- if (bufTJ == null) {
- bufTJ = new StringBuffer();
- }
- if (bufTJ.length() > 0) {
- bufTJ.append(endText).append(" ");
- }
- if (bufTJ.length() == 0) {
- bufTJ.append("[");
- }
- bufTJ.append(PDFNumber.doubleOut(adjust, DEC - 4));
- bufTJ.append(" ");
- bufTJ.append(startText);
- }
-
- /**
- * Writes a "TJ" command, writing out the accumulated buffer with the characters and glyph
- * positioning values. The buffer is reset afterwards.
- */
- public void writeTJ() {
- if (isInString()) {
- bufTJ.append(endText).append("] TJ\n");
- write(bufTJ.toString());
- bufTJ.setLength(0);
- }
- }
-
- private boolean isInString() {
- return bufTJ != null && bufTJ.length() > 0;
- }
-
- }
|