From 9db4cd299225d337c263aede1ef038cec001d8bc Mon Sep 17 00:00:00 2001 From: Adrian Cumiskey Date: Wed, 5 Nov 2008 13:41:13 +0000 Subject: [PATCH] Improved font selection strategy when matching AWT Font against FontTriplet. Fonts looking too big still when processed through AFPTextHandler. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_AFPGOCAResources@711563 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/afp/AFPTextHandler.java | 49 +++++-- .../org/apache/fop/afp/AFPTextPainter.java | 121 +++++++++--------- src/java/org/apache/fop/fonts/FontInfo.java | 52 ++++++++ .../org/apache/fop/svg/PDFGraphics2D.java | 2 +- 4 files changed, 150 insertions(+), 74 deletions(-) diff --git a/src/java/org/apache/fop/afp/AFPTextHandler.java b/src/java/org/apache/fop/afp/AFPTextHandler.java index be6aba51c..77dc641b9 100644 --- a/src/java/org/apache/fop/afp/AFPTextHandler.java +++ b/src/java/org/apache/fop/afp/AFPTextHandler.java @@ -51,6 +51,7 @@ public class AFPTextHandler implements TextHandler { /** * Main constructor. + * * @param g2d the AFPGraphics2D instance */ public AFPTextHandler(AFPGraphics2D g2d) { @@ -66,6 +67,27 @@ public class AFPTextHandler implements TextHandler { return g2d.getFontInfo(); } + /** + * Registers a page font + * + * @param internalFontName the internal font name + * @param fontSize the font size + * @return a font reference + */ + private int registerPageFont(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, + afpFont, + fontSize + ); + return afpFontAttributes.getFontReference(); + } + /** * Add a text string to the current data object of the AFP datastream. * The text is painted using text operations. @@ -77,26 +99,28 @@ public class AFPTextHandler implements TextHandler { GraphicsObject graphicsObj = g2d.getGraphicsObject(); Color color = g2d.getColor(); + // set the color AFPPaintingState paintingState = g2d.getPaintingState(); if (paintingState.setColor(color)) { graphicsObj.setColor(color); } + + // set the character set + int fontReference = 0; if (overrideFont != null) { - FontInfo fontInfo = getFontInfo(); - AFPPageFonts pageFonts = paintingState.getPageFonts(); String internalFontName = overrideFont.getFontName(); int fontSize = overrideFont.getFontSize(); - if (paintingState.setFontName(internalFontName) || paintingState.setFontSize(fontSize)) { - AFPFont font = (AFPFont)fontInfo.getFonts().get(internalFontName); - AFPFontAttributes afpFontAttributes = pageFonts.registerFont( - internalFontName, - font, - fontSize - ); - int fontReference = afpFontAttributes.getFontReference(); - graphicsObj.setCharacterSet(fontReference); - } + fontReference = registerPageFont(internalFontName, fontSize); + } else { + java.awt.Font awtFont = g2d.getFont(); + AffineTransform fontTransform = awtFont.getTransform(); + FontInfo fontInfo = getFontInfo(); + Font fopFont = fontInfo.getFontInstanceForAWTFont(awtFont); + String internalFontName = fopFont.getFontName(); + int fontSize = fopFont.getFontSize(); + fontReference = registerPageFont(internalFontName, fontSize); } + graphicsObj.setCharacterSet(fontReference); // calculate x, y plotting coordinates from graphics context AffineTransform at = g2d.getTransform(); @@ -104,6 +128,7 @@ public class AFPTextHandler implements TextHandler { float[] dstPts = new float[srcPts.length]; at.transform(srcPts, 0, dstPts, 0, 1); + // add the character string graphicsObj.addString(str, Math.round(dstPts[X]), Math.round(dstPts[Y])); } diff --git a/src/java/org/apache/fop/afp/AFPTextPainter.java b/src/java/org/apache/fop/afp/AFPTextPainter.java index d0d54fd75..7e3f3405f 100644 --- a/src/java/org/apache/fop/afp/AFPTextPainter.java +++ b/src/java/org/apache/fop/afp/AFPTextPainter.java @@ -5,9 +5,9 @@ * 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. @@ -19,34 +19,33 @@ package org.apache.fop.afp; +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.Point2D; import java.awt.geom.Rectangle2D; +import java.io.IOException; import java.text.AttributedCharacterIterator; import java.text.CharacterIterator; -import java.awt.font.TextAttribute; -import java.awt.Shape; -import java.awt.Paint; -import java.awt.Color; -import java.io.IOException; -import java.util.List; 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.fonts.Font; import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontTriplet; -import org.apache.batik.dom.svg.SVGOMTextElement; -import org.apache.batik.gvt.text.Mark; -import org.apache.batik.gvt.TextPainter; -import org.apache.batik.gvt.TextNode; -import org.apache.batik.gvt.text.GVTAttributedCharacterIterator; -import org.apache.batik.gvt.text.TextPaintInfo; -import org.apache.batik.gvt.font.GVTFontFamily; -import org.apache.batik.gvt.renderer.StrokingTextPainter; - /** * Renders the attributed character iterator of a TextNode. @@ -57,17 +56,17 @@ import org.apache.batik.gvt.renderer.StrokingTextPainter; * 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 AFPTextHandler nativeTextHandler; + + 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 + protected static final TextPainter PROXY_PAINTER = StrokingTextPainter.getInstance(); /** @@ -95,11 +94,11 @@ public class AFPTextPainter implements TextPainter { paintTextRuns(node.getTextRuns(), g2d, loc); } } - + private boolean hasUnsupportedAttributes(TextNode node) { Iterator iter = node.getTextRuns().iterator(); while (iter.hasNext()) { - StrokingTextPainter.TextRun + StrokingTextPainter.TextRun run = (StrokingTextPainter.TextRun)iter.next(); AttributedCharacterIterator aci = run.getACI(); boolean hasUnsupported = hasUnsupportedAttributes(aci); @@ -111,24 +110,24 @@ public class AFPTextPainter implements TextPainter { } private boolean hasUnsupportedAttributes(AttributedCharacterIterator aci) { - boolean hasunsupported = false; - + boolean hasUnsupported = false; + String text = getText(aci); Font font = makeFont(aci); if (hasUnsupportedGlyphs(text, font)) { log.trace("-> Unsupported glyphs found"); - hasunsupported = true; + hasUnsupported = true; } - + TextPaintInfo tpi = (TextPaintInfo) aci.getAttribute( GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO); - if ((tpi != null) + 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; + hasUnsupported = true; } //Alpha is not supported @@ -137,7 +136,7 @@ public class AFPTextPainter implements TextPainter { Color col = (Color)foreground; if (col.getAlpha() != 255) { log.trace("-> transparency found"); - hasunsupported = true; + hasUnsupported = true; } } @@ -145,30 +144,30 @@ public class AFPTextPainter implements TextPainter { GVTAttributedCharacterIterator.TextAttribute.LETTER_SPACING); if (letSpace != null) { log.trace("-> letter spacing found"); - hasunsupported = true; + hasUnsupported = true; } Object wordSpace = aci.getAttribute( GVTAttributedCharacterIterator.TextAttribute.WORD_SPACING); if (wordSpace != null) { log.trace("-> word spacing found"); - hasunsupported = true; + hasUnsupported = true; } - + Object lengthAdjust = aci.getAttribute( GVTAttributedCharacterIterator.TextAttribute.LENGTH_ADJUST); if (lengthAdjust != null) { log.trace("-> length adjustments found"); - hasunsupported = true; + hasUnsupported = true; } Object writeMod = aci.getAttribute( GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE); - if (writeMod != null + if (writeMod != null && !GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE_LTR.equals( writeMod)) { log.trace("-> Unsupported writing modes found"); - hasunsupported = true; + hasUnsupported = true; } Object vertOr = aci.getAttribute( @@ -176,22 +175,22 @@ public class AFPTextPainter implements TextPainter { if (GVTAttributedCharacterIterator.TextAttribute.ORIENTATION_ANGLE.equals( vertOr)) { log.trace("-> vertical orientation found"); - hasunsupported = true; + 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 + hasUnsupported = true; //Filter spans } - - if (hasunsupported) { + + if (hasUnsupported) { log.trace("Unsupported attributes found in ACI, using StrokingTextPainter"); } - return hasunsupported; + return hasUnsupported; } /** @@ -204,7 +203,7 @@ public class AFPTextPainter implements TextPainter { Point2D currentloc = loc; Iterator i = textRuns.iterator(); while (i.hasNext()) { - StrokingTextPainter.TextRun + StrokingTextPainter.TextRun run = (StrokingTextPainter.TextRun)i.next(); currentloc = paintTextRun(run, g2d, currentloc); } @@ -227,7 +226,7 @@ public class AFPTextPainter implements TextPainter { // font Font font = makeFont(aci); nativeTextHandler.setOverrideFont(font); - + // color TextPaintInfo tpi = (TextPaintInfo) aci.getAttribute( GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO); @@ -240,7 +239,7 @@ public class AFPTextPainter implements TextPainter { g2d.setColor(col); } g2d.setPaint(foreground); - + String txt = getText(aci); float advance = getStringWidth(txt, font); float tx = 0; @@ -270,7 +269,7 @@ public class AFPTextPainter implements TextPainter { } finally { nativeTextHandler.setOverrideFont(null); } - loc.setLocation(loc.getX() + (double)advance, loc.getY()); + loc.setLocation(loc.getX() + advance, loc.getY()); return loc; } @@ -304,25 +303,25 @@ public class AFPTextPainter implements TextPainter { } 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)) - ? "italic" - : "normal"; + ? 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)) + return ((taWeight != null) && (taWeight.floatValue() > 1.0)) ? Font.WEIGHT_BOLD : Font.WEIGHT_NORMAL; } @@ -415,8 +414,8 @@ public class AFPTextPainter implements TextPainter { * @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 + /* (todo) getBounds2D() is too slow + * because it uses the StrokingTextPainter. We should implement this * method ourselves. */ return PROXY_PAINTER.getBounds2D(node); } @@ -436,7 +435,7 @@ public class AFPTextPainter implements TextPainter { /** * Get the mark. - * This does nothing since the output is pdf and not interactive. + * This does nothing since the output is AFP and not interactive. * @param node the text node * @param pos the position * @param all select all @@ -448,7 +447,7 @@ public class AFPTextPainter implements TextPainter { /** * Select at. - * This does nothing since the output is pdf and not interactive. + * 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 @@ -460,7 +459,7 @@ public class AFPTextPainter implements TextPainter { /** * Select to. - * This does nothing since the output is pdf and not interactive. + * 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 @@ -472,7 +471,7 @@ public class AFPTextPainter implements TextPainter { /** * Selec first. - * This does nothing since the output is pdf and not interactive. + * This does nothing since the output is AFP and not interactive. * @param node the text node * @return null */ @@ -482,7 +481,7 @@ public class AFPTextPainter implements TextPainter { /** * Select last. - * This does nothing since the output is pdf and not interactive. + * This does nothing since the output is AFP and not interactive. * @param node the text node * @return null */ @@ -492,7 +491,7 @@ public class AFPTextPainter implements TextPainter { /** * Get selected. - * This does nothing since the output is pdf and not interactive. + * This does nothing since the output is AFP and not interactive. * @param start the start mark * @param finish the finish mark * @return null @@ -503,7 +502,7 @@ public class AFPTextPainter implements TextPainter { /** * Get the highlighted shape. - * This does nothing since the output is pdf and not interactive. + * This does nothing since the output is AFP and not interactive. * @param beginMark the start mark * @param endMark the end mark * @return null diff --git a/src/java/org/apache/fop/fonts/FontInfo.java b/src/java/org/apache/fop/fonts/FontInfo.java index ad838708d..1a04d129a 100644 --- a/src/java/org/apache/fop/fonts/FontInfo.java +++ b/src/java/org/apache/fop/fonts/FontInfo.java @@ -318,6 +318,7 @@ public class FontInfo { /** * Retrieves a (possibly cached) Font instance based on a FontTriplet and a font size. + * * @param triplet the font triplet designating the requested font * @param fontSize the font size * @return the requested Font instance @@ -341,6 +342,57 @@ public class FontInfo { return font; } + private List/**/ getTripletsForName(String fontName) { + List/**/ matchedTriplets = new java.util.ArrayList/**/(); + Iterator it = triplets.keySet().iterator(); + while (it.hasNext()) { + FontTriplet triplet = (FontTriplet)it.next(); + String tripletName = triplet.getName(); + if (tripletName.toLowerCase().equals(fontName.toLowerCase())) { + matchedTriplets.add(triplet); + } + } + return matchedTriplets; + } + + /** + * Returns a suitable internal font given an AWT Font instance. + * + * @param awtFont the AWT font + * @return a best matching internal Font + */ + public Font getFontInstanceForAWTFont(java.awt.Font awtFont) { + String awtFontName = awtFont.getName(); + String awtFontFamily = awtFont.getFamily(); + String awtFontStyle = awtFont.isItalic() ? Font.STYLE_ITALIC : Font.STYLE_NORMAL; + int awtFontWeight = awtFont.isBold() ? Font.WEIGHT_BOLD : Font.WEIGHT_NORMAL; + + FontTriplet matchedTriplet = null; + List/**/ triplets = getTripletsForName(awtFontName); + if (!triplets.isEmpty()) { + Iterator it = triplets.iterator(); + while (it.hasNext()) { + FontTriplet triplet = (FontTriplet)it.next(); + boolean styleMatched = triplet.getStyle().equals(awtFontStyle); + boolean weightMatched = triplet.getWeight() == awtFontWeight; + if (styleMatched && weightMatched) { + matchedTriplet = triplet; + break; + } + } + } + + // not matched on font name so do a lookup using family + if (matchedTriplet == null) { + if (awtFontFamily.equals("sanserif")) { + awtFontFamily = "sans-serif"; + } + matchedTriplet = fontLookup(awtFontFamily, awtFontStyle, awtFontWeight); + } + float awtFontSize = awtFont.getSize2D(); + return getFontInstance(matchedTriplet, (int)(awtFontSize * 1000 + 0.5)); + } + /** * Lookup a font. *
diff --git a/src/java/org/apache/fop/svg/PDFGraphics2D.java b/src/java/org/apache/fop/svg/PDFGraphics2D.java index 179ebb90a..172fafd0c 100644 --- a/src/java/org/apache/fop/svg/PDFGraphics2D.java +++ b/src/java/org/apache/fop/svg/PDFGraphics2D.java @@ -1325,7 +1325,7 @@ public class PDFGraphics2D extends AbstractGraphics2D { if (ovFontState == null) { java.awt.Font gFont = getFont(); fontTransform = gFont.getTransform(); - fontState = getInternalFontForAWTFont(gFont); + fontState = fontInfo.getFontInstanceForAWTFont(gFont); } else { fontState = fontInfo.getFontInstance( ovFontState.getFontTriplet(), ovFontState.getFontSize()); -- 2.39.5