deweese 02/04/30 12:08:48 Modified: samples batikLogo.svg samples/extensions flowText.svg sources/org/apache/batik/bridge SVGGVTFont.java SVGTextElementBridge.java sources/org/apache/batik/extension/svg BatikExtConstants.java SVGFlowTextElementBridge.java sources/org/apache/batik/gvt/font AWTGVTGlyphVector.java GVTGlyphVector.java MultiGlyphVector.java SVGGVTGlyphVector.java sources/org/apache/batik/gvt/renderer StrokingTextPainter.java sources/org/apache/batik/gvt/text GlyphIterator.java GlyphLayout.java LineInfo.java MarginInfo.java test-resources/org/apache/batik/test samplesRendering.xml Log: 1) Normal Text fixes: a) Fixed handling of some exotic text cases (text on a path with multiple following non text path text elements, text path using anchors with following non text path text). b) Now only create text chunks when coordinate in layout direction is absolute. c) Added isGlyphVisible method to GVTGlyphVector d) Fixed a bug in the caching of metrics in AWTGVTGlyphVector. 2) flowText fixes: a) Added support for first-line-left/right-margin _attribute_ on flowPara element (only takes user space lengths). b) Now properly handles whitespace at start and end of a line that is flowed (also hides those spaces from display (although they will appear in the clipboard text). c) Fixed/Improved handling of flowLine element. d) Improved lineMetrics function in SVGGVTFont 3) Added existing tests into sampleRendering.xml that were missing: a) Added colorSwitch, flowText, multi, solidColor, solidColor2 to "extensions" testGroup b) Added textLength.svg to "tests.spec.text" testGroup. Revision Changes Path 1.10 +8 -1 xml-batik/samples/batikLogo.svg Index: batikLogo.svg =================================================================== RCS file: /home/cvs/xml-batik/samples/batikLogo.svg,v retrieving revision 1.9 retrieving revision 1.10 diff -u -r1.9 -r1.10 --- batikLogo.svg 30 Nov 2001 08:28:58 -0000 1.9 +++ batikLogo.svg 30 Apr 2002 19:08:47 -0000 1.10 @@ -17,7 +17,7 @@ <!-- @author [EMAIL PROTECTED] --> <!-- @author [EMAIL PROTECTED] --> <!-- @author [EMAIL PROTECTED] --> -<!-- @version $Id: batikLogo.svg,v 1.9 2001/11/30 08:28:58 vhardy Exp $ --> +<!-- @version $Id: batikLogo.svg,v 1.10 2002/04/30 19:08:47 deweese Exp $ --> <!-- ====================================================================== --> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="body" width="450" height="500" viewBox="0 0 450 500"> @@ -110,6 +110,13 @@ c0,6.852,0.531,11.344,1.602,13.477c1.066,2.133,3.086,3.883,6.059,5.25c1.219,0.535,3.121,0.992,5.715,1.371c0.078,0.023,0.152,0.031,0.23,0.031c2.133,0.152,3.523,0.492,4.172,1.023s0.973,1.363,0.973,2.5c0,0.836-0.344,1.496-1.027,1.988 s-1.594,0.738-2.734,0.738c-0.305,0-2.758-0.211-7.355-0.625c-4.602-0.414-8.992-0.625-13.172-0.625c-6.309,0-12.313,0.375-18.016,1.125c-0.914,0.082-1.445,0.125-1.594,0.125c-0.836,0-1.523-0.25-2.055-0.746s-0.797-1.09-0.797-1.777 c0-0.766,0.262-1.473,0.789-2.121c0.523-0.648,1.613-1.434,3.27-2.355c0.375-0.227,0.789-0.492,1.242-0.797c1.273-0.758,2.215-1.445,2.816-2.055c1.277-1.367,2.16-3.074,2.648-5.129c0.488-2.051,0.734-5.926,0.734-11.629z"/> + </glyph> + + <glyph unicode="*" glyph-name="*" horiz-adv-x="120"> + <g transform="scale(1, -1)"> + <use xlink:href="#Batik_Squiggle" + transform="scale(2,2) translate(0,-67)" /> + </g> </glyph> <hkern g1="B" g2="a" k="5"/> 1.6 +2 -2 xml-batik/samples/extensions/flowText.svg Index: flowText.svg =================================================================== RCS file: /home/cvs/xml-batik/samples/extensions/flowText.svg,v retrieving revision 1.5 retrieving revision 1.6 diff -u -r1.5 -r1.6 --- flowText.svg 24 Apr 2002 12:33:29 -0000 1.5 +++ flowText.svg 30 Apr 2002 19:08:47 -0000 1.6 @@ -14,7 +14,7 @@ <!-- Tests various text on a path --> <!-- --> <!-- @author [EMAIL PROTECTED] --> -<!-- @version $Id: flowText.svg,v 1.5 2002/04/24 12:33:29 deweese Exp $ --> +<!-- @version $Id: flowText.svg,v 1.6 2002/04/30 19:08:47 deweese Exp $ --> <!-- ====================================================================== --> <?xml-stylesheet type="text/css" href="extension.css" ?> @@ -41,7 +41,7 @@ <batik:region x="233" y="80" width="200" height="400"/> </batik:flowRegion> <batik:flowDiv> - <batik:flowPara bottom-margin="10" >This is an <batik:flowSpan font-size="40" fill="crimson">ex­ample</batik:flowSpan> of a very long string that is split ‍across multi­ple lines via text wrap­ping.</batik:flowPara> + <batik:flowPara first-line-left-margin="20" bottom-margin="10" >This is an <batik:flowSpan font-size="40" fill="crimson">ex­ample</batik:flowSpan> of a very long string that is split ‍across multi­ple lines via text wrap­ping.</batik:flowPara> <batik:flowPara justification="middle" top-margin="10" left-margin="10" right-margin="10" bottom-margin="10"><batik:flowLine>Now check if text wrapping handles a number of tricky</batik:flowLine> situations: averylongrunonwordthatspansmultiplelines<batik:flowSpan font-weight="bold">with<batik:flowSpan fill="crimson">embedded</batik:flowSpan>span</batik:flowSpan>elements & <batik:flowSpan fill="green" dy="-.3em" font-size="80%">super</batik:flowSpan><batik:flowSpan dy=".3em"> or </batik:flowSpan><batik:flowSpan fill="darkgreen" dy=".3em" font-size="80%">sub</batik:flowSpan><batik:flowSpan dy="-.3em"> scripts.</batik:flowSpan></batik:flowPara> <batik:flowPara top-margin="10" justification="end">Now we are just <batik:flowSpan font-size="30" fill="blue">about</batik:flowSpan> to go to the next flow rect <batik:flowSpan font-size="10">(note if the 'about' were included on the last line of the previous flow rect the line would not have fit and the whole line would have moved here).</batik:flowSpan></batik:flowPara> <batik:flowPara margin="10" justification="full"> I'll keep going because I want to make sure that it properly stops when it hits the end of all of the the flow regions defined. Also the last line includes text in a larger font size so it will not fit. Thus the end of this sentence will be cut off because the line size gets <batik:flowSpan font-size="35">tall</batik:flowSpan>er</batik:flowPara> 1.10 +21 -17 xml-batik/sources/org/apache/batik/bridge/SVGGVTFont.java Index: SVGGVTFont.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/bridge/SVGGVTFont.java,v retrieving revision 1.9 retrieving revision 1.10 diff -u -r1.9 -r1.10 --- SVGGVTFont.java 27 Apr 2002 22:41:57 -0000 1.9 +++ SVGGVTFont.java 30 Apr 2002 19:08:47 -0000 1.10 @@ -42,7 +42,7 @@ * Represents an SVG font. * * @author <a href="mailto:[EMAIL PROTECTED]">Bella Robinson</a> - * @version $Id: SVGGVTFont.java,v 1.9 2002/04/27 22:41:57 deweese Exp $ + * @version $Id: SVGGVTFont.java,v 1.10 2002/04/30 19:08:47 deweese Exp $ */ public final class SVGGVTFont implements GVTFont, SVGConstants { @@ -64,6 +64,8 @@ private String language; private String orientation; + private GVTLineMetrics lineMetrics=null; + /** * Constructs a new SVGGVTFont of the specified size. * @@ -767,6 +769,9 @@ */ public GVTLineMetrics getLineMetrics(CharacterIterator ci, int beginIndex, int limit, FontRenderContext frc) { + if (lineMetrics != null) + return lineMetrics; + float fontHeight = fontFace.getUnitsPerEm(); float scale = fontSize/fontHeight; @@ -778,22 +783,21 @@ baselineOffsets[Font.CENTER_BASELINE] = (ascent+descent)/2-ascent; baselineOffsets[Font.HANGING_BASELINE] = -ascent; - float strikethroughOffset = fontFace.getStrikethroughPosition() * - -scale; - float strikethroughThickness = fontFace.getStrikethroughThickness() * - scale; - float underlineOffset = fontFace.getUnderlinePosition() * scale; - float underlineThickness = fontFace.getUnderlineThickness() * scale; - float overlineOffset = fontFace.getOverlinePosition() * -scale; - float overlineThickness = fontFace.getOverlineThickness() * scale; - - - return new GVTLineMetrics(ascent, Font.ROMAN_BASELINE, - baselineOffsets, descent, fontHeight, - fontHeight, limit-beginIndex, - strikethroughOffset, strikethroughThickness, - underlineOffset, underlineThickness, - overlineOffset, overlineThickness); + float stOffset = fontFace.getStrikethroughPosition() * -scale; + float stThickness = fontFace.getStrikethroughThickness() * scale; + float ulOffset = fontFace.getUnderlinePosition() * scale; + float ulThickness = fontFace.getUnderlineThickness() * scale; + float olOffset = fontFace.getOverlinePosition() * -scale; + float olThickness = fontFace.getOverlineThickness() * scale; + + + lineMetrics = new GVTLineMetrics + (ascent, Font.ROMAN_BASELINE, baselineOffsets, descent, + fontHeight, fontHeight, limit-beginIndex, + stOffset, stThickness, + ulOffset, ulThickness, + olOffset, olThickness); + return lineMetrics; } /** 1.66 +17 -1 xml-batik/sources/org/apache/batik/bridge/SVGTextElementBridge.java Index: SVGTextElementBridge.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/bridge/SVGTextElementBridge.java,v retrieving revision 1.65 retrieving revision 1.66 diff -u -r1.65 -r1.66 --- SVGTextElementBridge.java 29 Apr 2002 13:20:18 -0000 1.65 +++ SVGTextElementBridge.java 30 Apr 2002 19:08:47 -0000 1.66 @@ -68,7 +68,7 @@ * * @author <a href="[EMAIL PROTECTED]">Stephane Hillion</a> * @author <a href="[EMAIL PROTECTED]">Bill Haneman</a> - * @version $Id: SVGTextElementBridge.java,v 1.65 2002/04/29 13:20:18 tkormann Exp $ + * @version $Id: SVGTextElementBridge.java,v 1.66 2002/04/30 19:08:47 deweese Exp $ */ public class SVGTextElementBridge extends AbstractGraphicsNodeBridge { @@ -904,6 +904,22 @@ } return result; + } + + public String toString() { + switch (count) { + case 0: + return ""; + case 1: + return (String)strings.get(0); + } + + StringBuffer sb = new StringBuffer(); + Iterator it = strings.iterator(); + while (it.hasNext()) { + sb.append((String)it.next()); + } + return sb.toString(); } } 1.11 +11 -5 xml-batik/sources/org/apache/batik/extension/svg/BatikExtConstants.java Index: BatikExtConstants.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/extension/svg/BatikExtConstants.java,v retrieving revision 1.10 retrieving revision 1.11 diff -u -r1.10 -r1.11 --- BatikExtConstants.java 24 Apr 2002 12:33:29 -0000 1.10 +++ BatikExtConstants.java 30 Apr 2002 19:08:47 -0000 1.11 @@ -47,21 +47,27 @@ public static final String BATIK_EXT_HEIGHT_ATTRIBUTE = "height"; - /** Attribute name for margine psudo-attribute */ + /** Attribute name for margin psudo-attribute */ public static final String BATIK_EXT_MARGIN_ATTRIBUTE = "margin"; - /** Attribute name for margine psudo-attribute */ + /** Attribute name for top-margin attribute */ public static final String BATIK_EXT_TOP_MARGIN_ATTRIBUTE = "top-margin"; - /** Attribute name for margine psudo-attribute */ + /** Attribute name for right-margin attribute */ public static final String BATIK_EXT_RIGHT_MARGIN_ATTRIBUTE = "right-margin"; - /** Attribute name for margine psudo-attribute */ + /** Attribute name for bottom-margin attribute */ public static final String BATIK_EXT_BOTTOM_MARGIN_ATTRIBUTE = "bottom-margin"; - /** Attribute name for margine psudo-attribute */ + /** Attribute name for left-margin attribute */ public static final String BATIK_EXT_LEFT_MARGIN_ATTRIBUTE = "left-margin"; + /** Attribute name for first-line-left-margin attribute */ + public static final String BATIK_EXT_FIRST_LINE_LEFT_MARGIN_ATTRIBUTE = + "first-line-left-margin"; + /** Attribute name for first-line-right-margin attribute */ + public static final String BATIK_EXT_FIRST_LINE_RIGHT_MARGIN_ATTRIBUTE = + "first-line-right-margin"; /** Attribute name for margine psudo-attribute */ public static final String BATIK_EXT_JUSTIFICATION_ATTRIBUTE = "justification"; 1.4 +41 -25 xml-batik/sources/org/apache/batik/extension/svg/SVGFlowTextElementBridge.java Index: SVGFlowTextElementBridge.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/extension/svg/SVGFlowTextElementBridge.java,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- SVGFlowTextElementBridge.java 24 Apr 2002 12:33:29 -0000 1.3 +++ SVGFlowTextElementBridge.java 30 Apr 2002 19:08:47 -0000 1.4 @@ -39,7 +39,7 @@ * Bridge class for the <flowText> element. * * @author <a href="[EMAIL PROTECTED]">Thomas DeWeese</a> - * @version $Id: SVGFlowTextElementBridge.java,v 1.3 2002/04/24 12:33:29 deweese Exp $ + * @version $Id: SVGFlowTextElementBridge.java,v 1.4 2002/04/30 19:08:47 deweese Exp $ */ public class SVGFlowTextElementBridge extends SVGTextElementBridge implements BatikExtConstants { @@ -289,34 +289,26 @@ // Layer in the PARAGRAPH/LINE_BREAK Attributes so we can // break up text chunks. AttributedString ret = asb.toAttributedString(); - int start=0; - List emptyPara = null; - int prevLN = -1; - Iterator lnIter = lnLocs.iterator(); - // The Working Group (in conjunction with XHTML working group - // has decided that multiple line elements collapse so I don't - // use the counting I do. + // Note: The Working Group (in conjunction with XHTML working + // group) has decided that multiple line elements collapse. + int prevLN = 0; int lnCount = 0; + Iterator lnIter = lnLocs.iterator(); while (lnIter.hasNext()) { int nextLN = ((Integer)lnIter.next()).intValue(); - if (nextLN == prevLN) { - lnCount++; - } else { - if (prevLN != -1) - ret.addAttribute(FLOW_LINE_BREAK, - new Integer(1), // new Integer(lnCount), - prevLN-1, prevLN); - prevLN = nextLN; - lnCount = 1; - } + if (nextLN == prevLN) continue; + + ret.addAttribute(FLOW_LINE_BREAK, + new Object(), + prevLN, nextLN); + // System.out.println("Attr: [" + prevLN + "," + nextLN + "]"); + prevLN = nextLN; } - if (prevLN != -1) - ret.addAttribute(FLOW_LINE_BREAK, - new Integer(1), // new Integer(lnCount), - prevLN-1, prevLN); + int start=0; int end; + List emptyPara = null; for (int i=0; i<paraElems.size(); i++, start=end) { Element elem = (Element)paraElems.get(i); end = ((Integer)paraEnds.get(i)).intValue(); @@ -326,7 +318,7 @@ emptyPara.add(makeMarginInfo(elem)); continue; } - + // System.out.println("Para: [" + start + ", " + end + "]"); ret.addAttribute(FLOW_PARAGRAPH, makeMarginInfo(elem), start, end); if (emptyPara != null) { ret.addAttribute(FLOW_EMPTY_PARAGRAPH, emptyPara, start, end); @@ -354,7 +346,6 @@ protected List gatherRects(BridgeContext ctx, Element rgn) { List ret = new LinkedList(); - UnitProcessor.Context uctx = UnitProcessor.createContext(ctx, rgn); for (Node n = rgn.getFirstChild(); n != null; n = n.getNextSibling()) { if (n.getNodeType() != Node.ELEMENT_NODE) continue; @@ -363,6 +354,8 @@ String ln = n.getLocalName(); if (ln.equals(BATIK_EXT_REGION_TAG)) { + UnitProcessor.Context uctx; + uctx = UnitProcessor.createContext(ctx, e); Rectangle2D r2d = buildRect(uctx, e); if (r2d != null) ret.add(r2d); @@ -471,6 +464,8 @@ if (ln.equals(BATIK_EXT_FLOW_LINE_TAG)) { fillAttributedStringBuffer(ctx, nodeElement, false, asb, lnLocs); + // System.out.println("Line: " + asb.length() + + // " - '" + asb + "'"); lnLocs.add(new Integer(asb.length())); } else if (ln.equals(BATIK_EXT_FLOW_SPAN_TAG) || ln.equals(SVG_ALT_GLYPH_TAG) || @@ -615,6 +610,26 @@ } } catch(NumberFormatException nfe) { /* nothing */ } + float flLeft = left; + float flRight = right; + + s = e.getAttributeNS(null, BATIK_EXT_FIRST_LINE_LEFT_MARGIN_ATTRIBUTE); + try { + if (s.length() != 0) { + float f = Float.parseFloat(s); + flLeft = f; + } + } catch(NumberFormatException nfe) { /* nothing */ } + + s = e.getAttributeNS(null,BATIK_EXT_FIRST_LINE_RIGHT_MARGIN_ATTRIBUTE); + try { + if (s.length() != 0) { + float f = Float.parseFloat(s); + flRight = f; + } + } catch(NumberFormatException nfe) { /* nothing */ } + + int justification = MarginInfo.JUSTIFY_START; s = e.getAttributeNS(null, BATIK_EXT_JUSTIFICATION_ATTRIBUTE); try { @@ -633,7 +648,8 @@ String ln = e.getLocalName(); boolean rgnBr = ln.equals(BATIK_EXT_FLOW_REGION_BREAK_TAG); - return new MarginInfo(top, right, bottom, left, justification, rgnBr); + return new MarginInfo(top, right, bottom, left, flLeft, flRight, + justification, rgnBr); } 1.18 +27 -15 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.17 retrieving revision 1.18 diff -u -r1.17 -r1.18 --- AWTGVTGlyphVector.java 7 Mar 2002 22:07:44 -0000 1.17 +++ AWTGVTGlyphVector.java 30 Apr 2002 19:08:47 -0000 1.18 @@ -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.17 2002/03/07 22:07:44 deweese Exp $ + * @version $Id: AWTGVTGlyphVector.java,v 1.18 2002/04/30 19:08:47 deweese Exp $ */ public class AWTGVTGlyphVector implements GVTGlyphVector { @@ -94,12 +94,12 @@ outline = null; logicalBounds = null; int numGlyphs = glyphVector.getNumGlyphs(); - glyphPositions = new Point2D.Float[numGlyphs+1]; + glyphPositions = new Point2D.Float [numGlyphs+1]; glyphTransforms = new AffineTransform[numGlyphs]; - glyphOutlines = new Shape[numGlyphs]; - glyphVisualBounds = new Shape[numGlyphs]; - glyphLogicalBounds = new Shape[numGlyphs]; - glyphVisible = new boolean[numGlyphs]; + glyphOutlines = new Shape [numGlyphs]; + glyphVisualBounds = new Shape [numGlyphs]; + glyphLogicalBounds = new Shape [numGlyphs]; + glyphVisible = new boolean [numGlyphs]; glyphMetrics = new GVTGlyphMetrics[numGlyphs]; for (int i = 0; i < numGlyphs; i++) { @@ -590,7 +590,7 @@ glyphVisualBounds [i] = null; glyphLogicalBounds[i] = null; glyphOutlines [i] = null; - + glyphMetrics [i] = null; Point2D glyphPos = defaultGlyphPositions[i]; glyphPositions[i] = new Point2D.Float ((float)((glyphPos.getX() * scaleFactor)-shiftLeft), @@ -621,9 +621,10 @@ logicalBounds = null; visualBounds = null; if (glyphIndex != getNumGlyphs()) { - glyphVisualBounds[glyphIndex] = null; + glyphVisualBounds [glyphIndex] = null; glyphLogicalBounds[glyphIndex] = null; - glyphOutlines[glyphIndex] = null; + glyphOutlines [glyphIndex] = null; + glyphMetrics [glyphIndex] = null; } } @@ -635,22 +636,33 @@ outline = null; logicalBounds = null; visualBounds = null; - glyphVisualBounds[glyphIndex] = null; + glyphVisualBounds [glyphIndex] = null; glyphLogicalBounds[glyphIndex] = null; - glyphOutlines[glyphIndex] = null; + glyphOutlines [glyphIndex] = null; + glyphMetrics [glyphIndex] = null; } /** * Tells the glyph vector whether or not to draw the specified glyph. */ public void setGlyphVisible(int glyphIndex, boolean visible) { + if (visible == glyphVisible[glyphIndex]) + return; glyphVisible[glyphIndex] = visible; - outline = null; + outline = null; logicalBounds = null; - visualBounds = null; - glyphVisualBounds[glyphIndex] = null; + visualBounds = null; + glyphVisualBounds [glyphIndex] = null; glyphLogicalBounds[glyphIndex] = null; - glyphOutlines[glyphIndex] = null; + glyphOutlines [glyphIndex] = null; + glyphMetrics [glyphIndex] = null; + } + + /** + * Returns true if specified glyph will be rendered. + */ + public boolean isGlyphVisible(int glyphIndex) { + return glyphVisible[glyphIndex]; } /** 1.8 +6 -1 xml-batik/sources/org/apache/batik/gvt/font/GVTGlyphVector.java Index: GVTGlyphVector.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/gvt/font/GVTGlyphVector.java,v retrieving revision 1.7 retrieving revision 1.8 diff -u -r1.7 -r1.8 --- GVTGlyphVector.java 18 Sep 2001 21:19:00 -0000 1.7 +++ GVTGlyphVector.java 30 Apr 2002 19:08:47 -0000 1.8 @@ -25,7 +25,7 @@ * An interface for all GVT GlyphVector classes. * * @author <a href="mailto:[EMAIL PROTECTED]">Bella Robinson</a> - * @version $Id: GVTGlyphVector.java,v 1.7 2001/09/18 21:19:00 deweese Exp $ + * @version $Id: GVTGlyphVector.java,v 1.8 2002/04/30 19:08:47 deweese Exp $ */ public interface GVTGlyphVector { @@ -143,6 +143,11 @@ * Tells the glyph vector whether or not to draw the specified glyph. */ void setGlyphVisible(int glyphIndex, boolean visible); + + /** + * Returns true if specified glyph will be drawn. + */ + public boolean isGlyphVisible(int glyphIndex); /** * Returns the number of chars represented by the glyphs within the 1.2 +8 -0 xml-batik/sources/org/apache/batik/gvt/font/MultiGlyphVector.java Index: MultiGlyphVector.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/gvt/font/MultiGlyphVector.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- MultiGlyphVector.java 7 Mar 2002 22:07:44 -0000 1.1 +++ MultiGlyphVector.java 30 Apr 2002 19:08:47 -0000 1.2 @@ -184,6 +184,14 @@ } /** + * Returns true if specified glyph will be drawn. + */ + public boolean isGlyphVisible(int glyphIndex) { + int idx = getGVIdx(glyphIndex); + return gvs[idx].isGlyphVisible(glyphIndex-off[idx]); + } + + /** * Returns an array of glyphcodes for the specified glyphs. */ public int[] getGlyphCodes(int beginGlyphIndex, int numEntries, 1.12 +11 -1 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.11 retrieving revision 1.12 diff -u -r1.11 -r1.12 --- SVGGVTGlyphVector.java 25 Apr 2002 00:45:42 -0000 1.11 +++ SVGGVTGlyphVector.java 30 Apr 2002 19:08:47 -0000 1.12 @@ -29,7 +29,7 @@ * A GVTGlyphVector class for SVG fonts. * * @author <a href="mailto:[EMAIL PROTECTED]">Bella Robinson</a> - * @version $Id: SVGGVTGlyphVector.java,v 1.11 2002/04/25 00:45:42 deweese Exp $ + * @version $Id: SVGGVTGlyphVector.java,v 1.12 2002/04/30 19:08:47 deweese Exp $ */ public final class SVGGVTGlyphVector implements GVTGlyphVector { @@ -672,10 +672,20 @@ * Tells the glyph vector whether or not to draw the specified glyph. */ public void setGlyphVisible(int glyphIndex, boolean visible) { + if (visible == glyphVisible[glyphIndex]) + return; + glyphVisible[glyphIndex] = visible; outline = null; logicalBounds = null; glyphLogicalBounds[glyphIndex] = null; + } + + /** + * Returns true if specified glyph will be rendered. + */ + public boolean isGlyphVisible(int glyphIndex) { + return glyphVisible[glyphIndex]; } /** 1.34 +229 -168 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.33 retrieving revision 1.34 diff -u -r1.33 -r1.34 --- StrokingTextPainter.java 29 Apr 2002 13:20:19 -0000 1.33 +++ StrokingTextPainter.java 30 Apr 2002 19:08:48 -0000 1.34 @@ -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.33 2002/04/29 13:20:19 tkormann Exp $ + * @version $Id: StrokingTextPainter.java,v 1.34 2002/04/30 19:08:48 deweese Exp $ */ public class StrokingTextPainter extends BasicTextPainter { @@ -211,6 +211,8 @@ textRuns = new ArrayList(); TextChunk chunk, prevChunk=null; int currentChunk = 0; + + Point2D location = node.getLocation(); do { // Text Chunks contain one or more TextRuns, which they // create from the ACI. @@ -221,11 +223,11 @@ chunkCharMaps[currentChunk], textRuns, prevChunk); - + // Adjust according to text-anchor property value chunkACIs[currentChunk].first(); if (chunk != null) { - adjustChunkOffsets(textRuns, chunk); + location = adjustChunkOffsets(location, textRuns, chunk); } prevChunk = chunk; currentChunk++; @@ -273,6 +275,10 @@ List aciList = new ArrayList(); int chunkStartIndex = aci.getBeginIndex(); + aci.first(); + Object writingMode = aci.getAttribute(WRITING_MODE); + boolean vertical = (writingMode == WRITING_MODE_TTB); + while (aci.setIndex(chunkStartIndex) != CharacterIterator.DONE) { TextPath prevTextPath = null; for (int start=chunkStartIndex, end=0; @@ -283,13 +289,17 @@ if (start != chunkStartIndex) { // If we aren't the first composite in a chunck see // if we need to form a new TextChunk... - Float runX = (Float) aci.getAttribute(XPOS); - Float runY = (Float) aci.getAttribute(YPOS); - - // Check if we have an absolute location - if ( ((runX != null) && !runX.isNaN()) - ||((runY != null) && !runY.isNaN())) - break; // If so end of chunk... + if (vertical) { + Float runY = (Float) aci.getAttribute(YPOS); + // Check for absolute location in layout direction. + if ((runY != null) && !runY.isNaN()) + break; // If so end of chunk... + } else { + Float runX = (Float) aci.getAttribute(XPOS); + // Check for absolute location in layout direction. + if ((runX != null) && !runX.isNaN()) + break; // If so end of chunk... + } // do additional check for the start of a textPath if ((prevTextPath == null) && (textPath != null)) @@ -328,28 +338,39 @@ // (technically we have to do this for // text-anchor:start as well but since that is the // default layout it doesn't matter in that case. - Float runX = (Float) aci.getAttribute(XPOS); - Float runY = (Float) aci.getAttribute(YPOS); - if (((runX == null) || runX.isNaN()) && - ((runY == null) || runY.isNaN())) - // No absolute positioning in this compound so continue. - continue; + if (vertical) { + Float runY = (Float) aci.getAttribute(YPOS); + // Check for absolute location in layout direction. + if ((runY == null) || runY.isNaN()) + // No absolute positioning in text direction continue + continue; + } else { + Float runX = (Float) aci.getAttribute(XPOS); + // Check for absolute location in layout direction. + if ((runX == null) || runX.isNaN()) + // No absolute positioning in text direction continue + continue; + } // Splitting the compound into one char chunks until // we run out of Xs. for (int i=start+1; i< end; i++) { aci.setIndex(i); - runX = (Float) aci.getAttribute(XPOS); - runY = (Float) aci.getAttribute(YPOS); - if (((runX == null) || runX.isNaN()) && - ((runY == null) || runY.isNaN())) - break; - aciList.add - (new AttributedCharacterSpanIterator(aci, i-1, i)); + if (vertical) { + Float runY = (Float) aci.getAttribute(YPOS); + if ((runY == null) || runY.isNaN()) + break; + } else { + Float runX = (Float) aci.getAttribute(XPOS); + if ((runX == null) || runX.isNaN()) + break; + } + aciList.add(new AttributedCharacterSpanIterator + (aci, i-1, i)); chunkStartIndex = i; } } - + // found the end of a text chunck int chunkEndIndex = aci.getIndex(); // System.out.println("Bounds: " + chunkStartIndex + @@ -370,113 +391,6 @@ return aciArray; } - private TextChunk getTextChunk(TextNode node, - AttributedCharacterIterator aci, - int [] charMap, - List textRuns, - TextChunk prevChunk) { - int beginChunk = 0; - if (prevChunk != null) - beginChunk = prevChunk.end; - int endChunk = beginChunk; - int begin = aci.getIndex(); - // System.out.println("New Chunk"); - if (aci.current() == CharacterIterator.DONE) - return null; - - Point2D advance = new Point2D.Float(0,0); - Point2D location; - if (prevChunk == null) { - location = node.getLocation(); - } else { - location = new Point2D.Float - ((float)(prevChunk.absLoc.getX()+prevChunk.advance.getX()), - (float)(prevChunk.absLoc.getY()+prevChunk.advance.getY())); - } - boolean isChunkStart = true; - TextPath prevTextPath = null; - Point2D prevTextPathAdvance = null; - do { - int start = aci.getRunStart(extendedAtts); - int end = aci.getRunLimit(extendedAtts); - - AttributedCharacterIterator runaci; - runaci = new AttributedCharacterSpanIterator(aci, start, end); - runaci.first(); - - boolean vertical = - (aci.getAttribute(WRITING_MODE) == WRITING_MODE_TTB); - Float runX = (Float) runaci.getAttribute(XPOS); - Float runY = (Float) runaci.getAttribute(YPOS); - - TextPath textPath = (TextPath) runaci.getAttribute(TEXTPATH); - - Point2D.Float offset; - if (textPath == null) { - if ((prevTextPath != null) && - (prevTextPathAdvance != null)) { - // this text is directly after some text on a path - offset = new Point2D.Float - ((float)prevTextPathAdvance.getX(), - (float)prevTextPathAdvance.getY()); - } else { - offset = new Point2D.Float - ((float) (location.getX()+advance.getX()), - (float) (location.getY()+advance.getY())); - } - - // Of course X and Y override all that... - if ((runX != null) && (!runX.isNaN())) - offset.x = runX.floatValue(); - - if ((runY != null) && (!runY.isNaN())) - offset.y = runY.floatValue(); - } else { - // is on a text path so ignore the text node's location - offset = new Point2D.Float((float)advance.getX(), - (float)advance.getY()); - // Only use the x or y in writing direction... - if (vertical) { - if ((runY != null) && (!runY.isNaN())) - offset.y = runY.floatValue(); - } else { - if ((runX != null) && (!runX.isNaN())) - offset.x = runX.floatValue(); - } - } - - int [] subCharMap = new int[end-start]; - for (int i=0; i<subCharMap.length; i++) { - subCharMap[i] = charMap[i+start-begin]; - } - TextSpanLayout layout = getTextLayoutFactory(). - createTextLayout(runaci, subCharMap, - offset, fontRenderContext); - // System.out.println("TextRun: " + start + "->" + end + - // " Start: " + isChunkStart); - TextRun run = new TextRun(layout, runaci, isChunkStart); - textRuns.add(run); - Point2D layoutAdvance = layout.getAdvance2D(); - if (isChunkStart) - location = layout.getOffset(); - - // System.out.println("layoutAdv: " + layoutAdvance); - - advance = new Point2D.Float - ((float) (advance.getX()+layoutAdvance.getX()), - (float) (advance.getY()+layoutAdvance.getY())); - ++endChunk; - prevTextPath = textPath; - prevTextPathAdvance = layout.getTextPathAdvance(); - if (aci.setIndex(end) == CharacterIterator.DONE) break; - isChunkStart = false; - } while (true); - // System.out.println("Adv: " + advance); - return new TextChunk(beginChunk, endChunk, location, advance); - } - - - /** * Returns a new AttributedCharacterIterator that contains resolved GVTFont * attributes. This is then used when creating the text runs so that the @@ -675,18 +589,74 @@ } + private TextChunk getTextChunk(TextNode node, + AttributedCharacterIterator aci, + int [] charMap, + List textRuns, + TextChunk prevChunk) { + int beginChunk = 0; + if (prevChunk != null) + beginChunk = prevChunk.end; + int endChunk = beginChunk; + int begin = aci.getIndex(); + // System.out.println("New Chunk"); + if (aci.current() == CharacterIterator.DONE) + return null; + + Point2D advance = new Point2D.Float(0,0); + boolean isChunkStart = true; + TextSpanLayout layout = null; + do { + int start = aci.getRunStart(extendedAtts); + int end = aci.getRunLimit(extendedAtts); + + AttributedCharacterIterator runaci; + runaci = new AttributedCharacterSpanIterator(aci, start, end); + + int [] subCharMap = new int[end-start]; + for (int i=0; i<subCharMap.length; i++) { + subCharMap[i] = charMap[i+start-begin]; + } + + Point2D.Float offset; + offset = new Point2D.Float((float)advance.getX(), + (float)advance.getY()); + + layout = getTextLayoutFactory().createTextLayout + (runaci, subCharMap, offset, fontRenderContext); + textRuns.add(new TextRun(layout, runaci, isChunkStart)); + // System.out.println("TextRun: " + start + "->" + end + + // " Start: " + isChunkStart); + + Point2D layoutAdvance = layout.getAdvance2D(); + // System.out.println("layoutAdv: " + layoutAdvance); + advance = new Point2D.Float + ((float) (advance.getX()+layoutAdvance.getX()), + (float) (advance.getY()+layoutAdvance.getY())); + ++endChunk; + if (aci.setIndex(end) == CharacterIterator.DONE) break; + isChunkStart = false; + } while (true); + + // System.out.println("Adv: " + advance); + // System.out.println("Chunks: [" + beginChunk + ", " + + // endChunk + "]"); + return new TextChunk(beginChunk, endChunk, advance); + } + + /** * Adjusts the position of the text runs within the specified text chunk * to account for any text anchor properties. */ - private void adjustChunkOffsets(List textRuns, + private Point2D adjustChunkOffsets(Point2D location, + List textRuns, 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; boolean doAdjust = true; if ((length == null) || length.isNaN()) @@ -705,17 +675,28 @@ float xScale = 1; float yScale = 1; - if (doAdjust) { + + r = (TextRun)textRuns.get(chunk.end-1); + TextSpanLayout layout = r.getLayout(); + GVTGlyphMetrics lastMetrics = + layout.getGlyphMetrics(layout.getGlyphCount()-1); + Rectangle2D lastBounds = lastMetrics.getBounds2D(); + + Point2D visualAdvance; + + if (!doAdjust) { + visualAdvance = new Point2D.Float + ((float)(chunk.advance.getX() + lastBounds.getWidth() - + lastMetrics.getHorizontalAdvance()), + (float)(chunk.advance.getY() + lastBounds.getHeight() - + lastMetrics.getVerticalAdvance())); + } else { + Point2D advance = chunk.advance; + // We have to do this here since textLength needs to be // handled at the text chunk level. Otherwise tspans get // messed up. float delta = 0; - r = (TextRun)textRuns.get(chunk.end-1); - TextSpanLayout layout = r.getLayout(); - GVTGlyphMetrics lastMetrics = - layout.getGlyphMetrics(layout.getGlyphCount()-1); - Rectangle2D lastBounds = lastMetrics.getBounds2D(); - if (layout.isVertical()) { if (lengthAdj == ADJUST_SPACING) { yScale = (float) @@ -725,19 +706,20 @@ double adv = (advance.getY()- lastMetrics.getVerticalAdvance() + lastBounds.getHeight()); - xScale = (float)(length.floatValue()/adv); + yScale = (float)(length.floatValue()/adv); } + visualAdvance = new Point2D.Float(0, length.floatValue()); } else { if (lengthAdj == ADJUST_SPACING) { xScale = (float) ((length.floatValue()-lastBounds.getWidth())/ (advance.getX()-lastMetrics.getHorizontalAdvance())); } else { - double adv = (advance.getX()- - lastMetrics.getHorizontalAdvance() + - lastBounds.getWidth()); + double adv = (advance.getX() + lastBounds.getWidth() - + lastMetrics.getHorizontalAdvance()); xScale = (float)(length.floatValue()/adv); } + visualAdvance = new Point2D.Float(length.floatValue(), 0); } // System.out.println("Adv: " + advance + " Len: " + length + @@ -748,52 +730,135 @@ layout = r.getLayout(); layout.setScale(xScale, yScale, lengthAdj==ADJUST_SPACING); Point2D lAdv = layout.getAdvance2D(); - adv.x += lAdv.getX(); - adv.y += lAdv.getY(); + adv.x += (float)lAdv.getX(); + adv.y += (float)lAdv.getY(); } chunk.advance = adv; } - - advance = chunk.advance; + if (true) { + visualAdvance = chunk.advance; + } float dx = 0f; float dy = 0f; switch(anchorType){ case TextNode.Anchor.ANCHOR_MIDDLE: - dx = (float) (-advance.getX()/2d); - dy = (float) (-advance.getY()/2d); + dx = (float) (-visualAdvance.getX()/2d); + dy = (float) (-visualAdvance.getY()/2d); break; case TextNode.Anchor.ANCHOR_END: - dx = (float) (-advance.getX()); - dy = (float) (-advance.getY()); + dx = (float) (-visualAdvance.getX()); + dy = (float) (-visualAdvance.getY()); break; default: break; // leave untouched } + // System.out.println("DX/DY: [" + dx + ", " + dy + "]"); r = (TextRun) textRuns.get(chunk.begin); - TextSpanLayout layout = r.getLayout(); - Point2D offset = layout.getOffset(); - float initX = (float)offset.getX(); - float initY = (float)offset.getY(); + layout = r.getLayout(); + AttributedCharacterIterator runaci = r.getACI(); + runaci.first(); + boolean vertical = layout.isVertical(); + Float runX = (Float) runaci.getAttribute(XPOS); + Float runY = (Float) runaci.getAttribute(YPOS); + TextPath textPath = (TextPath) runaci.getAttribute(TEXTPATH); + + float absX = (float)location.getX(); + float absY = (float)location.getY(); + float advX = 0; + float advY = 0; + float tpShiftX = 0; + float tpShiftY = 0; + float shiftX = 0; + float shiftY = 0; + + if (textPath == null) { + // Of course X and Y override all that... + if ((runX != null) && (!runX.isNaN())) + absX = runX.floatValue(); + + if ((runY != null) && (!runY.isNaN())) + absY = runY.floatValue(); + } else { + absX = 0; + absY = 0; + // Only use the x or y in writing direction... + if (vertical) { + if ((runY != null) && (!runY.isNaN())) { + absY = runY.floatValue(); + tpShiftY = absY; + } + } else { + if ((runX != null) && (!runX.isNaN())) { + absX = runX.floatValue(); + tpShiftX = absX; + } + } + } + // System.out.println("ABS: [" + absX + "," + absY + "]"); + float retX = absX; + float retY = absY; for (int n=chunk.begin; n<chunk.end; ++n) { r = (TextRun) textRuns.get(n); layout = r.getLayout(); - offset = layout.getOffset(); + runaci = r.getACI(); + runaci.first(); + textPath = (TextPath) runaci.getAttribute(TEXTPATH); + + Point2D offset = layout.getOffset(); + // System.out.println("Offset1: " + offset); + // System.out.println("Shift: [" + shiftX + "," + shiftY + "]"); + if (textPath != null) { + // For text path use relative values. + absX = tpShiftX; + absY = tpShiftY; + + shiftX = 0; + shiftY = 0; + } if (layout.isVertical()) { - float adj = (float)((offset.getY()-initY)*yScale); - offset = new Point2D.Float((float) offset.getX(), - (float)initY+adj+dy); + float adj = (float)((offset.getY()-shiftY)*yScale); + offset = new Point2D.Float((float)absX, + (float)absY+adj+dy); } else { - float adj = (float)((offset.getX()-initX)*xScale); - offset = new Point2D.Float((float)initX+adj+dx, - (float) offset.getY()); + float adj = (float)((offset.getX()-shiftX)*xScale); + offset = new Point2D.Float((float)absX+adj+dx, + (float)absY); } + // System.out.println("Offset2: " + offset); layout.setOffset(offset); + + if (textPath == null) { + Point2D ladv = layout.getAdvance2D(); + retX = (float)(offset.getX()+ladv.getX()); + retY = (float)(offset.getY()+ladv.getY()); + if (vertical) + absX += ladv.getX(); + else + absY += ladv.getY(); + } else { + Point2D ladv = layout.getAdvance2D(); + advX += (float)ladv.getX(); + advY += (float)ladv.getY(); + shiftX = advX; + shiftY = advY; + if (vertical) + tpShiftX += (float)ladv.getX(); + else + tpShiftY += (float)ladv.getY(); + + ladv = layout.getTextPathAdvance(); + absX = (float)ladv.getX(); + absY = (float)ladv.getY(); + retX = absX; + retY = absY; + } } + return new Point2D.Float(retX, retY); } /** @@ -1543,14 +1608,10 @@ public int begin; public int end; public Point2D advance; - public Point2D absLoc; - public TextChunk(int begin, int end, - Point2D absLoc, Point2D advance) { + public TextChunk(int begin, int end, Point2D advance) { this.begin = begin; this.end = end; - this.absLoc = new Point2D.Float((float) absLoc.getX(), - (float) absLoc.getY()); this.advance = new Point2D.Float((float) advance.getX(), (float) advance.getY()); } 1.5 +122 -55 xml-batik/sources/org/apache/batik/gvt/text/GlyphIterator.java Index: GlyphIterator.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/gvt/text/GlyphIterator.java,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- GlyphIterator.java 25 Apr 2002 00:45:42 -0000 1.4 +++ GlyphIterator.java 30 Apr 2002 19:08:48 -0000 1.5 @@ -41,12 +41,21 @@ // The ACI index of current glyph. int aciIdx = -1; + // The number of characters in ACI for current Glyph. + int charCount = -1; + // The total advance for line including last non-space glyph float adv = 0; // The total advance for line including spaces at end of line. float adj = 0; - // The runLimit (for current font) + // The runLimit for current font int runLimit = 0; + + // The runLimit for current line element (need a line break at end). + int lineBreakRunLimit = 0; + int lineBreakCount = 0; + + GVTFont font = null; int fontStart = 0; float maxAscent = 0; @@ -74,20 +83,21 @@ // The current left shift (inherited from previous line). int leftShift = 0; + Point2D gvBase = null; public GlyphIterator(AttributedCharacterIterator aci, GVTGlyphVector gv) { - this.aci = aci; - this.gv = gv; - - this.idx = 0; - this.chIdx = 0; - this.lineIdx = 0; - this.aciIdx = aci.getBeginIndex(); - this.ch = aci.first(); - this.chIdx = 0; - this.frc = gv.getFontRenderContext(); + this.aci = aci; + this.gv = gv; + + this.idx = 0; + this.chIdx = -1; + this.lineIdx = 0; + this.aciIdx = aci.getBeginIndex(); + this.charCount = gv.getCharacterCount(idx, idx); + this.ch = aci.first(); + this.frc = gv.getFontRenderContext(); this.font = (GVTFont)aci.getAttribute(GVT_FONT); if (font == null) { @@ -100,9 +110,15 @@ // Figure out where the font size might change again... this.runLimit = aci.getRunLimit(TEXT_COMPOUND_DELIMITER); + + this.lineBreakRunLimit = aci.getRunLimit(FLOW_LINE_BREAK); + Object o = (Object)aci.getAttribute(FLOW_LINE_BREAK); + this.lineBreakCount = (o == null)?0:1; + this.numGlyphs = gv.getNumGlyphs(); this.gp = gv.getGlyphPositions(0, this.numGlyphs+1, null); + this.gvBase = new Point2D.Float(gp[0], gp[1]); this.adv = getCharWidth(); this.adj = getCharAdvance(); } @@ -122,13 +138,17 @@ gi.idx = this.idx; gi.chIdx = this.chIdx; gi.aciIdx = this.aciIdx; + gi.charCount = this.charCount; gi.adv = this.adv; gi.adj = this.adj; gi.runLimit = this.runLimit; gi.ch = this.ch; - gi.chIdx = this.chIdx; gi.numGlyphs = this.numGlyphs; gi.gp = this.gp; + gi.gvBase = this.gvBase; + + gi.lineBreakRunLimit = this.lineBreakRunLimit; + gi.lineBreakCount = this.lineBreakCount; gi.frc = this.frc; gi.font = this.font; @@ -166,6 +186,12 @@ public float getAdv() { return adv; } /** + * @return The origin of the glyph vector (the point all glyphs are + * layed out with respect to). + */ + public Point2D getOrigin() { return gvBase; } + + /** * @return The current adjustment for the line. This is the ammount * that needs to be subracted from the following line to get it back * to the start of the next line. @@ -174,7 +200,7 @@ public float getMaxFontSize() { if (aciIdx >= fontStart) { - int newFS = aciIdx + gv.getCharacterCount(idx,idx); + int newFS = aciIdx + charCount; updateLineMetrics(newFS); fontStart = newFS; } @@ -183,7 +209,7 @@ public float getMaxAscent() { if (aciIdx >= fontStart) { - int newFS = aciIdx + gv.getCharacterCount(idx,idx); + int newFS = aciIdx + charCount; updateLineMetrics(newFS); fontStart = newFS; } @@ -192,7 +218,7 @@ public float getMaxDescent() { if (aciIdx >= fontStart) { - int newFS = aciIdx + gv.getCharacterCount(idx,idx); + int newFS = aciIdx + charCount; updateLineMetrics(newFS); fontStart = newFS; } @@ -228,9 +254,22 @@ } public int getLineBreaks() { - Integer i = (Integer)aci.getAttribute(FLOW_LINE_BREAK); - if (i == null) return 0; - return i.intValue(); + int ret = 0; + if (aciIdx+charCount >= lineBreakRunLimit) { + // Next char is outside this line element so break after + // the current char. + ret = lineBreakCount; + + // Update the lineBreakRunLimit, this is a bit tricky since + // The attribute doesn't change until the next glyph... + aci.setIndex(aciIdx+charCount); + lineBreakRunLimit = aci.getRunLimit(FLOW_LINE_BREAK); + aci.setIndex(aciIdx); // Restore location... + + Object o = (Object)aci.getAttribute(FLOW_LINE_BREAK); + lineBreakCount = (o == null)?0:1; + } + return ret; } /** @@ -243,32 +282,14 @@ // Special handling for soft hyphens and zero-width spaces gv.setGlyphVisible(idx, false); float chAdv = getCharAdvance(); - float chW = getCharWidth(); - adv -= chW; adj -= chAdv; - if (leftShiftIdx == null) { - leftShiftIdx = new int[1]; - leftShiftIdx[0] = idx; - leftShiftAmt = new float[1]; - leftShiftAmt[0] = chAdv; - } else { - int [] newLeftShiftIdx = new int[leftShiftIdx.length+1]; - for (int i=0; i<leftShiftIdx.length; i++) - newLeftShiftIdx[i] = leftShiftIdx[i]; - newLeftShiftIdx[leftShiftIdx.length] = idx; - leftShiftIdx = newLeftShiftIdx; - - float [] newLeftShiftAmt = new float[leftShiftAmt.length+1]; - for (int i=0; i<leftShiftAmt.length; i++) - newLeftShiftAmt[i] = leftShiftAmt[i]; - newLeftShiftAmt[leftShiftAmt.length] = chAdv; - leftShiftAmt = newLeftShiftAmt; - } + addLeftShift(idx, chAdv); } - aciIdx += gv.getCharacterCount(idx,idx); + aciIdx += charCount; ch = aci.setIndex(aciIdx); idx++; + charCount = gv.getCharacterCount(idx,idx); if (idx == numGlyphs) return; if (aciIdx >= runLimit) { @@ -290,6 +311,27 @@ } } + protected void addLeftShift(int idx, float chAdv) { + if (leftShiftIdx == null) { + leftShiftIdx = new int[1]; + leftShiftIdx[0] = idx; + leftShiftAmt = new float[1]; + leftShiftAmt[0] = chAdv; + } else { + int [] newLeftShiftIdx = new int[leftShiftIdx.length+1]; + for (int i=0; i<leftShiftIdx.length; i++) + newLeftShiftIdx[i] = leftShiftIdx[i]; + newLeftShiftIdx[leftShiftIdx.length] = idx; + leftShiftIdx = newLeftShiftIdx; + + float [] newLeftShiftAmt = new float[leftShiftAmt.length+1]; + for (int i=0; i<leftShiftAmt.length; i++) + newLeftShiftAmt[i] = leftShiftAmt[i]; + newLeftShiftAmt[leftShiftAmt.length] = chAdv; + leftShiftAmt = newLeftShiftAmt; + } + } + protected void updateLineMetrics(int end) { GVTLineMetrics glm = font.getLineMetrics (aci, fontStart, end, frc); @@ -301,9 +343,9 @@ if (fontSz > maxFontSize) maxFontSize = fontSz; } - public LineInfo getLineInfo(Point2D.Float loc, - float lineWidth, - boolean partial) { + public LineInfo newLine(Point2D.Float loc, + float lineWidth, + boolean partial) { if (ch == SOFT_HYPHEN) { gv.setGlyphVisible(idx, true); } @@ -324,23 +366,48 @@ leftShiftIdx = null; leftShiftAmt = null; - return new LineInfo(aci, gv, lineIdx, idx+1, loc, adv, adj, - getCharWidth(chIdx), lineWidth, partial); - } + int lineInfoIdx = idx+1; + float lineInfoAdv = adv; + float lineInfoAdj = adj; + float lineInfoChW; + int hideIdx; + if (chIdx != -1) { + lineInfoChW = getCharWidth(chIdx); + hideIdx = chIdx+1; + } else { + lineInfoChW = 0; + hideIdx = 0; + } + + while (!done()) { + adv=0; + adj=0; + + if ((ch == ZERO_WIDTH_SPACE) || + (ch == ZERO_WIDTH_JOINER)) + gv.setGlyphVisible(idx, false); + + ch = 0; // Disable soft-hyphen/ZWS advance adjustment. + nextChar(); + + if (isPrinting()) break; + + lineInfoIdx = idx+1; + lineInfoAdj += adj; + } + // hide trailing spaces if any + for (int i = hideIdx; i<lineInfoIdx; i++) + gv.setGlyphVisible(i, false); - public void newLine() { - adv=0; - adj=0; maxAscent = -Float.MAX_VALUE; maxDescent = -Float.MAX_VALUE; maxFontSize = -Float.MAX_VALUE; - if ((ch == ZERO_WIDTH_SPACE) || - (ch == ZERO_WIDTH_JOINER)) - gv.setGlyphVisible(idx, false); - ch = 0; // Disable soft-hyphen/ZWS advance adjustment. - nextChar(); lineIdx = idx; + + return new LineInfo(loc, aci, gv, lineIdx, lineInfoIdx, + lineInfoAdj, lineInfoAdv, lineInfoChW, + lineWidth, partial); } public boolean isPrinting() { @@ -376,8 +443,8 @@ * the rightmost part of the glyph. */ protected float getCharWidth(int gvIdx) { - Rectangle2D lcBound = gv.getGlyphVisualBounds(chIdx).getBounds2D(); - Point2D lcLoc = gv.getGlyphPosition(chIdx); + Rectangle2D lcBound = gv.getGlyphVisualBounds(gvIdx).getBounds2D(); + Point2D lcLoc = gv.getGlyphPosition(gvIdx); return (float)(lcBound.getX()+lcBound.getWidth()- lcLoc.getX()); } } 1.41 +74 -50 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.40 retrieving revision 1.41 diff -u -r1.40 -r1.41 --- GlyphLayout.java 27 Apr 2002 22:41:57 -0000 1.40 +++ GlyphLayout.java 30 Apr 2002 19:08:48 -0000 1.41 @@ -47,7 +47,7 @@ * @see org.apache.batik.gvt.text.TextSpanLayout * * @author <a href="[EMAIL PROTECTED]>Bill Haneman</a> - * @version $Id: GlyphLayout.java,v 1.40 2002/04/27 22:41:57 deweese Exp $ + * @version $Id: GlyphLayout.java,v 1.41 2002/04/30 19:08:48 deweese Exp $ */ public class GlyphLayout implements TextSpanLayout { @@ -245,16 +245,17 @@ * (e.g. if the aci has multiple X or Y values). */ public void setOffset(Point2D offset) { - + // System.out.println("SetOffset: " + offset + " - " + this.offset); if ((offset.getX() != this.offset.getX()) || (offset.getY() != this.offset.getY())) { - if (layoutApplied) { + if ((layoutApplied)||(spacingApplied)) { // Already layed out need to shift glyph positions to // account for new offset. float dx = (float)(offset.getX()-this.offset.getX()); float dy = (float)(offset.getY()-this.offset.getY()); int numGlyphs = gv.getNumGlyphs(); + // System.out.println("DXY: [" + dx +","+dy+"]"); 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, @@ -358,7 +359,7 @@ * of glyph layout. */ public Point2D getAdvance2D() { - syncLayout(); + adjustTextSpacing(); return advance; } @@ -488,7 +489,9 @@ char ch = aci.setIndex(currentChar); int glyphCharIndex = charMap[currentChar-start]; if ((glyphCharIndex >= beginCharIndex) && - (glyphCharIndex <= endCharIndex)) { + (glyphCharIndex <= endCharIndex) && + gv.isGlyphVisible(i)) { + Shape gbounds = gv.getGlyphLogicalBounds(i); if (gbounds != null) { // We got something... @@ -1009,10 +1012,10 @@ Float x=null, y=null, dx=null, dy=null, rotation=null; Object baseline=null; - float init_x_pos = (float) offset.getX(); - float init_y_pos = (float) offset.getY(); - float curr_x_pos = init_x_pos; - float curr_y_pos = init_y_pos; + float shift_x_pos = 0; + float shift_y_pos = 0; + float curr_x_pos = (float)offset.getX(); + float curr_y_pos = (float)offset.getY(); while (i < numGlyphs) { //System.out.println("limit: " + runLimit + ", " + aciIndex); @@ -1093,16 +1096,18 @@ } if ((x != null) && !x.isNaN()) { - if (i != 0) /// First x is factored into offset... - curr_x_pos = x.floatValue(); + if (i == 0) + shift_x_pos = (float)(x.floatValue()-offset.getX()); + curr_x_pos = x.floatValue()-shift_x_pos; } if (dx != null && !dx.isNaN()) { curr_x_pos += dx.floatValue(); } if ((y != null) && !y.isNaN()) { - if (i != 0) - curr_y_pos = y.floatValue(); + if (i == 0) + shift_y_pos = (float)(y.floatValue()-offset.getY()); + curr_y_pos = y.floatValue()-shift_y_pos; } if (dy != null && !dy.isNaN()) { curr_y_pos += dy.floatValue(); @@ -1208,9 +1213,8 @@ // 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, - curr_y_pos - init_y_pos); + advance = new Point2D.Float((float)(curr_x_pos - offset.getX()), + (float)(curr_y_pos - offset.getY())); layoutApplied = true; spacingApplied = false; @@ -1925,9 +1929,6 @@ if (cRect == null) break; } } - aci.first(); - MarginInfo mi = (MarginInfo)aci.getAttribute(FLOW_PARAGRAPH); - justification = mi.getJustification(); List gvl = new LinkedList(); List layouts = (List)clIter.next(); @@ -1942,6 +1943,13 @@ // System.out.println("Glyphs: " + numGlyphs); + aci.first(); + MarginInfo mi = (MarginInfo)aci.getAttribute(FLOW_PARAGRAPH); + if (mi == null) { + continue; + } + justification = mi.getJustification(); + if (cRect == null) { for(int idx=0; idx <numGlyphs; idx++) gv.setGlyphVisible(idx, false); @@ -1967,10 +1975,12 @@ } prevBotMargin = mi.getBottomMargin(); - x0 = (float)cRect.getX() + mi.getLeftMargin(); + float leftMargin = mi.getFirstLineLeftMargin(); + float rightMargin = mi.getFirstLineRightMargin(); + + x0 = (float)cRect.getX() + leftMargin; y0 = (float)cRect.getY(); - width = (float)(cRect.getWidth() - - (mi.getLeftMargin()+mi.getRightMargin())); + width = (float)(cRect.getWidth() - (leftMargin+rightMargin)); height = (float)cRect.getHeight(); List lineInfos = new LinkedList(); @@ -1978,6 +1988,16 @@ float prevDesc = 0.0f; GlyphIterator gi = new GlyphIterator(aci, gv); GlyphIterator breakGI = null, newBreakGI = null; + + if (!gi.done() && !gi.isPrinting()) { + // This will place any preceeding whitespace on an imaginary + // line that preceeds the real first line of the paragraph. + lineInfos.add(gi.newLine + (new Point2D.Float(x0, y0+dy), + width, true)); + } + + GlyphIterator lineGI = gi.copy(); boolean firstLine = true; while (!gi.done()) { @@ -1995,11 +2015,10 @@ } cRect = (Rectangle2D)flowRectsIter.next(); - x0 = (float) cRect.getX() + mi.getLeftMargin(); + x0 = (float) cRect.getX() + leftMargin; y0 = (float) cRect.getY(); width = (float)(cRect.getWidth() - - (mi.getLeftMargin()+ - mi.getRightMargin())); + (leftMargin+rightMargin)); height = (float)cRect.getHeight(); // New rect so no previous row to consider... @@ -2024,13 +2043,13 @@ if (doBreak) nextLineMult -= 1; nextLineMult += lnBreaks; - partial = true; doBreak = true; + partial = true; } if (!doBreak) { // System.out.println("No Brk Adv: " + gi.getAdv()); - // We don't need to break the line because of this char + // We don't need to break the line because of this glyph // So we just check if we need to update our break loc. if ((gi.isBreakChar()) || (breakGI == null) || @@ -2055,7 +2074,7 @@ // System.out.println(" Brk Adv: " + gi.getAdv()); // We will now attempt to break the line just - // after the breakGI. + // after 'gi'. // Note we are trying to figure out where the current // line is going to be placed (not the next line). We @@ -2098,10 +2117,10 @@ // Get info for new flow rect. cRect = (Rectangle2D)flowRectsIter.next(); - x0 = (float) cRect.getX() + mi.getLeftMargin(); + x0 = (float) cRect.getX() + leftMargin; y0 = (float) cRect.getY(); width = (float)(cRect.getWidth() - - (mi.getLeftMargin()+mi.getRightMargin())); + (leftMargin+rightMargin)); height = (float)cRect.getHeight(); // New rect so no previous row to consider... @@ -2118,14 +2137,18 @@ prevDesc = newDesc + (nextLineMult-1)*lineBoxHeight; nextLineMult = 0f; + lineInfos.add(gi.newLine + (new Point2D.Float(x0, y0+dy), width, partial)); - // System.out.println("Fit: " + dy); - lineInfos.add(gi.getLineInfo - (new Point2D.Float(x0, y0+dy), - width, - partial)); - gi.newLine(); + x0 -= leftMargin; + width += leftMargin+rightMargin; + + leftMargin = mi.getLeftMargin(); + rightMargin = mi.getRightMargin(); + x0 += leftMargin; + width -= leftMargin+rightMargin; + firstLine = false; // The line fits in the current flow rectangle. lineGI = gi.copy(lineGI); @@ -2137,7 +2160,7 @@ while(idx <numGlyphs) gv.setGlyphVisible(idx++, false); - layoutChunk(aci, gv, justification, lineInfos); + layoutChunk(aci, gv, gi.getOrigin(), justification, lineInfos); if (mi.isFlowRegionBreak()) { // Move to next flow region.. @@ -2152,17 +2175,18 @@ } public static void layoutChunk(AttributedCharacterIterator aci, - GVTGlyphVector gv, int justification, + GVTGlyphVector gv, Point2D origin, + int justification, List lineInfos) { Iterator lInfoIter = lineInfos.iterator(); int numGlyphs = gv.getNumGlyphs(); float [] gp = gv.getGlyphPositions(0, numGlyphs+1, null); - Point2D.Float lineLoc = null; - float lineAdv = 0; - float lineAdj = 0; + Point2D.Float lineLoc = null; + float lineAdv = 0; + float lineVAdv = 0; - float xOrig=gp[0]; - float yOrig=gp[1]; + float xOrig=(float)origin.getX(); + float yOrig=(float)origin.getY(); float xScale=1; float xAdj=0; @@ -2179,7 +2203,7 @@ // Always comes through here on first char... // Update offset for new line based on last line length - xOrig += lineAdj; + xOrig += lineAdv; // Get new values for everything... if (!lInfoIter.hasNext()) @@ -2188,7 +2212,7 @@ lineEnd = li.getEndIdx(); lineLoc = li.getLocation(); lineAdv = li.getAdvance(); - lineAdj = li.getOffset(); + lineVAdv = li.getVisualAdvance(); charW = li.getLastCharWidth(); lineWidth = li.getLineWidth(); partial = li.isPartialLine(); @@ -2198,13 +2222,13 @@ // Recalc justification info. switch (justification) { case 0: default: break; // Left - case 1: xAdj = (lineWidth-lineAdv)/2; break; // Center - case 2: xAdj = lineWidth-lineAdv; break; // Right + case 1: xAdj = (lineWidth-lineVAdv)/2; break; // Center + case 2: xAdj = lineWidth-lineVAdv; break; // Right case 3: // Full if ((!partial) && (lineEnd != i+1)) { // More than one char on line... // Scale char spacing to fill line. - xScale = (lineWidth-charW)/(lineAdv-charW); + xScale = (lineWidth-charW)/(lineVAdv-charW); } break; } @@ -2217,8 +2241,8 @@ float x = xOrig; float y = yOrig; if (lineLoc != null) { - x = lineLoc.x + (gp[2*i] -xOrig)*xScale+xAdj; - y = lineLoc.y + (gp[2*i+1]-yOrig); + x = lineLoc.x + (gp[2*i] -xOrig)*xScale+xAdj; + y = lineLoc.y + (gp[2*i+1]-yOrig); } gv.setGlyphPosition(i, new Point2D.Float(x, y)); } 1.2 +18 -10 xml-batik/sources/org/apache/batik/gvt/text/LineInfo.java Index: LineInfo.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/gvt/text/LineInfo.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- LineInfo.java 29 Mar 2002 20:00:55 -0000 1.1 +++ LineInfo.java 30 Apr 2002 19:08:48 -0000 1.2 @@ -12,46 +12,54 @@ import java.text.AttributedCharacterIterator; import org.apache.batik.gvt.font.GVTGlyphVector; +/** + * This class encapsulates the layout information about a single line + * in a multi-line flow. + */ public class LineInfo { + + Point2D.Float loc; AttributedCharacterIterator aci; GVTGlyphVector gv; int startIdx; int endIdx; - Point2D.Float loc; float advance; - float offset; + float visualAdvance; float lastCharWidth; float lineWidth; boolean partial; - public LineInfo(AttributedCharacterIterator aci, + /** + * + */ + public LineInfo(Point2D.Float loc, + AttributedCharacterIterator aci, GVTGlyphVector gv, int startIdx, int endIdx, - Point2D.Float loc, float advance, - float offset, + float visualAdvance, float lastCharWidth, float lineWidth, boolean partial) { + this.loc = loc; this.aci = aci; this.gv = gv; this.startIdx = startIdx; this.endIdx = endIdx; - this.loc = loc; this.advance = advance; - this.offset = offset; + this.visualAdvance = visualAdvance; this.lastCharWidth = lastCharWidth; this.lineWidth = lineWidth; this.partial = partial; } - + + public Point2D.Float getLocation() { return loc; } public AttributedCharacterIterator getACI() { return aci; } public GVTGlyphVector getGlyphVector() { return gv; } public int getStartIdx() { return startIdx; } public int getEndIdx() { return endIdx; } - public Point2D.Float getLocation() { return loc; } public float getAdvance() { return advance; } - public float getOffset() { return offset; } + public float getVisualAdvance() { return visualAdvance; } public float getLastCharWidth() { return lastCharWidth; } public float getLineWidth() { return lineWidth; } public boolean isPartialLine() { return partial; } 1.3 +23 -6 xml-batik/sources/org/apache/batik/gvt/text/MarginInfo.java Index: MarginInfo.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/gvt/text/MarginInfo.java,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- MarginInfo.java 12 Apr 2002 14:36:48 -0000 1.2 +++ MarginInfo.java 30 Apr 2002 19:08:48 -0000 1.3 @@ -18,22 +18,33 @@ protected float right; protected float bottom; protected float left; + + protected float firstLineLeft; + protected float firstLineRight; + protected int justification; protected boolean flowRegionBreak; public MarginInfo(float top, float right, float bottom, float left, + float firstLineLeft, float firstLineRight, int justification, boolean flowRegionBreak) { this.top = top; this.right = right; this.bottom = bottom; this.left = left; + + this.firstLineLeft = firstLineLeft; + this.firstLineRight = firstLineRight; + this.justification = justification; this.flowRegionBreak = flowRegionBreak; } public MarginInfo(float margin, int justification) { setMargin(margin); + this.justification = justification; + this.flowRegionBreak = false; } public void setMargin(float margin) { @@ -41,11 +52,17 @@ this.right = margin; this.bottom = margin; this.left = margin; + this.firstLineLeft = margin; + this.firstLineRight = margin; } - public float getTopMargin() { return top; } - public float getRightMargin() { return right; } - public float getBottomMargin() { return bottom; } - public float getLeftMargin() { return left; } - public int getJustification() { return justification; } - public boolean isFlowRegionBreak() { return flowRegionBreak; } + public float getTopMargin() { return top; } + public float getRightMargin() { return right; } + public float getBottomMargin() { return bottom; } + public float getLeftMargin() { return left; } + + public float getFirstLineLeftMargin() { return firstLineLeft; } + public float getFirstLineRightMargin() { return firstLineLeft; } + + public int getJustification() { return justification; } + public boolean isFlowRegionBreak() { return flowRegionBreak; } } 1.67 +22 -6 xml-batik/test-resources/org/apache/batik/test/samplesRendering.xml Index: samplesRendering.xml =================================================================== RCS file: /home/cvs/xml-batik/test-resources/org/apache/batik/test/samplesRendering.xml,v retrieving revision 1.66 retrieving revision 1.67 diff -u -r1.66 -r1.67 --- samplesRendering.xml 29 Apr 2002 13:20:20 -0000 1.66 +++ samplesRendering.xml 30 Apr 2002 19:08:48 -0000 1.67 @@ -8,7 +8,7 @@ <!-- ========================================================================= --> <!-- @author [EMAIL PROTECTED] --> -<!-- @version $Id: samplesRendering.xml,v 1.66 2002/04/29 13:20:20 tkormann Exp $ --> +<!-- @version $Id: samplesRendering.xml,v 1.67 2002/04/30 19:08:48 deweese Exp $ --> <!-- ========================================================================= --> <testSuite id="samplesRendering" name="samples and samples/test Rendering" class="org.apache.batik.test.svg.SamplesRenderingTest"> @@ -43,17 +43,32 @@ <test id="samples/textRotateShadows.svg" /> </testGroup> - <!-- ======================================================================= --> - <!-- Samples extensions files --> - <!-- ======================================================================= --> + <!-- ================================================================== --> + <!-- Samples extensions files --> + <!-- ================================================================== --> <testGroup id="extensions" name="extensions rendering"> + <test id="samples/extensions/colorSwitch.svg" > + <property name="Validating" class="java.lang.Boolean" value="false" /> + </test> + <test id="samples/extensions/histogramNormalization.svg" > + <property name="Validating" class="java.lang.Boolean" value="false" /> + </test> + <test id="samples/extensions/flowText.svg" > + <property name="Validating" class="java.lang.Boolean" value="false" /> + </test> + <test id="samples/extensions/multi.svg" > + <property name="Validating" class="java.lang.Boolean" value="false" /> + </test> <test id="samples/extensions/regularPolygon.svg" > <property name="Validating" class="java.lang.Boolean" value="false" /> </test> - <test id="samples/extensions/star.svg" > + <test id="samples/extensions/solidColor.svg" > <property name="Validating" class="java.lang.Boolean" value="false" /> </test> - <test id="samples/extensions/histogramNormalization.svg" > + <test id="samples/extensions/solidColor2.svg" > + <property name="Validating" class="java.lang.Boolean" value="false" /> + </test> + <test id="samples/extensions/star.svg" > <property name="Validating" class="java.lang.Boolean" value="false" /> </test> </testGroup> @@ -244,6 +259,7 @@ <test id="samples/tests/spec/text/textFeatures.svg" /> <test id="samples/tests/spec/text/textLayout.svg" /> <test id="samples/tests/spec/text/textLayout2.svg" /> + <test id="samples/tests/spec/text/textLength.svg" /> <test id="samples/tests/spec/text/textOnPath.svg" /> <test id="samples/tests/spec/text/textOnPathSpaces.svg" /> <test id="samples/tests/spec/text/textPCDATA.svg" />
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]