/* * $Id$ * Copyright (C) 2001 The Apache Software Foundation. All rights reserved. * For details on use and redistribution please refer to the * LICENSE file included with these sources. */ package org.apache.fop.svg; import java.awt.Graphics2D; import java.awt.*; import java.text.AttributedCharacterIterator; import java.awt.font.FontRenderContext; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.Font; import java.text.AttributedCharacterIterator; import java.text.AttributedString; import java.text.CharacterIterator; import java.awt.font.TextLayout; import java.awt.font.TextAttribute; import java.util.ArrayList; import java.util.HashSet; import java.util.*; import java.util.Set; import org.apache.batik.gvt.text.Mark; import org.apache.batik.gvt.*; import org.apache.batik.gvt.text.*; import org.apache.batik.gvt.renderer.*; import org.apache.batik.gvt.font.*; import org.apache.fop.layout.*; /** * Renders the attributed character iterator of a TextNode. * * @author Keiron Liddle * @version $Id$ */ public class PDFTextPainter implements TextPainter { FontState fontState; public PDFTextPainter(FontState fs) { fontState = fs; } /** * Paints the specified attributed character iterator using the * specified Graphics2D and context and font context. * @param node the TextNode to paint * @param g2d the Graphics2D to use * @param context the rendering context. */ public void paint(TextNode node, Graphics2D g2d, GraphicsNodeRenderContext context) { // System.out.println("PDFText paint"); String txt = node.getText(); Point2D loc = node.getLocation(); AttributedCharacterIterator aci = node.getAttributedCharacterIterator(); // reset position to start of char iterator if (aci.getBeginIndex() == aci.getEndIndex()) { return; } char ch = aci.first(); if (ch == AttributedCharacterIterator.DONE) { return; } TextNode.Anchor anchor = (TextNode.Anchor)aci.getAttribute(GVTAttributedCharacterIterator.TextAttribute.ANCHOR_TYPE); Vector gvtFonts = (Vector)aci.getAttribute(GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES); Paint forg = (Paint)aci.getAttribute(TextAttribute.FOREGROUND); Float size = (Float)aci.getAttribute(TextAttribute.SIZE); Stroke stroke = (Stroke)aci.getAttribute(GVTAttributedCharacterIterator.TextAttribute.STROKE); Float xpos = (Float)aci.getAttribute(GVTAttributedCharacterIterator.TextAttribute.X); Float ypos = (Float)aci.getAttribute(GVTAttributedCharacterIterator.TextAttribute.Y); Float posture = (Float)aci.getAttribute(TextAttribute.POSTURE); Float taWeight = (Float)aci.getAttribute(TextAttribute.WEIGHT); if (forg instanceof Color) { g2d.setColor((Color)forg); } g2d.setPaint(forg); g2d.setStroke(stroke); String style = ((posture != null) && (posture.floatValue() > 0.0)) ? "italic" : "normal"; String weight = ((taWeight != null) && (taWeight.floatValue() > 1.0)) ? "bold" : "normal"; FontInfo fi = fontState.getFontInfo(); boolean found = false; if (gvtFonts != null) { for (Enumeration e = gvtFonts.elements(); e.hasMoreElements(); ) { GVTFontFamily fam = (GVTFontFamily)e.nextElement(); String name = fam.getFamilyName(); if (fi.hasFont(name, weight, style)) { try { int fsize = (int)size.floatValue(); fontState = new FontState(fontState.getFontInfo(), name, style, weight, fsize * 1000, 0); } catch (org.apache.fop.apps.FOPException fope) { fope.printStackTrace(); } found = true; break; } } } if (!found) { try { int fsize = (int)size.floatValue(); fontState = new FontState(fontState.getFontInfo(), "any", style, weight, fsize * 1000, 0); } catch (org.apache.fop.apps.FOPException fope) { fope.printStackTrace(); } } else { if(g2d instanceof PDFGraphics2D) { ((PDFGraphics2D)g2d).setOverrideFontState(fontState); } } int fStyle = Font.PLAIN; if (fontState.getFontWeight().equals("bold")) { if (fontState.getFontStyle().equals("italic")) { fStyle = Font.BOLD | Font.ITALIC; } else { fStyle = Font.BOLD; } } else { if (fontState.getFontStyle().equals("italic")) { fStyle = Font.ITALIC; } else { fStyle = Font.PLAIN; } } Font font = new Font(fontState.getFontFamily(), fStyle, (int)(fontState.getFontSize() / 1000)); g2d.setFont(font); float advance = getStringWidth(txt); float tx = 0; if (anchor != null) { switch (anchor.getType()) { case TextNode.Anchor.ANCHOR_MIDDLE: tx = -advance / 2; break; case TextNode.Anchor.ANCHOR_END: tx = -advance; } } g2d.drawString(txt, (float)(loc.getX() + tx), (float)(loc.getY())); } public float getStringWidth(String str) { float wordWidth = 0; float whitespaceWidth = fontState.width(fontState.mapChar(' ')); for (int i = 0; i < str.length(); i++) { float charWidth; char c = str.charAt(i); if (!((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t'))) { charWidth = fontState.width(fontState.mapChar(c)); if (charWidth <= 0) charWidth = whitespaceWidth; } else { charWidth = whitespaceWidth; } wordWidth += charWidth; } return wordWidth / 1000f; } /** * Initiates a text selection on a particular AttributedCharacterIterator, * using the text/font metrics employed by this TextPainter instance. * @param x the x coordinate, in the text layout's coordinate system, * of the selection event. * @param y the y coordinate, in the text layout's coordinate system, * of the selection event. * @param aci the AttributedCharacterIterator describing the text * @param context the GraphicsNodeRenderContext to use when doing text layout. * @return an instance of Mark which encapsulates the state necessary to * implement hit testing and text selection. */ public Mark selectAt(double x, double y, AttributedCharacterIterator aci, TextNode node, GraphicsNodeRenderContext context) { System.out.println("PDFText selectAt"); return null; } /** * Continues a text selection on a particular AttributedCharacterIterator, * using the text/font metrics employed by this TextPainter instance. * @param x the x coordinate, in the text layout's coordinate system, * of the selection event. * @param y the y coordinate, in the text layout's coordinate system, * of the selection event. * @param aci the AttributedCharacterIterator describing the text * @param context the GraphicsNodeRenderContext to use when doing text layout. * @return an instance of Mark which encapsulates the state necessary to * implement hit testing and text selection. */ public Mark selectTo(double x, double y, Mark beginMark, AttributedCharacterIterator aci, TextNode node, GraphicsNodeRenderContext context) { System.out.println("PDFText selectTo"); return null; } /** * Select all of the text represented by an AttributedCharacterIterator, * using the text/font metrics employed by this TextPainter instance. * @param x the x coordinate, in the text layout's coordinate system, * of the selection event. * @param y the y coordinate, in the text layout's coordinate system, * of the selection event. * @param aci the AttributedCharacterIterator describing the text * @param context the GraphicsNodeRenderContext to use when doing text layout. * @return an instance of Mark which encapsulates the state necessary to * implement hit testing and text selection. */ public Mark selectAll(double x, double y, AttributedCharacterIterator aci, TextNode node, GraphicsNodeRenderContext context) { System.out.println("PDFText selectAll"); return null; } /** * Selects the first glyph in the text node. */ public Mark selectFirst(double x, double y, AttributedCharacterIterator aci, TextNode node, GraphicsNodeRenderContext context) { System.out.println("PDFText selectFirst"); return null; } /** * Selects the last glyph in the text node. */ public Mark selectLast(double x, double y, AttributedCharacterIterator aci, TextNode node, GraphicsNodeRenderContext context) { System.out.println("PDFText selectLast"); return null; } /* * Get an array of index pairs corresponding to the indices within an * AttributedCharacterIterator regions bounded by two Marks. * Note that the instances of Mark passed to this function * must come * from the same TextPainter that generated them via selectAt() and * selectTo(), since the TextPainter implementation may rely on hidden * implementation details of its own Mark implementation. */ public int[] getSelected(AttributedCharacterIterator aci, Mark start, Mark finish) { System.out.println("PDFText getSelected"); return null; } /* * Get a Shape in userspace coords which encloses the textnode * glyphs bounded by two Marks. * Note that the instances of Mark passed to this function * must come * from the same TextPainter that generated them via selectAt() and * selectTo(), since the TextPainter implementation may rely on hidden * implementation details of its own Mark implementation. */ public Shape getHighlightShape(Mark beginMark, Mark endMark) { System.out.println("PDFText getHighlightShape"); return null; } /* * Get a Shape in userspace coords which defines the textnode glyph outlines. * @param node the TextNode to measure * @param frc the font rendering context. * @param includeDecoration whether to include text decoration * outlines. * @param includeStroke whether to create the "stroke shape outlines" * instead of glyph outlines. */ public Shape getShape(TextNode node, FontRenderContext frc) { System.out.println("PDFText getShape"); return null; } /* * Get a Shape in userspace coords which defines the textnode glyph outlines. * @param node the TextNode to measure * @param frc the font rendering context. * @param includeDecoration whether to include text decoration * outlines. * @param includeStroke whether to create the "stroke shape outlines" * instead of glyph outlines. */ public Shape getDecoratedShape(TextNode node, FontRenderContext frc) { System.out.println("PDFText getDecoratedShape"); return new Rectangle(1, 1); } /* * Get a Rectangle2D in userspace coords which encloses the textnode * glyphs composed from an AttributedCharacterIterator. * @param node the TextNode to measure * @param g2d the Graphics2D to use * @param context rendering context. */ public Rectangle2D getBounds(TextNode node, FontRenderContext frc) { System.out.println("PDFText getBounds"); return null; } /* * Get a Rectangle2D in userspace coords which encloses the textnode * glyphs composed from an AttributedCharacterIterator, inclusive of * glyph decoration (underline, overline, strikethrough). * @param node the TextNode to measure * @param g2d the Graphics2D to use * @param context rendering context. */ public Rectangle2D getDecoratedBounds(TextNode node, FontRenderContext frc) { System.out.println("PDFText getDecoratedBounds"); return null; } /* * Get a Rectangle2D in userspace coords which encloses the * textnode glyphs (as-painted, inclusive of decoration and stroke, but * exclusive of filters, etc.) composed from an AttributedCharacterIterator. * @param node the TextNode to measure * @param g2d the Graphics2D to use * @param context rendering context. */ public Rectangle2D getPaintedBounds(TextNode node, FontRenderContext frc) { // System.out.println("PDFText getPaintedBounds"); return null; } }