From 714492e8e4959e4151ef2ed1d41bf32ea83c9220 Mon Sep 17 00:00:00 2001 From: Adrian Cumiskey Date: Tue, 18 Nov 2008 14:13:51 +0000 Subject: [PATCH] Removed AbstractGraphics2DImagePainter and Graphics2DImagePainterGOCA (painting preparator mechanism). Adjusted AFPGraphics2D to TextHandler changes in XG commons. Factored an AbstractFOPTextPainter which is extended by AFPTextPainter. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_AFPGOCAResources@718598 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/fop/afp/AFPDataObjectFactory.java | 2 + .../org/apache/fop/afp/AFPGraphics2D.java | 6 +- .../fop/afp/Graphics2DImagePainterGOCA.java | 71 --- .../modca/PreprocessPresentationObject.java | 3 +- .../apache/fop/afp/svg/AFPBridgeContext.java | 2 +- .../apache/fop/afp/svg/AFPTextHandler.java | 37 +- .../apache/fop/afp/svg/AFPTextPainter.java | 496 +--------------- .../batik/Graphics2DImagePainterImpl.java | 55 +- .../AbstractGraphics2DImagePainter.java | 55 -- .../render/afp/AFPImageHandlerGraphics2D.java | 22 +- .../apache/fop/render/afp/AFPRenderer.java | 86 +-- .../apache/fop/render/afp/AFPSVGHandler.java | 9 +- .../fop/svg/AbstractFOPTextPainter.java | 528 ++++++++++++++++++ .../org/apache/fop/svg/FOPTextHandler.java | 27 + 14 files changed, 670 insertions(+), 729 deletions(-) delete mode 100644 src/java/org/apache/fop/afp/Graphics2DImagePainterGOCA.java delete mode 100644 src/java/org/apache/fop/render/AbstractGraphics2DImagePainter.java create mode 100644 src/java/org/apache/fop/svg/AbstractFOPTextPainter.java create mode 100644 src/java/org/apache/fop/svg/FOPTextHandler.java diff --git a/src/java/org/apache/fop/afp/AFPDataObjectFactory.java b/src/java/org/apache/fop/afp/AFPDataObjectFactory.java index d2e5a7a62..c333f5987 100644 --- a/src/java/org/apache/fop/afp/AFPDataObjectFactory.java +++ b/src/java/org/apache/fop/afp/AFPDataObjectFactory.java @@ -130,6 +130,8 @@ public class AFPDataObjectFactory { // paint to graphics object Graphics2DImagePainter painter = graphicsObjectInfo.getPainter(); Rectangle2D area = graphicsObjectInfo.getArea(); + g2d.scale(1, -1); + g2d.translate(0, -area.getHeight()); painter.paint(g2d, area); // return painted graphics object diff --git a/src/java/org/apache/fop/afp/AFPGraphics2D.java b/src/java/org/apache/fop/afp/AFPGraphics2D.java index 2494eba69..0a8161a3b 100644 --- a/src/java/org/apache/fop/afp/AFPGraphics2D.java +++ b/src/java/org/apache/fop/afp/AFPGraphics2D.java @@ -88,7 +88,7 @@ public class AFPGraphics2D extends AbstractGraphics2D implements NativeImageHand private GraphicsObject graphicsObj = null; /** Fallback text handler */ - protected TextHandler fallbackTextHandler = new StrokingTextHandler(this); + protected TextHandler fallbackTextHandler = new StrokingTextHandler(); /** Custom text handler */ protected TextHandler customTextHandler = null; @@ -367,9 +367,9 @@ public class AFPGraphics2D extends AbstractGraphics2D implements NativeImageHand public void drawString(String str, float x, float y) { try { if (customTextHandler != null && !textAsShapes) { - customTextHandler.drawString(str, x, y); + customTextHandler.drawString(this, str, x, y); } else { - fallbackTextHandler.drawString(str, x, y); + fallbackTextHandler.drawString(this, str, x, y); } } catch (IOException ioe) { handleIOException(ioe); diff --git a/src/java/org/apache/fop/afp/Graphics2DImagePainterGOCA.java b/src/java/org/apache/fop/afp/Graphics2DImagePainterGOCA.java deleted file mode 100644 index 6a7538550..000000000 --- a/src/java/org/apache/fop/afp/Graphics2DImagePainterGOCA.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* $Id$ */ - -package org.apache.fop.afp; - -import java.awt.Dimension; -import java.awt.Graphics2D; -import java.awt.geom.Rectangle2D; - -import org.apache.batik.bridge.BridgeContext; -import org.apache.batik.gvt.GraphicsNode; -import org.apache.fop.image.loader.batik.Graphics2DImagePainterImpl; -import org.apache.xmlgraphics.java2d.Graphics2DPainterPreparator; - -/** - * Graphics2DImagePainter implementation for GOCA - */ -public class Graphics2DImagePainterGOCA extends Graphics2DImagePainterImpl { - - /** - * Main Constructor - * - * @param root the graphics node root - * @param ctx the bridge context - * @param imageSize the image size - */ - public Graphics2DImagePainterGOCA(GraphicsNode root, BridgeContext ctx, Dimension imageSize) { - super(root, ctx, imageSize); - } - - /** {@inheritDoc} */ - protected Graphics2DPainterPreparator getPreparator() { - return new Graphics2DPainterPreparator() { - - /** {@inheritdoc} */ - public void prepare(Graphics2D g2d, Rectangle2D area) { - double tx = area.getX(); - double ty = area.getHeight() + area.getY(); - if (tx != 0 || ty != 0) { - g2d.translate(tx, ty); - } - - float iw = (float) ctx.getDocumentSize().getWidth(); - float ih = (float) ctx.getDocumentSize().getHeight(); - float w = (float) area.getWidth(); - float h = (float) area.getHeight(); - float sx = w / iw; - float sy = -(h / ih); - if (sx != 1.0 || sy != 1.0) { - g2d.scale(sx, sy); - } - } - }; - } -} \ No newline at end of file diff --git a/src/java/org/apache/fop/afp/modca/PreprocessPresentationObject.java b/src/java/org/apache/fop/afp/modca/PreprocessPresentationObject.java index 279b31201..72e261662 100644 --- a/src/java/org/apache/fop/afp/modca/PreprocessPresentationObject.java +++ b/src/java/org/apache/fop/afp/modca/PreprocessPresentationObject.java @@ -138,8 +138,7 @@ public class PreprocessPresentationObject extends AbstractTripletStructuredObjec } os.write(data); - // Triplets - super.writeContent(os); + writeTriplets(os); } } diff --git a/src/java/org/apache/fop/afp/svg/AFPBridgeContext.java b/src/java/org/apache/fop/afp/svg/AFPBridgeContext.java index 5434b5b9d..039c1ab91 100644 --- a/src/java/org/apache/fop/afp/svg/AFPBridgeContext.java +++ b/src/java/org/apache/fop/afp/svg/AFPBridgeContext.java @@ -81,7 +81,7 @@ public class AFPBridgeContext extends AbstractFOPBridgeContext { super.registerSVGBridges(); if (fontInfo != null) { - AFPTextHandler textHandler = new AFPTextHandler(g2d); + AFPTextHandler textHandler = new AFPTextHandler(fontInfo); g2d.setCustomTextHandler(textHandler); TextPainter textPainter = new AFPTextPainter(textHandler); diff --git a/src/java/org/apache/fop/afp/svg/AFPTextHandler.java b/src/java/org/apache/fop/afp/svg/AFPTextHandler.java index d78e5c16e..f44fde269 100644 --- a/src/java/org/apache/fop/afp/svg/AFPTextHandler.java +++ b/src/java/org/apache/fop/afp/svg/AFPTextHandler.java @@ -20,6 +20,7 @@ package org.apache.fop.afp.svg; import java.awt.Color; +import java.awt.Graphics2D; import java.io.IOException; import org.apache.commons.logging.Log; @@ -32,29 +33,30 @@ import org.apache.fop.afp.fonts.AFPPageFonts; import org.apache.fop.afp.modca.GraphicsObject; import org.apache.fop.fonts.Font; import org.apache.fop.fonts.FontInfo; -import org.apache.xmlgraphics.java2d.TextHandler; +import org.apache.fop.svg.FOPTextHandler; /** * Specialized TextHandler implementation that the AFPGraphics2D class delegates to to paint text * using AFP GOCA text operations. */ -public class AFPTextHandler implements TextHandler { +public class AFPTextHandler implements FOPTextHandler { /** logging instance */ private static Log log = LogFactory.getLog(AFPTextHandler.class); - private AFPGraphics2D g2d = null; - /** Overriding FontState */ protected Font overrideFont = null; + /** Font information */ + private final FontInfo fontInfo; + /** * Main constructor. * - * @param g2d the AFPGraphics2D instance + * @param fontInfo the AFPGraphics2D instance */ - public AFPTextHandler(AFPGraphics2D g2d) { - this.g2d = g2d; + public AFPTextHandler(FontInfo fontInfo) { + this.fontInfo = fontInfo; } /** @@ -63,21 +65,20 @@ public class AFPTextHandler implements TextHandler { * @return the FontInfo object */ public FontInfo getFontInfo() { - return g2d.getFontInfo(); + return fontInfo; } /** * Registers a page font * * @param internalFontName the internal font name + * @param internalFontName the internal font name * @param fontSize the font size * @return a font reference */ - private int registerPageFont(String internalFontName, int fontSize) { + private int registerPageFont(AFPPageFonts pageFonts, String internalFontName, int fontSize) { FontInfo fontInfo = getFontInfo(); AFPFont afpFont = (AFPFont)fontInfo.getFonts().get(internalFontName); - AFPPaintingState paintingState = g2d.getPaintingState(); - AFPPageFonts pageFonts = paintingState.getPageFonts(); // register if necessary AFPFontAttributes afpFontAttributes = pageFonts.registerFont( internalFontName, @@ -87,14 +88,21 @@ public class AFPTextHandler implements TextHandler { return afpFontAttributes.getFontReference(); } + /** {@inheritDoc} */ + public void drawString(String text, float x, float y) throws IOException { + // TODO Remove me after removing the deprecated method in TextHandler. + throw new UnsupportedOperationException("Deprecated method!"); + } + /** * Add a text string to the current data object of the AFP datastream. * The text is painted using text operations. * * {@inheritDoc} */ - public void drawString(String str, float x, float y) throws IOException { + public void drawString(Graphics2D g, String str, float x, float y) throws IOException { log.debug("drawString() str=" + str + ", x=" + x + ", y=" + y); + AFPGraphics2D g2d = (AFPGraphics2D)g; GraphicsObject graphicsObj = g2d.getGraphicsObject(); Color color = g2d.getColor(); @@ -106,10 +114,11 @@ public class AFPTextHandler implements TextHandler { // set the character set int fontReference = 0; + AFPPageFonts pageFonts = paintingState.getPageFonts(); if (overrideFont != null) { String internalFontName = overrideFont.getFontName(); int fontSize = overrideFont.getFontSize(); - fontReference = registerPageFont(internalFontName, fontSize); + fontReference = registerPageFont(pageFonts, internalFontName, fontSize); } else { java.awt.Font awtFont = g2d.getFont(); // AffineTransform fontTransform = awtFont.getTransform(); @@ -117,7 +126,7 @@ public class AFPTextHandler implements TextHandler { Font fopFont = fontInfo.getFontInstanceForAWTFont(awtFont); String internalFontName = fopFont.getFontName(); int fontSize = fopFont.getFontSize(); - fontReference = registerPageFont(internalFontName, fontSize); + fontReference = registerPageFont(pageFonts, internalFontName, fontSize); } graphicsObj.setCharacterSet(fontReference); diff --git a/src/java/org/apache/fop/afp/svg/AFPTextPainter.java b/src/java/org/apache/fop/afp/svg/AFPTextPainter.java index a5758732a..c6a38b1b5 100644 --- a/src/java/org/apache/fop/afp/svg/AFPTextPainter.java +++ b/src/java/org/apache/fop/afp/svg/AFPTextPainter.java @@ -19,34 +19,8 @@ package org.apache.fop.afp.svg; -import java.awt.Color; -import java.awt.Graphics2D; -import java.awt.Paint; -import java.awt.Shape; -import java.awt.font.TextAttribute; -import java.awt.geom.AffineTransform; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; -import java.io.IOException; -import java.text.AttributedCharacterIterator; -import java.text.CharacterIterator; -import java.util.Iterator; -import java.util.List; - -import org.apache.batik.dom.svg.SVGOMTextElement; -import org.apache.batik.gvt.TextNode; -import org.apache.batik.gvt.TextPainter; -import org.apache.batik.gvt.font.GVTFontFamily; -import org.apache.batik.gvt.renderer.StrokingTextPainter; -import org.apache.batik.gvt.text.GVTAttributedCharacterIterator; -import org.apache.batik.gvt.text.Mark; -import org.apache.batik.gvt.text.TextPaintInfo; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.fop.afp.AFPGraphics2D; -import org.apache.fop.fonts.Font; -import org.apache.fop.fonts.FontInfo; -import org.apache.fop.fonts.FontTriplet; +import org.apache.fop.svg.AbstractFOPTextPainter; +import org.apache.fop.svg.FOPTextHandler; /** @@ -57,472 +31,14 @@ import org.apache.fop.fonts.FontTriplet; * drawString. If the text is complex or the cannot be translated * into a simple drawString the StrokingTextPainter is used instead. */ -public class AFPTextPainter implements TextPainter { - - /** the logger for this class */ - protected Log log = LogFactory.getLog(AFPTextPainter.class); - - private final AFPTextHandler nativeTextHandler; - - /** - * Use the stroking text painter to get the bounds and shape. - * Also used as a fallback to draw the string with strokes. - */ - protected static final TextPainter - PROXY_PAINTER = StrokingTextPainter.getInstance(); +public class AFPTextPainter extends AbstractFOPTextPainter { /** - * Create a new PS text painter with the given font information. + * Create a new text painter with the given font information. * @param nativeTextHandler the NativeTextHandler instance used for text painting */ - public AFPTextPainter(AFPTextHandler nativeTextHandler) { - this.nativeTextHandler = nativeTextHandler; - } - - /** - * 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 - */ - public void paint(TextNode node, Graphics2D g2d) { - Point2D loc = node.getLocation(); - log.debug("painting text node " + node); - if (hasUnsupportedAttributes(node)) { - log.debug("hasUnsuportedAttributes"); - PROXY_PAINTER.paint(node, g2d); - } else { - log.debug("allAttributesSupported"); - paintTextRuns(node.getTextRuns(), g2d, loc); - } - } - - private boolean hasUnsupportedAttributes(TextNode node) { - Iterator iter = node.getTextRuns().iterator(); - while (iter.hasNext()) { - StrokingTextPainter.TextRun - run = (StrokingTextPainter.TextRun)iter.next(); - AttributedCharacterIterator aci = run.getACI(); - boolean hasUnsupported = hasUnsupportedAttributes(aci); - if (hasUnsupported) { - return true; - } - } - return false; - } - - private boolean hasUnsupportedAttributes(AttributedCharacterIterator aci) { - boolean hasUnsupported = false; - - Font font = getFont(aci); - String text = getText(aci); - if (hasUnsupportedGlyphs(text, font)) { - log.trace("-> Unsupported glyphs found"); - hasUnsupported = true; - } - - TextPaintInfo tpi = (TextPaintInfo) aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO); - if ((tpi != null) - && ((tpi.strokeStroke != null && tpi.strokePaint != null) - || (tpi.strikethroughStroke != null) - || (tpi.underlineStroke != null) - || (tpi.overlineStroke != null))) { - log.trace("-> under/overlines etc. found"); - hasUnsupported = true; - } - - //Alpha is not supported - Paint foreground = (Paint) aci.getAttribute(TextAttribute.FOREGROUND); - if (foreground instanceof Color) { - Color col = (Color)foreground; - if (col.getAlpha() != 255) { - log.trace("-> transparency found"); - hasUnsupported = true; - } - } - - Object letSpace = aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.LETTER_SPACING); - if (letSpace != null) { - log.trace("-> letter spacing found"); - hasUnsupported = true; - } - - Object wordSpace = aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.WORD_SPACING); - if (wordSpace != null) { - log.trace("-> word spacing found"); - hasUnsupported = true; - } - - Object lengthAdjust = aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.LENGTH_ADJUST); - if (lengthAdjust != null) { - log.trace("-> length adjustments found"); - hasUnsupported = true; - } - - Object writeMod = aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE); - if (writeMod != null - && !GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE_LTR.equals( - writeMod)) { - log.trace("-> Unsupported writing modes found"); - hasUnsupported = true; - } - - Object vertOr = aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION); - if (GVTAttributedCharacterIterator.TextAttribute.ORIENTATION_ANGLE.equals( - vertOr)) { - log.trace("-> vertical orientation found"); - hasUnsupported = true; - } - - Object rcDel = aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.TEXT_COMPOUND_DELIMITER); - //Batik 1.6 returns null here which makes it impossible to determine whether this can - //be painted or not, i.e. fall back to stroking. :-( - if (rcDel != null && !(rcDel instanceof SVGOMTextElement)) { - log.trace("-> spans found"); - hasUnsupported = true; //Filter spans - } - - if (hasUnsupported) { - log.trace("Unsupported attributes found in ACI, using StrokingTextPainter"); - } - return hasUnsupported; - } - - /** - * Paint a list of text runs on the Graphics2D at a given location. - * @param textRuns the list of text runs - * @param g2d the Graphics2D to paint to - * @param loc the current location of the "cursor" - */ - protected void paintTextRuns(List textRuns, Graphics2D g2d, Point2D loc) { - Point2D currentloc = loc; - Iterator i = textRuns.iterator(); - while (i.hasNext()) { - StrokingTextPainter.TextRun - run = (StrokingTextPainter.TextRun)i.next(); - currentloc = paintTextRun(run, g2d, currentloc); - } - } - - /** - * Paint a single text run on the Graphics2D at a given location. - * @param run the text run to paint - * @param g2d the Graphics2D to paint to - * @param loc the current location of the "cursor" - * @return the new location of the "cursor" after painting the text run - */ - protected Point2D paintTextRun(StrokingTextPainter.TextRun run, Graphics2D g2d, Point2D loc) { - AttributedCharacterIterator aci = run.getACI(); - aci.first(); - - updateLocationFromACI(aci, loc); - AffineTransform at = g2d.getTransform(); - loc = at.transform(loc, null); - - // font - Font font = getFont(aci); - if (font != null) { - nativeTextHandler.setOverrideFont(font); - } - - // color - TextPaintInfo tpi = (TextPaintInfo) aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO); - if (tpi == null) { - return loc; - } - Paint foreground = tpi.fillPaint; - if (foreground instanceof Color) { - Color col = (Color)foreground; - g2d.setColor(col); - } - g2d.setPaint(foreground); - - // text - String txt = getText(aci); - float advance = getStringWidth(txt, font); - float tx = 0; - TextNode.Anchor anchor = (TextNode.Anchor)aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.ANCHOR_TYPE); - if (anchor != null) { - switch (anchor.getType()) { - case TextNode.Anchor.ANCHOR_MIDDLE: - tx = -advance / 2; - break; - case TextNode.Anchor.ANCHOR_END: - tx = -advance; - break; - default: //nop - } - } - - // draw string - double x = loc.getX(); - double y = loc.getY(); - try { - try { - nativeTextHandler.drawString(txt, (float)x + tx, (float)y); - } catch (IOException ioe) { - if (g2d instanceof AFPGraphics2D) { - ((AFPGraphics2D)g2d).handleIOException(ioe); - } - } - } finally { - nativeTextHandler.setOverrideFont(null); - } - loc.setLocation(loc.getX() + advance, loc.getY()); - return loc; - } - - /** - * Extract the raw text from an ACI. - * @param aci ACI to inspect - * @return the extracted text - */ - protected String getText(AttributedCharacterIterator aci) { - StringBuffer sb = new StringBuffer(aci.getEndIndex() - aci.getBeginIndex()); - for (char c = aci.first(); c != CharacterIterator.DONE; c = aci.next()) { - sb.append(c); - } - return sb.toString(); - } - - private void updateLocationFromACI( - AttributedCharacterIterator aci, - Point2D loc) { - //Adjust position of span - Float xpos = (Float)aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.X); - Float ypos = (Float)aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.Y); - Float dxpos = (Float)aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.DX); - Float dypos = (Float)aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.DY); - if (xpos != null) { - loc.setLocation(xpos.doubleValue(), loc.getY()); - } - if (ypos != null) { - loc.setLocation(loc.getX(), ypos.doubleValue()); - } - if (dxpos != null) { - loc.setLocation(loc.getX() + dxpos.doubleValue(), loc.getY()); - } - if (dypos != null) { - loc.setLocation(loc.getX(), loc.getY() + dypos.doubleValue()); - } - } - - private String getStyle(AttributedCharacterIterator aci) { - Float posture = (Float)aci.getAttribute(TextAttribute.POSTURE); - return ((posture != null) && (posture.floatValue() > 0.0)) - ? Font.STYLE_ITALIC - : Font.STYLE_NORMAL; - } - - private int getWeight(AttributedCharacterIterator aci) { - Float taWeight = (Float)aci.getAttribute(TextAttribute.WEIGHT); - return ((taWeight != null) && (taWeight.floatValue() > 1.0)) - ? Font.WEIGHT_BOLD - : Font.WEIGHT_NORMAL; - } - - private Font getFont(AttributedCharacterIterator aci) { - Float fontSize = (Float)aci.getAttribute(TextAttribute.SIZE); - String style = getStyle(aci); - int weight = getWeight(aci); - - FontInfo fontInfo = nativeTextHandler.getFontInfo(); - String fontFamily = null; - List gvtFonts = (List) aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES); - if (gvtFonts != null) { - Iterator i = gvtFonts.iterator(); - while (i.hasNext()) { - GVTFontFamily fam = (GVTFontFamily) i.next(); - /* (todo) Enable SVG Font painting - if (fam instanceof SVGFontFamily) { - PROXY_PAINTER.paint(node, g2d); - return; - }*/ - fontFamily = fam.getFamilyName(); - if (fontInfo.hasFont(fontFamily, style, weight)) { - FontTriplet triplet = fontInfo.fontLookup( - fontFamily, style, weight); - int fsize = (int)(fontSize.floatValue() * 1000); - return fontInfo.getFontInstance(triplet, fsize); - } - } - } - FontTriplet triplet = fontInfo.fontLookup("any", style, Font.WEIGHT_NORMAL); - int fsize = (int)(fontSize.floatValue() * 1000); - return fontInfo.getFontInstance(triplet, fsize); - } - - private float getStringWidth(String str, Font font) { - float wordWidth = 0; - float whitespaceWidth = font.getWidth(font.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 = font.getWidth(font.mapChar(c)); - if (charWidth <= 0) { - charWidth = whitespaceWidth; - } - } else { - charWidth = whitespaceWidth; - } - wordWidth += charWidth; - } - return wordWidth / 1000f; - } - - private boolean hasUnsupportedGlyphs(String str, Font font) { - for (int i = 0; i < str.length(); i++) { - char c = str.charAt(i); - if (!((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t'))) { - if (!font.hasChar(c)) { - return true; - } - } - } - return false; - } - - /** - * Get the outline shape of the text characters. - * This uses the StrokingTextPainter to get the outline - * shape since in theory it should be the same. - * - * @param node the text node - * @return the outline shape of the text characters - */ - public Shape getOutline(TextNode node) { - return PROXY_PAINTER.getOutline(node); - } - - /** - * Get the bounds. - * This uses the StrokingTextPainter to get the bounds - * since in theory it should be the same. - * - * @param node the text node - * @return the bounds of the text - */ - public Rectangle2D getBounds2D(TextNode node) { - /* (todo) getBounds2D() is too slow - * because it uses the StrokingTextPainter. We should implement this - * method ourselves. */ - return PROXY_PAINTER.getBounds2D(node); - } - - /** - * Get the geometry bounds. - * This uses the StrokingTextPainter to get the bounds - * since in theory it should be the same. - * - * @param node the text node - * @return the bounds of the text - */ - public Rectangle2D getGeometryBounds(TextNode node) { - return PROXY_PAINTER.getGeometryBounds(node); - } - - // Methods that have no purpose for PS - - /** - * Get the mark. - * This does nothing since the output is AFP and not interactive. - * - * @param node the text node - * @param pos the position - * @param all select all - * @return null - */ - public Mark getMark(TextNode node, int pos, boolean all) { - return null; - } - - /** - * Select at. - * This does nothing since the output is AFP and not interactive. - * - * @param x the x position - * @param y the y position - * @param node the text node - * @return null - */ - public Mark selectAt(double x, double y, TextNode node) { - return null; - } - - /** - * Select to. - * This does nothing since the output is AFP and not interactive. - * - * @param x the x position - * @param y the y position - * @param beginMark the start mark - * @return null - */ - public Mark selectTo(double x, double y, Mark beginMark) { - return null; - } - - /** - * Selec first. - * This does nothing since the output is AFP and not interactive. - * - * @param node the text node - * @return null - */ - public Mark selectFirst(TextNode node) { - return null; - } - - /** - * Select last. - * This does nothing since the output is AFP and not interactive. - * - * @param node the text node - * @return null - */ - public Mark selectLast(TextNode node) { - return null; - } - - /** - * Get selected. - * This does nothing since the output is AFP and not interactive. - * - * @param start the start mark - * @param finish the finish mark - * @return null - */ - public int[] getSelected(Mark start, Mark finish) { - return null; - } - - /** - * Get the highlighted shape. - * This does nothing since the output is AFP and not interactive. - * - * @param beginMark the start mark - * @param endMark the end mark - * @return null - */ - public Shape getHighlightShape(Mark beginMark, Mark endMark) { - return null; + public AFPTextPainter(FOPTextHandler nativeTextHandler) { + super(nativeTextHandler); } } diff --git a/src/java/org/apache/fop/image/loader/batik/Graphics2DImagePainterImpl.java b/src/java/org/apache/fop/image/loader/batik/Graphics2DImagePainterImpl.java index 87907eddf..ea92f748b 100644 --- a/src/java/org/apache/fop/image/loader/batik/Graphics2DImagePainterImpl.java +++ b/src/java/org/apache/fop/image/loader/batik/Graphics2DImagePainterImpl.java @@ -6,15 +6,18 @@ import java.awt.geom.Rectangle2D; import org.apache.batik.bridge.BridgeContext; import org.apache.batik.gvt.GraphicsNode; -import org.apache.fop.render.AbstractGraphics2DImagePainter; -import org.apache.xmlgraphics.java2d.Graphics2DPainterPreparator; + +import org.apache.xmlgraphics.java2d.Graphics2DImagePainter; /** * A generic graphics 2D image painter implementation */ -public class Graphics2DImagePainterImpl extends AbstractGraphics2DImagePainter { +public class Graphics2DImagePainterImpl implements Graphics2DImagePainter { + private final GraphicsNode root; + /** the Batik bridge context */ protected final BridgeContext ctx; + /** the intrinsic size of the image */ protected final Dimension imageSize; /** @@ -25,7 +28,7 @@ public class Graphics2DImagePainterImpl extends AbstractGraphics2DImagePainter { * @param imageSize the image size */ public Graphics2DImagePainterImpl(GraphicsNode root, BridgeContext ctx, Dimension imageSize) { - super(root); + this.root = root; this.imageSize = imageSize; this.ctx = ctx; } @@ -35,30 +38,30 @@ public class Graphics2DImagePainterImpl extends AbstractGraphics2DImagePainter { return imageSize; } - /** {@inheritDoc} */ - protected Graphics2DPainterPreparator getPreparator() { - return new Graphics2DPainterPreparator() { + private void prepare(Graphics2D g2d, Rectangle2D area) { + // If no viewbox is defined in the svg file, a viewbox of 100x100 is + // assumed, as defined in SVGUserAgent.getViewportSize() + double tx = area.getX(); + double ty = area.getY(); + if (tx != 0 || ty != 0) { + g2d.translate(tx, ty); + } - public void prepare(Graphics2D g2d, Rectangle2D area) { - // If no viewbox is defined in the svg file, a viewbox of 100x100 is - // assumed, as defined in SVGUserAgent.getViewportSize() - double tx = area.getX(); - double ty = area.getY(); - if (tx != 0 || ty != 0) { - g2d.translate(tx, ty); - } + float iw = (float) ctx.getDocumentSize().getWidth(); + float ih = (float) ctx.getDocumentSize().getHeight(); + float w = (float) area.getWidth(); + float h = (float) area.getHeight(); + float sx = w / iw; + float sy = h / ih; + if (sx != 1.0 || sy != 1.0) { + g2d.scale(sx, sy); + } + } - float iw = (float) ctx.getDocumentSize().getWidth(); - float ih = (float) ctx.getDocumentSize().getHeight(); - float w = (float) area.getWidth(); - float h = (float) area.getHeight(); - float sx = w / iw; - float sy = h / ih; - if (sx != 1.0 || sy != 1.0) { - g2d.scale(sx, sy); - } - } - }; + /** {@inheritDoc} */ + public void paint(Graphics2D g2d, Rectangle2D area) { + prepare(g2d, area); + root.paint(g2d); } } \ No newline at end of file diff --git a/src/java/org/apache/fop/render/AbstractGraphics2DImagePainter.java b/src/java/org/apache/fop/render/AbstractGraphics2DImagePainter.java deleted file mode 100644 index 4a3c7d954..000000000 --- a/src/java/org/apache/fop/render/AbstractGraphics2DImagePainter.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.apache.fop.render; - -import java.awt.Graphics2D; -import java.awt.geom.Rectangle2D; - -import org.apache.batik.gvt.GraphicsNode; -import org.apache.xmlgraphics.java2d.Graphics2DPainterPreparator; - -/** - * An initializable graphics 2D image painter - */ -public abstract class AbstractGraphics2DImagePainter - implements org.apache.xmlgraphics.java2d.Graphics2DImagePainter { - - private final GraphicsNode root; - - protected Graphics2DPainterPreparator preparator; - - /** - * Main constructor - * - * @param root a graphics node root - */ - public AbstractGraphics2DImagePainter(GraphicsNode root) { - this.root = root; - } - - /** - * Sets the graphics 2D painter preparator - * - * @param initializer the graphics 2D preparator - */ - public void setPreparator(Graphics2DPainterPreparator preparator) { - this.preparator = preparator; - } - - /** - * Returns the graphics 2D painter preparator - * - * @return the graphics 2D painter preparator - */ - protected Graphics2DPainterPreparator getPreparator() { - return this.preparator; - } - - /** {@inheritDoc} */ - public void paint(Graphics2D g2d, Rectangle2D area) { - Graphics2DPainterPreparator preparator = getPreparator(); - if (preparator != null) { - preparator.prepare(g2d, area); - } - root.paint(g2d); - } - -} diff --git a/src/java/org/apache/fop/render/afp/AFPImageHandlerGraphics2D.java b/src/java/org/apache/fop/render/afp/AFPImageHandlerGraphics2D.java index e8fe1f4f1..fa3f00cdb 100644 --- a/src/java/org/apache/fop/render/afp/AFPImageHandlerGraphics2D.java +++ b/src/java/org/apache/fop/render/afp/AFPImageHandlerGraphics2D.java @@ -19,13 +19,11 @@ package org.apache.fop.render.afp; -import java.awt.geom.AffineTransform; import java.io.IOException; import org.apache.fop.afp.AFPDataObjectInfo; import org.apache.fop.afp.AFPGraphics2D; import org.apache.fop.afp.AFPGraphicsObjectInfo; -import org.apache.fop.afp.AFPObjectAreaInfo; import org.apache.fop.afp.AFPPaintingState; import org.apache.fop.afp.AFPResourceInfo; import org.apache.fop.afp.AFPResourceLevel; @@ -51,7 +49,8 @@ public class AFPImageHandlerGraphics2D extends AFPImageHandler { public AFPDataObjectInfo generateDataObjectInfo( AFPRendererImageInfo rendererImageInfo) throws IOException { - AFPRendererContext rendererContext = (AFPRendererContext)rendererImageInfo.getRendererContext(); + AFPRendererContext rendererContext + = (AFPRendererContext)rendererImageInfo.getRendererContext(); AFPInfo afpInfo = rendererContext.getInfo(); ImageGraphics2D imageG2D = (ImageGraphics2D)rendererImageInfo.getImage(); Graphics2DImagePainter painter = imageG2D.getGraphics2DImagePainter(); @@ -67,11 +66,11 @@ public class AFPImageHandlerGraphics2D extends AFPImageHandler { return null; } else { AFPGraphicsObjectInfo graphicsObjectInfo - = (AFPGraphicsObjectInfo)super.generateDataObjectInfo(rendererImageInfo); + = (AFPGraphicsObjectInfo)super.generateDataObjectInfo(rendererImageInfo); AFPResourceInfo resourceInfo = graphicsObjectInfo.getResourceInfo(); //level not explicitly set/changed so default to inline for GOCA graphic objects - // (due to a bug in the IBM AFP Workbench Viewer (2.04.01.07) - hard copy works just fine) + // (due to a bug in the IBM AFP Workbench Viewer (2.04.01.07), hard copy works just fine) if (!resourceInfo.levelChanged()) { resourceInfo.setLevel(new AFPResourceLevel(AFPResourceLevel.INLINE)); } @@ -86,22 +85,9 @@ public class AFPImageHandlerGraphics2D extends AFPImageHandler { graphicsObjectInfo.setGraphics2D(g2d); - // translate to current location - AFPPaintingState paintingState = afpInfo.getPaintingState(); - AffineTransform at = paintingState.getData().getTransform(); - g2d.translate(at.getTranslateX(), at.getTranslateY()); - // set painter graphicsObjectInfo.setPainter(painter); - // invert y-axis for GOCA - final int sx = 1; - final int sy = -1; - AFPObjectAreaInfo objectAreaInfo = graphicsObjectInfo.getObjectAreaInfo(); - int height = objectAreaInfo.getHeight(); - g2d.translate(0, height); - g2d.scale(sx, sy); - return graphicsObjectInfo; } } diff --git a/src/java/org/apache/fop/render/afp/AFPRenderer.java b/src/java/org/apache/fop/render/afp/AFPRenderer.java index ede556ecb..b85dd96f9 100644 --- a/src/java/org/apache/fop/render/afp/AFPRenderer.java +++ b/src/java/org/apache/fop/render/afp/AFPRenderer.java @@ -332,37 +332,6 @@ public class AFPRenderer extends AbstractPathOrientedRenderer { dataStream.endPage(); } - /** {@inheritDoc} */ - public void clip() { - // TODO - log.debug("NYI clip()"); - } - - /** {@inheritDoc} */ - public void clipRect(float x, float y, float width, float height) { - // TODO - log.debug("NYI clipRect(x=" + x + ",y=" + y - + ",width=" + width + ", height=" + height + ")"); - } - - /** {@inheritDoc} */ - public void moveTo(float x, float y) { - // TODO - log.debug("NYI moveTo(x=" + x + ",y=" + y + ")"); - } - - /** {@inheritDoc} */ - public void lineTo(float x, float y) { - // TODO - log.debug("NYI lineTo(x=" + x + ",y=" + y + ")"); - } - - /** {@inheritDoc} */ - public void closePath() { - // TODO - log.debug("NYI closePath()"); - } - /** {@inheritDoc} */ public void drawBorderLine(float x1, float y1, float x2, float y2, boolean horz, boolean startOrBefore, int style, Color col) { @@ -535,18 +504,6 @@ public class AFPRenderer extends AbstractPathOrientedRenderer { paintingState.pop(); } - /** Indicates the beginning of a text object. */ - public void beginTextObject() { - //TODO PDF specific maybe? - log.debug("NYI beginTextObject()"); - } - - /** Indicates the end of a text object. */ - public void endTextObject() { - //TODO PDF specific maybe? - log.debug("NYI endTextObject()"); - } - /** {@inheritDoc} */ public void renderImage(Image image, Rectangle2D pos) { drawImage(image.getURL(), pos, image.getForeignAttributes()); @@ -809,4 +766,47 @@ public class AFPRenderer extends AbstractPathOrientedRenderer { concatenateTransformationMatrix(at); } + /** {@inheritDoc} */ + public void clip() { + // TODO +// log.debug("NYI clip()"); + } + + /** {@inheritDoc} */ + public void clipRect(float x, float y, float width, float height) { + // TODO +// log.debug("NYI clipRect(x=" + x + ",y=" + y +// + ",width=" + width + ", height=" + height + ")"); + } + + /** {@inheritDoc} */ + public void moveTo(float x, float y) { + // TODO +// log.debug("NYI moveTo(x=" + x + ",y=" + y + ")"); + } + + /** {@inheritDoc} */ + public void lineTo(float x, float y) { + // TODO +// log.debug("NYI lineTo(x=" + x + ",y=" + y + ")"); + } + + /** {@inheritDoc} */ + public void closePath() { + // TODO +// log.debug("NYI closePath()"); + } + + /** Indicates the beginning of a text object. */ + public void beginTextObject() { + //TODO PDF specific maybe? +// log.debug("NYI beginTextObject()"); + } + + /** Indicates the end of a text object. */ + public void endTextObject() { + //TODO PDF specific maybe? +// log.debug("NYI endTextObject()"); + } + } diff --git a/src/java/org/apache/fop/render/afp/AFPSVGHandler.java b/src/java/org/apache/fop/render/afp/AFPSVGHandler.java index 2800686d9..bf74b4053 100644 --- a/src/java/org/apache/fop/render/afp/AFPSVGHandler.java +++ b/src/java/org/apache/fop/render/afp/AFPSVGHandler.java @@ -34,10 +34,10 @@ import org.apache.fop.afp.AFPPaintingState; import org.apache.fop.afp.AFPResourceInfo; import org.apache.fop.afp.AFPResourceManager; import org.apache.fop.afp.AFPUnitConverter; -import org.apache.fop.afp.Graphics2DImagePainterGOCA; import org.apache.fop.afp.svg.AFPBridgeContext; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.fonts.FontInfo; +import org.apache.fop.image.loader.batik.Graphics2DImagePainterImpl; import org.apache.fop.render.AbstractGenericSVGHandler; import org.apache.fop.render.Renderer; import org.apache.fop.render.RendererContext; @@ -67,9 +67,6 @@ public class AFPSVGHandler extends AbstractGenericSVGHandler { } } - private static final int X = 0; - private static final int Y = 1; - /** * Render the SVG document. * @@ -101,7 +98,7 @@ public class AFPSVGHandler extends AbstractGenericSVGHandler { } // Create a new AFPGraphics2D - final boolean textAsShapes = false; + final boolean textAsShapes = afpInfo.strokeText(); AFPGraphics2D g2d = afpInfo.createGraphics2D(textAsShapes); AFPPaintingState paintingState = g2d.getPaintingState(); @@ -220,7 +217,7 @@ public class AFPSVGHandler extends AbstractGenericSVGHandler { painter = super.createGraphics2DImagePainter(root, ctx, imageSize); } else { // paint as GOCA Graphics - painter = new Graphics2DImagePainterGOCA(root, ctx, imageSize); + painter = new Graphics2DImagePainterImpl(root, ctx, imageSize); } return painter; } diff --git a/src/java/org/apache/fop/svg/AbstractFOPTextPainter.java b/src/java/org/apache/fop/svg/AbstractFOPTextPainter.java new file mode 100644 index 000000000..560c76cb5 --- /dev/null +++ b/src/java/org/apache/fop/svg/AbstractFOPTextPainter.java @@ -0,0 +1,528 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.svg; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.Shape; +import java.awt.font.TextAttribute; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.io.IOException; +import java.text.AttributedCharacterIterator; +import java.text.CharacterIterator; +import java.util.Iterator; +import java.util.List; + +import org.apache.batik.dom.svg.SVGOMTextElement; +import org.apache.batik.gvt.TextNode; +import org.apache.batik.gvt.TextPainter; +import org.apache.batik.gvt.font.GVTFontFamily; +import org.apache.batik.gvt.renderer.StrokingTextPainter; +import org.apache.batik.gvt.text.GVTAttributedCharacterIterator; +import org.apache.batik.gvt.text.Mark; +import org.apache.batik.gvt.text.TextPaintInfo; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.fop.afp.AFPGraphics2D; +import org.apache.fop.fonts.Font; +import org.apache.fop.fonts.FontInfo; +import org.apache.fop.fonts.FontTriplet; + + +/** + * Renders the attributed character iterator of a TextNode. + * This class draws the text directly into the Graphics2D so that + * the text is not drawn using shapes. + * If the text is simple enough to draw then it sets the font and calls + * drawString. If the text is complex or the cannot be translated + * into a simple drawString the StrokingTextPainter is used instead. + */ +public abstract class AbstractFOPTextPainter implements TextPainter { + + /** the logger for this class */ + protected Log log = LogFactory.getLog(AbstractFOPTextPainter.class); + + private final FOPTextHandler nativeTextHandler; + + /** + * Use the stroking text painter to get the bounds and shape. + * Also used as a fallback to draw the string with strokes. + */ + protected static final TextPainter + PROXY_PAINTER = StrokingTextPainter.getInstance(); + + /** + * Create a new PS text painter with the given font information. + * @param nativeTextHandler the NativeTextHandler instance used for text painting + */ + public AbstractFOPTextPainter(FOPTextHandler nativeTextHandler) { + this.nativeTextHandler = nativeTextHandler; + } + + /** + * 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 + */ + public void paint(TextNode node, Graphics2D g2d) { + Point2D loc = node.getLocation(); + log.debug("painting text node " + node); + if (hasUnsupportedAttributes(node)) { + log.debug("hasUnsuportedAttributes"); + PROXY_PAINTER.paint(node, g2d); + } else { + log.debug("allAttributesSupported"); + paintTextRuns(node.getTextRuns(), g2d, loc); + } + } + + private boolean hasUnsupportedAttributes(TextNode node) { + Iterator iter = node.getTextRuns().iterator(); + while (iter.hasNext()) { + StrokingTextPainter.TextRun + run = (StrokingTextPainter.TextRun)iter.next(); + AttributedCharacterIterator aci = run.getACI(); + boolean hasUnsupported = hasUnsupportedAttributes(aci); + if (hasUnsupported) { + return true; + } + } + return false; + } + + private boolean hasUnsupportedAttributes(AttributedCharacterIterator aci) { + boolean hasUnsupported = false; + + Font font = getFont(aci); + String text = getText(aci); + if (hasUnsupportedGlyphs(text, font)) { + log.trace("-> Unsupported glyphs found"); + hasUnsupported = true; + } + + TextPaintInfo tpi = (TextPaintInfo) aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO); + if ((tpi != null) + && ((tpi.strokeStroke != null && tpi.strokePaint != null) + || (tpi.strikethroughStroke != null) + || (tpi.underlineStroke != null) + || (tpi.overlineStroke != null))) { + log.trace("-> under/overlines etc. found"); + hasUnsupported = true; + } + + //Alpha is not supported + Paint foreground = (Paint) aci.getAttribute(TextAttribute.FOREGROUND); + if (foreground instanceof Color) { + Color col = (Color)foreground; + if (col.getAlpha() != 255) { + log.trace("-> transparency found"); + hasUnsupported = true; + } + } + + Object letSpace = aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.LETTER_SPACING); + if (letSpace != null) { + log.trace("-> letter spacing found"); + hasUnsupported = true; + } + + Object wordSpace = aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.WORD_SPACING); + if (wordSpace != null) { + log.trace("-> word spacing found"); + hasUnsupported = true; + } + + Object lengthAdjust = aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.LENGTH_ADJUST); + if (lengthAdjust != null) { + log.trace("-> length adjustments found"); + hasUnsupported = true; + } + + Object writeMod = aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE); + if (writeMod != null + && !GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE_LTR.equals( + writeMod)) { + log.trace("-> Unsupported writing modes found"); + hasUnsupported = true; + } + + Object vertOr = aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.VERTICAL_ORIENTATION); + if (GVTAttributedCharacterIterator.TextAttribute.ORIENTATION_ANGLE.equals( + vertOr)) { + log.trace("-> vertical orientation found"); + hasUnsupported = true; + } + + Object rcDel = aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.TEXT_COMPOUND_DELIMITER); + //Batik 1.6 returns null here which makes it impossible to determine whether this can + //be painted or not, i.e. fall back to stroking. :-( + if (rcDel != null && !(rcDel instanceof SVGOMTextElement)) { + log.trace("-> spans found"); + hasUnsupported = true; //Filter spans + } + + if (hasUnsupported) { + log.trace("Unsupported attributes found in ACI, using StrokingTextPainter"); + } + return hasUnsupported; + } + + /** + * Paint a list of text runs on the Graphics2D at a given location. + * @param textRuns the list of text runs + * @param g2d the Graphics2D to paint to + * @param loc the current location of the "cursor" + */ + protected void paintTextRuns(List textRuns, Graphics2D g2d, Point2D loc) { + Point2D currentloc = loc; + Iterator i = textRuns.iterator(); + while (i.hasNext()) { + StrokingTextPainter.TextRun + run = (StrokingTextPainter.TextRun)i.next(); + currentloc = paintTextRun(run, g2d, currentloc); + } + } + + /** + * Paint a single text run on the Graphics2D at a given location. + * @param run the text run to paint + * @param g2d the Graphics2D to paint to + * @param loc the current location of the "cursor" + * @return the new location of the "cursor" after painting the text run + */ + protected Point2D paintTextRun(StrokingTextPainter.TextRun run, Graphics2D g2d, Point2D loc) { + AttributedCharacterIterator aci = run.getACI(); + aci.first(); + + updateLocationFromACI(aci, loc); + AffineTransform at = g2d.getTransform(); + loc = at.transform(loc, null); + + // font + Font font = getFont(aci); + if (font != null) { + nativeTextHandler.setOverrideFont(font); + } + + // color + TextPaintInfo tpi = (TextPaintInfo) aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO); + if (tpi == null) { + return loc; + } + Paint foreground = tpi.fillPaint; + if (foreground instanceof Color) { + Color col = (Color)foreground; + g2d.setColor(col); + } + g2d.setPaint(foreground); + + // text + String txt = getText(aci); + float advance = getStringWidth(txt, font); + float tx = 0; + TextNode.Anchor anchor = (TextNode.Anchor)aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.ANCHOR_TYPE); + if (anchor != null) { + switch (anchor.getType()) { + case TextNode.Anchor.ANCHOR_MIDDLE: + tx = -advance / 2; + break; + case TextNode.Anchor.ANCHOR_END: + tx = -advance; + break; + default: //nop + } + } + + // draw string + double x = loc.getX(); + double y = loc.getY(); + try { + try { + nativeTextHandler.drawString(g2d, txt, (float)x + tx, (float)y); + } catch (IOException ioe) { + if (g2d instanceof AFPGraphics2D) { + ((AFPGraphics2D)g2d).handleIOException(ioe); + } + } + } finally { + nativeTextHandler.setOverrideFont(null); + } + loc.setLocation(loc.getX() + advance, loc.getY()); + return loc; + } + + /** + * Extract the raw text from an ACI. + * @param aci ACI to inspect + * @return the extracted text + */ + protected String getText(AttributedCharacterIterator aci) { + StringBuffer sb = new StringBuffer(aci.getEndIndex() - aci.getBeginIndex()); + for (char c = aci.first(); c != CharacterIterator.DONE; c = aci.next()) { + sb.append(c); + } + return sb.toString(); + } + + private void updateLocationFromACI( + AttributedCharacterIterator aci, + Point2D loc) { + //Adjust position of span + Float xpos = (Float)aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.X); + Float ypos = (Float)aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.Y); + Float dxpos = (Float)aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.DX); + Float dypos = (Float)aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.DY); + if (xpos != null) { + loc.setLocation(xpos.doubleValue(), loc.getY()); + } + if (ypos != null) { + loc.setLocation(loc.getX(), ypos.doubleValue()); + } + if (dxpos != null) { + loc.setLocation(loc.getX() + dxpos.doubleValue(), loc.getY()); + } + if (dypos != null) { + loc.setLocation(loc.getX(), loc.getY() + dypos.doubleValue()); + } + } + + private String getStyle(AttributedCharacterIterator aci) { + Float posture = (Float)aci.getAttribute(TextAttribute.POSTURE); + return ((posture != null) && (posture.floatValue() > 0.0)) + ? Font.STYLE_ITALIC + : Font.STYLE_NORMAL; + } + + private int getWeight(AttributedCharacterIterator aci) { + Float taWeight = (Float)aci.getAttribute(TextAttribute.WEIGHT); + return ((taWeight != null) && (taWeight.floatValue() > 1.0)) + ? Font.WEIGHT_BOLD + : Font.WEIGHT_NORMAL; + } + + private Font getFont(AttributedCharacterIterator aci) { + Float fontSize = (Float)aci.getAttribute(TextAttribute.SIZE); + String style = getStyle(aci); + int weight = getWeight(aci); + + FontInfo fontInfo = nativeTextHandler.getFontInfo(); + String fontFamily = null; + List gvtFonts = (List) aci.getAttribute( + GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES); + if (gvtFonts != null) { + Iterator i = gvtFonts.iterator(); + while (i.hasNext()) { + GVTFontFamily fam = (GVTFontFamily) i.next(); + /* (todo) Enable SVG Font painting + if (fam instanceof SVGFontFamily) { + PROXY_PAINTER.paint(node, g2d); + return; + }*/ + fontFamily = fam.getFamilyName(); + if (fontInfo.hasFont(fontFamily, style, weight)) { + FontTriplet triplet = fontInfo.fontLookup( + fontFamily, style, weight); + int fsize = (int)(fontSize.floatValue() * 1000); + return fontInfo.getFontInstance(triplet, fsize); + } + } + } + FontTriplet triplet = fontInfo.fontLookup("any", style, Font.WEIGHT_NORMAL); + int fsize = (int)(fontSize.floatValue() * 1000); + return fontInfo.getFontInstance(triplet, fsize); + } + + private float getStringWidth(String str, Font font) { + float wordWidth = 0; + float whitespaceWidth = font.getWidth(font.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 = font.getWidth(font.mapChar(c)); + if (charWidth <= 0) { + charWidth = whitespaceWidth; + } + } else { + charWidth = whitespaceWidth; + } + wordWidth += charWidth; + } + return wordWidth / 1000f; + } + + private boolean hasUnsupportedGlyphs(String str, Font font) { + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (!((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t'))) { + if (!font.hasChar(c)) { + return true; + } + } + } + return false; + } + + /** + * Get the outline shape of the text characters. + * This uses the StrokingTextPainter to get the outline + * shape since in theory it should be the same. + * + * @param node the text node + * @return the outline shape of the text characters + */ + public Shape getOutline(TextNode node) { + return PROXY_PAINTER.getOutline(node); + } + + /** + * Get the bounds. + * This uses the StrokingTextPainter to get the bounds + * since in theory it should be the same. + * + * @param node the text node + * @return the bounds of the text + */ + public Rectangle2D getBounds2D(TextNode node) { + /* (todo) getBounds2D() is too slow + * because it uses the StrokingTextPainter. We should implement this + * method ourselves. */ + return PROXY_PAINTER.getBounds2D(node); + } + + /** + * Get the geometry bounds. + * This uses the StrokingTextPainter to get the bounds + * since in theory it should be the same. + * + * @param node the text node + * @return the bounds of the text + */ + public Rectangle2D getGeometryBounds(TextNode node) { + return PROXY_PAINTER.getGeometryBounds(node); + } + + // Methods that have no purpose for PS + + /** + * Get the mark. + * This does nothing since the output is AFP and not interactive. + * + * @param node the text node + * @param pos the position + * @param all select all + * @return null + */ + public Mark getMark(TextNode node, int pos, boolean all) { + return null; + } + + /** + * Select at. + * This does nothing since the output is AFP and not interactive. + * + * @param x the x position + * @param y the y position + * @param node the text node + * @return null + */ + public Mark selectAt(double x, double y, TextNode node) { + return null; + } + + /** + * Select to. + * This does nothing since the output is AFP and not interactive. + * + * @param x the x position + * @param y the y position + * @param beginMark the start mark + * @return null + */ + public Mark selectTo(double x, double y, Mark beginMark) { + return null; + } + + /** + * Selec first. + * This does nothing since the output is AFP and not interactive. + * + * @param node the text node + * @return null + */ + public Mark selectFirst(TextNode node) { + return null; + } + + /** + * Select last. + * This does nothing since the output is AFP and not interactive. + * + * @param node the text node + * @return null + */ + public Mark selectLast(TextNode node) { + return null; + } + + /** + * Get selected. + * This does nothing since the output is AFP and not interactive. + * + * @param start the start mark + * @param finish the finish mark + * @return null + */ + public int[] getSelected(Mark start, Mark finish) { + return null; + } + + /** + * Get the highlighted shape. + * This does nothing since the output is AFP and not interactive. + * + * @param beginMark the start mark + * @param endMark the end mark + * @return null + */ + public Shape getHighlightShape(Mark beginMark, Mark endMark) { + return null; + } + +} diff --git a/src/java/org/apache/fop/svg/FOPTextHandler.java b/src/java/org/apache/fop/svg/FOPTextHandler.java new file mode 100644 index 000000000..8fa9eeedd --- /dev/null +++ b/src/java/org/apache/fop/svg/FOPTextHandler.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.svg; + +public interface FOPTextHandler extends org.apache.xmlgraphics.java2d.TextHandler { + + void setOverrideFont(org.apache.fop.fonts.Font font); + + org.apache.fop.fonts.FontInfo getFontInfo(); +} -- 2.39.5