bella 01/07/04 23:23:33 Modified: sources/org/apache/batik/gvt/font AWTGVTFont.java AWTGVTGlyphVector.java FontFamilyResolver.java GVTFont.java Glyph.java SVGGVTGlyphVector.java Log: - improved text highlight shape - fixed up text decoration problems - added support for the lang and orientation glyph attributes - added supoort for Arabic shaping and ligatures Revision Changes Path 1.6 +110 -21 xml-batik/sources/org/apache/batik/gvt/font/AWTGVTFont.java Index: AWTGVTFont.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/gvt/font/AWTGVTFont.java,v retrieving revision 1.5 retrieving revision 1.6 diff -u -r1.5 -r1.6 --- AWTGVTFont.java 2001/06/12 23:49:39 1.5 +++ AWTGVTFont.java 2001/07/05 06:23:31 1.6 @@ -18,12 +18,19 @@ import java.util.HashMap; import java.awt.geom.AffineTransform; +import java.text.AttributedCharacterIterator; +import java.text.AttributedString; +import java.text.StringCharacterIterator; +import org.apache.batik.gvt.text.ArabicTextHandler; +import org.apache.batik.gvt.text.GVTAttributedCharacterIterator; + + /** * This is a wrapper class for a java.awt.Font instance. * * @author <a href="mailto:[EMAIL PROTECTED]">Bella Robinson</a> - * @version $Id: AWTGVTFont.java,v 1.5 2001/06/12 23:49:39 bella Exp $ + * @version $Id: AWTGVTFont.java,v 1.6 2001/07/05 06:23:31 bella Exp $ */ public final class AWTGVTFont implements GVTFont { @@ -32,6 +39,8 @@ /** * Creates a new AWTGVTFont that wraps the given Font. + * + * @param font The font object to wrap. */ public AWTGVTFont(Font font) { awtFont = font; @@ -40,6 +49,8 @@ /** * Creates a new AWTGVTFont with the specified attributes. + * + * @param attributes Contains attributes of the font to create. */ public AWTGVTFont(Map attributes) { awtFont = new Font(attributes); @@ -48,6 +59,10 @@ /** * Creates a new AWTGVTFont from the specified name, style and point size. + * + * @param name The name of the new font. + * @param style The required font style. + * @param size The required font size. */ public AWTGVTFont(String name, int style, int size) { awtFont = new Font(name, style, size); @@ -55,22 +70,32 @@ } /** - * Checks if this Font has a glyph for the specified character. + * Checks if this font can display the specified character. + * + * @param c The character to check. + * @return Whether or not the character can be displayed. */ public boolean canDisplay(char c) { return awtFont.canDisplay(c); } /** - * Indicates whether or not this Font can display the characters in the - * specified text starting at start and ending at limit. + * Indicates whether or not this font can display the characters in the + * specified text starting at start and ending at limit. + * + * @param text An array containing the characters to check. + * @param start The index of the first character to check. + * @param limit The index of the last character to check. + * + * @return The index of the first char this font cannot display. Will be + * -1 if it can display all characters in the specified range. */ public int canDisplayUpTo(char[] text, int start, int limit) { return awtFont.canDisplayUpTo(text, start, limit); } /** - * Indicates whether or not this Font can display the the characters in + * Indicates whether or not this font can display the the characters in * the specified CharacterIterator starting at start and ending at limit. */ public int canDisplayUpTo(CharacterIterator iter, int start, int limit) { @@ -78,7 +103,7 @@ } /** - * Indicates whether or not this Font can display a specified String. + * Indicates whether or not this font can display a specified String. */ public int canDisplayUpTo(String str) { return awtFont.canDisplayUpTo(str); @@ -90,10 +115,18 @@ */ public GVTGlyphVector createGlyphVector(FontRenderContext frc, char[] chars) { + + StringCharacterIterator sci = new StringCharacterIterator(new String(chars)); + if (getSize() < 1) { - return new AWTGVTGlyphVector(tenPtFont.createGlyphVector(frc, chars), new AWTGVTFont(tenPtFont), getSize()/10f); + // Because of a bug with GlyphVectors created by fonts with + // sizes < 1pt, we need to use a 10pt font and then apply a scale + // factor to reduce it to its correct size. + // Have a look at AWTGVTGlyphVector. + return new AWTGVTGlyphVector(tenPtFont.createGlyphVector(frc, chars), + new AWTGVTFont(tenPtFont), getSize()/10f, sci); } - return new AWTGVTGlyphVector(awtFont.createGlyphVector(frc, chars), this, 1); + return new AWTGVTGlyphVector(awtFont.createGlyphVector(frc, chars), this, 1, sci); } /** @@ -102,10 +135,29 @@ */ public GVTGlyphVector createGlyphVector(FontRenderContext frc, CharacterIterator ci) { + + AWTGVTGlyphVector awtGlyphVector; + if (getSize() < 1) { - return new AWTGVTGlyphVector(tenPtFont.createGlyphVector(frc, ci), new AWTGVTFont(tenPtFont), getSize()/10f); + // Because of a bug with GlyphVectors created by fonts with + // sizes < 1pt, we need to use a 10pt font and then apply a scale + // factor to reduce it to its correct size. + // Have a look at AWTGVTGlyphVector. + awtGlyphVector = new AWTGVTGlyphVector(tenPtFont.createGlyphVector(frc, ci), + new AWTGVTFont(tenPtFont), getSize()/10f, ci); + } else { + awtGlyphVector = new AWTGVTGlyphVector(awtFont.createGlyphVector(frc, ci), this, 1, ci); } - return new AWTGVTGlyphVector(awtFont.createGlyphVector(frc, ci), this, 1); + + if (ci instanceof AttributedCharacterIterator) { + AttributedCharacterIterator aci = (AttributedCharacterIterator)ci; + AttributedString as = new AttributedString(aci); + if (ArabicTextHandler.containsArabic(as)) { + String substString = ArabicTextHandler.createSubstituteString(aci); + return createGlyphVector(frc, substString); + } + } + return awtGlyphVector; } /** @@ -113,12 +165,17 @@ * array and the specified FontRenderContext. */ public GVTGlyphVector createGlyphVector(FontRenderContext frc, - int[] glyphCodes) { + int[] glyphCodes, CharacterIterator ci) { if (getSize() < 1) { - return new AWTGVTGlyphVector(tenPtFont.createGlyphVector(frc, glyphCodes), new AWTGVTFont(tenPtFont), getSize()/10f); + // Because of a bug with GlyphVectors created by fonts with + // sizes < 1pt, we need to use a 10pt font and then apply a scale + // factor to reduce it to its correct size. + // Have a look at AWTGVTGlyphVector. + return new AWTGVTGlyphVector(tenPtFont.createGlyphVector(frc, glyphCodes), + new AWTGVTFont(tenPtFont), getSize()/10f, ci); } return new AWTGVTGlyphVector(awtFont.createGlyphVector(frc, glyphCodes), - this, 1); + this, 1, ci); } /** @@ -126,10 +183,18 @@ * the specified FontRenderContext. */ public GVTGlyphVector createGlyphVector(FontRenderContext frc, String str) { + + StringCharacterIterator sci = new StringCharacterIterator(str); + if (getSize() < 1) { - return new AWTGVTGlyphVector(tenPtFont.createGlyphVector(frc, str), new AWTGVTFont(tenPtFont), getSize()/10f); + // Because of a bug with GlyphVectors created by fonts with + // sizes < 1pt, we need to use a 10pt font and then apply a scale + // factor to reduce it to its correct size. + // Have a look at AWTGVTGlyphVector. + return new AWTGVTGlyphVector(tenPtFont.createGlyphVector(frc, str), + new AWTGVTFont(tenPtFont), getSize()/10f, sci); } - return new AWTGVTGlyphVector(awtFont.createGlyphVector(frc, str), this, 1); + return new AWTGVTGlyphVector(awtFont.createGlyphVector(frc, str), this, 1, sci); } /** @@ -147,7 +212,12 @@ public GVTLineMetrics getLineMetrics(char[] chars, int beginIndex, int limit, FontRenderContext frc) { if (getSize() < 1) { - return new GVTLineMetrics(tenPtFont.getLineMetrics(chars, beginIndex, limit, frc), getSize()/10f); + // Because of a bug with LineMetrics created by fonts with + // sizes < 1pt, we need to use a 10pt font and then apply a scale + // factor to reduce the metrics to their correct size. + // Have a look at GVTLineMetrics. + return new GVTLineMetrics(tenPtFont.getLineMetrics(chars, beginIndex, limit, frc), + getSize()/10f); } return new GVTLineMetrics(awtFont.getLineMetrics(chars, beginIndex, limit, frc)); } @@ -158,7 +228,12 @@ public GVTLineMetrics getLineMetrics(CharacterIterator ci, int beginIndex, int limit, FontRenderContext frc) { if (getSize() < 1) { - return new GVTLineMetrics(tenPtFont.getLineMetrics(ci, beginIndex, limit, frc), getSize()/10f); + // Because of a bug with LineMetrics created by fonts with + // sizes < 1pt, we need to use a 10pt font and then apply a scale + // factor to reduce the metrics to their correct size. + // Have a look at GVTLineMetrics. + return new GVTLineMetrics(tenPtFont.getLineMetrics(ci, beginIndex, limit, frc), + getSize()/10f); } return new GVTLineMetrics(awtFont.getLineMetrics(ci, beginIndex, limit, frc)); } @@ -169,7 +244,12 @@ */ public GVTLineMetrics getLineMetrics(String str, FontRenderContext frc) { if (getSize() < 1) { - return new GVTLineMetrics(tenPtFont.getLineMetrics(str, frc), getSize()/10f); + // Because of a bug with LineMetrics created by fonts with + // sizes < 1pt, we need to use a 10pt font and then apply a scale + // factor to reduce the metrics to their correct size. + // Have a look at GVTLineMetrics. + return new GVTLineMetrics(tenPtFont.getLineMetrics(str, frc), + getSize()/10f); } return new GVTLineMetrics(awtFont.getLineMetrics(str, frc)); } @@ -180,7 +260,12 @@ public GVTLineMetrics getLineMetrics(String str, int beginIndex, int limit, FontRenderContext frc) { if (getSize() < 1) { - return new GVTLineMetrics(tenPtFont.getLineMetrics(str, beginIndex, limit, frc), getSize()/10f); + // Because of a bug with LineMetrics created by fonts with + // sizes < 1pt, we need to use a 10pt font and then apply a scale + // factor to reduce the metrics to their correct size. + // Have a look at GVTLineMetrics. + return new GVTLineMetrics(tenPtFont.getLineMetrics(str, beginIndex, limit, frc), + getSize()/10f); } return new GVTLineMetrics(awtFont.getLineMetrics(str, beginIndex, limit, frc)); } @@ -193,19 +278,23 @@ } /** - * Returns the horizontal kerning value of this glyph pair. + * Returns the horizontal kerning value for this glyph pair. */ public float getHKern(int glyphCode1, int glyphCode2) { return 0f; } /** - * Returns the vertical kerning value of this glyph pair. + * Returns the vertical kerning value for this glyph pair. */ public float getVKern(int glyphCode1, int glyphCode2) { return 0f; } + /** + * Returns a string representation of this font. This is for debugging + * purposes only. + */ public String toString() { return awtFont.getFontName(); } 1.6 +155 -22 xml-batik/sources/org/apache/batik/gvt/font/AWTGVTGlyphVector.java Index: AWTGVTGlyphVector.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/gvt/font/AWTGVTGlyphVector.java,v retrieving revision 1.5 retrieving revision 1.6 diff -u -r1.5 -r1.6 --- AWTGVTGlyphVector.java 2001/06/12 23:49:40 1.5 +++ AWTGVTGlyphVector.java 2001/07/05 06:23:31 1.6 @@ -22,19 +22,22 @@ import java.awt.Stroke; import org.apache.batik.gvt.GraphicsNodeRenderContext; import org.apache.batik.gvt.text.GVTAttributedCharacterIterator; +import org.apache.batik.gvt.text.ArabicTextHandler; import java.text.AttributedCharacterIterator; +import java.text.CharacterIterator; import java.awt.font.TextAttribute; /** * This is a wrapper class for a java.awt.font.GlyphVector instance. * * @author <a href="mailto:[EMAIL PROTECTED]">Bella Robinson</a> - * @version $Id: AWTGVTGlyphVector.java,v 1.5 2001/06/12 23:49:40 bella Exp $ + * @version $Id: AWTGVTGlyphVector.java,v 1.6 2001/07/05 06:23:31 bella Exp $ */ public final class AWTGVTGlyphVector implements GVTGlyphVector { private final GlyphVector awtGlyphVector; private final AWTGVTFont gvtFont; + private CharacterIterator ci; private Point2D[] glyphPositions; private Point2D[] defaultGlyphPositions; @@ -54,11 +57,24 @@ /** * Creates and new AWTGVTGlyphVector from the specified GlyphVector * and AWTGVTFont objects. + * + * @param glyphVector The glyph vector that this one will be based upon. + * @param font The font that is creating this glyph vector. + * @param scaleFactor The scale factor to apply to the glyph vector. + * IMPORTANT: This is only required because the GlyphVector class doesn't + * handle font sizes less than 1 correctly. By using the scale factor we + * can use a GlyphVector created by a larger font and then scale it down to + * the correct size. + * @param ci The character string that this glyph vector represents. */ - public AWTGVTGlyphVector(GlyphVector glyphVector, AWTGVTFont font, float scaleFactor) { - awtGlyphVector = glyphVector; - gvtFont = font; + public AWTGVTGlyphVector(GlyphVector glyphVector, AWTGVTFont font, + float scaleFactor, CharacterIterator ci) { + + this.awtGlyphVector = glyphVector; + this.gvtFont = font; this.scaleFactor = scaleFactor; + this.ci = ci; + int numGlyphs = glyphVector.getNumGlyphs(); outline = null; logicalBounds = null; @@ -122,14 +138,22 @@ } return glyphLogicalBounds[glyphIndex]; } - + /** + * Calculates the logical bounds for each glyph. The logical bounds are + * what is used for highlighting the glyphs when selected. + */ private void computeGlyphLogicalBounds() { GVTLineMetrics lineMetrics = gvtFont.getLineMetrics("By", awtGlyphVector.getFontRenderContext()); float ascent = lineMetrics.getAscent() * scaleFactor; float descent = lineMetrics.getDescent() * scaleFactor; + Shape[] tempLogicalBounds = new Shape[getNumGlyphs()]; + boolean[] rotated = new boolean[getNumGlyphs()]; + double maxWidth = -1; + double maxHeight = -1; + for (int i = 0; i < getNumGlyphs(); i++) { if (glyphVisible[i]) { @@ -143,35 +167,127 @@ float glyphX = (float)(getGlyphPosition(i).getX()); float glyphY = (float)getGlyphPosition(i).getY() - ascent; float glyphWidth = glyphMetrics.getHorizontalAdvance(); - if (i < getNumGlyphs()-1) { - float nextY = (float)getGlyphPosition(i+1).getY() - ascent; - if (glyphY == nextY) { - float nextX = (float)getGlyphPosition(i+1).getX(); - glyphWidth = Math.max(glyphWidth, nextX - glyphX); - } - } - float glyphHeight = ascent + descent; + float glyphHeight = glyphMetrics.getVerticalAdvance(); - glyphLogicalBounds[i] = new Rectangle2D.Double(glyphX, glyphY, - glyphWidth, glyphHeight); + tempLogicalBounds[i] = new Rectangle2D.Double(glyphX, glyphY, + glyphWidth, glyphHeight); + if (glyphWidth > maxWidth) maxWidth = glyphWidth; + if (glyphHeight > maxHeight) maxHeight = glyphHeight; + rotated[i] = false; - } else { // the glyph is transformed so just return the neat bounds + } else { // the glyph is transformed Shape glyphOutline = awtGlyphVector.getGlyphOutline(i); Rectangle2D glyphBounds = glyphOutline.getBounds2D(); + // store three corner points so we can determine whether the glyph is rotated + Point2D p1 = new Point2D.Double(glyphBounds.getMinX(), glyphBounds.getMinY()); + Point2D p2 = new Point2D.Double(glyphBounds.getMaxX(), glyphBounds.getMinY()); + Point2D p3 = new Point2D.Double(glyphBounds.getMinX(), glyphBounds.getMaxY()); + AffineTransform tr = AffineTransform.getTranslateInstance(getGlyphPosition(i).getX(), - getGlyphPosition(i).getY()); + getGlyphPosition(i).getY()); tr.concatenate(glyphTransform); tr.scale(scaleFactor, scaleFactor); - glyphLogicalBounds[i] = tr.createTransformedShape(glyphBounds); + tempLogicalBounds[i] = tr.createTransformedShape(glyphBounds); + + Point2D tp1 = new Point2D.Double(); + Point2D tp2 = new Point2D.Double(); + Point2D tp3 = new Point2D.Double(); + tr.transform(p1, tp1); + tr.transform(p2, tp2); + tr.transform(p3, tp3); + + if ((Math.abs(tp1.getX() - tp2.getX()) < 0.001 + || Math.abs(tp1.getX() - tp3.getX()) < 0.001) + && (Math.abs(tp1.getY() - tp2.getY()) < 0.001 + || Math.abs(tp1.getY() - tp3.getY()) < 0.001)) { + rotated[i] = false; + } else { + rotated[i] = true; + } + if (glyphBounds.isEmpty()) { + if (i > 0) { + // can't tell if rotated or not, make it the same as the + // previous glyph + rotated[i] = rotated[i-1]; + } else { + rotated[i] = true; + } + } + + Rectangle2D rectBounds = tempLogicalBounds[i].getBounds2D(); + if (rectBounds.getWidth() > maxWidth) maxWidth = rectBounds.getWidth(); + if (rectBounds.getHeight() > maxHeight) maxHeight = rectBounds.getHeight(); } } else { // the glyph is not drawn - glyphLogicalBounds[i] = null; + tempLogicalBounds[i] = null; + } + } + + // if appropriate, join adjacent glyph logical bounds + GeneralPath logicalBoundsPath = new GeneralPath(); + for (int i = 0; i < getNumGlyphs(); i++) { + if (tempLogicalBounds[i] != null) { + logicalBoundsPath.append(tempLogicalBounds[i], false); + } + } + Rectangle2D fullBounds = logicalBoundsPath.getBounds2D(); + + if (fullBounds.getHeight() < maxHeight*1.5) { + // make all glyphs tops and bottoms the same as the full bounds + for (int i = 0; i < getNumGlyphs(); i++) { + // first make sure that the glyph logical bounds are not rotated + if (!rotated[i] && tempLogicalBounds[i] != null) { + Rectangle2D glyphBounds = tempLogicalBounds[i].getBounds2D(); + double x = glyphBounds.getMinX(); + double width = glyphBounds.getWidth(); + if (i < getNumGlyphs()-1 && tempLogicalBounds[i+1] != null) { + // make this glyph extend to the start of the next one + Rectangle2D nextGlyphBounds = tempLogicalBounds[i+1].getBounds2D(); + if (nextGlyphBounds.getX() > x) { // going left to right + width = nextGlyphBounds.getX() - x; + } else { + double newGlyphX = nextGlyphBounds.getX() + nextGlyphBounds.getWidth(); + width += (x - newGlyphX); + x = newGlyphX; + } + } + tempLogicalBounds[i] = new Rectangle2D.Double(x, fullBounds.getMinY(), + width, fullBounds.getHeight()); + } + } + } + if (fullBounds.getWidth() < maxWidth*1.5) { + // make all glyphs left and right edges the same as the full bounds + for (int i = 0; i < getNumGlyphs(); i++) { + // first make sure that the glyph logical bounds are not rotated + if (!rotated[i] && tempLogicalBounds[i] != null) { + Rectangle2D glyphBounds = tempLogicalBounds[i].getBounds2D(); + double y = glyphBounds.getMinY(); + double height = glyphBounds.getHeight(); + if (i < getNumGlyphs()-1 && tempLogicalBounds[i+1] != null) { + // make this glyph extend to the start of the next one + Rectangle2D nextGlyphBounds = tempLogicalBounds[i+1].getBounds2D(); + if (nextGlyphBounds.getY() > y) { // going top to bottom + height = nextGlyphBounds.getY() - y; + } else { + double newGlyphY = nextGlyphBounds.getY() + nextGlyphBounds.getHeight(); + height += (y - newGlyphY); + y = newGlyphY; + } + } + tempLogicalBounds[i] = new Rectangle2D.Double(fullBounds.getMinX(), y, + fullBounds.getWidth(), height); + } } } + for (int i = 0; i < getNumGlyphs(); i++) { + glyphLogicalBounds[i] = tempLogicalBounds[i]; + } + } @@ -203,7 +319,6 @@ AffineTransform tr = AffineTransform.getTranslateInstance(getGlyphPosition(glyphIndex).getX(), getGlyphPosition(glyphIndex).getY()); - AffineTransform glyphTransform = getGlyphTransform(glyphIndex); if (glyphTransform != null) { tr.concatenate(glyphTransform); @@ -344,15 +459,24 @@ awtGlyphVector.performDefaultLayout(); outline = null; logicalBounds = null; + float shiftLeft = 0; for (int i = 0; i < getNumGlyphs(); i++) { Point2D glyphPos = awtGlyphVector.getGlyphPosition(i); - glyphPositions[i] = new Point2D.Float((float)(glyphPos.getX() * scaleFactor), + glyphPositions[i] = new Point2D.Float((float)((glyphPos.getX()-shiftLeft) * scaleFactor), (float)(glyphPos.getY() * scaleFactor)); defaultGlyphPositions[i] = glyphPositions[i]; glyphTransforms[i] = null; glyphVisualBounds[i] = null; glyphLogicalBounds[i] = null; glyphOutlines[i] = null; + + // if c is a transparent arabic char then need to shift the following + // glyphs left so that the current glyph is overwritten + char c = ci.setIndex(i + ci.getBeginIndex()); + if (ArabicTextHandler.arabicCharTransparent(c)) { + GlyphMetrics gm = awtGlyphVector.getGlyphMetrics(i); + shiftLeft = gm.getAdvance(); + } } } @@ -405,8 +529,17 @@ } if (endGlyphIndex > getNumGlyphs()-1) { endGlyphIndex = getNumGlyphs()-1; + } + int charCount = 0; + for (int i = ci.getBeginIndex() + startGlyphIndex; i <= ci.getBeginIndex() + endGlyphIndex; i++) { + char c = ci.setIndex(i); + if (ArabicTextHandler.isLigature(c)) { + charCount += ArabicTextHandler.getNumChars(c); + } else { + charCount++; + } } - return endGlyphIndex - startGlyphIndex + 1; + return charCount; } 1.4 +9 -4 xml-batik/sources/org/apache/batik/gvt/font/FontFamilyResolver.java Index: FontFamilyResolver.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/gvt/font/FontFamilyResolver.java,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- FontFamilyResolver.java 2001/06/12 23:49:40 1.3 +++ FontFamilyResolver.java 2001/07/05 06:23:31 1.4 @@ -20,7 +20,7 @@ * The is a utility class that is used for resolving UnresolvedFontFamilies. * * @author <a href="mailto:[EMAIL PROTECTED]">Bella Robinson</a> - * @version $Id: FontFamilyResolver.java,v 1.3 2001/06/12 23:49:40 bella Exp $ + * @version $Id: FontFamilyResolver.java,v 1.4 2001/07/05 06:23:31 bella Exp $ */ public class FontFamilyResolver { @@ -34,7 +34,7 @@ * List of all available fonts on the current system, plus a few common * alternatives. */ - protected final static Map fonts = new HashMap(11); + protected final static Map fonts = new HashMap(); protected final static Vector awtFontFamilies = new Vector(); protected final static Vector awtFonts = new Vector(); @@ -43,10 +43,10 @@ * This sets up the list of available fonts. */ static { + fonts.put("sans-serif", "SansSerif"); fonts.put("serif", "Serif"); fonts.put("Times", "Serif"); fonts.put("Times New Roman", "Serif"); - fonts.put("sans-serif", "SansSerif"); fonts.put("cursive", "Dialog"); fonts.put("fantasy", "Symbol"); fonts.put("monospace", "Monospaced"); @@ -79,6 +79,10 @@ } } + // first add the default font + awtFontFamilies.add(defaultFont); + awtFonts.add(new AWTGVTFont(defaultFont.getFamilyName(), 0, 12)); + Collection fontValues = fonts.values(); Iterator iter = fontValues.iterator(); while(iter.hasNext()) { @@ -140,7 +144,8 @@ for (int i = 0; i < awtFontFamilies.size(); i++) { AWTFontFamily fontFamily = (AWTFontFamily)awtFontFamilies.get(i); AWTGVTFont font = (AWTGVTFont)awtFonts.get(i); - if (font.canDisplay(c)) { + if (font.canDisplay(c) && fontFamily.getFamilyName().indexOf("Song") == -1) { + // the awt font for "MS Song" doesn't display chinese glyphs correctly return fontFamily; } } 1.6 +3 -2 xml-batik/sources/org/apache/batik/gvt/font/GVTFont.java Index: GVTFont.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/gvt/font/GVTFont.java,v retrieving revision 1.5 retrieving revision 1.6 diff -u -r1.5 -r1.6 --- GVTFont.java 2001/06/12 23:49:40 1.5 +++ GVTFont.java 2001/07/05 06:23:31 1.6 @@ -16,7 +16,7 @@ * An interface for all GVT font classes. * * @author <a href="mailto:[EMAIL PROTECTED]">Bella Robinson</a> - * @version $Id: GVTFont.java,v 1.5 2001/06/12 23:49:40 bella Exp $ + * @version $Id: GVTFont.java,v 1.6 2001/07/05 06:23:31 bella Exp $ */ public interface GVTFont { @@ -59,7 +59,8 @@ * array and the specified FontRenderContext. */ public GVTGlyphVector createGlyphVector(FontRenderContext frc, - int[] glyphCodes); + int[] glyphCodes, + CharacterIterator ci); /** * Returns a new GlyphVector object created with the specified String and * the specified FontRenderContext. 1.5 +28 -4 xml-batik/sources/org/apache/batik/gvt/font/Glyph.java Index: Glyph.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/gvt/font/Glyph.java,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- Glyph.java 2001/06/12 23:49:41 1.4 +++ Glyph.java 2001/07/05 06:23:32 1.5 @@ -10,6 +10,7 @@ import org.apache.batik.gvt.GraphicsNode; import org.apache.batik.gvt.GraphicsNodeRenderContext; +import org.apache.batik.gvt.text.ArabicTextHandler; import java.awt.Graphics2D; import java.awt.geom.Point2D; import java.awt.geom.AffineTransform; @@ -23,7 +24,7 @@ * attributes. * * @author <a href="mailto:[EMAIL PROTECTED]">Bella Robinson</a> - * @version $Id: Glyph.java,v 1.4 2001/06/12 23:49:41 bella Exp $ + * @version $Id: Glyph.java,v 1.5 2001/07/05 06:23:32 bella Exp $ */ public class Glyph { @@ -33,6 +34,7 @@ private String orientation; private String arabicForm; private String lang; + private Point2D horizOrigin; private Point2D vertOrigin; private float horizAdvX; private float vertAdvY; @@ -56,6 +58,7 @@ * only, or empty which indicates that the glpyh can be use in both. * @param arabicForm * @param lang + * @param horizOrigin * @param vertOrigin * @param horizAdvX * @param vertAdvY @@ -64,8 +67,8 @@ */ public Glyph(GraphicsNode glyphNode, String unicode, Vector names, String orientation, String arabicForm, String lang, - Point2D vertOrigin, float horizAdvX, float vertAdvY, - int glyphCode, float kernScale) { + Point2D horizOrigin, Point2D vertOrigin, float horizAdvX, + float vertAdvY, int glyphCode, float kernScale) { if (glyphNode == null) { throw new IllegalArgumentException(); @@ -73,10 +76,12 @@ if (unicode == null) { throw new IllegalArgumentException(); } + if (horizOrigin == null) { + throw new IllegalArgumentException(); + } if (vertOrigin == null) { throw new IllegalArgumentException(); } - // should really check all of the other paramters too this.glyphNode = glyphNode; this.unicode = unicode; @@ -84,9 +89,19 @@ this.orientation = orientation; this.arabicForm = arabicForm; this.lang = lang; + this.horizOrigin = horizOrigin; this.vertOrigin = vertOrigin; this.horizAdvX = horizAdvX; this.vertAdvY = vertAdvY; + if (this.unicode != null) { + if (this.unicode.length() > 0 + && ArabicTextHandler.arabicCharTransparent(this.unicode.charAt(0))) { + // if this glyph is arabic and transparent, + // then it doesn't cause any advance + this.horizAdvX = 0; + this.vertAdvY = 0; + } + } this.kernScale = kernScale; this.glyphCode = glyphCode; this.position = new Point2D.Float(0,0); @@ -149,6 +164,15 @@ */ public String getLang() { return lang; + } + + /** + * Returns the horizontal origin of this glyph. + * + * @return The horizontal origin. + */ + public Point2D getHorizOrigin() { + return horizOrigin; } /** 1.6 +123 -16 xml-batik/sources/org/apache/batik/gvt/font/SVGGVTGlyphVector.java Index: SVGGVTGlyphVector.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/gvt/font/SVGGVTGlyphVector.java,v retrieving revision 1.5 retrieving revision 1.6 diff -u -r1.5 -r1.6 --- SVGGVTGlyphVector.java 2001/06/12 23:49:42 1.5 +++ SVGGVTGlyphVector.java 2001/07/05 06:23:32 1.6 @@ -31,7 +31,7 @@ * A GVTGlyphVector class for SVG fonts. * * @author <a href="mailto:[EMAIL PROTECTED]">Bella Robinson</a> - * @version $Id: SVGGVTGlyphVector.java,v 1.5 2001/06/12 23:49:42 bella Exp $ + * @version $Id: SVGGVTGlyphVector.java,v 1.6 2001/07/05 06:23:32 bella Exp $ */ public final class SVGGVTGlyphVector implements GVTGlyphVector { @@ -44,6 +44,14 @@ private Shape[] glyphLogicalBounds; private boolean[] glyphVisible; + /** + * Constructs an SVGGVTGlyphVector. + * + * @param font The font that is creating this glyph vector. + * @param glyphs An array containing the glyphs that form the basis for this + * glyph vector. + * @param frc The current font render context. + */ public SVGGVTGlyphVector(GVTFont font, Glyph[] glyphs, FontRenderContext frc) { this.font = font; this.glyphs = glyphs; @@ -142,7 +150,6 @@ float ascent = 0; float descent = 0; - if (font != null) { // font will only be null if this glyph vector is for an altGlyph GVTLineMetrics lineMetrics = font.getLineMetrics("By", frc); @@ -153,44 +160,144 @@ descent = -descent; } } + Shape[] tempLogicalBounds = new Shape[getNumGlyphs()]; + boolean[] rotated = new boolean[getNumGlyphs()]; + + double maxWidth = -1; + double maxHeight = -1; for (int i = 0; i < getNumGlyphs(); i++) { if (glyphVisible[i]) { + AffineTransform glyphTransform = getGlyphTransform(i); GVTGlyphMetrics glyphMetrics = getGlyphMetrics(i); if (glyphTransform == null && ascent != 0) { - float glyphX = (float)getGlyphPosition(i).getX(); + float glyphX = (float)(getGlyphPosition(i).getX()); float glyphY = (float)getGlyphPosition(i).getY() - ascent; float glyphWidth = glyphMetrics.getHorizontalAdvance(); - if (i < getNumGlyphs()-1) { - float nextY = (float)getGlyphPosition(i+1).getY() - ascent; - if (glyphY == nextY) { - float nextX = (float)getGlyphPosition(i+1).getX(); - glyphWidth = Math.max(glyphWidth, nextX - glyphX); - } - } float glyphHeight = ascent + descent; + + tempLogicalBounds[i] = new Rectangle2D.Double(glyphX, glyphY, + glyphWidth, glyphHeight); + if (glyphWidth > maxWidth) maxWidth = glyphWidth; + if (glyphHeight > maxHeight) maxHeight = glyphHeight; + rotated[i] = false; - glyphLogicalBounds[i] = new Rectangle2D.Double(glyphX, glyphY, glyphWidth, glyphHeight); + } else { // the glyph is transformed - } else { - Shape glyphBounds = glyphMetrics.getBounds2D(); + Rectangle2D glyphBounds = glyphMetrics.getBounds2D(); AffineTransform tr = AffineTransform.getTranslateInstance(getGlyphPosition(i).getX(), - getGlyphPosition(i).getY()); + getGlyphPosition(i).getY()); if (glyphTransform != null) { tr.concatenate(glyphTransform); } - glyphLogicalBounds[i] = tr.createTransformedShape(glyphBounds); + + tempLogicalBounds[i] = tr.createTransformedShape(glyphBounds); + + // store three corner points so we can determine whether the glyph is rotated + Point2D p1 = new Point2D.Double(glyphBounds.getMinX(), glyphBounds.getMinY()); + Point2D p2 = new Point2D.Double(glyphBounds.getMaxX(), glyphBounds.getMinY()); + Point2D p3 = new Point2D.Double(glyphBounds.getMinX(), glyphBounds.getMaxY()); + + Point2D tp1 = new Point2D.Double(); + Point2D tp2 = new Point2D.Double(); + Point2D tp3 = new Point2D.Double(); + tr.transform(p1, tp1); + tr.transform(p2, tp2); + tr.transform(p3, tp3); + + if ((Math.abs(tp1.getX() - tp2.getX()) < 0.001 + || Math.abs(tp1.getX() - tp3.getX()) < 0.001) + && (Math.abs(tp1.getY() - tp2.getY()) < 0.001 + || Math.abs(tp1.getY() - tp3.getY()) < 0.001)) { + rotated[i] = false; + } else { + rotated[i] = true; + } + if (glyphBounds.isEmpty()) { + if (i > 0) { + // can't tell if rotated or not, make it the same as the + // previous glyph + rotated[i] = rotated[i-1]; + } else { + rotated[i] = true; + } + } + + Rectangle2D rectBounds = tempLogicalBounds[i].getBounds2D(); + if (rectBounds.getWidth() > maxWidth) maxWidth = rectBounds.getWidth(); + if (rectBounds.getHeight() > maxHeight) maxHeight = rectBounds.getHeight(); } } else { // the glyph is not drawn - glyphLogicalBounds[i] = null; + tempLogicalBounds[i] = null; } } + // if appropriate, join adjacent glyph logical bounds + GeneralPath logicalBoundsPath = new GeneralPath(); + for (int i = 0; i < getNumGlyphs(); i++) { + if (tempLogicalBounds[i] != null) { + logicalBoundsPath.append(tempLogicalBounds[i], false); + } + } + Rectangle2D fullBounds = logicalBoundsPath.getBounds2D(); + + if (fullBounds.getHeight() < maxHeight*1.5) { + // make all glyphs tops and bottoms the same as the full bounds + for (int i = 0; i < getNumGlyphs(); i++) { + // first make sure that the glyph logical bounds are not rotated + if (!rotated[i] && tempLogicalBounds[i] != null) { + Rectangle2D glyphBounds = tempLogicalBounds[i].getBounds2D(); + double x = glyphBounds.getMinX(); + double width = glyphBounds.getWidth(); + if (i < getNumGlyphs()-1 && tempLogicalBounds[i+1] != null) { + // make this glyph extend to the start of the next one + Rectangle2D nextGlyphBounds = tempLogicalBounds[i+1].getBounds2D(); + if (nextGlyphBounds.getX() > x) { // going left to right + width = nextGlyphBounds.getX() - x; + } else { + double newGlyphX = nextGlyphBounds.getX() + nextGlyphBounds.getWidth(); + width += (x - newGlyphX); + x = newGlyphX; + } + } + tempLogicalBounds[i] = new Rectangle2D.Double(x, fullBounds.getMinY(), + width, fullBounds.getHeight()); + } + } + } + if (fullBounds.getWidth() < maxWidth*1.5) { + // make all glyphs left and right edges the same as the full bounds + for (int i = 0; i < getNumGlyphs(); i++) { + // first make sure that the glyph logical bounds are not rotated + if (!rotated[i] && tempLogicalBounds[i] != null) { + Rectangle2D glyphBounds = tempLogicalBounds[i].getBounds2D(); + double y = glyphBounds.getMinY(); + double height = glyphBounds.getHeight(); + if (i < getNumGlyphs()-1 && tempLogicalBounds[i+1] != null) { + // make this glyph extend to the start of the next one + Rectangle2D nextGlyphBounds = tempLogicalBounds[i+1].getBounds2D(); + if (nextGlyphBounds.getY() > y) { // going top to bottom + height = nextGlyphBounds.getY() - y; + } else { + double newGlyphY = nextGlyphBounds.getY() + nextGlyphBounds.getHeight(); + height += (y - newGlyphY); + y = newGlyphY; + } + } + tempLogicalBounds[i] = new Rectangle2D.Double(fullBounds.getMinX(), y, + fullBounds.getWidth(), height); + } + } + } + + for (int i = 0; i < getNumGlyphs(); i++) { + glyphLogicalBounds[i] = tempLogicalBounds[i]; + } } /** --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]