deweese     02/03/07 14:07:45

  Modified:    sources/org/apache/batik/ext/awt/image/codec/tiff
                        TIFFImageEncoder.java
               sources/org/apache/batik/gvt AbstractGraphicsNode.java
               sources/org/apache/batik/gvt/filter
                        GraphicsNodeRable8Bit.java
               sources/org/apache/batik/gvt/font AWTGVTGlyphVector.java
                        GVTLineMetrics.java SVGGVTGlyphVector.java
               sources/org/apache/batik/gvt/renderer
                        StrokingTextPainter.java
               sources/org/apache/batik/gvt/text GlyphLayout.java
               sources/org/apache/batik/transcoder/image
                        ImageTranscoder.java PNGTranscoder.java
                        TIFFTranscoder.java
  Added:       sources/org/apache/batik/gvt/font MultiGlyphVector.java
               test-references/samples/tests/spec/scripting .cvsignore
  Log:
  1) XResolution/YResolution/ResolutionUnits tags are now written
     based on users dpi request.
  2) Added support for FORCE_TRANSPARENT_WHITE hint to TIFF.
  3) GlyphVector implementations now support getting glyph position for
     one more than numGyphs, which is the total advance for GV.
  4) Fixed a small bug in lineOffsets for scaled fonts.
  5) Added preliminary implementation of 1.1 text wrapping (currently
     no bridge for the functionality).
  
  Revision  Changes    Path
  1.2       +39 -37    
xml-batik/sources/org/apache/batik/ext/awt/image/codec/tiff/TIFFImageEncoder.java
  
  Index: TIFFImageEncoder.java
  ===================================================================
  RCS file: 
