CVSROOT: /sources/classpath Module name: classpath Changes by: Sven de Marothy <smarothy> 06/06/09 20:23:55
Modified files: . : ChangeLog gnu/java/awt/peer/gtk: FreetypeGlyphVector.java GdkFontPeer.java java/awt/font : TextLayout.java java/text : Bidi.java Log message: 2006-06-08 Sven de Marothy <[EMAIL PROTECTED]> * java/text/Bidi.java: Treat WS as neutral for rules N1 & N2. * gnu/java/awt/peer/gtk/FreetypeGlyphVector.java New constructor for bidirectionality. (getGlyphMetrics): Return whitespace glyphs. (getLogicalBounds): Offset rectangles to correct positions. * gnu/java/awt/peer/gtk/GdkFontPeer.java (getBaselineFor): Default to ROMAN_BASELINE. (GdkFontLineMetrics): Guess some values for underline and strikethrough. (layoutGlyphVector): Use bidirectionality. * java/awt/font/TextLayout.java: Implement, mostly. CVSWeb URLs: http://cvs.savannah.gnu.org/viewcvs/classpath/ChangeLog?cvsroot=classpath&r1=1.7743&r2=1.7744 http://cvs.savannah.gnu.org/viewcvs/classpath/gnu/java/awt/peer/gtk/FreetypeGlyphVector.java?cvsroot=classpath&r1=1.3&r2=1.4 http://cvs.savannah.gnu.org/viewcvs/classpath/gnu/java/awt/peer/gtk/GdkFontPeer.java?cvsroot=classpath&r1=1.15&r2=1.16 http://cvs.savannah.gnu.org/viewcvs/classpath/java/awt/font/TextLayout.java?cvsroot=classpath&r1=1.7&r2=1.8 http://cvs.savannah.gnu.org/viewcvs/classpath/java/text/Bidi.java?cvsroot=classpath&r1=1.5&r2=1.6 Patches: Index: ChangeLog =================================================================== RCS file: /sources/classpath/classpath/ChangeLog,v retrieving revision 1.7743 retrieving revision 1.7744 diff -u -b -r1.7743 -r1.7744 --- ChangeLog 9 Jun 2006 17:02:31 -0000 1.7743 +++ ChangeLog 9 Jun 2006 20:23:54 -0000 1.7744 @@ -1,3 +1,17 @@ +2006-06-08 Sven de Marothy <[EMAIL PROTECTED]> + + * java/text/Bidi.java: Treat WS as neutral for rules N1 & N2. + * gnu/java/awt/peer/gtk/FreetypeGlyphVector.java + New constructor for bidirectionality. + (getGlyphMetrics): Return whitespace glyphs. + (getLogicalBounds): Offset rectangles to correct positions. + * gnu/java/awt/peer/gtk/GdkFontPeer.java + (getBaselineFor): Default to ROMAN_BASELINE. + (GdkFontLineMetrics): Guess some values for underline and + strikethrough. + (layoutGlyphVector): Use bidirectionality. + * java/awt/font/TextLayout.java: Implement, mostly. + 2006-06-09 Anthony Green <[EMAIL PROTECTED]> PR classpath/27888: Index: gnu/java/awt/peer/gtk/FreetypeGlyphVector.java =================================================================== RCS file: /sources/classpath/classpath/gnu/java/awt/peer/gtk/FreetypeGlyphVector.java,v retrieving revision 1.3 retrieving revision 1.4 diff -u -b -r1.3 -r1.4 --- gnu/java/awt/peer/gtk/FreetypeGlyphVector.java 7 Jun 2006 23:48:05 -0000 1.3 +++ gnu/java/awt/peer/gtk/FreetypeGlyphVector.java 9 Jun 2006 20:23:55 -0000 1.4 @@ -82,10 +82,25 @@ private AffineTransform[] glyphTransforms; /** + * Keep track of which glyphs are whitespace, since we don't have + * reporting from the peers yet. TextLayout needs this for justification. + */ + private boolean[] whiteSpace; + + /** * Create a glyphvector from a given (Freetype) font and a String. */ public FreetypeGlyphVector(Font f, String s, FontRenderContext frc) { + this(f, s, frc, Font.LAYOUT_LEFT_TO_RIGHT); + } + + /** + * Create a glyphvector from a given (Freetype) font and a String. + */ + public FreetypeGlyphVector(Font f, String s, FontRenderContext frc, + int flags) + { this.s = s; this.font = f; this.frc = frc; @@ -94,6 +109,14 @@ peer = (GdkFontPeer)font.getPeer(); getGlyphs(); + if( flags == Font.LAYOUT_RIGHT_TO_LEFT ) + { + // reverse the glyph ordering. + int[] temp = new int[ nGlyphs ]; + for(int i = 0; i < nGlyphs; i++) + temp[ i ] = glyphCodes[ nGlyphs - i - 1]; + glyphCodes = temp; + } performDefaultLayout(); } @@ -180,10 +203,12 @@ */ public void performDefaultLayout() { + whiteSpace = new boolean[ nGlyphs ]; glyphTransforms = new AffineTransform[ nGlyphs ]; double x = 0; for(int i = 0; i < nGlyphs; i++) { + whiteSpace[i] = Character.isWhitespace( glyphCodes[ i ] ); GlyphMetrics gm = getGlyphMetrics( i ); Rectangle2D r = gm.getBounds2D(); glyphTransforms[ i ] = AffineTransform.getTranslateInstance(x, 0); @@ -237,17 +262,20 @@ /** * Returns the metrics of a single glyph. + * FIXME: Not all glyph types are supported. */ public GlyphMetrics getGlyphMetrics(int glyphIndex) { double[] val = getMetricsNative( glyphCodes[ glyphIndex ] ); if( val == null ) return null; + byte type = whiteSpace[ glyphIndex ] ? + GlyphMetrics.WHITESPACE : GlyphMetrics.STANDARD; return new GlyphMetrics( true, (float)val[1], (float)val[2], new Rectangle2D.Double( val[3], val[4], val[5], val[6] ), - GlyphMetrics.STANDARD ); + type ); } /** @@ -319,7 +347,12 @@ Rectangle2D rect = (Rectangle2D)getGlyphLogicalBounds( 0 ); for( int i = 1; i < nGlyphs; i++ ) - rect = rect.createUnion( (Rectangle2D)getGlyphLogicalBounds( i ) ); + { + Rectangle2D r2 = (Rectangle2D)getGlyphLogicalBounds( i ); + Point2D p = getGlyphPosition( i ); + r2.setRect( p.getX(), p.getY(), r2.getWidth(), r2.getHeight() ); + rect = rect.createUnion( r2 ); + } return rect; } Index: gnu/java/awt/peer/gtk/GdkFontPeer.java =================================================================== RCS file: /sources/classpath/classpath/gnu/java/awt/peer/gtk/GdkFontPeer.java,v retrieving revision 1.15 retrieving revision 1.16 diff -u -b -r1.15 -r1.16 --- gnu/java/awt/peer/gtk/GdkFontPeer.java 7 Jun 2006 13:54:32 -0000 1.15 +++ gnu/java/awt/peer/gtk/GdkFontPeer.java 9 Jun 2006 20:23:55 -0000 1.16 @@ -252,18 +252,24 @@ public byte getBaselineFor (Font font, char c) { - throw new UnsupportedOperationException (); + // FIXME: Actually check. + return Font.ROMAN_BASELINE; } - protected class GdkFontLineMetrics extends LineMetrics + private static class GdkFontLineMetrics extends LineMetrics { - FontMetrics fm; - int nchars; + private FontMetrics fm; + private int nchars; + private float strikethroughOffset, strikethroughThickness, + underlineOffset, underlineThickness; - public GdkFontLineMetrics (FontMetrics m, int n) + public GdkFontLineMetrics (GdkFontPeer fp, FontMetrics m, int n) { fm = m; nchars = n; + strikethroughOffset = 0f; + strikethroughThickness = underlineThickness = ((float)fp.size) / 12f; + underlineOffset = 0f; } public float getAscent() @@ -273,6 +279,7 @@ public int getBaselineIndex() { + // FIXME return Font.ROMAN_BASELINE; } @@ -303,7 +310,7 @@ public LineMetrics getLineMetrics (Font font, CharacterIterator ci, int begin, int limit, FontRenderContext rc) { - return new GdkFontLineMetrics (getFontMetrics (font), limit - begin); + return new GdkFontLineMetrics (this, getFontMetrics (font), limit - begin); } public Rectangle2D getMaxCharBounds (Font font, FontRenderContext rc) @@ -350,20 +357,15 @@ char[] chars, int start, int limit, int flags) { - int nchars = (limit - start) + 1; - char[] nc = new char[nchars]; - - for (int i = 0; i < nchars; ++i) - nc[i] = chars[start + i]; - - return createGlyphVector (font, frc, - new StringCharacterIterator (new String (nc))); + return new FreetypeGlyphVector( font, new String( chars, start, + limit - start), + frc, flags); } public LineMetrics getLineMetrics (Font font, String str, FontRenderContext frc) { - return new GdkFontLineMetrics (getFontMetrics (font), str.length ()); + return new GdkFontLineMetrics (this, getFontMetrics (font), str.length ()); } public FontMetrics getFontMetrics (Font font) Index: java/awt/font/TextLayout.java =================================================================== RCS file: /sources/classpath/classpath/java/awt/font/TextLayout.java,v retrieving revision 1.7 retrieving revision 1.8 diff -u -b -r1.7 -r1.8 --- java/awt/font/TextLayout.java 2 Jul 2005 20:32:29 -0000 1.7 +++ java/awt/font/TextLayout.java 9 Jun 2006 20:23:55 -0000 1.8 @@ -1,5 +1,5 @@ /* TextLayout.java -- - Copyright (C) 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,8 +38,7 @@ package java.awt.font; -import gnu.java.awt.ClasspathToolkit; -import gnu.java.awt.peer.ClasspathTextLayoutPeer; +import gnu.classpath.NotImplementedException; import java.awt.Font; import java.awt.Graphics2D; @@ -47,116 +46,269 @@ import java.awt.Toolkit; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; import java.text.AttributedCharacterIterator; import java.text.AttributedString; +import java.text.Bidi; import java.util.Map; /** - * @author Michael Koch + * @author Sven de Marothy */ public final class TextLayout implements Cloneable { - public static final CaretPolicy DEFAULT_CARET_POLICY = new CaretPolicy (); - ClasspathTextLayoutPeer peer; + private GlyphVector[] runs; + private Font font; + private FontRenderContext frc; + private String string; + private Rectangle2D boundsCache; + private LineMetrics lm; + + /** + * Start and end character indices of the runs. + * First index is the run number, second is 0 or 1 for the starting + * and ending character index of the run, respectively. + */ + private int[][] runIndices; - public static class CaretPolicy - { - public CaretPolicy () - { - // Do nothing here. - } + /** + * Base directionality, determined from the first char. + */ + private boolean leftToRight; - public TextHitInfo getStrongCaret (TextHitInfo hit1, TextHitInfo hit2, - TextLayout layout) + /** + * Whether this layout contains whitespace or not. + */ + private boolean hasWhitespace = false; + + /** + * The default caret policy. + */ + static TextLayout.CaretPolicy DEFAULT_CARET_POLICY = new CaretPolicy(); + + /** + * Constructs a TextLayout. + */ + public TextLayout (String string, Font font, FontRenderContext frc) { - return layout.peer.getStrongCaret(hit1, hit2); + this.font = font; + this.frc = frc; + this.string = string; + lm = font.getLineMetrics(string, frc); + + // Get base direction and whitespace info + getStringProperties(); + + if( Bidi.requiresBidi( string.toCharArray(), 0, string.length() ) ) + { + Bidi bidi = new Bidi( string, leftToRight ? + Bidi.DIRECTION_LEFT_TO_RIGHT : + Bidi.DIRECTION_RIGHT_TO_LEFT ); + int rc = bidi.getRunCount(); + byte[] table = new byte[ rc ]; + for(int i = 0; i < table.length; i++) + table[i] = (byte)bidi.getRunLevel(i); + + runs = new GlyphVector[ rc ]; + runIndices = new int[rc][2]; + for(int i = 0; i < runs.length; i++) + { + runIndices[i][0] = bidi.getRunStart( i ); + runIndices[i][1] = bidi.getRunLimit( i ); + if( runIndices[i][0] != runIndices[i][1] ) // no empty runs. + { + runs[i] = font.layoutGlyphVector + ( frc, string.toCharArray(), + runIndices[i][0], runIndices[i][1], + ((table[i] & 1) == 0) ? Font.LAYOUT_LEFT_TO_RIGHT : + Font.LAYOUT_RIGHT_TO_LEFT ); + } + } + Bidi.reorderVisually( table, 0, runs, 0, runs.length ); + } + else + { + runs = new GlyphVector[ 1 ]; + runIndices = new int[1][2]; + runIndices[0][0] = 0; + runIndices[0][1] = string.length(); + runs[ 0 ] = font.layoutGlyphVector( frc, string.toCharArray(), + 0, string.length(), + leftToRight ? + Font.LAYOUT_LEFT_TO_RIGHT : + Font.LAYOUT_RIGHT_TO_LEFT ); } } - public TextLayout (AttributedCharacterIterator text, FontRenderContext frc) + public TextLayout (String string, Map attributes, FontRenderContext frc) { - AttributedString as = new AttributedString (text); - ClasspathToolkit tk = (ClasspathToolkit)(Toolkit.getDefaultToolkit ()); - peer = tk.getClasspathTextLayoutPeer(as, frc); + this( string, new Font( attributes ), frc ); } - public TextLayout (String string, Font font, FontRenderContext frc) + public TextLayout (AttributedCharacterIterator text, FontRenderContext frc) + throws NotImplementedException { - AttributedString as = new AttributedString (string); - as.addAttribute (TextAttribute.FONT, font); - ClasspathToolkit tk = (ClasspathToolkit)(Toolkit.getDefaultToolkit ()); - peer = tk.getClasspathTextLayoutPeer(as, frc); + throw new Error ("not implemented"); } - public TextLayout (String string, Map attributes, FontRenderContext frc) + /** + * Scan the character run for the first strongly directional character, + * which in turn defines the base directionality of the whole layout. + */ + private void getStringProperties() { - AttributedString as = new AttributedString (string, attributes); - ClasspathToolkit tk = (ClasspathToolkit)(Toolkit.getDefaultToolkit ()); - peer = tk.getClasspathTextLayoutPeer(as, frc); + boolean gotDirection = false; + int i = 0; + + leftToRight = true; + while( i < string.length() && !gotDirection ) + switch( Character.getDirectionality( string.charAt( i++ ) ) ) + { + case Character.DIRECTIONALITY_LEFT_TO_RIGHT: + case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING: + case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE: + gotDirection = true; + break; + + case Character.DIRECTIONALITY_RIGHT_TO_LEFT: + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC: + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING: + case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE: + leftToRight = false; + gotDirection = true; + break; + } + + // Determine if there's whitespace in the thing. + // Ignore trailing chars. + i = string.length() - 1; + hasWhitespace = false; + while( i >= 0 && Character.isWhitespace( string.charAt(i) ) ) + i--; + // Check the remaining chars + while( i >= 0 ) + if( Character.isWhitespace( string.charAt(i--) ) ) + hasWhitespace = true; } protected Object clone () { - try - { - TextLayout tl = (TextLayout) super.clone (); - tl.peer = (ClasspathTextLayoutPeer) this.peer.clone(); - return tl; + return new TextLayout( string, font, frc ); } - catch (CloneNotSupportedException e) - { - // This should never occur - throw new InternalError (); - } - } - public void draw (Graphics2D g2, float x, float y) { - peer.draw(g2, x, y); + for(int i = 0; i < runs.length; i++) + { + g2.drawGlyphVector(runs[i], x, y); + Rectangle2D r = runs[i].getLogicalBounds(); + x += r.getWidth(); + } } public boolean equals (Object obj) { - if (! (obj instanceof TextLayout)) + if( !( obj instanceof TextLayout) ) return false; - return equals ((TextLayout) obj); + return equals( (TextLayout) obj ); } public boolean equals (TextLayout tl) { - return this.peer.equals(tl.peer); + if( runs.length != tl.runs.length ) + return false; + // Compare all glyph vectors. + for( int i = 0; i < runs.length; i++ ) + if( !runs[i].equals( tl.runs[i] ) ) + return false; + return true; } public float getAdvance () { - return peer.getAdvance(); + float totalAdvance = 0f; + for(int i = 0; i < runs.length; i++) + totalAdvance += runs[i].getLogicalBounds().getWidth(); + return totalAdvance; } public float getAscent () { - return peer.getAscent(); + return lm.getAscent(); } public byte getBaseline () { - return peer.getBaseline(); + return (byte)lm.getBaselineIndex(); } public float[] getBaselineOffsets () { - return peer.getBaselineOffsets(); + return lm.getBaselineOffsets(); } public Shape getBlackBoxBounds (int firstEndpoint, int secondEndpoint) { - return peer.getBlackBoxBounds(firstEndpoint, secondEndpoint); + if( firstEndpoint < 0 || secondEndpoint > getCharacterCount() ) + return new Rectangle2D.Float(); + + GeneralPath gp = new GeneralPath(); + int i = 0; // run index + double advance = 0; + + // go to first run + while( runIndices[i + 1][1] < firstEndpoint ) + { + advance += runs[i].getLogicalBounds().getWidth(); + i++; + } + + int j = 0; // index into the run. + if( runIndices[i][1] - runIndices[i][0] > 1 ) + { + while( runs[i].getGlyphCharIndex( j + 1 ) < + (firstEndpoint - runIndices[i][0] ) )j++; + } + + gp.append(runs[i].getGlyphVisualBounds( j ), false); + boolean keepGoing = true;; + + do + { + while( j < runs[i].getNumGlyphs() && + runs[i].getGlyphCharIndex( j ) + runIndices[i][0] < + secondEndpoint ) + { + Rectangle2D r2 = (runs[i].getGlyphVisualBounds( j )). + getBounds2D(); + Point2D p = runs[i].getGlyphPosition( j ); + r2.setRect( advance + p.getX(), r2.getY(), + r2.getWidth(), r2.getHeight() ); + gp.append(r2, false); + j++; + } + + if( j >= runs[i].getNumGlyphs() ) + { + advance += runs[i].getLogicalBounds().getWidth(); + i++; + j = 0; + } + else + keepGoing = false; + } + while( keepGoing ); + + return gp; } public Rectangle2D getBounds() { - return peer.getBounds(); + if( boundsCache == null ) + boundsCache = getOutline(new AffineTransform()).getBounds(); + return boundsCache; } public float[] getCaretInfo (TextHitInfo hit) @@ -165,144 +317,272 @@ } public float[] getCaretInfo (TextHitInfo hit, Rectangle2D bounds) + throws NotImplementedException { - return peer.getCaretInfo(hit, bounds); + throw new Error ("not implemented"); } public Shape getCaretShape (TextHitInfo hit) { - return getCaretShape(hit, getBounds()); + return getCaretShape( hit, getBounds() ); } public Shape getCaretShape (TextHitInfo hit, Rectangle2D bounds) + throws NotImplementedException { - return peer.getCaretShape(hit, bounds); + throw new Error ("not implemented"); } public Shape[] getCaretShapes (int offset) { - return getCaretShapes(offset, getBounds()); + return getCaretShapes( offset, getBounds() ); } public Shape[] getCaretShapes (int offset, Rectangle2D bounds) + throws NotImplementedException { - return getCaretShapes(offset, getBounds(), DEFAULT_CARET_POLICY); - } - - public Shape[] getCaretShapes (int offset, Rectangle2D bounds, - TextLayout.CaretPolicy policy) - { - return peer.getCaretShapes(offset, bounds, policy); + throw new Error ("not implemented"); } public int getCharacterCount () { - return peer.getCharacterCount(); + return string.length(); } public byte getCharacterLevel (int index) + throws NotImplementedException { - return peer.getCharacterLevel(index); + throw new Error ("not implemented"); } public float getDescent () { - return peer.getDescent(); + return lm.getDescent(); } public TextLayout getJustifiedLayout (float justificationWidth) { - return peer.getJustifiedLayout(justificationWidth); + TextLayout newLayout = (TextLayout)clone(); + + if( hasWhitespace ) + newLayout.handleJustify( justificationWidth ); + + return newLayout; } public float getLeading () { - return peer.getLeading(); + return lm.getLeading(); } public Shape getLogicalHighlightShape (int firstEndpoint, int secondEndpoint) { - return getLogicalHighlightShape (firstEndpoint, secondEndpoint, getBounds()); + return getLogicalHighlightShape( firstEndpoint, secondEndpoint, + getBounds() ); } public Shape getLogicalHighlightShape (int firstEndpoint, int secondEndpoint, Rectangle2D bounds) { - return peer.getLogicalHighlightShape(firstEndpoint, secondEndpoint, bounds); + if( firstEndpoint < 0 || secondEndpoint > getCharacterCount() ) + return new Rectangle2D.Float(); + + int i = 0; // run index + double advance = 0; + + // go to first run + while( runIndices[i + 1][1] < firstEndpoint ) + { + advance += runs[i].getLogicalBounds().getWidth(); + i++; } - public int[] getLogicalRangesForVisualSelection (TextHitInfo firstEndpoint, - TextHitInfo secondEndpoint) + int j = 0; // index into the run. + if( runIndices[i][1] - runIndices[i][0] > 1 ) { - return peer.getLogicalRangesForVisualSelection(firstEndpoint, secondEndpoint); + while( runs[i].getGlyphCharIndex( j + 1 ) < + (firstEndpoint - runIndices[i][0] ) )j++; } - public TextHitInfo getNextLeftHit (int offset) + Rectangle2D r = (runs[i].getGlyphLogicalBounds( j )).getBounds2D(); + boolean keepGoing = true;; + + do + { + while( j < runs[i].getNumGlyphs() && + runs[i].getGlyphCharIndex( j ) + runIndices[i][0] < + secondEndpoint ) { - return getNextLeftHit(offset, DEFAULT_CARET_POLICY); + Rectangle2D r2 = (runs[i].getGlyphLogicalBounds( j )). + getBounds2D(); + Point2D p = runs[i].getGlyphPosition( j ); + r2.setRect( advance + p.getX(), r2.getY(), + r2.getWidth(), r2.getHeight() ); + r = r.createUnion( r2 ); + j++; } - public TextHitInfo getNextLeftHit (int offset, TextLayout.CaretPolicy policy) + if( j >= runs[i].getNumGlyphs() ) { - return peer.getNextLeftHit(offset, policy); + advance += runs[i].getLogicalBounds().getWidth(); + i++; + j = 0; } + else + keepGoing = false; + } + while( keepGoing ); - public TextHitInfo getNextLeftHit (TextHitInfo hit) + return r; + } + + public int[] getLogicalRangesForVisualSelection (TextHitInfo firstEndpoint, + TextHitInfo secondEndpoint) + throws NotImplementedException { - return getNextLeftHit(hit.getCharIndex()); + throw new Error ("not implemented"); } - public TextHitInfo getNextRightHit (int offset) + public TextHitInfo getNextLeftHit (int offset) + throws NotImplementedException { - return getNextRightHit(offset, DEFAULT_CARET_POLICY); + throw new Error ("not implemented"); } - public TextHitInfo getNextRightHit (int offset, TextLayout.CaretPolicy policy) + public TextHitInfo getNextLeftHit (TextHitInfo hit) + throws NotImplementedException { - return peer.getNextRightHit(offset, policy); + throw new Error ("not implemented"); + } + + public TextHitInfo getNextRightHit (int offset) + throws NotImplementedException + { + throw new Error ("not implemented"); } public TextHitInfo getNextRightHit (TextHitInfo hit) + throws NotImplementedException { - return getNextRightHit(hit.getCharIndex()); + throw new Error ("not implemented"); } public Shape getOutline (AffineTransform tx) { - return peer.getOutline(tx); + float x = 0f; + GeneralPath gp = new GeneralPath(); + for(int i = 0; i < runs.length; i++) + { + gp.append( runs[i].getOutline( x, 0f ), false ); + Rectangle2D r = runs[i].getLogicalBounds(); + x += r.getWidth(); + } + gp.transform( tx ); + return gp; } public float getVisibleAdvance () { - return peer.getVisibleAdvance(); + float totalAdvance = 0f; + + if( runs.length <= 0 ) + return 0f; + + // No trailing whitespace + if( !Character.isWhitespace( string.charAt( string.length() -1 ) ) ) + return getAdvance(); + + // Get length of all runs up to the last + for(int i = 0; i < runs.length - 1; i++) + totalAdvance += runs[i].getLogicalBounds().getWidth(); + + int lastRun = runIndices[ runs.length - 1 ][0]; + int j = string.length() - 1; + while( j >= lastRun && Character.isWhitespace( string.charAt( j ) ) ) j--; + + if( j < lastRun ) + return totalAdvance; // entire last run is whitespace + + int lastNonWSChar = j - lastRun; + j = 0; + while( runs[ runs.length - 1 ].getGlyphCharIndex( j ) + <= lastNonWSChar ) + { + totalAdvance += runs[ runs.length - 1 ].getGlyphLogicalBounds( j ). + getBounds2D().getWidth(); + j ++; + } + + return totalAdvance; } public Shape getVisualHighlightShape (TextHitInfo firstEndpoint, TextHitInfo secondEndpoint) { - return getVisualHighlightShape(firstEndpoint, secondEndpoint, getBounds()); + return getVisualHighlightShape( firstEndpoint, secondEndpoint, + getBounds() ); } public Shape getVisualHighlightShape (TextHitInfo firstEndpoint, TextHitInfo secondEndpoint, Rectangle2D bounds) + throws NotImplementedException { - return peer.getVisualHighlightShape(firstEndpoint, secondEndpoint, bounds); + throw new Error ("not implemented"); } public TextHitInfo getVisualOtherHit (TextHitInfo hit) + throws NotImplementedException { - return peer.getVisualOtherHit(hit); + throw new Error ("not implemented"); } + /** + * This is a protected method of a <code>final</code> class, meaning + * it exists only to taunt you. + */ protected void handleJustify (float justificationWidth) { - peer.handleJustify(justificationWidth); + // We assume that the text has non-trailing whitespace. + // First get the change in width to insert into the whitespaces. + double deltaW = justificationWidth - getVisibleAdvance(); + int nglyphs = 0; // # of whitespace chars + + // determine last non-whitespace char. + int lastNWS = string.length() - 1; + while( Character.isWhitespace( string.charAt( lastNWS ) ) ) lastNWS--; + + // locations of the glyphs. + int[] wsglyphs = new int[string.length() * 10]; + for(int run = 0; run < runs.length; run++ ) + for(int i = 0; i < runs[run].getNumGlyphs(); i++ ) + { + int cindex = runIndices[run][0] + runs[run].getGlyphCharIndex( i ); + if( Character.isWhitespace( string.charAt( cindex ) ) ) + // && cindex < lastNWS ) + { + wsglyphs[ nglyphs * 2 ] = run; + wsglyphs[ nglyphs * 2 + 1] = i; + nglyphs++; + } } - public int hashCode () + deltaW = deltaW / nglyphs; // Change in width per whitespace glyph + double w = 0; + int cws = 0; + // Shift all characters + for(int run = 0; run < runs.length; run++ ) + for(int i = 0; i < runs[ run ].getNumGlyphs(); i++ ) { - return peer.hashCode(); + if( wsglyphs[ cws * 2 ] == run && wsglyphs[ cws * 2 + 1 ] == i ) + { + cws++; // update 'current whitespace' + w += deltaW; // increment the shift + } + Point2D p = runs[ run ].getGlyphPosition( i ); + p.setLocation( p.getX() + w, p.getY() ); + runs[ run ].setGlyphPosition( i, p ); + } } public TextHitInfo hitTestChar (float x, float y) @@ -312,21 +592,48 @@ public TextHitInfo hitTestChar (float x, float y, Rectangle2D bounds) { - return peer.hitTestChar(x, y, bounds); + return hitTestChar( x, y, getBounds() ); } public boolean isLeftToRight () { - return peer.isLeftToRight(); + return leftToRight; } public boolean isVertical () { - return peer.isVertical(); + return false; // FIXME: How do you create a vertical layout? + } + + public int hashCode () + throws NotImplementedException + { + throw new Error ("not implemented"); } public String toString () { - return peer.toString(); + return "TextLayout [string:"+string+", Font:"+font+" Rendercontext:"+ + frc+"]"; + } + + /** + * Inner class describing a caret policy + */ + public static class CaretPolicy + { + public CaretPolicy() + { + } + + public TextHitInfo getStrongCaret(TextHitInfo hit1, + TextHitInfo hit2, + TextLayout layout) + throws NotImplementedException + { + throw new Error ("not implemented"); + } } } + + Index: java/text/Bidi.java =================================================================== RCS file: /sources/classpath/classpath/java/text/Bidi.java,v retrieving revision 1.5 retrieving revision 1.6 diff -u -b -r1.5 -r1.6 --- java/text/Bidi.java 8 Jun 2006 23:32:13 -0000 1.5 +++ java/text/Bidi.java 9 Jun 2006 20:23:55 -0000 1.6 @@ -644,6 +644,7 @@ case Character.DIRECTIONALITY_OTHER_NEUTRALS: case Character.DIRECTIONALITY_SEGMENT_SEPARATOR: case Character.DIRECTIONALITY_PARAGRAPH_SEPARATOR: + case Character.DIRECTIONALITY_WHITESPACE: if (neutralStart == -1) neutralStart = i; break;