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]