deweese 02/01/24 13:35:33 Modified: sources/org/apache/batik/ext/awt/image/rendered ProfileRed.java sources/org/apache/batik/gvt/renderer StrokingTextPainter.java sources/org/apache/batik/gvt/text GlyphLayout.java TextSpanLayout.java Added: samples/tests/spec/text textLength.svg Log: 1) Fixes for textLength with tspan (still problem when tspan first thing in text element). 2) Added test for textLength with anchor. Revision Changes Path 1.1 xml-batik/samples/tests/spec/text/textLength.svg Index: textLength.svg =================================================================== <?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> <!-- ====================================================================== --> <!-- 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. --> <!-- ====================================================================== --> <!-- ====================================================================== --> <!-- Tests text-anchor on tspan elements --> <!-- --> <!-- @author [EMAIL PROTECTED] --> <!-- @version $Id: textLength.svg,v 1.1 2002/01/24 21:35:32 deweese Exp $ --> <!-- ====================================================================== --> <?xml-stylesheet type="text/css" href="../../resources/style/test.css" ?> <svg width="450" height="500" viewBox="0 0 450 500"> <text class="title" x="50%" y="40">textLength with Anchor</text> <defs> <g id="fill-rgn"> <rect x="200" y="10" width="50" height="20" fill="#DDE8FF" stroke="none"/> <line x1="225" y1="8" x2="225" y2="32" stroke="red"/> </g> <g id="bg1"> <rect x="50" y="0" width ="350" height="50" fill="lightGrey" stroke="black"/> <use xlink:href="#fill-rgn"/> </g> <g id="bg2"> <rect x="50" y="0" width ="350" height="50" fill="white" stroke="black"/> <use xlink:href="#fill-rgn"/> </g> </defs> <g font-size="12" > <!-- no lengthadjust --> <g transform="translate(0, 50)"> <use xlink:href="#bg1"/> <text x="225" y="24" text-anchor="middle">xml-batik</text> <text x="225" y="45" text-anchor="middle">No textLength</text> </g> <!-- With lengthAdjust spacingAndGlyphs --> <g transform="translate(0, 100)"> <use xlink:href="#bg2"/> <text x="225" y="24" text-anchor="middle" textLength="50" lengthAdjust="spacingAndGlyphs">B</text> <text x="225" y="45" text-anchor="middle">textLength="50" lengthAdjust="spacingAndGlyphs"</text> </g> <!-- with lengthAdjust spacing --> <g transform="translate(0, 150)"> <use xlink:href="#bg1"/> <text x="225" y="24" text-anchor="middle" textLength="50" lengthAdjust="spacing">B</text> <text x="225" y="45" text-anchor="middle">textLength="50" lengthAdjust="spacing"</text> </g> <!-- with lengthAdjust spacing --> <g transform="translate(0, 200)"> <use xlink:href="#bg2"/> <text x="225" y="24" text-anchor="middle" textLength="50" lengthAdjust="spacingAndGlyphs">Batik</text> <text x="225" y="45" text-anchor="middle">textLength="50" lengthAdjust="spacingAndGlyphs"</text> </g> <!-- with lengthAdjust spacing --> <g transform="translate(0, 250)"> <use xlink:href="#bg1"/> <text x="225" y="24" text-anchor="middle" textLength="50" lengthAdjust="spacing">Batik</text> <text x="225" y="45" text-anchor="middle">textLength="50" lengthAdjust="spacing"</text> </g> <!-- large number --> <g transform="translate(0, 300)"> <use xlink:href="#bg2"/> <text x="225" y="24" text-anchor="middle" textLength="50" lengthAdjust="spacingAndGlyphs">Apache Batik</text> <text x="225" y="45" text-anchor="middle">textLength="50" lengthAdjust="spacingAndGlyphs" (shrinking)</text> </g> <!-- large number, no anchor --> <g transform="translate(0, 350)"> <use xlink:href="#bg1"/> <text x="200" y="24" textLength="50" lengthAdjust="spacingAndGlyphs">Apache Batik</text> <text x="225" y="45" text-anchor="middle">textLength="50" lengthAdjust="spacingAndGlyphs" (no anchor)</text> </g> <!-- large number, tspan --> <g transform="translate(0, 400)"> <use xlink:href="#bg2"/> <text x="225" y="24" text-anchor="middle" textLength="50" lengthAdjust="spacingAndGlyphs">Apache <tspan fill="red">Batik</tspan>!!</text> <text x="225" y="45" text-anchor="middle">textLength="50" lengthAdjust="spacingAndGlyphs" (tspan)</text> </g> </g> <!-- ============================================================= --> <!-- Batik sample mark --> <!-- ============================================================= --> <use xlink:href="../../../batikLogo.svg#Batik_Tag_Box" /> </svg> 1.3 +54 -53 xml-batik/sources/org/apache/batik/ext/awt/image/rendered/ProfileRed.java Index: ProfileRed.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/ext/awt/image/rendered/ProfileRed.java,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- ProfileRed.java 15 Feb 2001 02:27:29 -0000 1.2 +++ ProfileRed.java 24 Jan 2002 21:35:32 -0000 1.3 @@ -37,7 +37,7 @@ * on its source * * @author <a href="mailto:[EMAIL PROTECTED]">Vincent Hardy</a> - * @version $Id: ProfileRed.java,v 1.2 2001/02/15 02:27:29 vhardy Exp $ + * @version $Id: ProfileRed.java,v 1.3 2002/01/24 21:35:32 deweese Exp $ */ public class ProfileRed extends AbstractRed { private static final ColorSpace sRGBCS @@ -89,17 +89,19 @@ * Note that if the number of components in the input image and * the number of components in the replacing ColorSpace do not * match, it is not possible to apply the conversion. - * d. A new BufferedImage is built, using the new ComponentColorModel - * and the data from the original image converted to the ComponentColorModel - * built in a. The alpha channel is excluded from that new BufferedImage. + * d. A new BufferedImage is built, using the new + * ComponentColorModel and the data from the original image + * converted to the ComponentColorModel built in a. The alpha + * channel is excluded from that new BufferedImage. * e. The BufferedImage created in d. is converted to sRGB using * ColorConvertOp * f. The alpha channel information is integrated back into the image. * - * IMPORTANT NOTE: The code uses a BandedSampleModel in c.) and d.) and discard the - * alpha channel during the color conversions (it is restored in f.)), because of - * bugs in the interleaved model with alpha. The BandedSampleModel did not cause - * any bug as of JDK 1.3. + * IMPORTANT NOTE: The code uses a BandedSampleModel in c.) and + * d.) and discard the alpha channel during the color conversions + * (it is restored in f.)), because of bugs in the interleaved + * model with alpha. The BandedSampleModel did not cause any bug + * as of JDK 1.3. */ public WritableRaster copyData(WritableRaster argbWR){ try{ @@ -140,25 +142,27 @@ if(!(imgCM instanceof ComponentColorModel) || !(img.getSampleModel() instanceof BandedSampleModel)){ ComponentColorModel imgCompCM - = new ComponentColorModel(imgCS, // Same ColorSpace as img - imgCM.getComponentSize(), // Array of number of bits per components - imgCM.hasAlpha(), // Same alpha as img - imgCM.isAlphaPremultiplied(), // Same premultiplication as img - imgCM.getTransparency(), // Same transparency as img - DataBuffer.TYPE_BYTE); // 8 bits per components. + = new ComponentColorModel + (imgCS, // Same ColorSpace as img + imgCM.getComponentSize(), // Number of bits/comp + imgCM.hasAlpha(), // Same alpha as img + imgCM.isAlphaPremultiplied(), // Same premult as img + imgCM.getTransparency(), // Same trans as img + DataBuffer.TYPE_BYTE); // 8 bit/component. WritableRaster wr - = Raster.createBandedRaster(DataBuffer.TYPE_BYTE, - argbWR.getWidth(), argbWR.getHeight(), - imgCompCM.getNumComponents(), - new Point(0, 0)); + = Raster.createBandedRaster + (DataBuffer.TYPE_BYTE, + argbWR.getWidth(), argbWR.getHeight(), + imgCompCM.getNumComponents(), + new Point(0, 0)); - BufferedImage imgComp - = new BufferedImage(imgCompCM, wr, imgCompCM.isAlphaPremultiplied(), null); + BufferedImage imgComp = new BufferedImage + (imgCompCM, wr, imgCompCM.isAlphaPremultiplied(), null); - BufferedImage srcImg - = new BufferedImage(imgCM, srcWR.createWritableTranslatedChild(0, 0), - imgCM.isAlphaPremultiplied(), null); + BufferedImage srcImg = new BufferedImage + (imgCM, srcWR.createWritableTranslatedChild(0, 0), + imgCM.isAlphaPremultiplied(), null); Graphics2D g = imgComp.createGraphics(); g.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, @@ -170,11 +174,11 @@ } /** - * Now, the input image is using a component color model. We can - * therefore create an image with the new profile, using a ComponentColorModel - * as well, because we know the number of components match (this was checked - * at the begining of this routine). - */ + * Now, the input image is using a component color + * model. We can therefore create an image with the new + * profile, using a ComponentColorModel as well, because + * we know the number of components match (this was + * checked at the begining of this routine). */ ComponentColorModel newCM = new ComponentColorModel(colorSpace, // ****** New ColorSpace ******** imgCM.getComponentSize(), // Array of number of bits per components @@ -185,34 +189,30 @@ // Build a raster with bands 0, 1 and 2 of img's raster DataBufferByte data = (DataBufferByte)srcWR.getDataBuffer(); - srcWR = Raster.createBandedRaster(data, img.getWidth(), img.getHeight(), - img.getWidth(), new int[]{0, 1, 2}, - new int[]{0, 0, 0}, new Point(0, 0)); - BufferedImage newImg = new BufferedImage(newCM, srcWR, - newCM.isAlphaPremultiplied(), null); + srcWR = Raster.createBandedRaster + (data, img.getWidth(), img.getHeight(), + img.getWidth(), new int[]{0, 1, 2}, + new int[]{0, 0, 0}, new Point(0, 0)); + BufferedImage newImg = new BufferedImage + (newCM, srcWR, newCM.isAlphaPremultiplied(), null); /** * Now, convert the image to sRGB */ - ComponentColorModel sRGBCompCM = - new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), - new int[]{8, 8, 8}, - false, - false, - Transparency.OPAQUE, - DataBuffer.TYPE_BYTE); - - WritableRaster wr - = Raster.createBandedRaster(DataBuffer.TYPE_BYTE, - img.getWidth(), img.getHeight(), - sRGBCompCM.getNumComponents(), - new Point(0, 0)); - - BufferedImage sRGBImage - = new BufferedImage(sRGBCompCM, - wr, - false, - null); + ComponentColorModel sRGBCompCM = new ComponentColorModel + (ColorSpace.getInstance(ColorSpace.CS_sRGB), + new int[]{8, 8, 8}, + false, + false, + Transparency.OPAQUE, + DataBuffer.TYPE_BYTE); + + WritableRaster wr = Raster.createBandedRaster + (DataBuffer.TYPE_BYTE, img.getWidth(), img.getHeight(), + sRGBCompCM.getNumComponents(), new Point(0, 0)); + + BufferedImage sRGBImage = new BufferedImage + (sRGBCompCM, wr, false, null); ColorConvertOp colorConvertOp = new ColorConvertOp(null); colorConvertOp.filter(newImg, sRGBImage); @@ -222,7 +222,8 @@ DataBufferByte rgbData = (DataBufferByte)wr.getDataBuffer(); byte[][] imgBanks = data.getBankData(); byte[][] rgbBanks = rgbData.getBankData(); - byte[][] argbBanks = {rgbBanks[0], rgbBanks[1], rgbBanks[2], imgBanks[3]}; + byte[][] argbBanks = {rgbBanks[0], rgbBanks[1], + rgbBanks[2], imgBanks[3]}; DataBufferByte argbData = new DataBufferByte(argbBanks, imgBanks[0].length); srcWR = Raster.createBandedRaster(argbData, img.getWidth(), img.getHeight(), img.getWidth(), new int[]{0, 1, 2, 3}, 1.27 +119 -19 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.26 retrieving revision 1.27 diff -u -r1.26 -r1.27 --- StrokingTextPainter.java 23 Jan 2002 14:14:09 -0000 1.26 +++ StrokingTextPainter.java 24 Jan 2002 21:35:32 -0000 1.27 @@ -40,6 +40,7 @@ import org.apache.batik.gvt.font.FontFamilyResolver; import org.apache.batik.gvt.font.GVTFont; import org.apache.batik.gvt.font.GVTFontFamily; +import org.apache.batik.gvt.font.GVTGlyphMetrics; import org.apache.batik.gvt.font.UnresolvedFontFamily; import org.apache.batik.gvt.text.AttributedCharacterSpanIterator; import org.apache.batik.gvt.text.BidiAttributedCharacterIterator; @@ -60,7 +61,7 @@ * @see org.apache.batik.gvt.text.GVTAttributedCharacterIterator * * @author <a href="[EMAIL PROTECTED]>Bill Haneman</a> - * @version $Id: StrokingTextPainter.java,v 1.26 2002/01/23 14:14:09 deweese Exp $ + * @version $Id: StrokingTextPainter.java,v 1.27 2002/01/24 21:35:32 deweese Exp $ */ public class StrokingTextPainter extends BasicTextPainter { @@ -96,6 +97,11 @@ AttributedCharacterIterator.Attribute ANCHOR_TYPE = GVTAttributedCharacterIterator.TextAttribute.ANCHOR_TYPE; + public static final Integer ADJUST_SPACING = + GVTAttributedCharacterIterator.TextAttribute.ADJUST_SPACING; + public static final Integer ADJUST_ALL = + GVTAttributedCharacterIterator.TextAttribute.ADJUST_ALL; + static Set extendedAtts = new HashSet(); static { @@ -179,8 +185,8 @@ TextChunk chunk, prevChunk=null; int currentChunk = 0; do { - // Text Chunks contain one or more TextRuns, which they create from - // the ACI. + // Text Chunks contain one or more TextRuns, which they + // create from the ACI. chunkACIs[currentChunk].first(); chunk = getTextChunk(node, @@ -192,8 +198,7 @@ // Adjust according to text-anchor property value chunkACIs[currentChunk].first(); if (chunk != null) { - adjustChunkOffsets(textRuns, chunk.advance, - chunk.begin, chunk.end); + adjustChunkOffsets(textRuns, chunk); } prevChunk = chunk; currentChunk++; @@ -595,15 +600,85 @@ * to account for any text anchor properties. */ private void adjustChunkOffsets(List textRuns, - Point2D advance, - int beginChunk, - int endChunk) { - - TextRun r = (TextRun) textRuns.get(beginChunk); + TextChunk chunk) { + TextRun r = (TextRun) textRuns.get(chunk.begin); int anchorType = r.getAnchorType(); + Float length = r.getLength(); + Integer lengthAdj = r.getLengthAdjust(); + Point2D advance = chunk.advance; + float dx = 0f; float dy = 0f; + boolean doAdjust = true; + if ((length == null) || length.isNaN()) + doAdjust = false; + + int numChars = 0; + for (int n=chunk.begin; n<chunk.end; ++n) { + r = (TextRun) textRuns.get(n); + AttributedCharacterIterator aci = r.getACI(); + numChars += aci.getEndIndex()-aci.getBeginIndex(); + } + if ((lengthAdj == + GVTAttributedCharacterIterator.TextAttribute.ADJUST_SPACING) && + (numChars == 1)) + doAdjust = false; + + float xScale = 1; + float yScale = 1; + if (doAdjust) { + if (lengthAdj == + GVTAttributedCharacterIterator.TextAttribute.ADJUST_SPACING) { + + } + if (lengthAdj == + GVTAttributedCharacterIterator.TextAttribute.ADJUST_ALL) { + } + + float delta = 0; + r = (TextRun)textRuns.get(chunk.end-1); + TextSpanLayout layout = r.getLayout(); + GVTGlyphMetrics lastCharMetrics = + layout.getGlyphMetrics(layout.getGlyphCount()-1); + Rectangle2D lastCharBounds = lastCharMetrics.getBounds2D(); + + if (r.getLayout().isVertical()) { + if (lengthAdj == ADJUST_SPACING) { + yScale = (float) + ((length.floatValue()-lastCharBounds.getHeight())/ + (advance.getY()-lastCharBounds.getHeight())); + } else { + yScale = (float)(length.floatValue()/advance.getY()); + } + dy = delta; + } else { + if (lengthAdj == ADJUST_SPACING) { + xScale = (float) + ((length.floatValue()-lastCharBounds.getWidth())/ + (advance.getX()-lastCharBounds.getWidth())); + } else { + xScale = (float)(length.floatValue()/advance.getX()); + } + dx = delta; + } + + // System.out.println("Adv: " + advance + " Len: " + length + + // " scale: [" + xScale + ", " + yScale + "]"); + Point2D.Float adv = new Point2D.Float(0,0); + for (int n=chunk.begin; n<chunk.end; ++n) { + r = (TextRun) textRuns.get(n); + layout = r.getLayout(); + layout.setScale(xScale, yScale, lengthAdj==ADJUST_SPACING); + Point2D lAdv = layout.getAdvance2D(); + adv.x += lAdv.getX(); + adv.y += lAdv.getY(); + } + chunk.advance = adv; + } + + advance = chunk.advance; + switch(anchorType){ case TextNode.Anchor.ANCHOR_MIDDLE: dx = (float) (-advance.getX()/2d); @@ -618,17 +693,26 @@ // leave untouched } - for (int n=beginChunk; n<endChunk; ++n) { - r = (TextRun) textRuns.get(n); + + r = (TextRun) textRuns.get(chunk.begin); TextSpanLayout layout = r.getLayout(); Point2D offset = layout.getOffset(); + float initX = (float)offset.getX(); + float initY = (float)offset.getY(); - if (layout.isVertical()) + for (int n=chunk.begin; n<chunk.end; ++n) { + r = (TextRun) textRuns.get(n); + layout = r.getLayout(); + offset = layout.getOffset(); + if (layout.isVertical()) { + float adj = (float)((offset.getY()-initY)*yScale); offset = new Point2D.Float((float) offset.getX(), - (float) offset.getY()+dy); - else - offset = new Point2D.Float((float) offset.getX()+dx, + (float)initY+adj+dy); + } else { + float adj = (float)((offset.getX()-initX)*xScale); + offset = new Point2D.Float((float)initX+adj+dx, (float) offset.getY()); + } layout.setOffset(offset); } } @@ -1416,6 +1500,8 @@ private TextSpanLayout layout; private int anchorType; private boolean firstRunInChunk; + private Float length; + private Integer lengthAdjust; public TextRun(TextSpanLayout layout, AttributedCharacterIterator aci, @@ -1425,13 +1511,14 @@ this.aci = aci; this.aci.first(); this.firstRunInChunk = firstRunInChunk; + this.anchorType = TextNode.Anchor.ANCHOR_START; + TextNode.Anchor anchor = (TextNode.Anchor) aci.getAttribute (GVTAttributedCharacterIterator.TextAttribute.ANCHOR_TYPE); - anchorType = TextNode.Anchor.ANCHOR_START; - if (anchor != null) { - anchorType = anchor.getType(); + this.anchorType = anchor.getType(); } + // if writing mode is right to left, then need to reverse the // text anchor positions if (aci.getAttribute(GVTAttributedCharacterIterator.TextAttribute.WRITING_MODE) @@ -1443,6 +1530,11 @@ } // leave middle as is } + + length = (Float) aci.getAttribute + (GVTAttributedCharacterIterator.TextAttribute.BBOX_WIDTH); + lengthAdjust = (Integer) aci.getAttribute + (GVTAttributedCharacterIterator.TextAttribute.LENGTH_ADJUST); } public AttributedCharacterIterator getACI() { @@ -1455,6 +1547,14 @@ public int getAnchorType() { return anchorType; + } + + public Float getLength() { + return length; + } + + public Integer getLengthAdjust() { + return lengthAdjust; } public boolean isFirstRunInChunk() { 1.34 +110 -126 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.33 retrieving revision 1.34 diff -u -r1.33 -r1.34 --- GlyphLayout.java 23 Jan 2002 14:14:09 -0000 1.33 +++ GlyphLayout.java 24 Jan 2002 21:35:32 -0000 1.34 @@ -42,7 +42,7 @@ * @see org.apache.batik.gvt.text.TextSpanLayout * * @author <a href="[EMAIL PROTECTED]>Bill Haneman</a> - * @version $Id: GlyphLayout.java,v 1.33 2002/01/23 14:14:09 deweese Exp $ + * @version $Id: GlyphLayout.java,v 1.34 2002/01/24 21:35:32 deweese Exp $ */ public class GlyphLayout implements TextSpanLayout { @@ -54,11 +54,13 @@ private AffineTransform transform; private Point2D advance; private Point2D offset; + private float xScale=1; + private float yScale=1; private Point2D prevCharPosition; private TextPath textPath; private Point2D textPathAdvance; private int [] charMap; - private boolean vertical; + private boolean vertical, adjSpacing=true; private static final AttributedCharacterIterator.Attribute X = GVTAttributedCharacterIterator.TextAttribute.X; @@ -142,7 +144,7 @@ // do the glyph layout this.gv.performDefaultLayout(); doExplicitGlyphLayout(false); - adjustTextSpacing(); + adjustTextSpacing(false); doPathLayout(false); } @@ -185,6 +187,25 @@ } /** + * Sets the scaling factor to use for string. if ajdSpacing is + * true then only the spacing between glyphs will be adjusted + * otherwise the glyphs and the spaces between them will be + * adjusted. + * @param xScale Scale factor to apply in X direction. + * @param yScale Scale factor to apply in Y direction. + * @param adjSpacing True if only spaces should be adjusted. + */ + public void setScale(float xScale, float yScale, boolean adjSpacing) { + this.xScale = xScale; + this.yScale = yScale; + this.adjSpacing = adjSpacing; + this.gv.performDefaultLayout(); + doExplicitGlyphLayout(false); + adjustTextSpacing(true); + doPathLayout(true); + } + + /** * Sets the text position used for the implicit origin * of glyph layout. Ignored if multiple explicit glyph * positioning attributes are present in ACI @@ -194,7 +215,7 @@ this.offset = offset; this.gv.performDefaultLayout(); doExplicitGlyphLayout(true); - adjustTextSpacing(); + adjustTextSpacing(true); doPathLayout(true); } @@ -251,6 +272,11 @@ return advance; } + + public GVTGlyphMetrics getGlyphMetrics(int glyphIndex) { + return gv.getGlyphMetrics(glyphIndex); + } + /** * Returns the position to used when drawing a text run after this one. * It takes into account the text path layout if there is one. @@ -1077,28 +1103,28 @@ /** * Does any spacing adjustments that may have been specified. */ - protected void adjustTextSpacing() { + protected void adjustTextSpacing(boolean applyScaling) { aci.first(); Boolean customSpacing = (Boolean) aci.getAttribute( GVTAttributedCharacterIterator.TextAttribute.CUSTOM_SPACING); - Float length = (Float) aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.BBOX_WIDTH); - Integer lengthAdjust = (Integer) aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.LENGTH_ADJUST); if ((customSpacing != null) && customSpacing.booleanValue()) { - applySpacingParams(length, lengthAdjust, - (Float) aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.KERNING), - (Float) aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.LETTER_SPACING), - (Float) aci.getAttribute( - GVTAttributedCharacterIterator.TextAttribute.WORD_SPACING)); + advance = doSpacing + ((Float) aci.getAttribute + (GVTAttributedCharacterIterator.TextAttribute.KERNING), + (Float) aci.getAttribute + (GVTAttributedCharacterIterator.TextAttribute.LETTER_SPACING), + (Float) aci.getAttribute + (GVTAttributedCharacterIterator.TextAttribute.WORD_SPACING)); } - if (lengthAdjust == - GVTAttributedCharacterIterator.TextAttribute.ADJUST_ALL) { - applyStretchTransform(length); + if (!applyScaling) + return; + + if (adjSpacing) { + rescaleSpacing(); + } else { + applyStretchTransform(); } } @@ -1107,90 +1133,78 @@ * * @param length The required length of the text. */ - protected void applyStretchTransform(Float length) { - if (length!= null && !length.isNaN()) { - double xscale = 1d; - double yscale = 1d; - if (vertical) { - yscale = length.floatValue()/gv.getVisualBounds().getHeight(); - } else { - xscale = length.floatValue()/gv.getVisualBounds().getWidth(); - } - Point2D startPos = gv.getGlyphPosition(0); - for (int i = 0; i < gv.getNumGlyphs(); i++) { - // transform the glyph position - Point2D glyphPos = gv.getGlyphPosition(i); - AffineTransform t = AffineTransform.getTranslateInstance - (startPos.getX(), startPos.getY()); - t.scale(xscale,yscale); - t.translate(-startPos.getX(), -startPos.getY()); - Point2D newGlyphPos = new Point2D.Float(); - t.transform(glyphPos, newGlyphPos); - gv.setGlyphPosition(i, newGlyphPos); + protected void applyStretchTransform() { + if (vertical) { + xScale = 1; + } else { + yScale = 1; + } - // stretch the glyph - AffineTransform glyphTransform = gv.getGlyphTransform(i); - if (glyphTransform != null) { - glyphTransform.preConcatenate - (AffineTransform.getScaleInstance(xscale, yscale)); - gv.setGlyphTransform(i, glyphTransform); - } else { - gv.setGlyphTransform - (i, AffineTransform.getScaleInstance(xscale, yscale)); - } + if ((xScale == 1) && (yScale==1)) + return; + + // System.out.println("Scale: [" + xScale + ", " + yScale + "]"); + + Point2D startPos = gv.getGlyphPosition(0); + for (int i = 0; i < gv.getNumGlyphs(); i++) { + // transform the glyph position + Point2D glyphPos = gv.getGlyphPosition(i); + AffineTransform t = AffineTransform.getTranslateInstance + (startPos.getX(), startPos.getY()); + t.scale(xScale,yScale); + t.translate(-startPos.getX(), -startPos.getY()); + Point2D newGlyphPos = new Point2D.Float(); + t.transform(glyphPos, newGlyphPos); + gv.setGlyphPosition(i, newGlyphPos); + + // stretch the glyph + AffineTransform glyphTransform = gv.getGlyphTransform(i); + if (glyphTransform != null) { + glyphTransform.preConcatenate + (AffineTransform.getScaleInstance(xScale, yScale)); + gv.setGlyphTransform(i, glyphTransform); + } else { + gv.setGlyphTransform + (i, AffineTransform.getScaleInstance(xScale, yScale)); } - advance = new Point2D.Float((float)(advance.getX()*xscale), - (float)(advance.getY()*yscale)); } + advance = new Point2D.Float((float)(advance.getX()*xScale), + (float)(advance.getY()*yScale)); } - /** - * Adjusts the spacing according to the specified parameters. - * - * @param length The required text length. - * @param lengthAdjust Indicates the method to use when adjusting - * the text length. - * @param kern The kerning adjustment to apply to the space - * between each char. - * @param letterSpacing The amount of spacing required between each char. - * @param wordSpacing The amount of spacing required between each word. */ - protected void applySpacingParams(Float length, - Integer lengthAdjust, - Float kern, - Float letterSpacing, - Float wordSpacing) { - - /** - * Two passes required when textLength is specified: - * First, apply spacing properties, - * then adjust spacing with new advances based on ratio - * of expected length to actual advance. - */ - - advance = doSpacing(kern, letterSpacing, wordSpacing); - if ((lengthAdjust == - GVTAttributedCharacterIterator.TextAttribute.ADJUST_SPACING) && - length!= null && !length.isNaN()) { // adjust if necessary - float xscale = 1f; - float yscale = 1f; - if (!vertical) { - float gvWidth = (float)gv.getVisualBounds().getWidth(); - float lastCharWidth = (float)gv.getGlyphMetrics - (gv.getNumGlyphs()-1).getBounds2D().getWidth(); - if (gvWidth > lastCharWidth) { - // System.out.println("Len: " + length.floatValue() + - // " LCW: " + lastCharWidth + - // " Wid: " + gvWidth); - xscale = ((length.floatValue()-lastCharWidth)/ - (gvWidth-lastCharWidth)); - } - } else { - yscale = (length.floatValue()/ - (float) gv.getVisualBounds().getHeight()); - } - rescaleSpacing(xscale, yscale); + * Rescales the spacing between each char by the specified scale factors. + */ + protected void rescaleSpacing() { + if (vertical) { + xScale = 1; + } else { + yScale = 1; + } + if ((xScale == 1) && (yScale==1)) + return; + Rectangle2D bounds = gv.getVisualBounds(); + float initX = (float) bounds.getX(); + float initY = (float) bounds.getY(); + int numGlyphs = gv.getNumGlyphs(); + float dx = 0f; + float dy = 0f; + for (int i = 0; i < numGlyphs; i++) { + Point2D gpos = gv.getGlyphPosition(i); + dx = (float)gpos.getX()-initX; + dy = (float)gpos.getY()-initY; + gv.setGlyphPosition(i, new Point2D.Float(initX+dx*xScale, + initY+dy*yScale)); } + Rectangle2D glyphB = gv.getGlyphMetrics(numGlyphs-1).getBounds2D(); + float xAdj = 0; + float yAdj = 0; + if (vertical) yAdj = (float)glyphB.getHeight(); + else xAdj = (float)glyphB.getWidth(); + + advance = new Point2D.Float + ((float)(initX+dx*xScale-offset.getX()+xAdj), + (float)(initY+dy*yScale-offset.getY()+yAdj)); } /** @@ -1376,36 +1390,6 @@ return newAdvance; } - /** - * Rescales the spacing between each char by the specified scale factors. - * - * @param xscale The amount to scale in the x direction. - * @param yscale The amount to scale in the y direction. - */ - protected void rescaleSpacing(float xscale, float yscale) { - Rectangle2D bounds = gv.getVisualBounds(); - float initX = (float) bounds.getX(); - float initY = (float) bounds.getY(); - int numGlyphs = gv.getNumGlyphs(); - float dx = 0f; - float dy = 0f; - for (int i = 0; i < numGlyphs; i++) { - Point2D gpos = gv.getGlyphPosition(i); - dx = (float)gpos.getX()-initX; - dy = (float)gpos.getY()-initY; - gv.setGlyphPosition(i, new Point2D.Float(initX+dx*xscale, - initY+dy*yscale)); - } - Rectangle2D glyphB = gv.getGlyphMetrics(numGlyphs-1).getBounds2D(); - float xAdj = 0; - float yAdj = 0; - if (vertical) yAdj = (float)glyphB.getHeight(); - else xAdj = (float)glyphB.getWidth(); - - advance = new Point2D.Float - ((float)(initX+dx*xscale-offset.getX()+xAdj), - (float)(initY+dy*yscale-offset.getY()+yAdj)); - } /** * Returns true if the specified character is within one of the Latin 1.12 +19 -1 xml-batik/sources/org/apache/batik/gvt/text/TextSpanLayout.java Index: TextSpanLayout.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/gvt/text/TextSpanLayout.java,v retrieving revision 1.11 retrieving revision 1.12 diff -u -r1.11 -r1.12 --- TextSpanLayout.java 18 Sep 2001 21:19:01 -0000 1.11 +++ TextSpanLayout.java 24 Jan 2002 21:35:32 -0000 1.12 @@ -14,6 +14,8 @@ import java.awt.geom.Point2D; import java.awt.geom.AffineTransform; +import org.apache.batik.gvt.font.GVTGlyphMetrics; + /** * Class that performs layout of attributed text strings into * glyph sets paintable by TextPainter instances. @@ -25,7 +27,7 @@ * @see org.apache.batik.gvt.TextPainter * * @author <a href="[EMAIL PROTECTED]>Bill Haneman</a> - * @version $Id: TextSpanLayout.java,v 1.11 2001/09/18 21:19:01 deweese Exp $ + * @version $Id: TextSpanLayout.java,v 1.12 2002/01/24 21:35:32 deweese Exp $ */ public interface TextSpanLayout { @@ -78,6 +80,11 @@ */ public Point2D getAdvance2D(); + /** + * Returns the Metrics for a particular glyph. + */ + public GVTGlyphMetrics getGlyphMetrics(int glyphIndex); + public Point2D getTextPathAdvance(); /** @@ -86,6 +93,17 @@ * glyph positioning attributes. */ public Point2D getOffset(); + + /** + * Sets the scaling factor to use for string. if ajdSpacing is + * true then only the spacing between glyphs will be adjusted + * otherwise the glyphs and the spaces between them will be + * adjusted. + * @param xScale Scale factor to apply in X direction. + * @param yScale Scale factor to apply in Y direction. + * @param adjSpacing True if only spaces should be adjusted. + */ + public void setScale(float xScale, float yScale, boolean adjSpacing); /** * Sets the text position used for the implicit origin
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]