You are viewing a plain text version of this content. The canonical link for it is here.
Posted to batik-dev@xmlgraphics.apache.org by be...@apache.org on 2001/07/05 08:23:33 UTC

cvs commit: xml-batik/sources/org/apache/batik/gvt/font AWTGVTFont.java AWTGVTGlyphVector.java FontFamilyResolver.java GVTFont.java Glyph.java SVGGVTGlyphVector.java

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:bella.robinson@cmis.csiro.au">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:bella.robinson@cmis.csiro.au">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:bella.robinson@cmis.csiro.au">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:bella.robinson@cmis.csiro.au">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:bella.robinson@cmis.csiro.au">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:bella.robinson@cmis.csiro.au">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: batik-dev-unsubscribe@xml.apache.org
For additional commands, e-mail: batik-dev-help@xml.apache.org