/home/cvs/xml-batik/sources/org/apache/batik/ext/awt/image/codec/tiff/TIFFImageEncoder.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- TIFFImageEncoder.java     16 May 2001 12:33:35 -0000      1.1
  +++ TIFFImageEncoder.java     7 Mar 2002 22:07:44 -0000       1.2
  @@ -192,7 +192,7 @@
           }
        IndexColorModel icm = null;
        int sizeOfColormap = 0; 
  -     int colormap[] = null;
  +        char colormap[] = null;
   
           // Set image type.
        int imageType = TIFF_UNSUPPORTED;
  @@ -343,11 +343,11 @@
   
            int redIndex = 0, greenIndex = sizeOfColormap;
            int blueIndex = 2 * sizeOfColormap;
  -         colormap = new int[sizeOfColormap * 3];
  +            colormap = new char[sizeOfColormap * 3];
            for (int i=0; i<sizeOfColormap; i++) {
  -             colormap[redIndex++] = (r[i] << 8) & 0xffff;
  -             colormap[greenIndex++] = (g[i] << 8) & 0xffff;
  -             colormap[blueIndex++] = (b[i] << 8) & 0xffff;
  +                colormap[redIndex++]   = (char)(((r[i] << 8) | r[i]) & 0xffff);
  +                colormap[greenIndex++] = (char)(((g[i] << 8) | g[i]) & 0xffff);
  +                colormap[blueIndex++]  = (char)(((b[i] << 8) | b[i]) & 0xffff);
            }
   
            sizeOfColormap *= 3;
  @@ -486,18 +486,21 @@
                                    TIFFField.TIFF_LONG, 1, 
                                    new long[] {(long)height}));
   
  +        char [] shortSampleSize = new char[numBands];
  +        for (int i=0; i<numBands; i++)
  +            shortSampleSize[i] = (char)sampleSize[i];
        fields.add(new TIFFField(TIFFImageDecoder.TIFF_BITS_PER_SAMPLE,
                                    TIFFField.TIFF_SHORT, numBands,
  -                                 sampleSize));
  +                                 shortSampleSize));
   
        fields.add(new TIFFField(TIFFImageDecoder.TIFF_COMPRESSION,
                                    TIFFField.TIFF_SHORT, 1, 
  -                                 new int[] {compression}));
  +                                 new char[] {(char)compression}));
   
        fields.add(
            new TIFFField(TIFFImageDecoder.TIFF_PHOTOMETRIC_INTERPRETATION,
                             TIFFField.TIFF_SHORT, 1, 
  -                          new int[] {photometricInterpretation}));
  +                                 new char[] {(char)photometricInterpretation}));
   
           if(!isTiled) {
               fields.add(new TIFFField(TIFFImageDecoder.TIFF_STRIP_OFFSETS,
  @@ -507,7 +510,7 @@
        
        fields.add(new TIFFField(TIFFImageDecoder.TIFF_SAMPLES_PER_PIXEL,
                                    TIFFField.TIFF_SHORT, 1, 
  -                                 new int[] {numBands}));
  +                                 new char[] {(char)numBands}));
   
           if(!isTiled) {
               fields.add(new TIFFField(TIFFImageDecoder.TIFF_ROWS_PER_STRIP, 
  @@ -544,9 +547,9 @@
           }
   
           if(numExtraSamples > 0) {
  -            int[] extraSamples = new int[numExtraSamples];
  +            char[] extraSamples = new char[numExtraSamples];
               for(int i = 0; i < numExtraSamples; i++) {
  -                extraSamples[i] = extraSampleType;
  +                extraSamples[i] = (char)extraSampleType;
               }
               fields.add(new TIFFField(TIFFImageDecoder.TIFF_EXTRA_SAMPLES,
                                        TIFFField.TIFF_SHORT, numExtraSamples, 
  @@ -556,7 +559,7 @@
           // Data Sample Format Extension fields.
           if(dataType != DataBuffer.TYPE_BYTE) {
               // SampleFormat
  -            int[] sampleFormat = new int[numBands];
  +            char[] sampleFormat = new char[numBands];
               if(dataType == DataBuffer.TYPE_FLOAT) {
                   sampleFormat[0] = 3;
               } else if(dataType == DataBuffer.TYPE_USHORT) {
  @@ -635,20 +638,20 @@
           if(imageType == TIFF_YCBCR) {
               // YCbCrSubSampling: 2 is the default so we must write 1 as
               // we do not (yet) do any subsampling.
  -            int subsampleH = 1;
  -            int subsampleV = 1;
  +            char subsampleH = 1;
  +            char subsampleV = 1;
   
               // If JPEG, update values.
               if(compression == COMP_JPEG_TTN2) {
                   // Determine maximum subsampling.
  -                subsampleH = jep.getHorizontalSubsampling(0);
  -                subsampleV = jep.getVerticalSubsampling(0);
  +                subsampleH = (char)jep.getHorizontalSubsampling(0);
  +                subsampleV = (char)jep.getVerticalSubsampling(0);
                   for(int i = 1; i < numBands; i++) {
  -                    int subH = jep.getHorizontalSubsampling(i);
  +                    char subH = (char)jep.getHorizontalSubsampling(i);
                       if(subH > subsampleH) {
                           subsampleH = subH;
                       }
  -                    int subV = jep.getVerticalSubsampling(i);
  +                    char subV = (char)jep.getVerticalSubsampling(i);
                       if(subV > subsampleV) {
                           subsampleV = subV;
                       }
  @@ -657,7 +660,7 @@
   
               fields.add(new TIFFField(TIFF_YCBCR_SUBSAMPLING,
                                        TIFFField.TIFF_SHORT, 2, 
  -                                     new int[] {subsampleH, subsampleV}));
  +                                     new char[] {subsampleH, subsampleV}));
   
   
               // YCbCr positioning.
  @@ -1461,28 +1464,22 @@
            // unsigned 8 bits
        case TIFFField.TIFF_BYTE:
            byte bytes[] = field.getAsBytes();
  -     
  -         for (int i=0; i<count; i++) {
  +            if (count > 4) count =4;
  +            for (int i=0; i<count; i++)
                output.write(bytes[i]);
  -         }
   
  -         for (int i = 0; i < (4 - count); i++) {
  +            for (int i = 0; i < (4 - count); i++)
                output.write(0);
  -         }
  -
            break;
            
            // unsigned 16 bits
        case TIFFField.TIFF_SHORT:
  -         int shorts[] = field.getAsInts();
  -
  -         for (int i=0; i<count; i++) {
  -             writeUnsignedShort(shorts[i]);
  -         }
  -
  -         for (int i = 0; i < (2 - count); i++) {
  -             writeUnsignedShort(0);
  -         }
  +            char chars[] = field.getAsChars();
  +        if (count > 2) count=2;
  +        for (int i=0; i<count; i++)
  +            writeUnsignedShort(chars[i]);
  +        for (int i = 0; i < (2 - count); i++)
  +            writeUnsignedShort(0);
   
            break;
            
  @@ -1517,8 +1514,13 @@
            
            // unsigned 16 bits
        case TIFFField.TIFF_SHORT:
  +         char chars[] = field.getAsChars();
  +         for (int i=0; i<count; i++) {
  +            writeUnsignedShort(chars[i]);
  +         }
  +         break;
        case TIFFField.TIFF_SSHORT:
  -         int shorts[] = field.getAsInts();
  +         short shorts[] = field.getAsShorts();
            for (int i=0; i<count; i++) {
                writeUnsignedShort(shorts[i]);
            }
  @@ -1545,8 +1547,8 @@
               double[] doubles = field.getAsDoubles();
            for (int i=0; i<count; i++) {
                   long longBits = Double.doubleToLongBits(doubles[i]);
  -                writeLong((int)(longBits >> 32));
  -             writeLong((int)(longBits & 0xffffffff));
  +                writeLong(longBits >>> 32);
  +             writeLong(longBits & 0xffffffff);
            }
               break;
   
  
  
  
  1.38      +5 -5      xml-batik/sources/org/apache/batik/gvt/AbstractGraphicsNode.java
  
  Index: AbstractGraphicsNode.java
  ===================================================================
  RCS file: 
/home/cvs/xml-batik/sources/org/apache/batik/gvt/AbstractGraphicsNode.java,v
  retrieving revision 1.37
  retrieving revision 1.38
  diff -u -r1.37 -r1.38
  --- AbstractGraphicsNode.java 6 Mar 2002 09:06:39 -0000       1.37
  +++ AbstractGraphicsNode.java 7 Mar 2002 22:07:44 -0000       1.38
  @@ -54,7 +54,7 @@
    * @author <a href="mailto:[EMAIL PROTECTED]";>Thierry Kormann</a>
    * @author <a href="mailto:[EMAIL PROTECTED]";>Emmanuel Tissandier</a>
    * @author <a href="mailto:[EMAIL PROTECTED]";>Thomas DeWeese</a>
  - * @version $Id: AbstractGraphicsNode.java,v 1.37 2002/03/06 09:06:39 vhardy Exp $
  + * @version $Id: AbstractGraphicsNode.java,v 1.38 2002/03/07 22:07:44 deweese Exp $
    */
   public abstract class AbstractGraphicsNode implements GraphicsNode {
   
  @@ -793,12 +793,12 @@
       }
   
       /**
  -     * Returns the bounds of this node's primitivePaint after applying the input
  -     * transform (if any), concatenated with this node's transform (if any).
  +     * Returns the bounds of this node's primitivePaint after applying
  +     * the input transform (if any), concatenated with this node's
  +     * transform (if any).
        *
        * @param txf the affine transform with which this node's transform should
  -     *        be concatenated. Should not be null.
  -     */
  +     *        be concatenated. Should not be null.  */
       public Rectangle2D getTransformedPrimitiveBounds(AffineTransform txf) {
           Rectangle2D tpBounds = getPrimitiveBounds();
           if (tpBounds == null) {
  
  
  
  1.17      +4 -7      
xml-batik/sources/org/apache/batik/gvt/filter/GraphicsNodeRable8Bit.java
  
  Index: GraphicsNodeRable8Bit.java
  ===================================================================
  RCS file: 
/home/cvs/xml-batik/sources/org/apache/batik/gvt/filter/GraphicsNodeRable8Bit.java,v
  retrieving revision 1.16
  retrieving revision 1.17
  diff -u -r1.16 -r1.17
  --- GraphicsNodeRable8Bit.java        19 Feb 2002 18:01:29 -0000      1.16
  +++ GraphicsNodeRable8Bit.java        7 Mar 2002 22:07:44 -0000       1.17
  @@ -40,7 +40,7 @@
    * createRendering methods.
    *
    * @author <a href="mailto:[EMAIL PROTECTED]";>Vincent Hardy</a>
  - * @version $Id: GraphicsNodeRable8Bit.java,v 1.16 2002/02/19 18:01:29 deweese Exp $
  + * @version $Id: GraphicsNodeRable8Bit.java,v 1.17 2002/03/07 22:07:44 deweese Exp $
    */
   public class GraphicsNodeRable8Bit 
       extends    AbstractRable 
  @@ -152,12 +152,10 @@
       public Rectangle2D getBounds2D(){
           if (usePrimitivePaint){
               Rectangle2D primitiveBounds = node.getPrimitiveBounds();
  -            if(primitiveBounds != null){
  -                return (Rectangle2D)(primitiveBounds.clone());
  -            }
  -            else{
  +            if(primitiveBounds == null)
                   return new Rectangle2D.Double(0, 0, 0, 0);
  -            }
  +
  +            return (Rectangle2D)(primitiveBounds.clone());
           }
   
           // When not using Primitive paint we return out bounds in our
  @@ -174,7 +172,6 @@
           if (at != null){
              bounds = at.createTransformedShape(bounds).getBounds2D();
           }
  -        
           return bounds;
       }
   
  
  
  
  1.17      +15 -6     
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.16
  retrieving revision 1.17
  diff -u -r1.16 -r1.17
  --- AWTGVTGlyphVector.java    13 Dec 2001 16:55:02 -0000      1.16
  +++ AWTGVTGlyphVector.java    7 Mar 2002 22:07:44 -0000       1.17
  @@ -34,7 +34,7 @@
    * 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.16 2001/12/13 16:55:02 tkormann Exp $
  + * @version $Id: AWTGVTGlyphVector.java,v 1.17 2002/03/07 22:07:44 deweese Exp $
    */
   public class AWTGVTGlyphVector implements GVTGlyphVector {
   
  @@ -94,7 +94,7 @@
           outline       = null;
           logicalBounds = null;
           int numGlyphs = glyphVector.getNumGlyphs();
  -        glyphPositions     = new Point2D.Float[numGlyphs];
  +        glyphPositions     = new Point2D.Float[numGlyphs+1];
           glyphTransforms    = new AffineTransform[numGlyphs];
           glyphOutlines      = new Shape[numGlyphs];
           glyphVisualBounds  = new Shape[numGlyphs];
  @@ -584,7 +584,8 @@
           visualBounds  = null;
           logicalBounds = null;
           float shiftLeft = 0;
  -        for (int i = 0; i < getNumGlyphs(); i++) {
  +        int i=0;
  +        for (; i < getNumGlyphs(); i++) {
               glyphTransforms   [i] = null;
               glyphVisualBounds [i] = null;
               glyphLogicalBounds[i] = null;
  @@ -602,6 +603,12 @@
                   shiftLeft += getGlyphMetrics(i).getHorizontalAdvance();
               }
           }
  +
  +        // Need glyph pos for point after last char...
  +        Point2D glyphPos = defaultGlyphPositions[i];
  +        glyphPositions[i] = new Point2D.Float
  +                ((float)((glyphPos.getX() * scaleFactor)-shiftLeft),
  +                 (float) (glyphPos.getY() * scaleFactor));
       }
   
       /**
  @@ -613,9 +620,11 @@
           outline = null;
           logicalBounds = null;
           visualBounds = null;
  -        glyphVisualBounds[glyphIndex] = null;
  -        glyphLogicalBounds[glyphIndex] = null;
  -        glyphOutlines[glyphIndex] = null;
  +        if (glyphIndex != getNumGlyphs()) {
  +            glyphVisualBounds[glyphIndex] = null;
  +            glyphLogicalBounds[glyphIndex] = null;
  +            glyphOutlines[glyphIndex] = null;
  +        }
       }
   
       /**
  
  
  
  1.5       +4 -1      xml-batik/sources/org/apache/batik/gvt/font/GVTLineMetrics.java
  
  Index: GVTLineMetrics.java
  ===================================================================
  RCS file: /home/cvs/xml-batik/sources/org/apache/batik/gvt/font/GVTLineMetrics.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- GVTLineMetrics.java       17 Sep 2001 16:28:27 -0000      1.4
  +++ GVTLineMetrics.java       7 Mar 2002 22:07:44 -0000       1.5
  @@ -14,7 +14,7 @@
    * GVTLineMetrics is a GVT version of java.awt.font.LineMetrics.
    *
    * @author <a href="mailto:[EMAIL PROTECTED]";>Bella Robinson</a>
  - * @version $Id: GVTLineMetrics.java,v 1.4 2001/09/17 16:28:27 tkormann Exp $
  + * @version $Id: GVTLineMetrics.java,v 1.5 2002/03/07 22:07:44 deweese Exp $
    */
   public class GVTLineMetrics {
   
  @@ -67,6 +67,9 @@
           this.ascent = lineMetrics.getAscent() * scaleFactor;
           this.baselineIndex = lineMetrics.getBaselineIndex();
           this.baselineOffsets = lineMetrics.getBaselineOffsets();
  +        for (int i=0; i<baselineOffsets.length; i++) {
  +            this.baselineOffsets[i] *= scaleFactor;
  +        }
           this.descent = lineMetrics.getDescent() * scaleFactor;
           this.height = lineMetrics.getHeight() * scaleFactor;
           this.leading = lineMetrics.getLeading();
  
  
  
  1.10      +33 -11    
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.9
  retrieving revision 1.10
  diff -u -r1.9 -r1.10
  --- SVGGVTGlyphVector.java    20 Sep 2001 19:36:05 -0000      1.9
  +++ SVGGVTGlyphVector.java    7 Mar 2002 22:07:44 -0000       1.10
  @@ -29,17 +29,18 @@
    * A GVTGlyphVector class for SVG fonts.
    *
    * @author <a href="mailto:[EMAIL PROTECTED]";>Bella Robinson</a>
  - * @version $Id: SVGGVTGlyphVector.java,v 1.9 2001/09/20 19:36:05 deweese Exp $
  + * @version $Id: SVGGVTGlyphVector.java,v 1.10 2002/03/07 22:07:44 deweese Exp $
    */
   public final class SVGGVTGlyphVector implements GVTGlyphVector {
   
  -    private GVTFont font;
  -    private Glyph[] glyphs;
  +    private GVTFont           font;
  +    private Glyph[]           glyphs;
       private FontRenderContext frc;
  -    private GeneralPath outline;
  -    private Rectangle2D logicalBounds;
  -    private Shape[] glyphLogicalBounds;
  -    private boolean[] glyphVisible;
  +    private GeneralPath       outline;
  +    private Rectangle2D       logicalBounds;
  +    private Shape[]           glyphLogicalBounds;
  +    private boolean[]         glyphVisible;
  +    private Point2D           endPos;
   
       /**
        * Constructs an SVGGVTGlyphVector.
  @@ -49,7 +50,8 @@
        * glyph vector.
        * @param frc The current font render context.
        */
  -    public SVGGVTGlyphVector(GVTFont font, Glyph[] glyphs, FontRenderContext frc) {
  +    public SVGGVTGlyphVector(GVTFont font, Glyph[] glyphs, 
  +                             FontRenderContext frc) {
           this.font = font;
           this.glyphs = glyphs;
           this.frc = frc;
  @@ -60,6 +62,11 @@
           for (int i = 0; i < glyphs.length; i++) {
               glyphVisible[i] = true;
           }
  +        
  +        endPos = glyphs[glyphs.length-1].getPosition();
  +        endPos = new Point2D.Float
  +            ((float)(endPos.getX()+glyphs[glyphs.length-1].getHorizAdvX()),
  +             (float)endPos.getY());
       }
   
       /**
  @@ -474,6 +481,9 @@
        * Returns the position of the specified glyph within this GlyphVector.
        */
       public Point2D getGlyphPosition(int glyphIndex) {
  +        if (glyphIndex == glyphs.length)
  +            return endPos;
  +
           if (glyphIndex < 0 || (glyphIndex > glyphs.length-1)) {
               throw new IndexOutOfBoundsException("glyphIndex: " + glyphIndex
               + ", is out of bounds. Should be between 0 and " + (glyphs.length-1) + 
".");
  @@ -496,7 +506,7 @@
                         + " is out of bounds, should be between 0 and "
                         + (glyphs.length-1));
           }
  -        if ((beginGlyphIndex+numEntries) > glyphs.length) {
  +        if ((beginGlyphIndex+numEntries) > glyphs.length+1) {
                throw new IndexOutOfBoundsException("beginGlyphIndex + numEntries ("
                          + beginGlyphIndex + "+" + numEntries
                          + ") exceeds the number of glpyhs in this GlyphVector");
  @@ -504,9 +514,15 @@
           if (positionReturn == null) {
               positionReturn = new float[numEntries*2];
           }
  +        if ((beginGlyphIndex+numEntries) == glyphs.length+1) {
  +            numEntries--;
  +            positionReturn[numEntries*2]   = (float)endPos.getX();
  +            positionReturn[numEntries*2+1] = (float)endPos.getY();
  +        }
           for (int i = beginGlyphIndex; i < (beginGlyphIndex+numEntries); i++) {
  -            Point2D glyphPos = glyphs[i].getPosition();
  -            positionReturn[(i-beginGlyphIndex)*2] = (float)glyphPos.getX();
  +            Point2D glyphPos;
  +            glyphPos = glyphs[i].getPosition();
  +            positionReturn[(i-beginGlyphIndex)*2]     = (float)glyphPos.getX();
               positionReturn[(i-beginGlyphIndex)*2 + 1] = (float)glyphPos.getY();
           }
           return positionReturn;
  @@ -615,6 +631,7 @@
               logicalBounds = null;
               outline = null;
           }
  +        endPos = new Point2D.Float(currentX, currentY);
       }
   
       /**
  @@ -622,6 +639,11 @@
        */
       public void setGlyphPosition(int glyphIndex, Point2D newPos)
                                    throws IndexOutOfBoundsException {
  +        if (glyphIndex == glyphs.length) {
  +            endPos = (Point2D)newPos.clone();
  +            return;
  +        }
  +
           if (glyphIndex < 0 || (glyphIndex > glyphs.length-1)) {
               throw new IndexOutOfBoundsException("glyphIndex: " + glyphIndex
               + ", is out of bounds. Should be between 0 and " + (glyphs.length-1) + 
".");
  
  
  
  1.1                  
xml-batik/sources/org/apache/batik/gvt/font/MultiGlyphVector.java
  
  Index: MultiGlyphVector.java
  ===================================================================
  /*****************************************************************************
   * Copyright (C) The Apache Software Foundation. All rights reserved.        *
   * ------------------------------------------------------------------------- *
   * This software is published under the terms of the Apache Software License *
   * version 1.1, a copy of which has been included with this distribution in  *
   * the LICENSE file.                                                         *
   *****************************************************************************/
  
  package org.apache.batik.gvt.font;
  
  import java.awt.Graphics2D;
  import java.awt.Paint;
  import java.awt.Shape;
  import java.awt.Stroke;
  
  import java.awt.font.FontRenderContext;
  import java.awt.font.GlyphJustificationInfo;
  import java.awt.font.GlyphMetrics;
  import java.awt.font.GlyphVector;
  import java.awt.font.TextAttribute;
  
  import java.awt.geom.AffineTransform;
  import java.awt.geom.GeneralPath;
  import java.awt.geom.Point2D;
  import java.awt.geom.Rectangle2D;
  
  import java.text.AttributedCharacterIterator;
  import java.text.CharacterIterator;
  
  import org.apache.batik.gvt.text.AttributedCharacterSpanIterator;
  
  import java.util.List;
  import java.util.Iterator;
  
  public class MultiGlyphVector implements GVTGlyphVector {
  
      GVTGlyphVector [] gvs;
      int [] nGlyphs;
      int [] off;
  
      int nGlyph;
  
      public MultiGlyphVector(List gvs) {
          this.gvs     = new GVTGlyphVector[gvs.size()];
          this.nGlyphs = new int[gvs.size()];
          this.off     = new int[gvs.size()];
  
          Iterator iter = gvs.iterator();
          int i=0;
          while (iter.hasNext()) {
              off[i]      = nGlyph;
  
              GVTGlyphVector gv = (GVTGlyphVector)iter.next();
              this.gvs[i] = gv;
              nGlyphs[i]  = gv.getNumGlyphs();
              nGlyph     += nGlyphs[i];
              i++;
          }
          nGlyphs[i-1]++;
      }
  
      /**
       * Returns the number of glyphs in this GlyphVector.
       */
      public int getNumGlyphs() {
          return nGlyph;
      }
  
  
      int getGVIdx(int glyphIdx) {
          if (glyphIdx > nGlyph) return -1;
          if (glyphIdx == nGlyph) return gvs.length-1;
          for (int i=0; i<nGlyphs.length; i++)
              if (glyphIdx-off[i] < nGlyphs[i]) return i;
          return -1;
      }
  
      /**
       * Returns the Font associated with this GlyphVector.
       */
      public GVTFont getFont() {
          throw new IllegalArgumentException("Can't be correctly Implemented");
      }
  
      /**
       * Returns the FontRenderContext associated with this GlyphVector.
       */
      public FontRenderContext getFontRenderContext() {
          return gvs[0].getFontRenderContext();
      }
  
      /**
       * Returns the glyphcode of the specified glyph.
       */
      public int getGlyphCode(int glyphIndex) {
          int idx = getGVIdx(glyphIndex);
          return gvs[idx].getGlyphCode(glyphIndex-off[idx]);
      }
  
      /**
       * Returns the justification information for the glyph at the specified
       * index into this GlyphVector.
       */
      public GlyphJustificationInfo getGlyphJustificationInfo(int glyphIndex) {
          int idx = getGVIdx(glyphIndex);
          return gvs[idx].getGlyphJustificationInfo(glyphIndex-off[idx]);
      }
  
      /**
       *  Returns the logical bounds of the specified glyph within this
       *  GlyphVector.
       */
      public Shape getGlyphLogicalBounds(int glyphIndex) {
          int idx = getGVIdx(glyphIndex);
          return gvs[idx].getGlyphLogicalBounds(glyphIndex-off[idx]);
      }
  
      /**
       * Returns the metrics of the glyph at the specified index into this
       * GlyphVector.
       */
      public GVTGlyphMetrics getGlyphMetrics(int glyphIndex) {
          int idx = getGVIdx(glyphIndex);
          return gvs[idx].getGlyphMetrics(glyphIndex-off[idx]);
      }
  
      /**
       * Returns a Shape whose interior corresponds to the visual representation
       * of the specified glyph within this GlyphVector.
       */
      public Shape getGlyphOutline(int glyphIndex) {
          int idx = getGVIdx(glyphIndex);
          return gvs[idx].getGlyphOutline(glyphIndex-off[idx]);
      }
  
      /**
       * Returns the position of the specified glyph within this GlyphVector.
       */
      public Point2D getGlyphPosition(int glyphIndex) {
          int idx = getGVIdx(glyphIndex);
          return gvs[idx].getGlyphPosition(glyphIndex-off[idx]);
      }
  
      /**
       * Gets the transform of the specified glyph within this GlyphVector.
       */
      public AffineTransform getGlyphTransform(int glyphIndex) {
          int idx = getGVIdx(glyphIndex);
          return gvs[idx].getGlyphTransform(glyphIndex-off[idx]);
      }
  
      /**
       * Returns the visual bounds of the specified glyph within the GlyphVector.
       */
      public Shape getGlyphVisualBounds(int glyphIndex) {
          int idx = getGVIdx(glyphIndex);
          return gvs[idx].getGlyphVisualBounds(glyphIndex-off[idx]);
      }
  
      /**
       * Sets the position of the specified glyph within this GlyphVector.
       */
      public void setGlyphPosition(int glyphIndex, Point2D newPos) {
          int idx = getGVIdx(glyphIndex);
          // System.out.println("setting: " + idx + " - " + (glyphIndex-off[idx]) +
          //                    " -> " + newPos);
          gvs[idx].setGlyphPosition(glyphIndex-off[idx], newPos);
      }
  
      /**
       * Sets the transform of the specified glyph within this GlyphVector.
       */
      public void setGlyphTransform(int glyphIndex, AffineTransform newTX) {
          int idx = getGVIdx(glyphIndex);
          gvs[idx].setGlyphTransform(glyphIndex-off[idx], newTX);
      }
  
      /**
       * Tells the glyph vector whether or not to draw the specified glyph.
       */
      public void setGlyphVisible(int glyphIndex, boolean visible) {
          int idx = getGVIdx(glyphIndex);
          gvs[idx].setGlyphVisible(glyphIndex-off[idx], visible);
      }
  
      /**
       * Returns an array of glyphcodes for the specified glyphs.
       */
      public int[] getGlyphCodes(int beginGlyphIndex, int numEntries, 
                          int[] codeReturn) {
          int [] ret = codeReturn;
          if (ret == null)
              ret = new int[numEntries];
          int [] tmp = null;
  
          int gvIdx = getGVIdx(beginGlyphIndex);
          int gi    = beginGlyphIndex-off[gvIdx];
          int i=0;
          GVTGlyphVector gv;
          while (numEntries != 0) {
              int len = numEntries;
              if (gi+len > nGlyphs[gvIdx])
                  len = nGlyphs[gvIdx]-gi;
              gv = gvs[gvIdx];
              if (i == 0) {
                  gv.getGlyphCodes(gi, len, ret);                
              } else {
                  if ((tmp == null) || (tmp.length < len))
                      tmp = new int[len];
  
                  gv.getGlyphCodes(gi, len, tmp);
                  for (int j=0; j<len; j++)
                      ret[i+j] = tmp[j];
              }
              gi=0;
              gvIdx++;
              numEntries -= len;
              i+=len;
          }
          return ret;
      }
  
  
      /**
       * Returns an array of glyph positions for the specified glyphs
       */
      public float[] getGlyphPositions(int beginGlyphIndex, 
                                int numEntries,
                                float[] positionReturn) {
          float [] ret = positionReturn;
          if (ret == null)
              ret = new float[numEntries*2];
          float [] tmp = null;
  
          int gvIdx = getGVIdx(beginGlyphIndex);
          int gi    = beginGlyphIndex-off[gvIdx];
          int i=0;
          GVTGlyphVector gv;
          while (numEntries != 0) {
              int len = numEntries;
              if (gi+len > nGlyphs[gvIdx])
                  len = nGlyphs[gvIdx]-gi;
  
              gv = gvs[gvIdx];
              if (i == 0) {
                  gv.getGlyphPositions(gi, len, ret);
              } else {
                  if ((tmp == null) || (tmp.length < len*2))
                      tmp = new float[len*2];
  
                  gv.getGlyphPositions(gi, len, tmp);
                  for (int j=0; j<len*2; j++)
                      ret[i+j] = tmp[j];
              }
              gi=0;
              gvIdx++;
              numEntries -= len;
              i+=len*2;
          }
          return ret;
      }
  
  
      /**
       *  Returns the logical bounds of this GlyphVector.
       */
      public Rectangle2D getLogicalBounds() {
          Rectangle2D ret = null;
          for (int idx=0; idx<gvs.length; idx++) {
              Rectangle2D b = gvs[idx].getLogicalBounds();
              if (ret == null) ret = b;
              else ret = ret.createUnion(b);
          }
          return ret;
      }
  
      /**
       * Returns a Shape whose interior corresponds to the visual representation
       * of this GlyphVector.
       */
      public Shape getOutline() {
          GeneralPath ret = null;
          for (int idx=0; idx<gvs.length; idx++) {
              Shape s = gvs[idx].getOutline();
              if (ret == null) ret = new GeneralPath(s);
              else ret.append(s, false);
          }
          return ret;
      }
  
      /**
       * Returns a Shape whose interior corresponds to the visual representation
       * of this GlyphVector, offset to x, y.
       */
      public Shape getOutline(float x, float y) {
          Shape outline = getOutline();
          AffineTransform tr = AffineTransform.getTranslateInstance(x,y);
          outline = tr.createTransformedShape(outline);
          return outline;
      }
  
      /**
       * Returns the visual bounds of this GlyphVector The visual bounds is the
       * tightest rectangle enclosing all non-background pixels in the rendered
       * representation of this GlyphVector.
       */
      public Rectangle2D getVisualBounds() {
          Rectangle2D ret = null;
          for (int idx=0; idx<gvs.length; idx++) {
              Rectangle2D b = gvs[idx].getVisualBounds();
              if (ret == null) ret = b;
              else ret = ret.createUnion(b);
          }
          return ret;
      }
  
      public void performDefaultLayout() {
          for (int idx=0; idx<gvs.length; idx++) {
              gvs[idx].performDefaultLayout();
          }
      }
  
  
      /**
       * Returns the number of chars represented by the glyphs within the
       * specified range.
       *
       * @param startGlyphIndex The index of the first glyph in the range.
       * @param endGlyphIndex The index of the last glyph in the range.
       * @return The number of chars.
       */
      public int getCharacterCount(int startGlyphIndex, int endGlyphIndex) {
          int idx1 = getGVIdx(startGlyphIndex);
          int idx2 = getGVIdx(endGlyphIndex);
          int ret=0;
          for (int idx=idx1; idx<=idx2; idx++) {
              int gi1 = startGlyphIndex-off[idx];
              int gi2 = endGlyphIndex-off[idx];
              if (gi2 >= nGlyphs[idx]) {
                  gi2 = nGlyphs[idx]-1;
              }
              ret += gvs[idx].getCharacterCount(gi1, gi2);
              startGlyphIndex += (gi2-gi1+1);
          }
          return ret;
      }
  
      /**
       * Draws the glyph vector.
       */
      public void draw(Graphics2D g2d,
                AttributedCharacterIterator aci) {
          int begin = aci.getBeginIndex();
          for (int idx=0; idx<gvs.length; idx++) {
              GVTGlyphVector gv = gvs[idx];
              int end = gv.getCharacterCount(0, gv.getNumGlyphs())+1;
              gv.draw(g2d, new AttributedCharacterSpanIterator(aci, begin, end));
              begin = end;
          }
      }
  
      
  }
  
  
  
  1.29      +30 -1     
xml-batik/sources/org/apache/batik/gvt/renderer/StrokingTextPainter.java
  
  Index: StrokingTextPainter.java
  ===================================================================
  RCS file: 
/home/cvs/xml-batik/sources/org/apache/batik/gvt/renderer/StrokingTextPainter.java,v
  retrieving revision 1.28
  retrieving revision 1.29
  diff -u -r1.28 -r1.29
  --- StrokingTextPainter.java  29 Jan 2002 16:19:00 -0000      1.28
  +++ StrokingTextPainter.java  7 Mar 2002 22:07:44 -0000       1.29
  @@ -61,7 +61,7 @@
    * @see org.apache.batik.gvt.text.GVTAttributedCharacterIterator
    *
    * @author <a href="[EMAIL PROTECTED]>Bill Haneman</a>
  - * @version $Id: StrokingTextPainter.java,v 1.28 2002/01/29 16:19:00 deweese Exp $
  + * @version $Id: StrokingTextPainter.java,v 1.29 2002/03/07 22:07:44 deweese Exp $
    */
   public class StrokingTextPainter extends BasicTextPainter {
   
  @@ -221,6 +221,35 @@
               currentChunk++;
            
           } while (chunk != null && currentChunk < chunkACIs.length);
  +
  +
  +        if (false) {
  +            // When doing text wrapping there should only ever be
  +            // one text chunk (as chunks are caused by use of 
  +            // the 'x' and 'y' attributes which aren't allowed in
  +            // the 'textFlow' element.
  +            Iterator i = textRuns.iterator();
  +            List layouts = new ArrayList();
  +            while (i.hasNext()) {
  +                TextRun tr = (TextRun)i.next();
  +                layouts.add(tr.getLayout());
  +            }
  +
  +            List rects = new ArrayList();
  +            rects.add(new Rectangle2D.Float( 17, 80, 200, 400));
  +            rects.add(new Rectangle2D.Float(233, 80, 200, 400));
  +
  +            List brLocs = new ArrayList();
  +            brLocs.add(new Integer(292));
  +            brLocs.add(new Integer(476));
  +            List pLocs = new ArrayList();
  +            pLocs.add(new Integer(96));
  +            pLocs.add(new Integer(175));
  +
  +            org.apache.batik.gvt.text.GlyphLayout.textWrapTextChunk
  +                (chunkACIs[0], layouts, rects, brLocs, pLocs, 3);
  +        }
  +
           // t1 = System.currentTimeMillis();
           // layoutTime += t1-t0;
           // System.out.println("Reorder: " + reorderTime + " FontMatching: " + 
fontMatchingTime + " Layout: " + layoutTime);
  
  
  
  1.36      +676 -58   xml-batik/sources/org/apache/batik/gvt/text/GlyphLayout.java
  
  Index: GlyphLayout.java
  ===================================================================
  RCS file: /home/cvs/xml-batik/sources/org/apache/batik/gvt/text/GlyphLayout.java,v
  retrieving revision 1.35
  retrieving revision 1.36
  diff -u -r1.35 -r1.36
  --- GlyphLayout.java  29 Jan 2002 16:19:01 -0000      1.35
  +++ GlyphLayout.java  7 Mar 2002 22:07:44 -0000       1.36
  @@ -23,14 +23,19 @@
   import java.awt.geom.Rectangle2D;
   import java.text.AttributedCharacterIterator;
   import java.text.CharacterIterator;
  -import java.util.Set;
  +
   import java.util.HashSet;
  +import java.util.Set;
  +import java.util.List;
  +import java.util.LinkedList;
  +import java.util.Iterator;
   
   import org.apache.batik.gvt.TextNode;
   import org.apache.batik.gvt.text.TextSpanLayout;
   import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
   import org.apache.batik.gvt.text.TextHit;
   import org.apache.batik.gvt.font.GVTGlyphVector;
  +import org.apache.batik.gvt.font.MultiGlyphVector;
   import org.apache.batik.gvt.font.AltGlyphHandler;
   import org.apache.batik.gvt.font.GVTLineMetrics;
   import org.apache.batik.gvt.font.GVTFont;
  @@ -42,7 +47,7 @@
    * @see org.apache.batik.gvt.text.TextSpanLayout
    *
    * @author <a href="[EMAIL PROTECTED]>Bill Haneman</a>
  - * @version $Id: GlyphLayout.java,v 1.35 2002/01/29 16:19:01 deweese Exp $
  + * @version $Id: GlyphLayout.java,v 1.36 2002/03/07 22:07:44 deweese Exp $
    */
   public class GlyphLayout implements TextSpanLayout {
   
  @@ -51,7 +56,6 @@
       private GVTLineMetrics metrics;
       private AttributedCharacterIterator aci;
       private FontRenderContext frc;
  -    private AffineTransform transform;
       private Point2D advance;
       private Point2D offset;
       private float   xScale=1;
  @@ -82,6 +86,9 @@
   
   
       
  +    public static final AttributedCharacterIterator.Attribute 
  +        TEXT_COMPOUND_DELIMITER 
  +        = GVTAttributedCharacterIterator.TextAttribute.TEXT_COMPOUND_DELIMITER;
   
       private static final AttributedCharacterIterator.Attribute X
           = GVTAttributedCharacterIterator.TextAttribute.X;
  @@ -118,6 +125,12 @@
           runAtts.add(BASELINE_SHIFT);
       }
   
  +    static Set szAtts = new HashSet();
  +
  +    static {
  +        szAtts.add(TextAttribute.SIZE);
  +    }
  +
   
   
       /**
  @@ -139,7 +152,6 @@
           this.aci = aci;
           this.frc = frc;
           this.offset = offset;
  -        this.transform = null;
           this.font = getFont();
           this.charMap = charMap;
   
  @@ -153,18 +165,28 @@
           this.textPath =  (TextPath) aci.getAttribute
               (GVTAttributedCharacterIterator.TextAttribute.TEXTPATH);
   
  -        AltGlyphHandler altGlyphHandler = (AltGlyphHandler)this.aci.getAttribute(
  -            GVTAttributedCharacterIterator.TextAttribute.ALT_GLYPH_HANDLER);
  +        AltGlyphHandler altGlyphHandler 
  +            = (AltGlyphHandler)this.aci.getAttribute
  +            (GVTAttributedCharacterIterator.TextAttribute.ALT_GLYPH_HANDLER);
           if (altGlyphHandler != null) {
  -            // this must be an altGlyph text element, try and create the alternate 
glyphs
  -            this.gv = altGlyphHandler.createGlyphVector(frc, this.font.getSize(), 
this.aci);
  +            // this must be an altGlyph text element, try and create
  +            // the alternate glyphs
  +            this.gv = altGlyphHandler.createGlyphVector
  +                (frc, this.font.getSize(), this.aci);
           }
           if (this.gv == null) {
  -            // either not an altGlyph or the altGlyphHandler failed to create a 
glyph vector
  +            // either not an altGlyph or the altGlyphHandler failed to
  +            // create a glyph vector
               this.gv = font.createGlyphVector(frc, this.aci);
           }
       }
   
  +
  +    GVTGlyphVector getGlyphVector() {
  +        return this.gv;
  +    }
  +
  +
       /**
        * Returns the current text position at the beginning
        * of glyph layout, before the application of explicit
  @@ -223,8 +245,8 @@
                   float dy = (float)(offset.getY()-this.offset.getY());
                   int numGlyphs = gv.getNumGlyphs();
   
  -                float [] gp = gv.getGlyphPositions(0, numGlyphs, null);
  -                for (int i=0; i<numGlyphs; i++) {
  +                float [] gp = gv.getGlyphPositions(0, numGlyphs+1, null);
  +                for (int i=0; i<=numGlyphs; i++) {
                       gv.setGlyphPosition(i, new Point2D.Float(gp[2*i]+dx,
                                                                gp[2*i+1]+dy));
                   }
  @@ -234,7 +256,7 @@
               // offset this will be factored in for any future layout
               // operations.
               this.offset = offset;
  -            
  +
               // We don't affect layoutApplied or spacingApplied since
               // they both work off the offset value.
   
  @@ -318,16 +340,7 @@
        */
       public void draw(Graphics2D g2d) {
           syncLayout();
  -
  -        AffineTransform t;
  -        if (transform != null) {
  -            t = g2d.getTransform();
  -            g2d.transform(transform);
  -            gv.draw(g2d, aci);
  -            g2d.setTransform(t);
  -        } else {
  -            gv.draw(g2d, aci);
  -        }
  +        gv.draw(g2d, aci);
       }
   
       /**
  @@ -346,11 +359,7 @@
       public Shape getOutline() {
           syncLayout();
   
  -        Shape s = gv.getOutline();
  -        if (transform != null) {
  -            s = transform.createTransformedShape(s);
  -        }
  -        return s;
  +        return gv.getOutline();
       }
   
       /**
  @@ -373,9 +382,6 @@
           if ((decorationType & DECORATION_OVERLINE) != 0) {
                ((GeneralPath) g).append(getOverlineShape(), false);
           }
  -        if (transform != null) {
  -            g = transform.createTransformedShape(g);
  -        }
           return g;
       }
   
  @@ -385,11 +391,7 @@
       public Rectangle2D getBounds() {
           syncLayout();
   
  -        Rectangle2D bounds = gv.getVisualBounds();
  -        if (transform != null) {
  -            bounds = transform.createTransformedShape(bounds).getBounds2D();
  -        }
  -        return bounds;
  +        return gv.getVisualBounds();
       }
   
       /**
  @@ -558,9 +560,6 @@
           }
           addPtsToPath(shape, topPts, botPts, ptIdx);
   
  -        if (transform != null) {
  -            return transform.createTransformedShape(shape);
  -        }
           return shape;
       }
   
  @@ -842,17 +841,6 @@
   
           TextHit textHit = null;
   
  -        // if this layout is transformed, need to apply the inverse
  -        // transform to the point
  -        if (transform != null) {
  -            try {
  -                Point2D p = new Point2D.Float(x, y);
  -                transform.inverseTransform(p, p);
  -                x = (float) p.getX();
  -                y = (float) p.getY();
  -            } catch (java.awt.geom.NoninvertibleTransformException nite) {;}
  -        }
  -
           int currentChar = 0;
           for (int i = 0; i < gv.getNumGlyphs(); i++) {
               Shape gbounds = gv.getGlyphLogicalBounds(i);
  @@ -907,6 +895,12 @@
           // not sure if this is correct behaviour or not
           y += overlineThickness;
   
  +        // Not certain what should be done here...
  +        // aci.first();
  +        // Float dy = (Float) aci.getAttribute(DY);
  +        // if (dy != null)
  +        //     y += dy.floatValue();
  +
           Stroke overlineStroke =
               new BasicStroke(overlineThickness);
           Rectangle2D logicalBounds = gv.getLogicalBounds();
  @@ -932,6 +926,12 @@
           BasicStroke underlineStroke =
               new BasicStroke(underlineThickness);
   
  +        // Not certain what should be done here...
  +        // aci.first();
  +        // Float dy = (Float) aci.getAttribute(DY);
  +        // if (dy != null)
  +        //     y += dy.floatValue();
  +
           Rectangle2D logicalBounds = gv.getLogicalBounds();
   
           return underlineStroke.createStrokedShape(
  @@ -950,6 +950,12 @@
           Stroke strikethroughStroke =
               new BasicStroke(strikethroughThickness);
   
  +        // Not certain what should be done here...
  +        // aci.first();
  +        // Float dy = (Float) aci.getAttribute(DY);
  +        // if (dy != null)
  +        //     y += dy.floatValue();
  +
           Rectangle2D logicalBounds = gv.getLogicalBounds();
           return strikethroughStroke.createStrokedShape(
                              new java.awt.geom.Line2D.Double(
  @@ -957,6 +963,7 @@
                              logicalBounds.getMaxX() - strikethroughThickness/2.0, 
offset.getY()+y));
       }
   
  +
       /**
        * Explicitly lays out each of the glyphs in the glyph
        * vector. This will handle any glyph position adjustments such as
  @@ -977,9 +984,8 @@
           int numGlyphs = gv.getNumGlyphs();
           // System.out.println("NumGlyphs: " + numGlyphs);
   
  -        float[] gp = gv.getGlyphPositions(0, numGlyphs, null);
  +        float[] gp = gv.getGlyphPositions(0, numGlyphs+1, null);
           float verticalFirstOffset = 0f;
  -        float largestAdvanceY = 0;
   
           boolean glyphOrientationAuto = isGlyphOrientationAuto();
           int glyphOrientationAngle = 0;
  @@ -989,6 +995,7 @@
           int i=0, aciIndex = aci.getBeginIndex();
           char ch = aci.first();
           int runLimit = aciIndex;
  +
           Float x=null, y=null, dx=null, dy=null, rotation=null;
           Object baseline=null;
   
  @@ -1188,6 +1195,8 @@
               ch = aci.setIndex(aciIndex);
               i++;
           }
  +        // Update last glyph pos
  +        gv.setGlyphPosition(i, new Point2D.Float(curr_x_pos,curr_y_pos));
   
           offset  = new Point2D.Float(init_x_pos, init_y_pos);
           advance = new Point2D.Float(curr_x_pos - init_x_pos, 
  @@ -1272,7 +1281,7 @@
   
           float dx = 0f;
           float dy = 0f;
  -        Point2D newPositions[] = new Point2D[numGlyphs];
  +        Point2D newPositions[] = new Point2D[numGlyphs+1];
           Point2D prevPos = gv.getGlyphPosition(0);
           float x = (float) prevPos.getX();
           float y = (float) prevPos.getY();
  @@ -1284,7 +1293,7 @@
           try {
               // do letter spacing first
               if ((numGlyphs > 1) && (doLetterSpacing || !autoKern)) {
  -                for (int i=1; i<numGlyphs; ++i) {
  +                for (int i=1; i<=numGlyphs; ++i) {
                       Point2D gpos = gv.getGlyphPosition(i);
                       dx = (float)gpos.getX()-(float)prevPos.getX();
                       dy = (float)gpos.getY()-(float)prevPos.getY();
  @@ -1310,7 +1319,7 @@
                       prevPos = gpos;
                   }
   
  -                for (int i=1; i<numGlyphs; ++i) { // assign the new positions
  +                for (int i=1; i<=numGlyphs; ++i) { // assign the new positions
                       if (newPositions[i] != null) {
                           gv.setGlyphPosition(i, newPositions[i]);
                       }
  @@ -1395,8 +1404,12 @@
                       }
                       prevPos = gpos;
                   }
  +                Point2D gPos = gv.getGlyphPosition(numGlyphs);
  +                x += (float) (gPos.getX()-prevPos.getX());
  +                y += (float) (gPos.getY()-prevPos.getY());
  +                newPositions[numGlyphs] = new Point2D.Float(x, y);
   
  -                for (int i=1; i<numGlyphs; ++i) { // assign the new positions
  +                for (int i=1; i<=numGlyphs; ++i) { // assign the new positions
                       if (newPositions[i] != null) {
                           gv.setGlyphPosition(i, newPositions[i]);
                       }
  @@ -1434,16 +1447,16 @@
           float initX   = (float) bounds.getX();
           float initY   = (float) bounds.getY();
           int numGlyphs = gv.getNumGlyphs();
  -        float [] gp   = gv.getGlyphPositions(0, numGlyphs, null);
  +        float [] gp   = gv.getGlyphPositions(0, numGlyphs+1, null);
           float dx = 0f;
           float dy = 0f;
  -        for (int i = 0; i < numGlyphs; i++) {
  +        for (int i = 0; i <= numGlyphs; i++) {
               dx = gp[2*i]  -initX;
               dy = gp[2*i+1]-initY;
               gv.setGlyphPosition(i, new Point2D.Float(initX+dx*xScale,
                                                        initY+dy*yScale));
   
  -            if (stretchGlyphs) {
  +            if ((stretchGlyphs) && (i != numGlyphs)) {
                   // stretch the glyph
                   AffineTransform glyphTransform = gv.getGlyphTransform(i);
                   if (glyphTransform != null) {
  @@ -1480,6 +1493,7 @@
               return;
           }
   
  +
           boolean horizontal = !isVertical();
   
           boolean glyphOrientationAuto = isGlyphOrientationAuto();
  @@ -1766,5 +1780,609 @@
               }
           }
           return glyphOrientationAngle;
  +    }
  +
  +
  +    // Issues: 
  +    //   Should the font size of non-printing chars affect line spacing?
  +    //   Does line breaking get done before/after ligatures?
  +    //   What should be done if the next glyph does not fit in the
  +    //   flow rect (very narrow flow rect).
  +    //      Print the one char anyway.
  +    //      Go to the next flow rect.
  +    //   Should dy be considered for line offsets? (super scripts)
  +    //   Should p's & br's carry over from flow rect to flow rect if
  +    //   so how much????
  +    //   
  +    //   For Full justification:
  +    //       Streach glyphs to fill line? (attribute?)
  +    //       What to do with partial line (last line in 'p', 'line'
  +    //       element, or 'div' element), still full justify, just left 
  +    //       justify, attribute?
  +    //       What to do when only one glyph on line? left or center or stretch?
  +    //       For full to look good I think the line must be able to squeeze a
  +    //         bit as well as grow (pretty easy to add).
  +    //
  +    // This Only does horizontal languages.
  +    // text-decoration won't work on this text.
  +    // Soft hyphen isn't handled yet.
  +    /**
  +     * This will wrap the text associated with <tt>aci</tt> and
  +     * <tt>layouts</tt>.
  +     * @param aci The Attributed Charater Iterator for text to wrap.
  +     *            used exclusively to access font information.
  +     * @param layouts A List of GlyphLayout objects that are to
  +     *                be wrapped as a whole.
  +     * @param flowRects A List of Rectangle2D representing the regions
  +     *                  to flow text into.
  +     * @param brLocs A List of Integer.  Each integer indicates the
  +     *               the aci index of the char after the end of a
  +     *               br/line element.  Note that a particular value can 
  +     *               be repeated several times for sequences of br/line
  +     *               elements without chars between them.
  +     * @param pLocs A List of Integer.  Each integer indicates the
  +     *               the aci index of the char after the end of a
  +     *               p element.  Note that a particular value can be
  +     *               repeated several times for sequences of p
  +     *               elements without chars between them.
  +     * @param justification an integer in the range: 0-3. 0 is left, 
  +     *                      1 is center, 2 is right, and 
  +     *                      3 is fully justified.
  +     */
  +    public static void textWrapTextChunk(AttributedCharacterIterator aci,
  +                                         List layouts,
  +                                         List flowRects,
  +                                         List brLocs,
  +                                         List pLocs,
  +                                         int justification) {
  +        int aciIndex = aci.getBeginIndex();
  +        int aciEnd   = aci.getEndIndex();
  +
  +        char ch = aci.first();
  +        
  +        Iterator iter = layouts.iterator();
  +
  +        // Make a list of the GlyphVectors so we can construct a
  +        // multiGlyphVector that makes them all look like one big
  +        // glyphVector
  +        List gvs = new LinkedList();
  +        while (iter.hasNext()) {
  +            GlyphLayout gl = (GlyphLayout)iter.next();
  +            gvs.add(gl.getGlyphVector());
  +        }
  +
  +        GVTGlyphVector gv = new MultiGlyphVector(gvs);
  +
  +        int numGlyphs = gv.getNumGlyphs();
  +        float[] gp = gv.getGlyphPositions(0, numGlyphs+1, null);
  +
  +        if (numGlyphs == 0) return;
  +
  +        // Now pull the line break locations out into an array so
  +        // I can jump around a bit easier.
  +        int brIdx = 0;
  +        int [] brLoc = new int[brLocs.size()+1];
  +        iter = brLocs.iterator();
  +        while (iter.hasNext())
  +            brLoc[brIdx++] = ((Integer)iter.next()).intValue();
  +        brLoc[brIdx] = aciEnd+1;
  +        brIdx = 0;
  +            
  +        // Now pull the paragraph break locations out into an array so
  +        // I can jump around a bit easier.
  +        int pIdx = 0;
  +        int [] pLoc = new int[pLocs.size()+1];
  +        iter = pLocs.iterator();
  +        while (iter.hasNext())
  +            pLoc[pIdx++] = ((Integer)iter.next()).intValue();
  +        pLoc[pIdx] = aciEnd+1;
  +        pIdx = 0;
  +        
  +
  +        // Get an iterator for the flow rects.
  +        Iterator flowRectsIter = flowRects.iterator();
  +        if (!flowRectsIter.hasNext()) {
  +            // No place to flow into, hide all glyphs.
  +            for (int i=0; i>numGlyphs; i++)
  +                gv.setGlyphVisible(i, false);
  +            return;
  +        }
  +
  +        // Ok get the info for the first rectangle...
  +        Rectangle2D cRect = (Rectangle2D)flowRectsIter.next();
  +        float x0     = (float)cRect.getX();
  +        float y0     = (float)cRect.getY();
  +        float width  = (float)cRect.getWidth();
  +        float height = (float)cRect.getHeight();
  +
  +        // Get the font size at the start of the string...
  +        float fontSize = 12;
  +        Float fsFloat = (Float)aci.getAttributes().get(TextAttribute.SIZE);
  +        if (fsFloat != null) {
  +            fontSize = fsFloat.floatValue();
  +        }
  +        // Figure out where the font size might change again...
  +        int runLimit  = aci.getRunLimit(TEXT_COMPOUND_DELIMITER);
  +
  +        // This is our current max font size
  +        float maxFontSize = fontSize;
  +        // This is the font size of the last printing char.
  +        float chFontSize  = fontSize;
  +
  +        // Information for backing up to word Break (used when
  +        // wrapping normal lines.  
  +
  +        // Glyph index of last break char seen (-1 if no break char on line)
  +        int   wBreakIdx         = -1;
  +        // The ACI index of last break char seen.
  +        int   wBreakACI         = -1;
  +        // Glyph index of last 'printing' char before last space seen 
  +        // (needed to do visual justification on glyph bounds).
  +        int   wBreakChIdx       = -1;
  +        // The total advance for line including last non-space char.
  +        float wBreakAdv         =  0;
  +        // The total advance for line including spaces at end of line.
  +        float wBreakAdj         =  0;
  +        // The font size at last space seen.
  +        float wBreakFontSize    = fontSize;
  +        // The runLimit (for fonts) at last space seen.
  +        int   wBreakRunLimit    = runLimit;
  +        // The max font size since last space seen (printable chars only).  
  +        float wBreakMaxFontSize = maxFontSize;
  +
  +        // Information for backing up to line start.
  +        // This is needed when we reach the end of a
  +        // line and realize it doesn't fit in the
  +        // current rect.
  +        // Glyph Index of first char on current line.
  +        int   lBreakIdx       = -1;
  +        // ACI Index of first char on current line.
  +        int   lBreakACI       = -1;
  +        // font size at start of line
  +        float lBreakFontSize  = fontSize;
  +        // runLimit (for font size) at start of line.
  +        int   lBreakRunLimit  = runLimit;
  +        // The index into the brLoc array at start of line
  +        int   lBreakBrIdx     = 0;
  +        // The index into the pLoc array at start of line
  +        int   lBreakPIdx      = 0;
  +
  +        // Offset from top of current rect for baseline.
  +        float dy       = 0;
  +        // Printing char advance on line so far.
  +        float adv      = 0;
  +        // Advance of current char plus any leading non-printing chars.
  +        float chAdv    = 0;
  +        // GlyphIndex of last printing char seen 
  +        // (used for full justification on glyph bounds).
  +        int   lastChar = 0;
  +        // The descent from previous line we need to incorporate
  +        float prevDesc = 0;
  +
  +
  +        // Used to know if we are doing the first glyph after line start.
  +        int  lineStart     = 0;
  +
  +        // Per-line information lists.  Used after line breaks have
  +        // been decided upon.
  +        List lineStarts    = new LinkedList(); // glyph index of line starts
  +        List lineLocs      = new LinkedList(); // Point2D of line start.
  +        List lineAdvs      = new LinkedList(); // Visual width of line.
  +        List lineAdjs      = new LinkedList(); // x Offset for line 
  +        List lineLastCharW = new LinkedList(); // used in full justification.
  +        List linePartial   = new LinkedList(); // should line be justified?
  +
  +        lineStarts.add(new Integer(0));
  +        int i;
  +        boolean chIsSpace    = isSpace(ch);
  +        boolean partial      = false;
  +        float   nextLineMult = 1.0f;
  +        float   lineMult     = 1.0f;
  +
  +        // Let's start looking at glyphs....
  +        for (i=0; i<numGlyphs; i++) {
  +
  +            // Update font size for this char if needed.
  +            if (aciIndex >= runLimit) {
  +                runLimit = aci.getRunLimit(TEXT_COMPOUND_DELIMITER);
  +                fsFloat = (Float)aci.getAttributes().get(TextAttribute.SIZE);
  +                if (fsFloat != null) {
  +                    fontSize = fsFloat.floatValue();
  +                } else {
  +                    fontSize = 12;
  +                }
  +                // System.out.println("ACI: " + aciIndex + 
  +                //                    " runLimit: " + runLimit +
  +                //                    " FS: " + fontSize);
  +            }
  +
  +            // Get advance for this glyph...
  +            float gmAdv = gp[2*i+2] - gp[2*i];
  +
  +            // Add to the 'Char adv' which includes any non printing
  +            // chars before this char.
  +            chAdv += gmAdv;
  +
  +            // System.out.println("Ch : '" + ch + "' Idx: " + aciIndex);
  +    
  +            // If it's a space remeber it as a breaking location but
  +            // don't check for line break yet (wait till non-space char).
  +            if (chIsSpace) {
  +                wBreakIdx      = i;
  +                wBreakChIdx    = lastChar;
  +                wBreakACI      = aciIndex;
  +                wBreakAdv      = adv;
  +                wBreakAdj      = adv+chAdv;
  +                wBreakFontSize = fontSize;
  +                wBreakRunLimit = runLimit;
  +
  +                // New break loc so incorporate wBreakMaxFontSize
  +                if (wBreakMaxFontSize > maxFontSize)
  +                    maxFontSize = wBreakMaxFontSize;
  +
  +                wBreakMaxFontSize = chFontSize;
  +            }
  +
  +            if (!chIsSpace) {
  +                // Ok we are a printing char so include all the
  +                // advaces we have collected.
  +                adv += chAdv;
  +
  +                // Remember the size of this char...
  +                chFontSize = fontSize;
  +            }
  +
  +            boolean doBreak = false;
  +
  +            // Don't break at first char in line 
  +            // (otherwise nothing will ever get drawn).
  +            if (!chIsSpace &&
  +                (lineStart != i) && 
  +                (adv > width)
  +                ) {
  +
  +                // Better logic for wrap (allow it to squeeze upto 1/2 as
  +                // much as it would streatch if word wrapped).
  +                // (((wBreakIdx == -1) && (adv > width)) ||  // no break avail
  +                //  (adv-width > (width-wBreakAdv)*.5))
  +
  +
  +
  +                if (wBreakIdx == -1) {
  +                    // Break in the middle of word.  Back out gmAdv
  +                    // since it will be used on the next line.
  +                    wBreakAdv   = adv-gmAdv;
  +                    wBreakAdj   = adv-gmAdv;
  +                    wBreakChIdx = lastChar;
  +
  +                    i--; // reconsider letter (will get inc'd later).
  +
  +                    // Include max font since last break char in max calc.
  +                    if (wBreakMaxFontSize > maxFontSize)
  +                        maxFontSize = wBreakMaxFontSize;
  +                } else {
  +                    // We have a break char to back up to.
  +                    // Reset state to just after breaking chars.
  +                    i          = wBreakIdx;
  +                    aciIndex   = wBreakACI;
  +                    fontSize   = wBreakFontSize;
  +                    chFontSize = wBreakFontSize;
  +                    runLimit   = wBreakRunLimit;
  +
  +                    aciIndex += gv.getCharacterCount(i,i);
  +                    ch        = aci.setIndex(aciIndex);
  +                    chIsSpace = isSpace(ch);
  +                }
  +
  +                nextLineMult = 1.0f;
  +                doBreak = true;
  +                partial = false;
  +            }
  +
  +            // Track how many p's and br's we hit here.
  +            int pCount  = 0;
  +            int brCount = 0;
  +            // did we just pass a p or br?
  +            if ((aciIndex >= pLoc [pIdx]) ||
  +                (aciIndex >= brLoc[brIdx])) {
  +
  +                // Count the P's here...
  +                while (aciIndex >= pLoc[pIdx]) {
  +                    pIdx++;
  +                    pCount++;
  +                    nextLineMult += 1.5;
  +                }
  +
  +                // Count the br's here...
  +                while (aciIndex >= brLoc[brIdx]) {
  +                    brIdx++;
  +                    brCount++;
  +                    nextLineMult += 1.0;
  +                }
  +
  +                if (doBreak) {
  +                    // If we were going to word wrap here anyway
  +                    // Don't double space...
  +                    nextLineMult -= 1.0f;
  +                } else {
  +                    // We need to specify the break
  +                    if (chIsSpace) {
  +                        wBreakAdv   = adv;
  +                        wBreakAdj   = adv+(chAdv-gmAdv);
  +                    } else {
  +                        // If a char with prior spaces then keep
  +                        // spaces on prev line and char on this line...
  +                        wBreakAdv   = adv-gmAdv;
  +                        wBreakAdj   = adv-gmAdv;
  +                    }
  +
  +                    wBreakChIdx = lastChar;
  +
  +                    i--; // reconsider letter.
  +
  +                    // Include max font since break as max.
  +                    if (wBreakMaxFontSize > maxFontSize)
  +                        maxFontSize = wBreakMaxFontSize;
  +                }
  +                
  +                doBreak = true;
  +                partial = true;
  +            }
  +
  +
  +            if (doBreak) {
  +                // We will now attempt to break the line just
  +                // before the current char.
  +
  +                // Note we are trying to figure out where the current
  +                // line is going to be placed (not the next line).  We
  +                // must wait until we have a potential line break so
  +                // we know how tall the line is.
  +
  +                // Get the nomial line advance based on the
  +                // largest font we encountered on line...
  +                float ladv = (maxFontSize*.8f+prevDesc)*1.1f;
  +
  +                // This is modified by any p's or br's we hit at
  +                // the end of the last line.
  +                dy += ladv*lineMult;
  +
  +                // Remember the effect of p's br's at the end of this line.
  +                lineMult = nextLineMult;
  +
  +                // Set ourself up for next line...
  +                nextLineMult = 0.0f;
  +
  +                // System.out.println("Break L [" + lineStart + "," + i + 
  +                //                    "] Loc: " + (y0+dy) + 
  +                //                    " adv: " + wBreakAdv +
  +                //                    " adj: " + wBreakAdj);
  +
  +                if ((dy + maxFontSize*.2f) <= height) {
  +                    // The line fits in the current flow rectangle.
  +
  +                    // System.out.println("Fits: " + (dy + maxFontSize*.2f));
  +
  +
  +                    // Now remember info about start of next line.
  +                    // (needed so we can back up if that line doesn't
  +                    // fit in current rectangle).
  +                    lBreakIdx      = i+1;
  +                    lBreakACI      = aciIndex;
  +                    lBreakFontSize = fontSize;
  +                    lBreakRunLimit = runLimit;
  +                    lBreakPIdx     = pIdx;
  +                    lBreakBrIdx    = brIdx;
  +
  +                    // Now we tweak line advance to account for
  +                    // visual bounds of last glyph.
  +                    Rectangle2D lastCharB = gv.getGlyphVisualBounds
  +                        (wBreakChIdx).getBounds2D();
  +                    Point2D     lastCharL = gv.getGlyphPosition
  +                        (wBreakChIdx);
  +                    float charW   = (float)
  +                        (lastCharB.getX()+lastCharB.getWidth()-
  +                         lastCharL.getX());
  +                    float charAdv = gp[2*wBreakChIdx+2]-gp[2*wBreakChIdx];
  +                    wBreakAdv -= charAdv-charW;
  +
  +                    // Add all the info about the current line to lists.
  +                    lineLocs.add   (new Point2D.Float(x0, y0+dy));
  +                    lineAdvs.add   (new Float(wBreakAdv));
  +                    lineAdjs.add   (new Float(wBreakAdj));
  +                    lineLastCharW.add (new Float(charW));
  +                    linePartial.add(new Boolean(partial));
  +
  +                    // Remember where next line starts.
  +                    lineStart = i+1;
  +                    lineStarts.add (new Integer(lineStart));
  +
  +                    // Remember how far down this line goes into
  +                    // next line.
  +                    prevDesc        = maxFontSize*.2f;
  +                } else {
  +                    // The current line doesn't fit in the current
  +                    // flow rectangle so we need to go move line to
  +                    // the next flow rectangle.
  +
  +                    // System.out.println("Doesn't Fit: " + 
  +                    //                    (dy + maxFontSize*.2f));
  +
  +
  +                    if (!flowRectsIter.hasNext())
  +                        break; // No flow rect stop layout here...
  +
  +                    // Remember how wide this rectangle is...
  +                    float oldWidth = width;
  +
  +                    // Get info for new flow rect.
  +                    cRect = (Rectangle2D)flowRectsIter.next();
  +                    x0     = (float)cRect.getX();
  +                    y0     = (float)cRect.getY();
  +                    width  = (float)cRect.getWidth();
  +                    height = (float)cRect.getHeight();
  +
  +                    // New rect so no previous row to consider...
  +                    dy        = 0;
  +                    prevDesc  = 0;
  +                    lineMult  = 1.0f; // don't pile up lineMults from
  +                                      // previous flows?
  +
  +                    if (cRect.getWidth() >= oldWidth) {
  +                        // new rect is same width or wider so
  +                        // we can reuse our work on this line.
  +
  +                        // Just undo anything we added in
  +                        if (!chIsSpace)
  +                            adv -= chAdv;
  +
  +                        chAdv -= gmAdv;
  +                        // Unadvance p's and br's for this char...
  +                        pIdx  -= pCount;
  +                        brIdx -= brCount;
  +                        continue;
  +                    }
  +
  +                    // new rect is smaller so back up to line start
  +                    // and try with new flow rect.
  +                    i          = lBreakIdx-1; // loop will inc...
  +                    aciIndex   = lBreakACI;
  +                    fontSize   = lBreakFontSize;
  +                    chFontSize = lBreakFontSize;
  +                    runLimit   = lBreakRunLimit;
  +                    pIdx       = lBreakPIdx;
  +                    brIdx      = lBreakBrIdx;
  +
  +                    ch       = aci.setIndex(aciIndex);
  +                    chIsSpace = isSpace(ch);
  +                }
  +
  +                // Set fontSize max's to last printing char size.
  +                maxFontSize       = chFontSize;
  +                wBreakMaxFontSize = chFontSize;
  +
  +                // New line so reset line advance info.
  +                adv=0;
  +                chAdv=0;
  +                wBreakIdx = -1;
  +                continue;
  +            }
  +
  +            if (!chIsSpace) {
  +                // Don't include spaces in max font size calc.
  +                if (chFontSize > wBreakMaxFontSize) {
  +                    wBreakMaxFontSize = chFontSize;
  +                }
  +
  +                // Remember this as last non-space char...
  +                lastChar = i;
  +
  +                // Reset char advance (incorporated into adv above).
  +                chAdv = 0;
  +            }
  +
  +            // Increment everything for ext char...
  +            aciIndex += gv.getCharacterCount(i,i);
  +            ch = aci.setIndex(aciIndex);
  +            chIsSpace = isSpace(ch);
  +        }
  +
  +        // Include max font since break char in max.
  +        if (wBreakMaxFontSize > maxFontSize)
  +            maxFontSize = wBreakMaxFontSize;
  +        dy += (maxFontSize*.8f+prevDesc)*1.1f;
  +        if ((dy + .2f*maxFontSize) >= height) {
  +            // Last line of text didn't fit...
  +            i = lineStart;
  +        } else {
  +            Rectangle2D lastCharB = gv.getGlyphVisualBounds
  +                (lastChar).getBounds2D();
  +            Point2D     lastCharL = gv.getGlyphPosition(lastChar);
  +            float charW   = (float)
  +                (lastCharB.getX()+lastCharB.getWidth()-
  +                 lastCharL.getX());
  +            float charAdv = gp[2*lastChar+2]-gp[2*lastChar];
  +
  +            lineStarts.add (new Integer(i));
  +            lineLocs.add   (new Point2D.Float(x0, y0+dy));
  +            lineAdvs.add   (new Float(adv-(charAdv-charW)));
  +            lineAdjs.add   (new Float(adv+chAdv));
  +            lineLastCharW.add (new Float(charW));
  +            linePartial.add(new Boolean(true));
  +        }
  +
  +        // ignore any glyphs i+  (didn't fit in rects.)  
  +        for (int j=i; j<numGlyphs; j++) 
  +            gv.setGlyphVisible(j, false);
  +
  +        // Limit ourselves to i glyphs...
  +        numGlyphs = i;
  +
  +
  +        Iterator lStartIter    =lineStarts.iterator();
  +        Iterator lLocIter      =lineLocs.iterator();
  +        Iterator lAdvIter      =lineAdvs.iterator();
  +        Iterator lAdjIter      =lineAdjs.iterator();
  +        Iterator lLastCharWIter=lineLastCharW.iterator();
  +        Iterator lPartIter      =linePartial.iterator();
  +
  +        lineStart             = ((Integer)lStartIter.next()).intValue();
  +        Point2D.Float lineLoc = null;
  +        float         lineAdv = 0;
  +        float         lineAdj = 0;
  +
  +        float xOrig=gp[0];
  +        float yOrig=gp[1];
  +
  +        float xScale=1;
  +        float xAdj=0;
  +        float charW;
  +
  +        // This loop goes through and puts glyphs where they belong
  +        // based on info collected in first trip through glyphVector...
  +        i =0;
  +        for (; i<numGlyphs; i++) {
  +            if (i == lineStart) {
  +                // Always comes through here on first char...
  +
  +                // Update offset for new line based on last line length
  +                xOrig += lineAdj;
  +
  +                // Get new values for everything...
  +                lineStart = ((Integer)lStartIter.next()).intValue();
  +                lineLoc   = (Point2D.Float)lLocIter.next();
  +                lineAdv   = (  (Float)lAdvIter.next())  .floatValue();
  +                lineAdj   = (  (Float)lAdjIter.next())  .floatValue();
  +                charW     = (  (Float)lLastCharWIter.next())  .floatValue();
  +                partial   = ((Boolean)lPartIter.next()) .booleanValue();
  +
  +                xAdj = 0;
  +                xScale = 1;
  +                // Recalc justification info.
  +                switch (justification) {
  +                case 0: default: break;                  // Left
  +                case 1: xAdj = (width-lineAdv)/2; break; // Center
  +                case 2: xAdj =  width-lineAdv;    break; // Right
  +                case 3:                                  // Full
  +                    if ((!partial) && (lineStart != i+1)) {
  +                        // More than one char on line...
  +                        // Scale char spacing to fill line.
  +                        xScale = (width-charW)/(lineAdv-charW);
  +                    }
  +                    break;
  +                }
  +            }
  +            float x = lineLoc.x + (gp[2*i]  -xOrig)*xScale+xAdj;
  +            float y = lineLoc.y + (gp[2*i+1]-yOrig);
  +            gv.setGlyphPosition(i, new Point2D.Float(x, y));
  +        }
  +
  +        float x = lineLoc.x + (gp[2*i]  -xOrig)*xScale+xAdj;
  +        float y = lineLoc.y + (gp[2*i+1]-yOrig);
  +        gv.setGlyphPosition(i, new Point2D.Float(x, y));
  +    }
  +
  +    protected static boolean isSpace(char ch) {
  +        return ((ch == ' ') || (ch == '\t'));
       }
   }
  
  
  
  1.37      +41 -3     
xml-batik/sources/org/apache/batik/transcoder/image/ImageTranscoder.java
  
  Index: ImageTranscoder.java
  ===================================================================
  RCS file: 
/home/cvs/xml-batik/sources/org/apache/batik/transcoder/image/ImageTranscoder.java,v
  retrieving revision 1.36
  retrieving revision 1.37
  diff -u -r1.36 -r1.37
  --- ImageTranscoder.java      6 Mar 2002 17:14:45 -0000       1.36
  +++ ImageTranscoder.java      7 Mar 2002 22:07:44 -0000       1.37
  @@ -101,7 +101,7 @@
    * millimeter conversion factor.
    *
    * @author <a href="mailto:[EMAIL PROTECTED]";>Thierry Kormann</a>
  - * @version $Id: ImageTranscoder.java,v 1.36 2002/03/06 17:14:45 tkormann Exp $
  + * @version $Id: ImageTranscoder.java,v 1.37 2002/03/07 22:07:44 deweese Exp $
    */
   public abstract class ImageTranscoder extends XMLAbstractTranscoder {
   
  @@ -247,8 +247,8 @@
               Px.preConcatenate(Mx);
           }
           // prepare the image to be painted
  -        int w = (int)width;
  -        int h = (int)height;
  +        int w = (int)(width+0.5);
  +        int h = (int)(height+0.5);
   
           // paint the SVG document using the bridge package
           // create the appropriate renderer
  @@ -751,4 +751,42 @@
       public static final TranscodingHints.Key KEY_BACKGROUND_COLOR
           = new PaintKey();
   
  +    /**
  +     * The forceTransparentWhite key.
  +     *
  +     * <TABLE BORDER="0" CELLSPACING="0" CELLPADDING="1">
  +     * <TR>
  +     * <TH VALIGN="TOP" ALIGN="RIGHT"><P ALIGN="RIGHT">Key: </TH>
  +     * <TD VALIGN="TOP">KEY_FORCE_TRANSPARENT_WHITE</TD></TR>
  +     * <TR>
  +     * <TH VALIGN="TOP" ALIGN="RIGHT"><P ALIGN="RIGHT">Value: </TH>
  +     * <TD VALIGN="TOP">Boolean</TD></TR>
  +     * <TR>
  +     * <TH VALIGN="TOP" ALIGN="RIGHT"><P ALIGN="RIGHT">Default: </TH>
  +     * <TD VALIGN="TOP">false</TD></TR>
  +     * <TR>
  +     * <TH VALIGN="TOP" ALIGN="RIGHT"><P ALIGN="RIGHT">Required: </TH>
  +     * <TD VALIGN="TOP">No</TD></TR>
  +     * <TR>
  +     * <TH VALIGN="TOP" ALIGN="RIGHT"><P ALIGN="RIGHT">Description: </TH>
  +
  +     * <TD VALIGN="TOP">It controls whether the encoder should force
  +     * the image's fully transparent pixels to be fully transparent
  +     * white instead of fully transparent black.  This is usefull when
  +     * the encoded file is displayed in a browser which does not
  +     * support transparency correctly and lets the image display with
  +     * a white background instead of a black background. <br />
  +     *
  +     * However, note that the modified image will display differently
  +     * over a white background in a viewer that supports
  +     * transparency.<br/>
  +     *
  +     * Not all Transcoders use this key (in particular some formats
  +     * can't preserve the alpha channel at all in which case this
  +     * is not used.
  +     * </TD></TR> 
  +     * </TABLE> 
  +     */
  +    public static final TranscodingHints.Key KEY_FORCE_TRANSPARENT_WHITE
  +        = new BooleanKey();
   }
  
  
  
  1.12      +2 -2      
xml-batik/sources/org/apache/batik/transcoder/image/PNGTranscoder.java
  
  Index: PNGTranscoder.java
  ===================================================================
  RCS file: 
/home/cvs/xml-batik/sources/org/apache/batik/transcoder/image/PNGTranscoder.java,v
  retrieving revision 1.11
  retrieving revision 1.12
  diff -u -r1.11 -r1.12
  --- PNGTranscoder.java        11 Feb 2002 18:03:42 -0000      1.11
  +++ PNGTranscoder.java        7 Mar 2002 22:07:44 -0000       1.12
  @@ -26,7 +26,7 @@
    * This class is an <tt>ImageTranscoder</tt> that produces a PNG image.
    *
    * @author <a href="mailto:[EMAIL PROTECTED]";>Thierry Kormann</a>
  - * @version $Id: PNGTranscoder.java,v 1.11 2002/02/11 18:03:42 deweese Exp $
  + * @version $Id: PNGTranscoder.java,v 1.12 2002/03/07 22:07:44 deweese Exp $
    */
   public class PNGTranscoder extends ImageTranscoder {
   
  @@ -158,7 +158,7 @@
        * </TABLE>
        */
       public static final TranscodingHints.Key KEY_FORCE_TRANSPARENT_WHITE
  -        = new BooleanKey();
  +        = ImageTranscoder.KEY_FORCE_TRANSPARENT_WHITE;
   
       /**
        * The gamma correction key.
  
  
  
  1.2       +111 -30   
xml-batik/sources/org/apache/batik/transcoder/image/TIFFTranscoder.java
  
  Index: TIFFTranscoder.java
  ===================================================================
  RCS file: 
/home/cvs/xml-batik/sources/org/apache/batik/transcoder/image/TIFFTranscoder.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- TIFFTranscoder.java       16 May 2001 12:34:16 -0000      1.1
  +++ TIFFTranscoder.java       7 Mar 2002 22:07:44 -0000       1.2
  @@ -24,6 +24,8 @@
   import org.apache.batik.transcoder.image.resources.Messages;
   import org.apache.batik.ext.awt.image.codec.tiff.TIFFEncodeParam;
   import org.apache.batik.ext.awt.image.codec.tiff.TIFFImageEncoder;
  +import org.apache.batik.ext.awt.image.codec.tiff.TIFFImageDecoder;
  +import org.apache.batik.ext.awt.image.codec.tiff.TIFFField;
   import org.apache.batik.ext.awt.image.rendered.FormatRed;
   import org.apache.batik.ext.awt.image.GraphicsUtil;
   
  @@ -32,14 +34,16 @@
    * This class is an <tt>ImageTranscoder</tt> that produces a TIFF image.
    *
    * @author <a href="mailto:[EMAIL PROTECTED]";>Thierry Kormann</a>
  - * @version $Id: TIFFTranscoder.java,v 1.1 2001/05/16 12:34:16 deweese Exp $
  + * @version $Id: TIFFTranscoder.java,v 1.2 2002/03/07 22:07:44 deweese Exp $
    */
   public class TIFFTranscoder extends ImageTranscoder {
   
       /**
        * Constructs a new transcoder that produces tiff images.
        */
  -    public TIFFTranscoder() { }
  +    public TIFFTranscoder() { 
  +        hints.put(KEY_FORCE_TRANSPARENT_WHITE, Boolean.FALSE);
  +    }
   
       /**
        * Creates a new ARGB image with the specified dimension.
  @@ -64,41 +68,78 @@
               throw new TranscoderException(
                   Messages.formatMessage("tiff.badoutput", null));
           }
  +
           TIFFEncodeParam params = new TIFFEncodeParam();
   
  +        float pixToMM = userAgent.getPixelToMM();
  +        // Pixs in 100 Meters
  +        int numPix      = (int)(pixToMM*(1000*100)+0.5); 
  +        int denom       = 100*100;  // Centimeters per 100 Meters;
  +        long [] rational = {(long)numPix, (long)denom};
  +        TIFFField [] fields = {
  +            new TIFFField(TIFFImageDecoder.TIFF_RESOLUTION_UNIT, 
  +                          TIFFField.TIFF_SHORT, 1, 
  +                          new char [] { (char)3 }),
  +            new TIFFField(TIFFImageDecoder.TIFF_X_RESOLUTION, 
  +                          TIFFField.TIFF_RATIONAL, 1, 
  +                          new long [][] { rational }),
  +            new TIFFField(TIFFImageDecoder.TIFF_Y_RESOLUTION, 
  +                          TIFFField.TIFF_RATIONAL, 1, 
  +                          new long [][] { rational }) 
  +                };
  +
  +        params.setExtraFields(fields);
  +
           //
  -        // This is a trick so that viewers which do not support
  -        // the alpha channel will see a white background (and not
  -        // a black one).
  +        // This is a trick so that viewers which do not support the alpha
  +        // channel will see a white background (and not a black one).
           //
  -        int w = img.getWidth(), h = img.getHeight();
  -        DataBufferInt biDB = (DataBufferInt)img.getRaster().getDataBuffer();
  +        boolean forceTransparentWhite = false;
  +
  +        if (hints.containsKey(KEY_FORCE_TRANSPARENT_WHITE)) {
  +            forceTransparentWhite =
  +                ((Boolean)hints.get
  +                 (KEY_FORCE_TRANSPARENT_WHITE)).booleanValue();
  +        }
  +
  +        int w = img.getWidth();
  +        int h = img.getHeight();
           SinglePixelPackedSampleModel sppsm;
           sppsm = (SinglePixelPackedSampleModel)img.getSampleModel();
  -        int scanStride = sppsm.getScanlineStride();
  -        int dbOffset = biDB.getOffset();
  -        int pixels[] = biDB.getBankData()[0];
  -        int p = dbOffset;
  -        int adjust = scanStride - w;
  -        int a=0, r=0, g=0, b=0, pel=0;
  -        for(int i=0; i<h; i++){
  -            for(int j=0; j<w; j++){
  -                pel = pixels[p];
  -                a = (pel >> 24) & 0xff;
  -                r = (pel >> 16) & 0xff;
  -                g = (pel >> 8 ) & 0xff;
  -                b =  pel        & 0xff;
  -                r = (255*(255 -a) + a*r)/255;
  -                g = (255*(255 -a) + a*g)/255;
  -                b = (255*(255 -a) + a*b)/255;
  -                pixels[p++] =
  -                            (a<<24 & 0xff000000) |
  -                            (r<<16 & 0xff0000) |
  -                            (g<<8  & 0xff00) |
  -                            (b     & 0xff);
  -             }
  -            p += adjust;
  +
  +        if (forceTransparentWhite) {
  +            //
  +            // This is a trick so that viewers which do not support
  +            // the alpha channel will see a white background (and not
  +            // a black one).
  +            //
  +            DataBufferInt biDB=(DataBufferInt)img.getRaster().getDataBuffer();
  +            int scanStride = sppsm.getScanlineStride();
  +            int dbOffset = biDB.getOffset();
  +            int pixels[] = biDB.getBankData()[0];
  +            int p = dbOffset;
  +            int adjust = scanStride - w;
  +            int a=0, r=0, g=0, b=0, pel=0;
  +            for(int i=0; i<h; i++){
  +                for(int j=0; j<w; j++){
  +                    pel = pixels[p];
  +                    a = (pel >> 24) & 0xff;
  +                    r = (pel >> 16) & 0xff;
  +                    g = (pel >> 8 ) & 0xff;
  +                    b =  pel        & 0xff;
  +                    r = (255*(255 -a) + a*r)/255;
  +                    g = (255*(255 -a) + a*g)/255;
  +                    b = (255*(255 -a) + a*b)/255;
  +                    pixels[p++] =
  +                        (a<<24 & 0xff000000) |
  +                        (r<<16 & 0xff0000) |
  +                        (g<<8  & 0xff00) |
  +                        (b     & 0xff);
  +                }
  +                p += adjust;
  +            }
           }
  +
           try {
               TIFFImageEncoder tiffEncoder = 
                   new TIFFImageEncoder(ostream, params);
  @@ -112,7 +153,47 @@
               RenderedImage rimg = new FormatRed(GraphicsUtil.wrap(img), sm);
               tiffEncoder.encode(rimg);
           } catch (IOException ex) {
  +            ex.printStackTrace();
               throw new TranscoderException(ex);
           }
       }
  +
  +
  +    // --------------------------------------------------------------------
  +    // Keys definition
  +    // --------------------------------------------------------------------
  +
  +    /**
  +     * The forceTransparentWhite key.
  +     *
  +     * <TABLE BORDER="0" CELLSPACING="0" CELLPADDING="1">
  +     * <TR>
  +     * <TH VALIGN="TOP" ALIGN="RIGHT"><P ALIGN="RIGHT">Key: </TH>
  +     * <TD VALIGN="TOP">KEY_FORCE_TRANSPARENT_WHITE</TD></TR>
  +     * <TR>
  +     * <TH VALIGN="TOP" ALIGN="RIGHT"><P ALIGN="RIGHT">Value: </TH>
  +     * <TD VALIGN="TOP">Boolean</TD></TR>
  +     * <TR>
  +     * <TH VALIGN="TOP" ALIGN="RIGHT"><P ALIGN="RIGHT">Default: </TH>
  +     * <TD VALIGN="TOP">false</TD></TR>
  +     * <TR>
  +     * <TH VALIGN="TOP" ALIGN="RIGHT"><P ALIGN="RIGHT">Required: </TH>
  +     * <TD VALIGN="TOP">No</TD></TR>
  +     * <TR>
  +     * <TH VALIGN="TOP" ALIGN="RIGHT"><P ALIGN="RIGHT">Description: </TH>
  +     * <TD VALIGN="TOP">It controls whether the encoder should
  +     * force the image's fully transparent pixels to be fully transparent
  +     * white instead of fully transparent black.  This is usefull when the
  +     * encoded TIFF is displayed in a viewer which does not support TIFF
  +     * transparency and lets the image display with a white background instead
  +     * of a black background. <br /> 
  +     *
  +     * However, note that the modified image will display differently
  +     * over a white background in a viewer that supports
  +     * transparency.</TD></TR>
  +     * </TABLE> 
  +     */
  +    public static final TranscodingHints.Key KEY_FORCE_TRANSPARENT_WHITE
  +        = ImageTranscoder.KEY_FORCE_TRANSPARENT_WHITE;
  +
   }
  
  
  
  1.1                  
xml-batik/test-references/samples/tests/spec/scripting/.cvsignore
  
  Index: .cvsignore
  ===================================================================
  accepted-variation
  candidate-reference
  candidate-variation
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to