You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PDFTextPainter.java 8.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  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.svg;
  19. import java.awt.BasicStroke;
  20. import java.awt.Color;
  21. import java.awt.Graphics2D;
  22. import java.awt.Paint;
  23. import java.awt.Shape;
  24. import java.awt.Stroke;
  25. import java.awt.geom.AffineTransform;
  26. import java.awt.geom.Ellipse2D;
  27. import java.awt.geom.GeneralPath;
  28. import java.awt.geom.Point2D;
  29. import java.text.AttributedCharacterIterator;
  30. import org.apache.batik.gvt.font.GVTGlyphVector;
  31. import org.apache.batik.gvt.text.TextPaintInfo;
  32. import org.apache.batik.gvt.text.TextSpanLayout;
  33. import org.apache.fop.fonts.Font;
  34. import org.apache.fop.fonts.FontInfo;
  35. import org.apache.fop.util.CharUtilities;
  36. /**
  37. * Renders the attributed character iterator of a {@link org.apache.batik.gvt.TextNode}.
  38. * This class draws the text directly into the PDFGraphics2D so that
  39. * the text is not drawn using shapes which makes the PDF files larger.
  40. * If the text is simple enough to draw then it sets the font and calls
  41. * drawString. If the text is complex or the cannot be translated
  42. * into a simple drawString the StrokingTextPainter is used instead.
  43. *
  44. * @version $Id$
  45. */
  46. class PDFTextPainter extends NativeTextPainter {
  47. private static final boolean DEBUG = false;
  48. /**
  49. * Create a new PDF text painter with the given font information.
  50. * @param fi the font info
  51. */
  52. public PDFTextPainter(FontInfo fi) {
  53. super(fi);
  54. }
  55. /** {@inheritDoc} */
  56. @Override
  57. protected boolean isSupported(Graphics2D g2d) {
  58. return g2d instanceof PDFGraphics2D;
  59. }
  60. /** {@inheritDoc} */
  61. @Override
  62. protected void paintTextRun(TextRun textRun, Graphics2D g2d) {
  63. AttributedCharacterIterator runaci = textRun.getACI();
  64. runaci.first();
  65. TextPaintInfo tpi = (TextPaintInfo)runaci.getAttribute(PAINT_INFO);
  66. if (tpi == null || !tpi.visible) {
  67. return;
  68. }
  69. if ((tpi != null) && (tpi.composite != null)) {
  70. g2d.setComposite(tpi.composite);
  71. }
  72. //------------------------------------
  73. TextSpanLayout layout = textRun.getLayout();
  74. logTextRun(runaci, layout);
  75. CharSequence chars = collectCharacters(runaci);
  76. runaci.first(); //Reset ACI
  77. final PDFGraphics2D pdf = (PDFGraphics2D)g2d;
  78. PDFTextUtil textUtil = new PDFTextUtil(pdf.fontInfo) {
  79. @Override
  80. protected void write(String code) {
  81. pdf.currentStream.write(code);
  82. }
  83. };
  84. if (DEBUG) {
  85. log.debug("Text: " + chars);
  86. pdf.currentStream.write("%Text: " + chars + "\n");
  87. }
  88. GeneralPath debugShapes = null;
  89. if (DEBUG) {
  90. debugShapes = new GeneralPath();
  91. }
  92. Font[] fonts = findFonts(runaci);
  93. if (fonts == null || fonts.length == 0) {
  94. //Draw using Java2D when no native fonts are available
  95. textRun.getLayout().draw(g2d);
  96. return;
  97. }
  98. pdf.saveGraphicsState();
  99. textUtil.concatMatrix(g2d.getTransform());
  100. Shape imclip = g2d.getClip();
  101. pdf.writeClip(imclip);
  102. applyColorAndPaint(tpi, pdf);
  103. textUtil.beginTextObject();
  104. textUtil.setFonts(fonts);
  105. boolean stroke = (tpi.strokePaint != null)
  106. && (tpi.strokeStroke != null);
  107. textUtil.setTextRenderingMode(tpi.fillPaint != null, stroke, false);
  108. AffineTransform localTransform = new AffineTransform();
  109. Point2D prevPos = null;
  110. double prevVisibleCharWidth = 0.0;
  111. GVTGlyphVector gv = layout.getGlyphVector();
  112. for (int index = 0, c = gv.getNumGlyphs(); index < c; index++) {
  113. char ch = chars.charAt(index);
  114. boolean visibleChar = gv.isGlyphVisible(index)
  115. || (CharUtilities.isAnySpace(ch) && !CharUtilities.isZeroWidthSpace(ch));
  116. logCharacter(ch, layout, index, visibleChar);
  117. if (!visibleChar) {
  118. continue;
  119. }
  120. Point2D glyphPos = gv.getGlyphPosition(index);
  121. AffineTransform glyphTransform = gv.getGlyphTransform(index);
  122. //TODO Glyph transforms could be refined so not every char has to be painted
  123. //with its own TJ command (stretch/squeeze case could be optimized)
  124. if (log.isTraceEnabled()) {
  125. log.trace("pos " + glyphPos + ", transform " + glyphTransform);
  126. }
  127. if (DEBUG) {
  128. Shape sh = gv.getGlyphLogicalBounds(index);
  129. if (sh == null) {
  130. sh = new Ellipse2D.Double(glyphPos.getX(), glyphPos.getY(), 2, 2);
  131. }
  132. debugShapes.append(sh, false);
  133. }
  134. //Exact position of the glyph
  135. localTransform.setToIdentity();
  136. localTransform.translate(glyphPos.getX(), glyphPos.getY());
  137. if (glyphTransform != null) {
  138. localTransform.concatenate(glyphTransform);
  139. }
  140. localTransform.scale(1, -1);
  141. boolean yPosChanged = (prevPos == null
  142. || prevPos.getY() != glyphPos.getY()
  143. || glyphTransform != null);
  144. if (yPosChanged) {
  145. if (index > 0) {
  146. textUtil.writeTJ();
  147. textUtil.writeTextMatrix(localTransform);
  148. }
  149. } else {
  150. double xdiff = glyphPos.getX() - prevPos.getX();
  151. //Width of previous character
  152. Font font = textUtil.getCurrentFont();
  153. double cw = prevVisibleCharWidth;
  154. double effxdiff = (1000 * xdiff) - cw;
  155. if (effxdiff != 0) {
  156. double adjust = (-effxdiff / font.getFontSize());
  157. textUtil.adjustGlyphTJ(adjust * 1000);
  158. }
  159. if (log.isTraceEnabled()) {
  160. log.trace("==> x diff: " + xdiff + ", " + effxdiff
  161. + ", charWidth: " + cw);
  162. }
  163. }
  164. Font f = textUtil.selectFontForChar(ch);
  165. if (f != textUtil.getCurrentFont()) {
  166. textUtil.writeTJ();
  167. textUtil.setCurrentFont(f);
  168. textUtil.writeTf(f);
  169. textUtil.writeTextMatrix(localTransform);
  170. }
  171. char paintChar = (CharUtilities.isAnySpace(ch) ? ' ' : ch);
  172. textUtil.writeTJChar(paintChar);
  173. //Update last position
  174. prevPos = glyphPos;
  175. prevVisibleCharWidth = textUtil.getCurrentFont().getCharWidth(chars.charAt(index));
  176. }
  177. textUtil.writeTJ();
  178. textUtil.endTextObject();
  179. pdf.restoreGraphicsState();
  180. if (DEBUG) {
  181. g2d.setStroke(new BasicStroke(0));
  182. g2d.setColor(Color.LIGHT_GRAY);
  183. g2d.draw(debugShapes);
  184. }
  185. }
  186. private void applyColorAndPaint(TextPaintInfo tpi, PDFGraphics2D pdf) {
  187. Paint fillPaint = tpi.fillPaint;
  188. Paint strokePaint = tpi.strokePaint;
  189. Stroke stroke = tpi.strokeStroke;
  190. int fillAlpha = PDFGraphics2D.OPAQUE;
  191. if (fillPaint instanceof Color) {
  192. Color col = (Color)fillPaint;
  193. pdf.applyColor(col, true);
  194. fillAlpha = col.getAlpha();
  195. }
  196. if (strokePaint instanceof Color) {
  197. Color col = (Color)strokePaint;
  198. pdf.applyColor(col, false);
  199. }
  200. pdf.applyPaint(fillPaint, true);
  201. pdf.applyStroke(stroke);
  202. if (strokePaint != null) {
  203. pdf.applyPaint(strokePaint, false);
  204. }
  205. pdf.applyAlpha(fillAlpha, PDFGraphics2D.OPAQUE);
  206. }
  207. }