Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

PDFTextUtil.java 9.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  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. /* $Id$ */
  18. package org.apache.fop.pdf;
  19. import java.awt.geom.AffineTransform;
  20. /**
  21. * Utility class for generating PDF text objects. It needs to be subclassed to add writing
  22. * functionality (see {@link #write(String)}).
  23. */
  24. public abstract class PDFTextUtil {
  25. /** The number of decimal places. */
  26. private static final int DEC = 8;
  27. /** PDF text rendering mode: Fill text */
  28. public static final int TR_FILL = 0;
  29. /** PDF text rendering mode: Stroke text */
  30. public static final int TR_STROKE = 1;
  31. /** PDF text rendering mode: Fill, then stroke text */
  32. public static final int TR_FILL_STROKE = 2;
  33. /** PDF text rendering mode: Neither fill nor stroke text (invisible) */
  34. public static final int TR_INVISIBLE = 3;
  35. /** PDF text rendering mode: Fill text and add to path for clipping */
  36. public static final int TR_FILL_CLIP = 4;
  37. /** PDF text rendering mode: Stroke text and add to path for clipping */
  38. public static final int TR_STROKE_CLIP = 5;
  39. /** PDF text rendering mode: Fill, then stroke text and add to path for clipping */
  40. public static final int TR_FILL_STROKE_CLIP = 6;
  41. /** PDF text rendering mode: Add text to path for clipping */
  42. public static final int TR_CLIP = 7;
  43. private boolean inTextObject = false;
  44. private String startText;
  45. private String endText;
  46. private boolean useMultiByte;
  47. private StringBuffer bufTJ;
  48. private int textRenderingMode = TR_FILL;
  49. private String currentFontName;
  50. private double currentFontSize;
  51. /**
  52. * Main constructor.
  53. */
  54. public PDFTextUtil() {
  55. //nop
  56. }
  57. /**
  58. * Writes PDF code.
  59. * @param code the PDF code to write
  60. */
  61. protected abstract void write(String code);
  62. private void writeAffineTransform(AffineTransform at, StringBuffer sb) {
  63. double[] lt = new double[6];
  64. at.getMatrix(lt);
  65. sb.append(PDFNumber.doubleOut(lt[0], DEC)).append(" ");
  66. sb.append(PDFNumber.doubleOut(lt[1], DEC)).append(" ");
  67. sb.append(PDFNumber.doubleOut(lt[2], DEC)).append(" ");
  68. sb.append(PDFNumber.doubleOut(lt[3], DEC)).append(" ");
  69. sb.append(PDFNumber.doubleOut(lt[4], DEC)).append(" ");
  70. sb.append(PDFNumber.doubleOut(lt[5], DEC));
  71. }
  72. private void writeChar(char ch, StringBuffer sb) {
  73. if (!useMultiByte) {
  74. if (ch < 32 || ch > 127) {
  75. sb.append("\\").append(Integer.toOctalString((int)ch));
  76. } else {
  77. switch (ch) {
  78. case '(':
  79. case ')':
  80. case '\\':
  81. sb.append("\\");
  82. break;
  83. default:
  84. }
  85. sb.append(ch);
  86. }
  87. } else {
  88. sb.append(PDFText.toUnicodeHex(ch));
  89. }
  90. }
  91. private void checkInTextObject() {
  92. if (!inTextObject) {
  93. throw new IllegalStateException("Not in text object");
  94. }
  95. }
  96. /**
  97. * Indicates whether we are in a text object or not.
  98. * @return true if we are in a text object
  99. */
  100. public boolean isInTextObject() {
  101. return inTextObject;
  102. }
  103. /**
  104. * Called when a new text object should be started. Be sure to call setFont() before
  105. * issuing any text painting commands.
  106. */
  107. public void beginTextObject() {
  108. if (inTextObject) {
  109. throw new IllegalStateException("Already in text object");
  110. }
  111. write("BT\n");
  112. this.inTextObject = true;
  113. }
  114. /**
  115. * Called when a text object should be ended.
  116. */
  117. public void endTextObject() {
  118. checkInTextObject();
  119. write("ET\n");
  120. this.inTextObject = false;
  121. initValues();
  122. }
  123. /**
  124. * Resets the state fields.
  125. */
  126. protected void initValues() {
  127. this.currentFontName = null;
  128. this.currentFontSize = 0.0;
  129. this.textRenderingMode = TR_FILL;
  130. }
  131. /**
  132. * Creates a "q" command, pushing a copy of the entire graphics state onto the stack.
  133. */
  134. public void saveGraphicsState() {
  135. write("q\n");
  136. }
  137. /**
  138. * Creates a "Q" command, restoring the entire graphics state to its former value by popping
  139. * it from the stack.
  140. */
  141. public void restoreGraphicsState() {
  142. write("Q\n");
  143. }
  144. /**
  145. * Creates a "cm" command.
  146. * @param at the transformation matrix
  147. */
  148. public void concatMatrix(AffineTransform at) {
  149. if (!at.isIdentity()) {
  150. writeTJ();
  151. StringBuffer sb = new StringBuffer();
  152. writeAffineTransform(at, sb);
  153. sb.append(" cm\n");
  154. write(sb.toString());
  155. }
  156. }
  157. /**
  158. * Writes a "Tf" command, setting a new current font.
  159. * @param fontName the name of the font to select
  160. * @param fontSize the font size (in points)
  161. */
  162. public void writeTf(String fontName, double fontSize) {
  163. checkInTextObject();
  164. write("/" + fontName + " " + PDFNumber.doubleOut(fontSize) + " Tf\n");
  165. this.startText = useMultiByte ? "<" : "(";
  166. this.endText = useMultiByte ? ">" : ")";
  167. }
  168. /**
  169. * Updates the current font. This method only writes a "Tf" if the current font changes.
  170. * @param fontName the name of the font to select
  171. * @param fontSize the font size (in points)
  172. * @param multiByte true indicates the font is a multi-byte font, false means single-byte
  173. */
  174. public void updateTf(String fontName, double fontSize, boolean multiByte) {
  175. checkInTextObject();
  176. if (!fontName.equals(this.currentFontName) || (fontSize != this.currentFontSize)) {
  177. writeTJ();
  178. this.currentFontName = fontName;
  179. this.currentFontSize = fontSize;
  180. this.useMultiByte = multiByte;
  181. writeTf(fontName, fontSize);
  182. }
  183. }
  184. /**
  185. * Sets the text rendering mode.
  186. * @param mode the rendering mode (value 0 to 7, see PDF Spec, constants: TR_*)
  187. */
  188. public void setTextRenderingMode(int mode) {
  189. if (mode < 0 || mode > 7) {
  190. throw new IllegalArgumentException(
  191. "Illegal value for text rendering mode. Expected: 0-7");
  192. }
  193. if (mode != this.textRenderingMode) {
  194. writeTJ();
  195. this.textRenderingMode = mode;
  196. write(this.textRenderingMode + " Tr\n");
  197. }
  198. }
  199. /**
  200. * Sets the text rendering mode.
  201. * @param fill true if the text should be filled
  202. * @param stroke true if the text should be stroked
  203. * @param addToClip true if the path should be added for clipping
  204. */
  205. public void setTextRenderingMode(boolean fill, boolean stroke, boolean addToClip) {
  206. int mode;
  207. if (fill) {
  208. mode = (stroke ? 2 : 0);
  209. } else {
  210. mode = (stroke ? 1 : 3);
  211. }
  212. if (addToClip) {
  213. mode += 4;
  214. }
  215. setTextRenderingMode(mode);
  216. }
  217. /**
  218. * Writes a "Tm" command, setting a new text transformation matrix.
  219. * @param localTransform the new text transformation matrix
  220. */
  221. public void writeTextMatrix(AffineTransform localTransform) {
  222. StringBuffer sb = new StringBuffer();
  223. writeAffineTransform(localTransform, sb);
  224. sb.append(" Tm ");
  225. write(sb.toString());
  226. }
  227. /**
  228. * Writes a char to the "TJ-Buffer".
  229. * @param codepoint the mapped character (code point/character code)
  230. */
  231. public void writeTJMappedChar(char codepoint) {
  232. if (bufTJ == null) {
  233. bufTJ = new StringBuffer();
  234. }
  235. if (bufTJ.length() == 0) {
  236. bufTJ.append("[").append(startText);
  237. }
  238. writeChar(codepoint, bufTJ);
  239. }
  240. /**
  241. * Writes a glyph adjust value to the "TJ-Buffer".
  242. * @param adjust the glyph adjust value in thousands of text unit space.
  243. */
  244. public void adjustGlyphTJ(double adjust) {
  245. if (bufTJ == null) {
  246. bufTJ = new StringBuffer();
  247. }
  248. if (bufTJ.length() > 0) {
  249. bufTJ.append(endText).append(" ");
  250. }
  251. if (bufTJ.length() == 0) {
  252. bufTJ.append("[");
  253. }
  254. bufTJ.append(PDFNumber.doubleOut(adjust, DEC - 4));
  255. bufTJ.append(" ");
  256. bufTJ.append(startText);
  257. }
  258. /**
  259. * Writes a "TJ" command, writing out the accumulated buffer with the characters and glyph
  260. * positioning values. The buffer is reset afterwards.
  261. */
  262. public void writeTJ() {
  263. if (isInString()) {
  264. bufTJ.append(endText).append("] TJ\n");
  265. write(bufTJ.toString());
  266. bufTJ.setLength(0);
  267. }
  268. }
  269. private boolean isInString() {
  270. return bufTJ != null && bufTJ.length() > 0;
  271. }
  272. }