You are viewing a plain text version of this content. The canonical link for it is here.
Posted to batik-dev@xmlgraphics.apache.org by de...@apache.org on 2002/04/03 06:58:22 UTC

cvs commit: xml-batik/sources/org/apache/batik/swing/gvt TextSelectionManager.java

deweese     02/04/02 20:58:22

  Modified:    samples/extensions flowText.svg
               sources/org/apache/batik/gvt AbstractGraphicsNode.java
                        Selector.java
               sources/org/apache/batik/gvt/renderer BasicTextPainter.java
                        StrokingTextPainter.java
               sources/org/apache/batik/gvt/text ConcreteTextSelector.java
                        GlyphIterator.java GlyphLayout.java
               sources/org/apache/batik/swing/gvt TextSelectionManager.java
  Log:
  1) flowText now support soft-hyphen, and zero width space/joiner
  2) flowText now properly calculates leading, ascent and descent for line.
  3) Text selections stay correct when content is animated
  4) Text selection color is better.
  
  Revision  Changes    Path
  1.2       +14 -4     xml-batik/samples/extensions/flowText.svg
  
  Index: flowText.svg
  ===================================================================
  RCS file: /home/cvs/xml-batik/samples/extensions/flowText.svg,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- flowText.svg	29 Mar 2002 20:00:54 -0000	1.1
  +++ flowText.svg	3 Apr 2002 04:58:21 -0000	1.2
  @@ -14,7 +14,7 @@
   <!-- Tests various text on a path                                           -->
   <!--                                                                        -->
   <!-- @author bella.robinson@cmis.csiro.au                                   -->
  -<!-- @version $Id: flowText.svg,v 1.1 2002/03/29 20:00:54 deweese Exp $   -->
  +<!-- @version $Id: flowText.svg,v 1.2 2002/04/03 04:58:21 deweese Exp $   -->
   <!-- ====================================================================== -->
   
   <?xml-stylesheet type="text/css" href="extension.css" ?>
  @@ -28,6 +28,12 @@
            <!-- Space after size 40 'that' is aci 45 -->
         <rect x="17"  y="80" width="200" height="400" fill="rgb(220,220,255)"/>
         <rect x="233" y="80" width="200" height="400" fill="rgb(220,220,255)"/>
  +
  +      <line x1="27"  y1="70" x2="27"  y2="490" stroke="grey" fill="none"/>
  +      <line x1="207" y1="70" x2="207" y2="490" stroke="grey" fill="none"/>
  +      <line x1="243" y1="70" x2="243" y2="490" stroke="grey" fill="none"/>
  +      <line x1="423" y1="70" x2="423" y2="490" stroke="grey" fill="none"/>
  +
         <batik:flowText font-size="20" style="text-anchor:start"
   	           xml:space="preserve">
            <batik:flowRegion>
  @@ -35,12 +41,16 @@
   	    <batik:rect 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">example</batik:flowSpan> of a very long string that is split across multiple lines via text wrapping.</batik:flowPara>
  -	    <batik:flowPara justification="middle" top-margin="10" left-margin="10" bottom-margin="10"><batik:flowLine>Now check if text wrapping handles a number of tricky situations: </batik:flowLine>averylongrunonwordthatspansmultiplelines<batik:flowSpan font-weight="bold">with<batik:flowSpan fill="crimson">embedded</batik:flowSpan>span</batik:flowSpan>elements &amp; <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 bottom-margin="10" >This is an <batik:flowSpan font-size="40" fill="crimson">ex&#x00AD;ample</batik:flowSpan> of a very long string that is split &#x200D;across multi&#x00AD;ple lines via text wrap&#x0AD;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 situations: </batik:flowLine>averylongrunonwordthatspansmultiplelines<batik:flowSpan font-weight="bold">with<batik:flowSpan fill="crimson">embedded</batik:flowSpan>span</batik:flowSpan>elements &amp; <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 the flow regions. I'm going to try and trick it by having the last line include larger text so it wouldn't fit vertically. The end of this sentence will be cut off because the line size gets larg<batik:flowSpan font-size="35">er</batik:flowSpan></batik:flowPara>
            </batik:flowDiv>
         </batik:flowText>
      </g>
  -</svg>
   
  +    <!-- ============================================================= -->
  +    <!-- Batik sample mark                                             -->
  +    <!-- ============================================================= -->
  +    <use xlink:href="../batikLogo.svg#Batik_Tag_Box" />
  +</svg>
  
  
  
  1.40      +2 -2      xml-batik/sources/org/apache/batik/gvt/AbstractGraphicsNode.java
  
  Index: AbstractGraphicsNode.java
  ===================================================================
  RCS file: /home/cvs/xml-batik/sources/org/apache/batik/gvt/AbstractGraphicsNode.java,v
  retrieving revision 1.39
  retrieving revision 1.40
  diff -u -r1.39 -r1.40
  --- AbstractGraphicsNode.java	20 Mar 2002 16:34:44 -0000	1.39
  +++ AbstractGraphicsNode.java	3 Apr 2002 04:58:21 -0000	1.40
  @@ -54,7 +54,7 @@
    * @author <a href="mailto:Thierry.Kormann@sophia.inria.fr">Thierry Kormann</a>
    * @author <a href="mailto:etissandier@ilog.fr">Emmanuel Tissandier</a>
    * @author <a href="mailto:Thomas.DeWeeese@Kodak.com">Thomas DeWeese</a>
  - * @version $Id: AbstractGraphicsNode.java,v 1.39 2002/03/20 16:34:44 tkormann Exp $
  + * @version $Id: AbstractGraphicsNode.java,v 1.40 2002/04/03 04:58:21 deweese Exp $
    */
   public abstract class AbstractGraphicsNode implements GraphicsNode {
   
  @@ -650,7 +650,7 @@
           GraphicsNodeChangeListener gncl;
           while (i.hasNext()) {
               gncl = (GraphicsNodeChangeListener)i.next();
  -            gncl.changeStarted(changeStartedEvent);
  +            gncl.changeCompleted(changeCompletedEvent);
           }
       }
   
  
  
  
  1.3       +3 -2      xml-batik/sources/org/apache/batik/gvt/Selector.java
  
  Index: Selector.java
  ===================================================================
  RCS file: /home/cvs/xml-batik/sources/org/apache/batik/gvt/Selector.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- Selector.java	20 Nov 2000 00:43:29 -0000	1.2
  +++ Selector.java	3 Apr 2002 04:58:21 -0000	1.3
  @@ -14,10 +14,11 @@
    * Interface which allows selection of GraphicsNodes and their contents.
    *
    * @author <a href="mailto:bill.haneman@ireland.sun.com">Bill Haneman</a>
  - * @version $Id: Selector.java,v 1.2 2000/11/20 00:43:29 billh Exp $
  + * @version $Id: Selector.java,v 1.3 2002/04/03 04:58:21 deweese Exp $
    */
   public interface Selector extends GraphicsNodeMouseListener,
  -                                  GraphicsNodeKeyListener {
  +                                  GraphicsNodeKeyListener,
  +                                  GraphicsNodeChangeListener {
   
       /**
        * Get the contents of the current selection buffer.
  
  
  
  1.12      +1 -8      xml-batik/sources/org/apache/batik/gvt/renderer/BasicTextPainter.java
  
  Index: BasicTextPainter.java
  ===================================================================
  RCS file: /home/cvs/xml-batik/sources/org/apache/batik/gvt/renderer/BasicTextPainter.java,v
  retrieving revision 1.11
  retrieving revision 1.12
  diff -u -r1.11 -r1.12
  --- BasicTextPainter.java	8 Oct 2001 15:01:25 -0000	1.11
  +++ BasicTextPainter.java	3 Apr 2002 04:58:21 -0000	1.12
  @@ -44,7 +44,7 @@
    *
    * @author <a href="bill.haneman@ireland.sun.com>Bill Haneman</a>
    * @author <a href="vincent.hardy@sun.com>Vincent Hardy</a>
  - * @version $Id: BasicTextPainter.java,v 1.11 2001/10/08 15:01:25 tkormann Exp $
  + * @version $Id: BasicTextPainter.java,v 1.12 2002/04/03 04:58:21 deweese Exp $
    */
   public abstract class BasicTextPainter implements TextPainter {
   
  @@ -190,18 +190,15 @@
       protected static class BasicMark implements Mark {
   	
           private TextNode       node;
  -        private TextSpanLayout layout;
           private TextHit        hit;
   
   	/**
   	 * Constructs a new Mark with the specified parameters.
   	 */
           protected BasicMark(TextNode node,
  -                            TextSpanLayout layout, 
                               TextHit hit) {
               this.hit    = hit;
               this.node   = node;
  -            this.layout = layout;
           }
   
           public TextHit getHit() {
  @@ -210,10 +207,6 @@
   
           public TextNode getTextNode() {
               return node;
  -        }
  -
  -        public TextSpanLayout getLayout() {
  -            return layout;
           }
       }
   }
  
  
  
  1.31      +43 -54    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.30
  retrieving revision 1.31
  diff -u -r1.30 -r1.31
  --- StrokingTextPainter.java	29 Mar 2002 20:00:55 -0000	1.30
  +++ StrokingTextPainter.java	3 Apr 2002 04:58:21 -0000	1.31
  @@ -61,7 +61,7 @@
    * @see org.apache.batik.gvt.text.GVTAttributedCharacterIterator
    *
    * @author <a href="bill.haneman@ireland.sun.com>Bill Haneman</a>
  - * @version $Id: StrokingTextPainter.java,v 1.30 2002/03/29 20:00:55 deweese Exp $
  + * @version $Id: StrokingTextPainter.java,v 1.31 2002/04/03 04:58:21 deweese Exp $
    */
   public class StrokingTextPainter extends BasicTextPainter {
   
  @@ -1348,23 +1348,8 @@
               (index > aci.getEndIndex()))
               return null;
   
  -        List textRuns = getTextRuns(node, aci);
  -
  -        // for each text run, see if it contains the current char.
  -        for (int i = 0; i < textRuns.size(); ++i) {
  -            TextRun textRun = (TextRun)textRuns.get(i);
  -            TextSpanLayout layout = textRun.getLayout();
  -
  -            int idx = layout.getGlyphIndex(index);
  -            if (idx != -1) {
  -                TextHit textHit = new TextHit(index, leadingEdge);
  -                return new BasicTextPainter.BasicMark
  -                    (node, layout, textHit);
  -                                                      
  -            }
  -        }
  -        // Couldn't find it's layout....
  -        return null;
  +        TextHit textHit = new TextHit(index, leadingEdge);
  +        return new BasicTextPainter.BasicMark(node, textHit);
       }
   
       protected Mark hitTest(double x, double y, TextNode node) {
  @@ -1380,7 +1365,7 @@
               TextSpanLayout layout = textRun.getLayout();
               TextHit textHit = layout.hitTestChar((float) x, (float) y);
               if (textHit != null && layout.getBounds().contains(x,y)) {
  -                return new BasicTextPainter.BasicMark(node, layout, textHit);
  +                return new BasicTextPainter.BasicMark(node, textHit);
               }
           }
   
  @@ -1394,11 +1379,7 @@
           AttributedCharacterIterator aci;
           aci = node.getAttributedCharacterIterator();
           TextHit textHit = new TextHit(aci.getBeginIndex(), false);
  -
  -        // get the list of text runs
  -        List textRuns = getTextRuns(node, aci);
  -        return new BasicTextPainter.BasicMark
  -            (node, ((TextRun)textRuns.get(0)).getLayout(), textHit);
  +        return new BasicTextPainter.BasicMark(node, textHit);
       }
   
       /**
  @@ -1408,12 +1389,7 @@
           AttributedCharacterIterator aci;
           aci = node.getAttributedCharacterIterator();
           TextHit textHit = new TextHit(aci.getEndIndex(), false);
  -        
  -        // get the list of text runs
  -        List textRuns = getTextRuns(node, aci);
  -        return  new BasicTextPainter.BasicMark
  -            (node, ((TextRun)textRuns.get(textRuns.size()-1)).getLayout(), 
  -             textHit);
  +        return  new BasicTextPainter.BasicMark(node, textHit);
       }
   
       /**
  @@ -1450,14 +1426,36 @@
           result[0] = start.getHit().getCharIndex();
           result[1] = finish.getHit().getCharIndex();
   
  -        // this next bit is to make sure that ligatures are selected properly
  -        TextSpanLayout startLayout =  start.getLayout();
  -        TextSpanLayout finishLayout = finish.getLayout();
  -
  -        int startGlyphIndex = startLayout.getGlyphIndex(result[0]);
  -        int finishGlyphIndex = finishLayout.getGlyphIndex(result[1]);
  -        int startCharCount = startLayout.getCharacterCount(startGlyphIndex, startGlyphIndex);
  -        int finishCharCount = finishLayout.getCharacterCount(finishGlyphIndex, finishGlyphIndex);
  +        // get the list of text runs
  +        List textRuns = getTextRuns(textNode, aci);
  +        Iterator trI = textRuns.iterator();
  +        int startGlyphIndex = -1;
  +        int endGlyphIndex = -1;
  +        TextSpanLayout startLayout=null, endLayout=null;
  +        while (trI.hasNext()) {
  +            TextRun tr = (TextRun)trI.next();
  +            TextSpanLayout tsl = tr.getLayout();
  +            if (startGlyphIndex == -1) {
  +                startGlyphIndex  = tsl.getGlyphIndex(result[0]);
  +                if (startGlyphIndex != -1)
  +                    startLayout = tsl;
  +            }
  +                
  +            if (endGlyphIndex == -1) {
  +                endGlyphIndex = tsl.getGlyphIndex(result[1]);
  +                if (endGlyphIndex != -1)
  +                    endLayout = tsl;
  +            }
  +            if ((startGlyphIndex != -1) && (endGlyphIndex != -1))
  +                break;
  +        }
  +        if ((startLayout == null) || (endLayout == null))
  +            return null;
  +                
  +        int startCharCount = startLayout.getCharacterCount
  +            (startGlyphIndex, startGlyphIndex);
  +        int endCharCount = endLayout.getCharacterCount
  +            (endGlyphIndex, endGlyphIndex);
           if (startCharCount > 1) {
               if (result[0] > result[1] && startLayout.isLeftToRight()) {
                   result[0] += startCharCount-1;
  @@ -1465,11 +1463,11 @@
                   result[0] -= startCharCount-1;
               }
           }
  -        if (finishCharCount > 1) {
  -            if (result[1] > result[0] && finishLayout.isLeftToRight()) {
  -                result[1] += finishCharCount-1;
  -            } else if (result[0] > result[1] && !finishLayout.isLeftToRight()) {
  -                result[1] -= finishCharCount-1;
  +        if (endCharCount > 1) {
  +            if (result[1] > result[0] && endLayout.isLeftToRight()) {
  +                result[1] += endCharCount-1;
  +            } else if (result[0] > result[1] && !endLayout.isLeftToRight()) {
  +                result[1] -= endCharCount-1;
               }
           }
   
  @@ -1501,6 +1499,8 @@
           TextNode textNode = begin.getTextNode();
           if (textNode != end.getTextNode()) 
               throw new Error("Markers are from different TextNodes!");
  +        if (textNode == null)
  +            return null;
   
           int beginIndex = begin.getHit().getCharIndex();
           int endIndex   = end.getHit().getCharIndex();
  @@ -1511,17 +1511,6 @@
   
               int tmpIndex = beginIndex;
               beginIndex = endIndex; endIndex = tmpIndex;
  -        }
  -
  -        TextSpanLayout beginLayout = null;
  -        TextSpanLayout endLayout = null;
  -        if ((begin != null) && (end != null)) {
  -            beginLayout = begin.getLayout();
  -            endLayout   = end.getLayout();
  -        }
  -
  -        if ((beginLayout == null) || (endLayout == null)) {
  -            return null;
           }
   
           // get the list of text runs
  
  
  
  1.11      +45 -6     xml-batik/sources/org/apache/batik/gvt/text/ConcreteTextSelector.java
  
  Index: ConcreteTextSelector.java
  ===================================================================
  RCS file: /home/cvs/xml-batik/sources/org/apache/batik/gvt/text/ConcreteTextSelector.java,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- ConcreteTextSelector.java	8 Oct 2001 15:01:25 -0000	1.10
  +++ ConcreteTextSelector.java	3 Apr 2002 04:58:21 -0000	1.11
  @@ -25,7 +25,9 @@
   import org.apache.batik.gvt.Selector;
   import org.apache.batik.gvt.Selectable;
   import org.apache.batik.gvt.GraphicsNode;
  +import org.apache.batik.gvt.RootGraphicsNode;
   import org.apache.batik.gvt.text.Mark;
  +import org.apache.batik.gvt.event.GraphicsNodeChangeEvent;
   import org.apache.batik.gvt.event.GraphicsNodeMouseEvent;
   import org.apache.batik.gvt.event.GraphicsNodeInputEvent;
   import org.apache.batik.gvt.event.GraphicsNodeEvent;
  @@ -39,7 +41,7 @@
    * A simple implementation of GraphicsNodeMouseListener for text selection.
    *
    * @author <a href="mailto:bill.haneman@ireland.sun.com">Bill Haneman</a>
  - * @version $Id: ConcreteTextSelector.java,v 1.10 2001/10/08 15:01:25 tkormann Exp $
  + * @version $Id: ConcreteTextSelector.java,v 1.11 2002/04/03 04:58:21 deweese Exp $
    */
   
   public class ConcreteTextSelector implements Selector {
  @@ -96,6 +98,18 @@
           report(evt, "keyTyped");
       }
   
  +    public void changeStarted (GraphicsNodeChangeEvent gnce) {
  +    }
  +    public void changeCompleted (GraphicsNodeChangeEvent gnce) {
  +        if (selectionNode == null) return;
  +        Shape newShape =
  +            ((Selectable)selectionNode).getHighlightShape();
  +        dispatchSelectionEvent
  +            (new SelectionEvent(getSelection(),
  +                                SelectionEvent.SELECTION_CHANGED,
  +                                newShape));
  +    }
  +
       public void setSelection(Mark begin, Mark end) {
           TextNode node = begin.getTextNode();
           if (node != end.getTextNode())
  @@ -146,14 +160,25 @@
               p = t.transform(p, null);
   
               if (isDeselectGesture(evt)) {
  +                if (selectionNode != null)
  +                    selectionNode.getRoot()
  +                        .removeTreeGraphicsNodeChangeListener(this);
   
                   dispatchSelectionEvent(
                           new SelectionEvent(null,
                                   SelectionEvent.SELECTION_CLEARED,
                                   null));
                   copyToClipboard(null);
  -
  +                selectionNode = null;
               } else if (isSelectStartGesture(evt)) {
  +                if (selectionNode != source) {
  +                    if (selectionNode != null)
  +                        selectionNode.getRoot()
  +                            .removeTreeGraphicsNodeChangeListener(this);
  +                    if (source != null)
  +                        source.getRoot()
  +                            .addTreeGraphicsNodeChangeListener(this);
  +                }
   
                   selectionNode = source;
                   ((Selectable) source).selectAt(p.getX(), p.getY());
  @@ -163,7 +188,14 @@
                                   null));
   
               } else if (isSelectEndGesture(evt)) {
  -
  +                if (selectionNode != source) {
  +                    if (selectionNode != null)
  +                        selectionNode.getRoot()
  +                            .removeTreeGraphicsNodeChangeListener(this);
  +                    if (source != null)
  +                        source.getRoot()
  +                            .addTreeGraphicsNodeChangeListener(this);
  +                }
                   selectionNode = source;
   
                   ((Selectable) source).selectTo(p.getX(), p.getY());
  @@ -176,13 +208,13 @@
                                   SelectionEvent.SELECTION_DONE,
                                   newShape));
                   copyToClipboard(oldSelection);
  -
               } else
   
               if (isSelectContinueGesture(evt)) {
   
                   if (selectionNode == source) {
  -                    boolean result = ((Selectable) source).selectTo(p.getX(), p.getY());
  +                    boolean result = ((Selectable) source).selectTo(p.getX(), 
  +                                                                    p.getY());
                       if (result) {
                           Shape newShape =
                           ((Selectable) source).getHighlightShape();
  @@ -193,8 +225,15 @@
                                   newShape));
                       }
                   }
  -
               } else if (isSelectAllGesture(evt)) {
  +                if (selectionNode != source) {
  +                    if (selectionNode != null)
  +                        selectionNode.getRoot()
  +                            .removeTreeGraphicsNodeChangeListener(this);
  +                    if (source != null)
  +                        source.getRoot()
  +                            .addTreeGraphicsNodeChangeListener(this);
  +                }
                   selectionNode = source;
                   
                   ((Selectable) source).selectAll(p.getX(), p.getY());
  
  
  
  1.2       +195 -45   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.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- GlyphIterator.java	29 Mar 2002 20:00:55 -0000	1.1
  +++ GlyphIterator.java	3 Apr 2002 04:58:21 -0000	1.2
  @@ -9,10 +9,14 @@
   package org.apache.batik.gvt.text;
   
   import java.awt.font.TextAttribute;
  +import java.awt.font.FontRenderContext;
   import java.awt.geom.Point2D;
   import java.awt.geom.Rectangle2D;
   import java.text.AttributedCharacterIterator;
  +import org.apache.batik.gvt.font.AWTGVTFont;
  +import org.apache.batik.gvt.font.GVTFont;
   import org.apache.batik.gvt.font.GVTGlyphVector;
  +import org.apache.batik.gvt.font.GVTLineMetrics;
   
   public class GlyphIterator {
       public static final AttributedCharacterIterator.Attribute FLOW_LINE_BREAK 
  @@ -25,6 +29,10 @@
           AttributedCharacterIterator.Attribute GVT_FONT 
           = GVTAttributedCharacterIterator.TextAttribute.GVT_FONT;
   
  +    public static final char SOFT_HYPHEN       = 0x00AD;
  +    public static final char ZERO_WIDTH_SPACE  = 0x200B;
  +    public static final char ZERO_WIDTH_JOINER = 0x200D;
  +
       // Glyph index of current glyph
       int   idx         = -1;
       // Glyph index of last 'printing' glyph.
  @@ -37,12 +45,13 @@
       float adv         =  0;
       // The total advance for line including spaces at end of line.
       float adj         =  0;
  -    // The current font size
  -    float fontSize    = 0;
       // The runLimit (for current font)
  -    int   runLimit    = 0;
  -    // The max font size on line (printable chars only).  
  -    float maxFontSize = 0;
  +    int     runLimit   = 0;
  +    GVTFont font       = null;
  +    int     fontStart  = 0;
  +    float   maxAscent  = 0;
  +    float   maxDescent = 0;
  +    float   maxFontSize = 0;
   
       float width = 0;
       // The current char (from ACI)
  @@ -51,8 +60,21 @@
       int numGlyphs = 0;
       // The AttributedCharacterIterator.
       AttributedCharacterIterator aci;
  +    // The GVTGlyphVector for this Text chunk.
       GVTGlyphVector gv;
  +    // The GlyphPositions for this GlyphVector (Shared)
       float [] gp;
  +    // The font render context for this GylphVector.
  +    FontRenderContext frc;
  +
  +    // The Indexes of new leftShift amounts (soft-hyphens)
  +    int   [] leftShiftIdx = null;
  +    // The amount of new leftShifts (soft-hyphen adv)
  +    float [] leftShiftAmt = null;
  +    // The current left shift (inherited from previous line).
  +    int leftShift = 0;
  +
  +
       
       public GlyphIterator(AttributedCharacterIterator aci,
                            GVTGlyphVector gv) {
  @@ -65,20 +87,23 @@
           this.aciIdx   = aci.getBeginIndex();
           this.ch       = aci.first();
           this.chIdx    = 0;
  -
  -        this.fontSize = 12;
  -        Float fsFloat = (Float)aci.getAttributes().get(TextAttribute.SIZE);
  -        if (fsFloat != null) {
  -            this.fontSize = fsFloat.floatValue();
  +        this.frc      = gv.getFontRenderContext();
  +        
  +        this.font = (GVTFont)aci.getAttribute(GVT_FONT);
  +        if (font == null) {
  +            font = new AWTGVTFont(aci.getAttributes());
           }
  -        this.maxFontSize = this.fontSize;
  +        fontStart = aciIdx;
  +        this.maxFontSize = -Float.MAX_VALUE;
  +        this.maxAscent   = -Float.MAX_VALUE;
  +        this.maxDescent  = -Float.MAX_VALUE;
   
           // Figure out where the font size might change again...
           this.runLimit  = aci.getRunLimit(TEXT_COMPOUND_DELIMITER);
   
           this.numGlyphs   = gv.getNumGlyphs();
           this.gp          = gv.getGlyphPositions(0, this.numGlyphs+1, null);
  -        this.adv = this.adj = getAdvance();
  +        this.adv = this.adj = getCharAdvance();
       }
   
       public GlyphIterator(GlyphIterator gi) {
  @@ -93,32 +118,82 @@
           if (gi == null)
               return new GlyphIterator(this);
   
  -        gi.idx         = this.idx;
  -        gi.chIdx       = this.chIdx;
  -        gi.aciIdx      = this.aciIdx;
  -        gi.adv         = this.adv;
  -        gi.adj         = this.adj;
  -        gi.fontSize    = this.fontSize;
  +        gi.idx        = this.idx;
  +        gi.chIdx      = this.chIdx;
  +        gi.aciIdx     = this.aciIdx;
  +        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.frc         = this.frc;
  +        gi.font        = this.font;
  +        gi.fontStart   = this.fontStart;
  +        gi.maxAscent   = this.maxAscent;
  +        gi.maxDescent  = this.maxDescent;
           gi.maxFontSize = this.maxFontSize;
  -        gi.runLimit    = this.runLimit;
  -        gi.ch          = this.ch;
  -        gi.chIdx       = this.chIdx;
  -        gi.numGlyphs   = this.numGlyphs;
   
  +        gi.leftShift    = this.leftShift;
  +        gi.leftShiftIdx = this.leftShiftIdx;
  +        gi.leftShiftAmt = this.leftShiftAmt;
           return gi;
       }
   
  +    /**
  +     * @return  The index into glyph vector for current character.
  +     */
       public int getGlyphIndex() { return idx; }
   
  +    /**
  +     * @return the current character.
  +     */
  +    public char getChar() { return ch; }
  +
  +    /**
  +     * @return The index into Attributed Character iterator for
  +     * current character.  
  +     */
       public int getACIIndex() { return aciIdx; }
   
  +    /**
  +     * @return The current advance for the line, this is the 'visual width'
  +     * of the current line.
  +     */
       public float getAdv() { return adv; }
   
  +    /**
  +     * @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.
  +     */
       public float getAdj() { return adj; }
   
  -    public float getFontSize() { return fontSize; }
  +    public float getMaxFontSize()  {
  +        if (aciIdx >= fontStart) {
  +            updateLineMetrics(aciIdx);
  +            fontStart = aciIdx + gv.getCharacterCount(idx,idx);
  +        }
  +        return maxFontSize; 
  +    }
  +
  +    public float getMaxAscent()  { 
  +        if (aciIdx >= fontStart) {
  +            updateLineMetrics(aciIdx);
  +            fontStart = aciIdx + gv.getCharacterCount(idx,idx);
  +        }
  +        return maxAscent; 
  +    }
   
  -    public float getMaxFontSize() { return maxFontSize; }
  +    public float getMaxDescent() { 
  +        if (aciIdx >= fontStart) {
  +            updateLineMetrics(aciIdx);
  +            fontStart = aciIdx + gv.getCharacterCount(idx,idx);
  +        }
  +        return maxDescent; 
  +    }
   
       public boolean isLastChar() {
           return (idx == (numGlyphs-1));
  @@ -129,9 +204,25 @@
       }
   
       public boolean isBreakChar() {
  -        return ((ch == ' ') || (ch == '\t'));
  +        switch (ch) {
  +        case GlyphIterator.ZERO_WIDTH_SPACE:  return true;            
  +        case GlyphIterator.ZERO_WIDTH_JOINER: return false;            
  +        case GlyphIterator.SOFT_HYPHEN:       return true;
  +        case ' ': case '\t':                  return true;
  +        default:                              return false;
  +        }
       }
   
  +    protected boolean isPrinting(char tstCH) {
  +        switch (ch) {
  +        case GlyphIterator.ZERO_WIDTH_SPACE:  return false;            
  +        case GlyphIterator.ZERO_WIDTH_JOINER: return false;            
  +        case GlyphIterator.SOFT_HYPHEN:       return true;
  +        case ' ': case '\t':                  return false;
  +        default:                              return true;
  +        }
  +    }        
  +       
       public int getLineBreaks() {
           Integer i = (Integer)aci.getAttribute(FLOW_LINE_BREAK);
           if (i == null) return 0;
  @@ -142,25 +233,51 @@
        * Move iterator to the next char.
        */
       public void nextChar() {
  +        if ((ch == SOFT_HYPHEN)      || 
  +            (ch == ZERO_WIDTH_SPACE) ||
  +            (ch == ZERO_WIDTH_JOINER)) {
  +            // Special handling for soft hyphens and zero-width spaces
  +            gv.setGlyphVisible(idx, false);
  +            float chAdv = getCharAdvance();
  +            adv -= chAdv;
  +            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;
  +            }
  +        }
  +
  +        int preIncACIIdx = aciIdx;
           aciIdx += gv.getCharacterCount(idx,idx);
           ch = aci.setIndex(aciIdx);
           idx++;
           if (idx == numGlyphs) return;
   
           if (aciIdx >= runLimit) {
  +            updateLineMetrics(preIncACIIdx);
               runLimit = aci.getRunLimit(TEXT_COMPOUND_DELIMITER);
  -            Float fsFloat = (Float)aci.getAttributes().get(TextAttribute.SIZE);
  -            if (fsFloat != null) {
  -                fontSize = fsFloat.floatValue();
  -            } else {
  -                fontSize = 12;
  +            font     = (GVTFont)aci.getAttribute(GVT_FONT);
  +            if (font == null) {
  +                font = new AWTGVTFont(aci.getAttributes());
               }
  +            fontStart = aciIdx;
           }
   
  -        if (fontSize > maxFontSize)
  -            maxFontSize = fontSize;
  -
  -        float chAdv = getAdvance();
  +        float chAdv = getCharAdvance();
           adj += chAdv;
           if (isPrinting()) {
               chIdx = idx;
  @@ -168,18 +285,49 @@
           }
       }
   
  +    protected void updateLineMetrics(int end) {
  +        GVTLineMetrics glm = font.getLineMetrics
  +            (aci, fontStart, end, frc);
  +        float ascent  = glm.getAscent();
  +        float descent = glm.getDescent();
  +        float fontSz  = font.getSize();
  +        if (ascent >  maxAscent)   maxAscent   = ascent;
  +        if (descent > maxDescent)  maxDescent  = descent;
  +        if (fontSz >  maxFontSize) maxFontSize = fontSz;
  +    }
  +
       public LineInfo getLineInfo(Point2D.Float loc, 
                                   float lineWidth, 
                                   boolean partial) {
  +        if (ch == SOFT_HYPHEN) {
  +            gv.setGlyphVisible(idx, true);
  +        }
  +
           // Tweak line advance to account for visual bounds of last 
           // printing glyph.
           Rectangle2D lcBound = gv.getGlyphVisualBounds(chIdx).getBounds2D();
           Point2D     lcLoc   = gv.getGlyphPosition(chIdx);
           float       charW   = (float)(lcBound.getX()+lcBound.getWidth()-
                                         lcLoc.getX());
  -        float charAdv = getAdvance(chIdx);
  +        float charAdv = getCharAdvance(chIdx);
           adv -= charAdv-charW;
  -        
  +
  +        int lsi = 0;
  +        int nextLSI;
  +        if (leftShiftIdx != null) nextLSI = leftShiftIdx[lsi];
  +        else                      nextLSI = idx+1;
  +        for (int ci=lineIdx; ci<=idx; ci++) {
  +            if (ci == nextLSI) {
  +                leftShift += leftShiftAmt[lsi++];
  +                if (lsi < leftShiftIdx.length)
  +                    nextLSI = leftShiftIdx[lsi];
  +            }
  +            gv.setGlyphPosition(ci, new Point2D.Float(gp[2*ci]-leftShift,
  +                                                      gp[2*ci+1]));
  +        }
  +        leftShiftIdx = null;
  +        leftShiftAmt = null;
  +
           return new LineInfo(aci, gv, lineIdx, idx+1, loc, adv, adj,
                               charW, lineWidth, partial);
       }
  @@ -187,16 +335,18 @@
       public void newLine() {
           adv=0;
           adj=0;
  -        maxFontSize = fontSize;
  -
  +        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;
       }
   
  -    protected boolean isPrinting(char tstCH) {
  -        return !((ch == ' ') || (ch == '\t'));
  -    }        
  -       
       public boolean isPrinting() {
           return isPrinting(ch);
       }
  @@ -204,14 +354,14 @@
       /**
        * Get the advance associated with the current glyph
        */
  -    public float getAdvance() {
  -        return getAdvance(idx);
  +    public float getCharAdvance() {
  +        return getCharAdvance(idx);
       }
   
       /**
        * Get the advance associated with any glyph
        */
  -    protected float getAdvance(int gvIdx) {
  +    protected float getCharAdvance(int gvIdx) {
           return gp[2*gvIdx+2] - gp[2*gvIdx];
       }
   }
  
  
  
  1.38      +48 -25    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.37
  retrieving revision 1.38
  diff -u -r1.37 -r1.38
  --- GlyphLayout.java	29 Mar 2002 20:00:55 -0000	1.37
  +++ GlyphLayout.java	3 Apr 2002 04:58:21 -0000	1.38
  @@ -47,7 +47,7 @@
    * @see org.apache.batik.gvt.text.TextSpanLayout
    *
    * @author <a href="bill.haneman@ireland.sun.com>Bill Haneman</a>
  - * @version $Id: GlyphLayout.java,v 1.37 2002/03/29 20:00:55 deweese Exp $
  + * @version $Id: GlyphLayout.java,v 1.38 2002/04/03 04:58:21 deweese Exp $
    */
   public class GlyphLayout implements TextSpanLayout {
   
  @@ -778,7 +778,7 @@
               // merging seperately, and anyways with this much space
               // between them the extra outline shouldn't hurt..
               GeneralPath gp = new GeneralPath();
  -            if (dist < 2*sz) {
  +            if (dist < sz) {
                   // Close enough to merge with previous char...
                   System.arraycopy(boxes, 0, chull, 0, 8);
                   int npts = makeConvexHull(chull, 8);
  @@ -1856,9 +1856,10 @@
           float y0     = (float)cRect.getY();
           float x0, width, height;
   
  -        float lineMult     = 1.0f;
  -        float nextLineMult = 0.0f;
  -        float dy           = 0.0f;
  +        boolean lineHeightRelative = true;
  +        float lineHeight           = 1.0f;
  +        float nextLineMult         = 0.0f;
  +        float dy                   = 0.0f;
   
           //System.out.println("Chunks: " + numChunks);
           
  @@ -1915,43 +1916,60 @@
   
               float prevDesc = 0.0f;
               GlyphIterator gi = new GlyphIterator(aci, gv);
  -            GlyphIterator breakGI  = null;
  +            GlyphIterator breakGI  = null, newBreakGI = null;
               GlyphIterator lineGI   =  gi.copy();
               while (!gi.done()) {
                   boolean doBreak = false;
                   boolean partial = false;
   
  -                if (!gi.isBreakChar() &&
  +                if (gi.isPrinting() &&
                       (breakGI != null) && // Don't break at first char in line
                       (gi.getAdv() > width)) {
                       gi = breakGI.copy(gi);  // Back up to break loc...
   
  -                    nextLineMult = 1.1f;
  +                    nextLineMult = 1;
                       doBreak = true;
                       partial = false;
                   } else if (gi.isLastChar()) {
  -                    nextLineMult = 1.1f;
  +                    nextLineMult = 1;
                       doBreak = true;
                       partial = true;
                   } 
                   int lnBreaks = gi.getLineBreaks();
                   if (lnBreaks != 0) {
                       if (doBreak)
  -                        nextLineMult -= 1.1;
  -                    nextLineMult += lnBreaks*1.1f;
  +                        nextLineMult -= 1;
  +                    nextLineMult += lnBreaks;
                       partial = true;
                       doBreak = true;
                   }
  +
                   if (!doBreak) {
  +                    // System.out.println("No Brk Adv: " + gi.getAdv());
  +                    // We don't need to break the line because of this char
  +                    // So we just check if we need to update our break loc.
                       if ((gi.isBreakChar()) ||
                           (breakGI == null)  ||
  -                        (!breakGI.isBreakChar()))
  -                        breakGI = gi.copy(breakGI);
  -
  -                    gi.nextChar();
  +                        (!breakGI.isBreakChar())) {
  +                        // Make this the new break if curr char is a
  +                        // break char or we don't have any break chars
  +                        // yet, or our current break char is also not
  +                        // a break char.
  +                        newBreakGI = gi.copy(newBreakGI);
  +                        gi.nextChar();
  +                        if (gi.getChar() != GlyphIterator.ZERO_WIDTH_JOINER) {
  +                            GlyphIterator tmpGI = breakGI;
  +                            breakGI = newBreakGI;
  +                            newBreakGI = tmpGI;
  +                        }
  +                    } else {
  +                        gi.nextChar();
  +                    }
                       continue;
                   }
   
  +                // System.out.println("   Brk Adv: " + gi.getAdv());
  +
                   // We will now attempt to break the line just
                   // after the breakGI.
   
  @@ -1962,16 +1980,19 @@
   
                   // Get the nomial line advance based on the
                   // largest font we encountered on line...
  -                // Fix this to be based on font ascent/descent.
  -                float ladv = (gi.getMaxFontSize()*.8f+prevDesc);
  -                    
  -                dy += ladv*lineMult;
  -                    // Remember the effect of p's br's at the end of this line.
  -                lineMult = nextLineMult;
  -                nextLineMult = 0f;
  +                float lineSize = gi.getMaxAscent()+gi.getMaxDescent(); 
  +                float lineBoxHeight;
  +                if (lineHeightRelative) 
  +                    lineBoxHeight = gi.getMaxFontSize()*lineHeight;
  +                else
  +                    lineBoxHeight = lineHeight;
  +                float halfLeading = (lineBoxHeight-lineSize)/2;
   
  +                float ladv = prevDesc + halfLeading + gi.getMaxAscent();
  +                float newDesc = halfLeading + gi.getMaxDescent();
   
  -                if ((dy + gi.getMaxFontSize()*.2f) > height)  {
  +                dy += ladv;
  +                if ((dy + newDesc) > height)  {
                       // The current Line doesn't fit in the
                       // current flow rectangle so we need to
                       // move line to the next flow rect.
  @@ -1998,7 +2019,6 @@
                       // New rect so no previous row to consider...
                       dy        = 0; // mi.getTopMargin();
                       prevDesc  = 0;
  -                    lineMult  = 1.0f; // don't pile up lineMults from
                       // previous flows?
   
                       if (gi.getAdv() > oldWidth) {
  @@ -2008,8 +2028,11 @@
                       continue;
                   }
   
  +                prevDesc = newDesc + (nextLineMult-1)*lineBoxHeight;
  +                nextLineMult = 0f;
  +
  +                
                   // System.out.println("Fit: " + dy);
  -                prevDesc = gi.getMaxFontSize()*.2f;
                   lineInfos.add(gi.getLineInfo
                                 (new Point2D.Float(x0, y0+dy),
                                  width,
  
  
  
  1.16      +2 -7      xml-batik/sources/org/apache/batik/swing/gvt/TextSelectionManager.java
  
  Index: TextSelectionManager.java
  ===================================================================
  RCS file: /home/cvs/xml-batik/sources/org/apache/batik/swing/gvt/TextSelectionManager.java,v
  retrieving revision 1.15
  retrieving revision 1.16
  diff -u -r1.15 -r1.16
  --- TextSelectionManager.java	15 Oct 2001 09:42:55 -0000	1.15
  +++ TextSelectionManager.java	3 Apr 2002 04:58:22 -0000	1.16
  @@ -36,7 +36,7 @@
    * This class represents an object which manage GVT text nodes selection.
    *
    * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
  - * @version $Id: TextSelectionManager.java,v 1.15 2001/10/15 09:42:55 tkormann Exp $
  + * @version $Id: TextSelectionManager.java,v 1.16 2002/04/03 04:58:22 deweese Exp $
    */
   public class TextSelectionManager {
   
  @@ -61,11 +61,6 @@
       protected Overlay selectionOverlay = new SelectionOverlay();
   
       /**
  -     * The GVT root.
  -     */
  -    protected GraphicsNode gvtRoot;
  -
  -    /**
        * The mouse listener.
        */
       protected MouseListener mouseListener;
  @@ -88,7 +83,7 @@
       /**
        * The color of the selection overlay.
        */
  -    protected Color selectionOverlayColor = new Color(200, 200, 255, 100);
  +    protected Color selectionOverlayColor = new Color(100, 100, 255, 100);
   
       /**
        * The color of the outline of the selection overlay.
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: batik-dev-unsubscribe@xml.apache.org
For additional commands, e-mail: batik-dev-help@xml.apache.org