Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

PSTextPainter.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. /*
  2. * $Id$
  3. * ============================================================================
  4. * The Apache Software License, Version 1.1
  5. * ============================================================================
  6. *
  7. * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
  8. *
  9. * Redistribution and use in source and binary forms, with or without modifica-
  10. * tion, are permitted provided that the following conditions are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright notice,
  13. * this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright notice,
  16. * this list of conditions and the following disclaimer in the documentation
  17. * and/or other materials provided with the distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution, if any, must
  20. * include the following acknowledgment: "This product includes software
  21. * developed by the Apache Software Foundation (http://www.apache.org/)."
  22. * Alternately, this acknowledgment may appear in the software itself, if
  23. * and wherever such third-party acknowledgments normally appear.
  24. *
  25. * 4. The names "FOP" and "Apache Software Foundation" must not be used to
  26. * endorse or promote products derived from this software without prior
  27. * written permission. For written permission, please contact
  28. * apache@apache.org.
  29. *
  30. * 5. Products derived from this software may not be called "Apache", nor may
  31. * "Apache" appear in their name, without prior written permission of the
  32. * Apache Software Foundation.
  33. *
  34. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
  35. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  36. * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  37. * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
  38. * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
  39. * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  40. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  41. * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  42. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  43. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  44. * ============================================================================
  45. *
  46. * This software consists of voluntary contributions made by many individuals
  47. * on behalf of the Apache Software Foundation and was originally created by
  48. * James Tauber <jtauber@jtauber.com>. For more information on the Apache
  49. * Software Foundation, please see <http://www.apache.org/>.
  50. */
  51. package org.apache.fop.render.ps;
  52. import java.awt.Graphics2D;
  53. import java.awt.geom.Point2D;
  54. import java.awt.geom.Rectangle2D;
  55. import java.awt.Font;
  56. import java.text.AttributedCharacterIterator;
  57. import java.awt.font.TextAttribute;
  58. import java.awt.Shape;
  59. import java.awt.Paint;
  60. import java.awt.Stroke;
  61. import java.awt.Color;
  62. import java.util.List;
  63. import java.util.Iterator;
  64. import org.apache.batik.gvt.text.Mark;
  65. import org.apache.batik.gvt.TextPainter;
  66. import org.apache.batik.gvt.TextNode;
  67. import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
  68. import org.apache.batik.gvt.font.GVTFontFamily;
  69. import org.apache.batik.bridge.SVGFontFamily;
  70. import org.apache.batik.gvt.renderer.StrokingTextPainter;
  71. import org.apache.fop.fonts.FontMetrics;
  72. import org.apache.fop.layout.FontState;
  73. import org.apache.fop.layout.FontInfo;
  74. /**
  75. * Renders the attributed character iterator of a <tt>TextNode</tt>.
  76. * This class draws the text directly into the PSGraphics2D so that
  77. * the text is not drawn using shapes which makes the PS files larger.
  78. * If the text is simple enough to draw then it sets the font and calls
  79. * drawString. If the text is complex or the cannot be translated
  80. * into a simple drawString the StrokingTextPainter is used instead.
  81. *
  82. * (todo) handle underline, overline and strikethrough
  83. * (todo) use drawString(AttributedCharacterIterator iterator...) for some
  84. *
  85. * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
  86. * @version $Id: PSTextPainter.java,v 1.15 2003/01/08 14:03:55 jeremias Exp $
  87. */
  88. public class PSTextPainter implements TextPainter {
  89. private FontInfo fontInfo;
  90. /**
  91. * Use the stroking text painter to get the bounds and shape.
  92. * Also used as a fallback to draw the string with strokes.
  93. */
  94. protected static final TextPainter PROXY_PAINTER =
  95. StrokingTextPainter.getInstance();
  96. /**
  97. * Create a new PS text painter with the given font information.
  98. * @param fi the fint info
  99. */
  100. public PSTextPainter(FontInfo fi) {
  101. fontInfo = fi;
  102. }
  103. /**
  104. * Paints the specified attributed character iterator using the
  105. * specified Graphics2D and context and font context.
  106. * @param node the TextNode to paint
  107. * @param g2d the Graphics2D to use
  108. */
  109. public void paint(TextNode node, Graphics2D g2d) {
  110. // System.out.println("PSText paint");
  111. String txt = node.getText();
  112. Point2D loc = node.getLocation();
  113. AttributedCharacterIterator aci =
  114. node.getAttributedCharacterIterator();
  115. // reset position to start of char iterator
  116. if (aci.getBeginIndex() == aci.getEndIndex()) {
  117. return;
  118. }
  119. char ch = aci.first();
  120. if (ch == AttributedCharacterIterator.DONE) {
  121. return;
  122. }
  123. TextNode.Anchor anchor;
  124. anchor = (TextNode.Anchor) aci.getAttribute(
  125. GVTAttributedCharacterIterator.TextAttribute.ANCHOR_TYPE);
  126. List gvtFonts;
  127. gvtFonts = (List) aci.getAttribute(
  128. GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES);
  129. Paint forg = (Paint) aci.getAttribute(TextAttribute.FOREGROUND);
  130. Paint strokePaint;
  131. strokePaint = (Paint) aci.getAttribute(
  132. GVTAttributedCharacterIterator.TextAttribute.STROKE_PAINT);
  133. Float size = (Float) aci.getAttribute(TextAttribute.SIZE);
  134. if (size == null) {
  135. return;
  136. }
  137. Stroke stroke = (Stroke) aci.getAttribute(
  138. GVTAttributedCharacterIterator.TextAttribute.STROKE);
  139. /*
  140. Float xpos = (Float) aci.getAttribute(
  141. GVTAttributedCharacterIterator.TextAttribute.X);
  142. Float ypos = (Float) aci.getAttribute(
  143. GVTAttributedCharacterIterator.TextAttribute.Y);
  144. */
  145. Float posture = (Float) aci.getAttribute(TextAttribute.POSTURE);
  146. Float taWeight = (Float) aci.getAttribute(TextAttribute.WEIGHT);
  147. boolean useStrokePainter = false;
  148. if (forg instanceof Color) {
  149. Color col = (Color) forg;
  150. if (col.getAlpha() != 255) {
  151. useStrokePainter = true;
  152. }
  153. g2d.setColor(col);
  154. }
  155. g2d.setPaint(forg);
  156. g2d.setStroke(stroke);
  157. if (strokePaint != null) {
  158. // need to draw using AttributedCharacterIterator
  159. useStrokePainter = true;
  160. }
  161. if (hasUnsupportedAttributes(aci)) {
  162. useStrokePainter = true;
  163. }
  164. // text contains unsupported information
  165. if (useStrokePainter) {
  166. PROXY_PAINTER.paint(node, g2d);
  167. return;
  168. }
  169. String style = ((posture != null) && (posture.floatValue() > 0.0))
  170. ? "italic" : "normal";
  171. int weight = ((taWeight != null)
  172. && (taWeight.floatValue() > 1.0)) ? FontInfo.BOLD
  173. : FontInfo.NORMAL;
  174. FontState fontState = null;
  175. FontInfo fi = fontInfo;
  176. boolean found = false;
  177. String fontFamily = null;
  178. if (gvtFonts != null) {
  179. Iterator i = gvtFonts.iterator();
  180. while (i.hasNext()) {
  181. GVTFontFamily fam = (GVTFontFamily) i.next();
  182. if (fam instanceof SVGFontFamily) {
  183. PROXY_PAINTER.paint(node, g2d);
  184. return;
  185. }
  186. fontFamily = fam.getFamilyName();
  187. if (fi.hasFont(fontFamily, style, weight)) {
  188. String fname = fontInfo.fontLookup(fontFamily, style,
  189. weight);
  190. FontMetrics metrics = fontInfo.getMetricsFor(fname);
  191. int fsize = (int)(size.floatValue() * 1000);
  192. fontState = new FontState(fname, metrics, fsize);
  193. found = true;
  194. break;
  195. }
  196. }
  197. }
  198. if (!found) {
  199. String fname =
  200. fontInfo.fontLookup("any", style, FontInfo.NORMAL);
  201. FontMetrics metrics = fontInfo.getMetricsFor(fname);
  202. int fsize = (int)(size.floatValue() * 1000);
  203. fontState = new FontState(fname, metrics, fsize);
  204. } else {
  205. if (g2d instanceof PSGraphics2D) {
  206. ((PSGraphics2D) g2d).setOverrideFontState(fontState);
  207. }
  208. }
  209. int fStyle = Font.PLAIN;
  210. if (weight == FontInfo.BOLD) {
  211. if (style.equals("italic")) {
  212. fStyle = Font.BOLD | Font.ITALIC;
  213. } else {
  214. fStyle = Font.BOLD;
  215. }
  216. } else {
  217. if (style.equals("italic")) {
  218. fStyle = Font.ITALIC;
  219. } else {
  220. fStyle = Font.PLAIN;
  221. }
  222. }
  223. Font font = new Font(fontFamily, fStyle,
  224. (int)(fontState.getFontSize() / 1000));
  225. g2d.setFont(font);
  226. float advance = getStringWidth(txt, fontState);
  227. float tx = 0;
  228. if (anchor != null) {
  229. switch (anchor.getType()) {
  230. case TextNode.Anchor.ANCHOR_MIDDLE:
  231. tx = -advance / 2;
  232. break;
  233. case TextNode.Anchor.ANCHOR_END:
  234. tx = -advance;
  235. }
  236. }
  237. g2d.drawString(txt, (float)(loc.getX() + tx), (float)(loc.getY()));
  238. }
  239. private boolean hasUnsupportedAttributes(AttributedCharacterIterator aci) {
  240. boolean hasunsupported = false;
  241. Object letSpace = aci.getAttribute(
  242. GVTAttributedCharacterIterator.TextAttribute.LETTER_SPACING);
  243. if (letSpace != null) {
  244. hasunsupported = true;
  245. }
  246. Object wordSpace = aci.getAttribute(
  247. GVTAttributedCharacterIterator.TextAttribute.WORD_SPACING);
  248. if (wordSpace != null) {
  249. hasunsupported = true;
  250. }
  251. AttributedCharacterIterator.Attribute key;
  252. key = GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE;
  253. Object writeMod = aci.getAttribute(key);
  254. if (!GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE_LTR.equals(
  255. writeMod)) {
  256. hasunsupported = true;
  257. }
  258. Object vertOr = aci.getAttribute(
  259. GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION);
  260. if (GVTAttributedCharacterIterator.TextAttribute.ORIENTATION_ANGLE.equals(
  261. vertOr)) {
  262. hasunsupported = true;
  263. }
  264. return hasunsupported;
  265. }
  266. private float getStringWidth(String str, FontState fontState) {
  267. float wordWidth = 0;
  268. float whitespaceWidth = fontState.getWidth(fontState.mapChar(' '));
  269. for (int i = 0; i < str.length(); i++) {
  270. float charWidth;
  271. char c = str.charAt(i);
  272. if (!((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t'))) {
  273. charWidth = fontState.getWidth(fontState.mapChar(c));
  274. if (charWidth <= 0) {
  275. charWidth = whitespaceWidth;
  276. }
  277. } else {
  278. charWidth = whitespaceWidth;
  279. }
  280. wordWidth += charWidth;
  281. }
  282. return wordWidth / 1000f;
  283. }
  284. /**
  285. * Get the outline shape of the text characters.
  286. * This uses the StrokingTextPainter to get the outline
  287. * shape since in theory it should be the same.
  288. *
  289. * @param node the text node
  290. * @return the outline shape of the text characters
  291. */
  292. public Shape getOutline(TextNode node) {
  293. return PROXY_PAINTER.getOutline(node);
  294. }
  295. /**
  296. * Get the bounds.
  297. * This uses the StrokingTextPainter to get the bounds
  298. * since in theory it should be the same.
  299. *
  300. * @param node the text node
  301. * @return the bounds of the text
  302. */
  303. public Rectangle2D getBounds2D(TextNode node) {
  304. return PROXY_PAINTER.getBounds2D(node);
  305. }
  306. /**
  307. * Get the geometry bounds.
  308. * This uses the StrokingTextPainter to get the bounds
  309. * since in theory it should be the same.
  310. * @param node the text node
  311. * @return the bounds of the text
  312. */
  313. public Rectangle2D getGeometryBounds(TextNode node) {
  314. return PROXY_PAINTER.getGeometryBounds(node);
  315. }
  316. // Methods that have no purpose for PS
  317. /**
  318. * Get the mark.
  319. * This does nothing since the output is pdf and not interactive.
  320. * @param node the text node
  321. * @param pos the position
  322. * @param all select all
  323. * @return null
  324. */
  325. public Mark getMark(TextNode node, int pos, boolean all) {
  326. System.out.println("PSText getMark");
  327. return null;
  328. }
  329. /**
  330. * Select at.
  331. * This does nothing since the output is pdf and not interactive.
  332. * @param x the x position
  333. * @param y the y position
  334. * @param node the text node
  335. * @return null
  336. */
  337. public Mark selectAt(double x, double y, TextNode node) {
  338. System.out.println("PSText selectAt");
  339. return null;
  340. }
  341. /**
  342. * Select to.
  343. * This does nothing since the output is pdf and not interactive.
  344. * @param x the x position
  345. * @param y the y position
  346. * @param beginMark the start mark
  347. * @return null
  348. */
  349. public Mark selectTo(double x, double y, Mark beginMark) {
  350. System.out.println("PSText selectTo");
  351. return null;
  352. }
  353. /**
  354. * Selec first.
  355. * This does nothing since the output is pdf and not interactive.
  356. * @param node the text node
  357. * @return null
  358. */
  359. public Mark selectFirst(TextNode node) {
  360. System.out.println("PSText selectFirst");
  361. return null;
  362. }
  363. /**
  364. * Select last.
  365. * This does nothing since the output is pdf and not interactive.
  366. * @param node the text node
  367. * @return null
  368. */
  369. public Mark selectLast(TextNode node) {
  370. System.out.println("PSText selectLast");
  371. return null;
  372. }
  373. /**
  374. * Get selected.
  375. * This does nothing since the output is pdf and not interactive.
  376. * @param start the start mark
  377. * @param finish the finish mark
  378. * @return null
  379. */
  380. public int[] getSelected(Mark start, Mark finish) {
  381. System.out.println("PSText getSelected");
  382. return null;
  383. }
  384. /**
  385. * Get the highlighted shape.
  386. * This does nothing since the output is pdf and not interactive.
  387. * @param beginMark the start mark
  388. * @param endMark the end mark
  389. * @return null
  390. */
  391. public Shape getHighlightShape(Mark beginMark, Mark endMark) {
  392. System.out.println("PSText getHighlightShape");
  393. return null;
  394. }
  395. }