1 17 18 19 20 package org.apache.fop.render.ps; 21 22 import java.awt.Graphics2D ; 23 import java.awt.geom.Point2D ; 24 import java.awt.geom.Rectangle2D ; 25 27 28 import java.text.AttributedCharacterIterator ; 29 import java.text.CharacterIterator ; 30 import java.awt.font.TextAttribute ; 31 import java.awt.Shape ; 32 import java.awt.Paint ; 33 import java.awt.Stroke ; 34 import java.awt.Color ; 35 import java.io.IOException ; 36 import java.util.List ; 37 import java.util.Iterator ; 38 39 import org.apache.commons.logging.Log; 40 import org.apache.commons.logging.LogFactory; 41 42 import org.apache.xmlgraphics.java2d.ps.PSGraphics2D; 43 44 import org.apache.batik.dom.svg.SVGOMTextElement; 45 import org.apache.batik.gvt.text.Mark; 46 import org.apache.batik.gvt.TextPainter; 47 import org.apache.batik.gvt.TextNode; 48 import org.apache.batik.gvt.text.GVTAttributedCharacterIterator; 49 import org.apache.batik.gvt.text.TextPaintInfo; 50 import org.apache.batik.gvt.font.GVTFontFamily; 51 import org.apache.batik.gvt.renderer.StrokingTextPainter; 52 53 import org.apache.fop.fonts.Font; 54 import org.apache.fop.fonts.FontInfo; 55 import org.apache.fop.fonts.FontTriplet; 56 57 71 public class PSTextPainter implements TextPainter { 72 73 74 protected Log log = LogFactory.getLog(PSTextPainter.class); 75 76 private NativeTextHandler nativeTextHandler; 77 79 83 protected static final TextPainter 84 PROXY_PAINTER = StrokingTextPainter.getInstance(); 85 86 90 public PSTextPainter(NativeTextHandler nativeTextHandler) { 91 this.nativeTextHandler = nativeTextHandler; 92 } 93 94 100 public void paint(TextNode node, Graphics2D g2d) { 101 String txt = node.getText(); 102 Point2D loc = node.getLocation(); 103 104 if (hasUnsupportedAttributes(node)) { 105 PROXY_PAINTER.paint(node, g2d); 106 } else { 107 paintTextRuns(node.getTextRuns(), g2d, loc); 108 } 109 } 110 111 112 private boolean hasUnsupportedAttributes(TextNode node) { 113 Iterator i = node.getTextRuns().iterator(); 114 while (i.hasNext()) { 115 StrokingTextPainter.TextRun 116 run = (StrokingTextPainter.TextRun)i.next(); 117 AttributedCharacterIterator aci = run.getACI(); 118 boolean hasUnsupported = hasUnsupportedAttributes(aci); 119 if (hasUnsupported) { 120 return true; 121 } 122 } 123 return false; 124 } 125 126 private boolean hasUnsupportedAttributes(AttributedCharacterIterator aci) { 127 boolean hasunsupported = false; 128 129 String text = getText(aci); 130 Font font = makeFont(aci); 131 if (hasUnsupportedGlyphs(text, font)) { 132 log.trace("-> Unsupported glyphs found"); 133 hasunsupported = true; 134 } 135 136 TextPaintInfo tpi = (TextPaintInfo) aci.getAttribute( 137 GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO); 138 if ((tpi != null) 139 && ((tpi.strokeStroke != null && tpi.strokePaint != null) 140 || (tpi.strikethroughStroke != null) 141 || (tpi.underlineStroke != null) 142 || (tpi.overlineStroke != null))) { 143 log.trace("-> under/overlines etc. found"); 144 hasunsupported = true; 145 } 146 147 Paint foreground = (Paint ) aci.getAttribute(TextAttribute.FOREGROUND); 149 if (foreground instanceof Color ) { 150 Color col = (Color )foreground; 151 if (col.getAlpha() != 255) { 152 log.trace("-> transparency found"); 153 hasunsupported = true; 154 } 155 } 156 157 Object letSpace = aci.getAttribute( 158 GVTAttributedCharacterIterator.TextAttribute.LETTER_SPACING); 159 if (letSpace != null) { 160 log.trace("-> letter spacing found"); 161 hasunsupported = true; 162 } 163 164 Object wordSpace = aci.getAttribute( 165 GVTAttributedCharacterIterator.TextAttribute.WORD_SPACING); 166 if (wordSpace != null) { 167 log.trace("-> word spacing found"); 168 hasunsupported = true; 169 } 170 171 Object lengthAdjust = aci.getAttribute( 172 GVTAttributedCharacterIterator.TextAttribute.LENGTH_ADJUST); 173 if (lengthAdjust != null) { 174 log.trace("-> length adjustments found"); 175 hasunsupported = true; 176 } 177 178 Object writeMod = aci.getAttribute( 179 GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE); 180 if (writeMod != null 181 && !GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE_LTR.equals( 182 writeMod)) { 183 log.trace("-> Unsupported writing modes found"); 184 hasunsupported = true; 185 } 186 187 Object vertOr = aci.getAttribute( 188 GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION); 189 if (GVTAttributedCharacterIterator.TextAttribute.ORIENTATION_ANGLE.equals( 190 vertOr)) { 191 log.trace("-> vertical orientation found"); 192 hasunsupported = true; 193 } 194 195 Object rcDel = aci.getAttribute( 196 GVTAttributedCharacterIterator.TextAttribute.TEXT_COMPOUND_DELIMITER); 197 if ( !(rcDel instanceof SVGOMTextElement)) { 200 log.trace("-> spans found"); 201 hasunsupported = true; } 203 204 if (hasunsupported) { 205 log.trace("Unsupported attributes found in ACI, using StrokingTextPainter"); 206 } 207 return hasunsupported; 208 } 209 210 216 protected void paintTextRuns(List textRuns, Graphics2D g2d, Point2D loc) { 217 Point2D currentloc = loc; 218 Iterator i = textRuns.iterator(); 219 while (i.hasNext()) { 220 StrokingTextPainter.TextRun 221 run = (StrokingTextPainter.TextRun)i.next(); 222 currentloc = paintTextRun(run, g2d, currentloc); 223 } 224 } 225 226 233 protected Point2D paintTextRun(StrokingTextPainter.TextRun run, Graphics2D g2d, Point2D loc) { 234 AttributedCharacterIterator aci = run.getACI(); 235 return paintACI(aci, g2d, loc); 236 } 237 238 243 protected String getText(AttributedCharacterIterator aci) { 244 StringBuffer sb = new StringBuffer (aci.getEndIndex() - aci.getBeginIndex()); 245 for (char c = aci.first(); c != CharacterIterator.DONE; c = aci.next()) { 246 sb.append(c); 247 } 248 return sb.toString(); 249 } 250 251 259 protected Point2D paintACI(AttributedCharacterIterator aci, Graphics2D g2d, Point2D loc) { 260 262 aci.first(); 263 264 updateLocationFromACI(aci, loc); 265 266 TextPaintInfo tpi = (TextPaintInfo) aci.getAttribute( 267 GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO); 268 269 if (tpi == null) { 270 return loc; 271 } 272 273 TextNode.Anchor anchor = (TextNode.Anchor)aci.getAttribute( 274 GVTAttributedCharacterIterator.TextAttribute.ANCHOR_TYPE); 275 276 List gvtFonts = (List )aci.getAttribute( 278 GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES); 279 Paint foreground = tpi.fillPaint; 280 Paint strokePaint = tpi.strokePaint; 281 Stroke stroke = tpi.strokeStroke; 282 283 Float fontSize = (Float )aci.getAttribute(TextAttribute.SIZE); 284 if (fontSize == null) { 285 return loc; 286 } 287 Float posture = (Float )aci.getAttribute(TextAttribute.POSTURE); 288 Float taWeight = (Float )aci.getAttribute(TextAttribute.WEIGHT); 289 290 if (foreground instanceof Color ) { 291 Color col = (Color )foreground; 292 g2d.setColor(col); 293 } 294 g2d.setPaint(foreground); 295 g2d.setStroke(stroke); 296 297 Font font = makeFont(aci); 298 java.awt.Font awtFont = makeAWTFont(aci, font); 299 300 g2d.setFont(awtFont); 301 302 String txt = getText(aci); 303 float advance = getStringWidth(txt, font); 304 float tx = 0; 305 if (anchor != null) { 306 switch (anchor.getType()) { 307 case TextNode.Anchor.ANCHOR_MIDDLE: 308 tx = -advance / 2; 309 break; 310 case TextNode.Anchor.ANCHOR_END: 311 tx = -advance; 312 break; 313 default: } 315 } 316 317 nativeTextHandler.setOverrideFont(font); 319 try { 320 try { 321 nativeTextHandler.drawString(txt, (float)(loc.getX() + tx), (float)(loc.getY())); 322 } catch (IOException ioe) { 323 if (g2d instanceof PSGraphics2D) { 324 ((PSGraphics2D)g2d).handleIOException(ioe); 325 } 326 } 327 } finally { 328 nativeTextHandler.setOverrideFont(null); 329 } 330 loc.setLocation(loc.getX() + (double)advance, loc.getY()); 331 return loc; 332 } 333 334 private void updateLocationFromACI( 335 AttributedCharacterIterator aci, 336 Point2D loc) { 337 Float xpos = (Float )aci.getAttribute( 339 GVTAttributedCharacterIterator.TextAttribute.X); 340 Float ypos = (Float )aci.getAttribute( 341 GVTAttributedCharacterIterator.TextAttribute.Y); 342 Float dxpos = (Float )aci.getAttribute( 343 GVTAttributedCharacterIterator.TextAttribute.DX); 344 Float dypos = (Float )aci.getAttribute( 345 GVTAttributedCharacterIterator.TextAttribute.DY); 346 if (xpos != null) { 347 loc.setLocation(xpos.doubleValue(), loc.getY()); 348 } 349 if (ypos != null) { 350 loc.setLocation(loc.getX(), ypos.doubleValue()); 351 } 352 if (dxpos != null) { 353 loc.setLocation(loc.getX() + dxpos.doubleValue(), loc.getY()); 354 } 355 if (dypos != null) { 356 loc.setLocation(loc.getX(), loc.getY() + dypos.doubleValue()); 357 } 358 } 359 360 private String getStyle(AttributedCharacterIterator aci) { 361 Float posture = (Float )aci.getAttribute(TextAttribute.POSTURE); 362 return ((posture != null) && (posture.floatValue() > 0.0)) 363 ? "italic" 364 : "normal"; 365 } 366 367 private int getWeight(AttributedCharacterIterator aci) { 368 Float taWeight = (Float )aci.getAttribute(TextAttribute.WEIGHT); 369 return ((taWeight != null) && (taWeight.floatValue() > 1.0)) 370 ? Font.BOLD 371 : Font.NORMAL; 372 } 373 374 private Font makeFont(AttributedCharacterIterator aci) { 375 Float fontSize = (Float )aci.getAttribute(TextAttribute.SIZE); 376 if (fontSize == null) { 377 fontSize = new Float (10.0f); 378 } 379 String style = getStyle(aci); 380 int weight = getWeight(aci); 381 382 boolean found = false; 383 FontInfo fontInfo = nativeTextHandler.getFontInfo(); 384 String fontFamily = null; 385 List gvtFonts = (List ) aci.getAttribute( 386 GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES); 387 if (gvtFonts != null) { 388 Iterator i = gvtFonts.iterator(); 389 while (i.hasNext()) { 390 GVTFontFamily fam = (GVTFontFamily) i.next(); 391 396 fontFamily = fam.getFamilyName(); 397 if (fontInfo.hasFont(fontFamily, style, weight)) { 398 FontTriplet triplet = fontInfo.fontLookup( 399 fontFamily, style, weight); 400 int fsize = (int)(fontSize.floatValue() * 1000); 401 return fontInfo.getFontInstance(triplet, fsize); 402 } 403 } 404 } 405 FontTriplet triplet = fontInfo.fontLookup("any", style, Font.NORMAL); 406 int fsize = (int)(fontSize.floatValue() * 1000); 407 return fontInfo.getFontInstance(triplet, fsize); 408 } 409 410 private java.awt.Font makeAWTFont(AttributedCharacterIterator aci, Font font) { 411 final String style = getStyle(aci); 412 final int weight = getWeight(aci); 413 int fStyle = java.awt.Font.PLAIN; 414 if (weight == Font.BOLD) { 415 fStyle |= java.awt.Font.BOLD; 416 } 417 if ("italic".equals(style)) { 418 fStyle |= java.awt.Font.ITALIC; 419 } 420 return new java.awt.Font (font.getFontName(), fStyle, 421 (int)(font.getFontSize() / 1000)); 422 } 423 424 private float getStringWidth(String str, Font font) { 425 float wordWidth = 0; 426 float whitespaceWidth = font.getWidth(font.mapChar(' ')); 427 428 for (int i = 0; i < str.length(); i++) { 429 float charWidth; 430 char c = str.charAt(i); 431 if (!((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t'))) { 432 charWidth = font.getWidth(font.mapChar(c)); 433 if (charWidth <= 0) { 434 charWidth = whitespaceWidth; 435 } 436 } else { 437 charWidth = whitespaceWidth; 438 } 439 wordWidth += charWidth; 440 } 441 return wordWidth / 1000f; 442 } 443 444 private boolean hasUnsupportedGlyphs(String str, Font font) { 445 for (int i = 0; i < str.length(); i++) { 446 float charWidth; 447 char c = str.charAt(i); 448 if (!((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t'))) { 449 if (!font.hasChar(c)) { 450 return true; 451 } 452 } 453 } 454 return false; 455 } 456 457 465 public Shape getOutline(TextNode node) { 466 return PROXY_PAINTER.getOutline(node); 467 } 468 469 477 public Rectangle2D getBounds2D(TextNode node) { 478 481 return PROXY_PAINTER.getBounds2D(node); 482 } 483 484 491 public Rectangle2D getGeometryBounds(TextNode node) { 492 return PROXY_PAINTER.getGeometryBounds(node); 493 } 494 495 497 505 public Mark getMark(TextNode node, int pos, boolean all) { 506 return null; 507 } 508 509 517 public Mark selectAt(double x, double y, TextNode node) { 518 return null; 519 } 520 521 529 public Mark selectTo(double x, double y, Mark beginMark) { 530 return null; 531 } 532 533 539 public Mark selectFirst(TextNode node) { 540 return null; 541 } 542 543 549 public Mark selectLast(TextNode node) { 550 return null; 551 } 552 553 560 public int[] getSelected(Mark start, Mark finish) { 561 return null; 562 } 563 564 571 public Shape getHighlightShape(Mark beginMark, Mark endMark) { 572 return null; 573 } 574 575 } 576 577 578 | Popular Tags |