diff options
Diffstat (limited to 'libjava/classpath/java/awt/font')
-rw-r--r-- | libjava/classpath/java/awt/font/FontRenderContext.java | 10 | ||||
-rw-r--r-- | libjava/classpath/java/awt/font/LineBreakMeasurer.java | 107 | ||||
-rw-r--r-- | libjava/classpath/java/awt/font/TextLayout.java | 232 | ||||
-rw-r--r-- | libjava/classpath/java/awt/font/TextMeasurer.java | 149 |
4 files changed, 309 insertions, 189 deletions
diff --git a/libjava/classpath/java/awt/font/FontRenderContext.java b/libjava/classpath/java/awt/font/FontRenderContext.java index 78564a647da..c50e5e5092a 100644 --- a/libjava/classpath/java/awt/font/FontRenderContext.java +++ b/libjava/classpath/java/awt/font/FontRenderContext.java @@ -83,7 +83,15 @@ public class FontRenderContext public boolean equals (FontRenderContext rhs) { - return (affineTransform.equals (rhs.getTransform ()) + if (rhs == null) + return false; + + if (affineTransform == null && rhs.affineTransform != null + || affineTransform != null && rhs.affineTransform == null) + return false; + + return ((affineTransform == rhs.affineTransform + || affineTransform.equals (rhs.getTransform ())) && isAntiAliased == rhs.isAntiAliased () && usesFractionalMetrics == rhs.usesFractionalMetrics ()); } diff --git a/libjava/classpath/java/awt/font/LineBreakMeasurer.java b/libjava/classpath/java/awt/font/LineBreakMeasurer.java index c2a6d45d9f5..816c7745c2b 100644 --- a/libjava/classpath/java/awt/font/LineBreakMeasurer.java +++ b/libjava/classpath/java/awt/font/LineBreakMeasurer.java @@ -41,57 +41,41 @@ package java.awt.font; import java.text.AttributedCharacterIterator; import java.text.AttributedString; import java.text.BreakIterator; -import java.awt.font.TextLayout; -import java.awt.font.FontRenderContext; import java.awt.Shape; public final class LineBreakMeasurer { private AttributedCharacterIterator text; private int position; - private FontRenderContext frc; - private TextLayout totalLayout; + private TextMeasurer tm; private int numChars; public LineBreakMeasurer(AttributedCharacterIterator text, BreakIterator breakIter, FontRenderContext frc) { - this.text = text; - this.frc = frc; - position = 0; - totalLayout = new TextLayout(text, frc); - numChars = totalLayout.getCharacterCount(); + this( text, frc ); } public LineBreakMeasurer(AttributedCharacterIterator text, FontRenderContext frc) { this.text = text; - this.frc = frc; position = 0; - totalLayout = new TextLayout(text, frc); - numChars = totalLayout.getCharacterCount(); + numChars = text.getEndIndex(); + tm = new TextMeasurer( text, frc ); } public void deleteChar(AttributedCharacterIterator newParagraph, int deletePos) { - totalLayout = new TextLayout(newParagraph, frc); - if( deletePos < 0 || deletePos > totalLayout.getCharacterCount() ) - throw new NullPointerException("Invalid deletePos:"+deletePos); - numChars = totalLayout.getCharacterCount(); - text = newParagraph; + tm.deleteChar( newParagraph, deletePos ); position = 0; } public void insertChar(AttributedCharacterIterator newParagraph, int insertPos) { - totalLayout = new TextLayout(newParagraph, frc); - if( insertPos < 0 || insertPos > totalLayout.getCharacterCount() ) - throw new NullPointerException("Invalid insertPos:"+insertPos); - numChars = totalLayout.getCharacterCount(); - text = newParagraph; + tm.insertChar( newParagraph, insertPos ); position = 0; } @@ -104,11 +88,9 @@ public final class LineBreakMeasurer boolean requireNextWord) { int next = nextOffset( wrappingWidth, offsetLimit, requireNextWord ); - AttributedCharacterIterator aci = (new AttributedString( text, - position, next ) - ).getIterator(); + TextLayout tl = tm.getLayout( position, next ); position = next; - return new TextLayout( aci, frc ); + return tl; } public int nextOffset(float wrappingWidth) @@ -119,69 +101,40 @@ public final class LineBreakMeasurer public int nextOffset(float wrappingWidth, int offsetLimit, boolean requireNextWord) { - Shape s = totalLayout.getBlackBoxBounds( position, offsetLimit ); - double remainingLength = s.getBounds2D().getWidth(); + int guessOffset = tm.getLineBreakIndex(position, wrappingWidth); + if( offsetLimit > numChars ) + offsetLimit = numChars; - int guessOffset = (int)( ( (double)wrappingWidth / (double)remainingLength) - * ( (double)numChars - (double)position ) ); - guessOffset += position; if( guessOffset > offsetLimit ) - guessOffset = offsetLimit; - - s = totalLayout.getBlackBoxBounds( position, guessOffset ); - double guessLength = s.getBounds2D().getWidth(); - - boolean makeSmaller = ( guessLength > wrappingWidth ); - int inc = makeSmaller ? -1 : 1; - boolean keepGoing = true; - - do { - guessOffset = guessOffset + inc; - if( guessOffset <= position || guessOffset > offsetLimit ) - { - keepGoing = false; - } - else - { - s = totalLayout.getBlackBoxBounds( position, guessOffset ); - guessLength = s.getBounds2D().getWidth(); - if( makeSmaller && ( guessLength <= wrappingWidth) ) - keepGoing = false; - if( !makeSmaller && ( guessLength >= wrappingWidth) ) - keepGoing = false; - } + text.setIndex( offsetLimit ); + return offsetLimit; } - while( keepGoing ); - if( !makeSmaller ) - guessOffset--; + text.setIndex( guessOffset ); - if( guessOffset >= offsetLimit ) - return offsetLimit; + // If we're on a breaking character, return directly + if( Character.isWhitespace( text.current() ) ) + return guessOffset; - text.setIndex( guessOffset ); + // Otherwise jump forward or backward to the last such char. if( !requireNextWord ) - { - char c = text.previous(); - while( !Character.isWhitespace( c ) && c != '-' && - guessOffset > position ) - { - guessOffset--; - c = text.previous(); - } - } + while( !Character.isWhitespace( text.previous() ) && + guessOffset > position ) + guessOffset--; else + while( !Character.isWhitespace( text.next() ) && + guessOffset < offsetLimit ) + guessOffset++; + + if( guessOffset > offsetLimit ) { - char c = text.next(); - while( !Character.isWhitespace( c ) && c != '-' && - guessOffset < offsetLimit ) - { - guessOffset++; - c = text.next(); - } + text.setIndex( offsetLimit ); + return offsetLimit; } + text.setIndex( guessOffset ); + return guessOffset; } diff --git a/libjava/classpath/java/awt/font/TextLayout.java b/libjava/classpath/java/awt/font/TextLayout.java index 4f8c1c644c1..b1473f25564 100644 --- a/libjava/classpath/java/awt/font/TextLayout.java +++ b/libjava/classpath/java/awt/font/TextLayout.java @@ -43,13 +43,12 @@ import gnu.classpath.NotImplementedException; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Shape; -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.CharacterIterator; import java.text.AttributedCharacterIterator; -import java.text.AttributedString; import java.text.Bidi; import java.util.Map; @@ -73,6 +72,12 @@ public final class TextLayout implements Cloneable private int[][] runIndices; /** + * Character indices. + * Fixt index is the glyphvector, second index is the (first) glyph. + */ + private int[][] charIndices; + + /** * Base directionality, determined from the first char. */ private boolean leftToRight; @@ -85,7 +90,7 @@ public final class TextLayout implements Cloneable /** * The default caret policy. */ - static TextLayout.CaretPolicy DEFAULT_CARET_POLICY = new CaretPolicy(); + public static final TextLayout.CaretPolicy DEFAULT_CARET_POLICY = new CaretPolicy(); /** * Constructs a TextLayout. @@ -139,6 +144,7 @@ public final class TextLayout implements Cloneable Font.LAYOUT_LEFT_TO_RIGHT : Font.LAYOUT_RIGHT_TO_LEFT ); } + setCharIndices(); } public TextLayout (String string, Map attributes, FontRenderContext frc) @@ -147,9 +153,97 @@ public final class TextLayout implements Cloneable } public TextLayout (AttributedCharacterIterator text, FontRenderContext frc) - throws NotImplementedException { - throw new Error ("not implemented"); + // FIXME: Very rudimentary. + this(getText(text), getFont(text), frc); + } + + /** + * Package-private constructor to make a textlayout from an existing one. + * This is used by TextMeasurer for returning sub-layouts, and it + * saves a lot of time in not having to relayout the text. + */ + TextLayout(TextLayout t, int startIndex, int endIndex) + { + font = t.font; + frc = t.frc; + boundsCache = null; + lm = t.lm; + leftToRight = t.leftToRight; + + if( endIndex > t.getCharacterCount() ) + endIndex = t.getCharacterCount(); + string = t.string.substring( startIndex, endIndex ); + + int startingRun = t.charIndices[startIndex][0]; + int nRuns = 1 + t.charIndices[endIndex - 1][0] - startingRun; + runIndices = new int[ nRuns ][2]; + + runs = new GlyphVector[ nRuns ]; + for( int i = 0; i < nRuns; i++ ) + { + GlyphVector run = t.runs[ i + startingRun ]; + // Copy only the relevant parts of the first and last runs. + int beginGlyphIndex = (i > 0) ? 0 : t.charIndices[startIndex][1]; + int numEntries = ( i < nRuns - 1) ? run.getNumGlyphs() : + 1 + t.charIndices[endIndex - 1][1] - beginGlyphIndex; + + int[] codes = run.getGlyphCodes(beginGlyphIndex, numEntries, null); + runs[ i ] = font.createGlyphVector( frc, codes ); + runIndices[ i ][0] = t.runIndices[i + startingRun][0] - startIndex; + runIndices[ i ][1] = t.runIndices[i + startingRun][1] - startIndex; + } + runIndices[ nRuns - 1 ][1] = endIndex - 1; + + setCharIndices(); + determineWhiteSpace(); + } + + private void setCharIndices() + { + charIndices = new int[ getCharacterCount() ][2]; + int i = 0; + int currentChar = 0; + for(int run = 0; run < runs.length; run++) + { + currentChar = -1; + for( int gi = 0; gi < runs[ run ].getNumGlyphs(); gi++) + { + if( runs[ run ].getGlyphCharIndex( gi ) != currentChar ) + { + charIndices[ i ][0] = run; + charIndices[ i ][1] = gi; + currentChar = runs[ run ].getGlyphCharIndex( gi ); + i++; + } + } + } + } + + private static String getText(AttributedCharacterIterator iter) + { + StringBuffer sb = new StringBuffer(); + int idx = iter.getIndex(); + for(char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) + sb.append(c); + iter.setIndex( idx ); + return sb.toString(); + } + + private static Font getFont(AttributedCharacterIterator iter) + { + Font f = (Font)iter.getAttribute(TextAttribute.FONT); + if( f == null ) + { + int size; + Float i = (Float)iter.getAttribute(TextAttribute.SIZE); + if( i != null ) + size = (int)i.floatValue(); + else + size = 14; + f = new Font("Dialog", Font.PLAIN, size ); + } + return f; } /** @@ -179,10 +273,14 @@ public final class TextLayout implements Cloneable gotDirection = true; break; } + determineWhiteSpace(); + } + private void determineWhiteSpace() + { // Determine if there's whitespace in the thing. // Ignore trailing chars. - i = string.length() - 1; + int i = string.length() - 1; hasWhitespace = false; while( i >= 0 && Character.isWhitespace( string.charAt(i) ) ) i--; @@ -251,56 +349,42 @@ public final class TextLayout implements Cloneable public Shape getBlackBoxBounds (int firstEndpoint, int secondEndpoint) { - if( firstEndpoint < 0 || secondEndpoint > getCharacterCount() ) + if( secondEndpoint - firstEndpoint <= 0 ) + return new Rectangle2D.Float(); // Hmm? + + 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 ri = charIndices[ firstEndpoint ][0]; + int gi = charIndices[ firstEndpoint ][1]; - int j = 0; // index into the run. - if( runIndices[i][1] - runIndices[i][0] > 1 ) + double advance = 0; + + for( int i = 0; i < ri; i++ ) + advance += runs[i].getLogicalBounds().getWidth(); + + for( int i = ri; i <= charIndices[ secondEndpoint - 1 ][0]; i++ ) { - while( runs[i].getGlyphCharIndex( j + 1 ) < - (firstEndpoint - runIndices[i][0] ) )j++; - } - - gp.append(runs[i].getGlyphVisualBounds( j ), false); - boolean keepGoing = true;; + int dg; + if( i == charIndices[ secondEndpoint - 1 ][0] ) + dg = charIndices[ secondEndpoint - 1][1]; + else + dg = runs[i].getNumGlyphs() - 1; - do - { - while( j < runs[i].getNumGlyphs() && - runs[i].getGlyphCharIndex( j ) + runIndices[i][0] < - secondEndpoint ) + for( int j = 0; j <= dg; j++ ) { Rectangle2D r2 = (runs[i].getGlyphVisualBounds( j )). getBounds2D(); Point2D p = runs[i].getGlyphPosition( j ); - r2.setRect( advance + p.getX(), r2.getY(), + r2.setRect( advance + r2.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; + advance += runs[i].getLogicalBounds().getWidth(); } - while( keepGoing ); - return gp; } @@ -384,55 +468,42 @@ public final class TextLayout implements Cloneable public Shape getLogicalHighlightShape (int firstEndpoint, int secondEndpoint, Rectangle2D bounds) { - if( firstEndpoint < 0 || secondEndpoint > getCharacterCount() ) + if( secondEndpoint - firstEndpoint <= 0 ) + return new Rectangle2D.Float(); // Hmm? + + if( firstEndpoint < 0 || secondEndpoint > getCharacterCount()) return new Rectangle2D.Float(); - int i = 0; // run index - double advance = 0; + Rectangle2D r = null; + int ri = charIndices[ firstEndpoint ][0]; + int gi = charIndices[ firstEndpoint ][1]; - // go to first run - if( i > 0 ) - while( runIndices[i + 1][1] < firstEndpoint ) - { - advance += runs[i].getLogicalBounds().getWidth(); - i++; - } + double advance = 0; + + for( int i = 0; i < ri; i++ ) + advance += runs[i].getLogicalBounds().getWidth(); - int j = 0; // index into the run. - if( runIndices[i][1] - runIndices[i][0] > 1 ) + for( int i = ri; i <= charIndices[ secondEndpoint - 1 ][0]; i++ ) { - while( runs[i].getGlyphCharIndex( j + 1 ) < - (firstEndpoint - runIndices[i][0] ) )j++; - } - - Rectangle2D r = (runs[i].getGlyphLogicalBounds( j )).getBounds2D(); - boolean keepGoing = true;; + int dg; // last index in this run to use. + if( i == charIndices[ secondEndpoint - 1 ][0] ) + dg = charIndices[ secondEndpoint - 1][1]; + else + dg = runs[i].getNumGlyphs() - 1; - do - { - while( j < runs[i].getNumGlyphs() && - runs[i].getGlyphCharIndex( j ) + runIndices[i][0] < - secondEndpoint ) + for(; gi <= dg; gi++ ) { - Rectangle2D r2 = (runs[i].getGlyphLogicalBounds( j )). + Rectangle2D r2 = (runs[i].getGlyphLogicalBounds( gi )). getBounds2D(); - Point2D p = runs[i].getGlyphPosition( j ); - r2.setRect( advance + p.getX(), r2.getY(), - r2.getWidth(), r2.getHeight() ); - r = r.createUnion( r2 ); - j++; + if( r == null ) + r = r2; + else + r = r.createUnion(r2); } + gi = 0; // reset glyph index into run for next run. - if( j >= runs[i].getNumGlyphs() ) - { - advance += runs[i].getLogicalBounds().getWidth(); - i++; - j = 0; - } - else - keepGoing = false; + advance += runs[i].getLogicalBounds().getWidth(); } - while( keepGoing ); return r; } @@ -593,8 +664,9 @@ public final class TextLayout implements Cloneable } public TextHitInfo hitTestChar (float x, float y, Rectangle2D bounds) + throws NotImplementedException { - return hitTestChar( x, y, getBounds() ); + throw new Error ("not implemented"); } public boolean isLeftToRight () diff --git a/libjava/classpath/java/awt/font/TextMeasurer.java b/libjava/classpath/java/awt/font/TextMeasurer.java index 18c286c57c1..00cab8a878d 100644 --- a/libjava/classpath/java/awt/font/TextMeasurer.java +++ b/libjava/classpath/java/awt/font/TextMeasurer.java @@ -1,5 +1,5 @@ /* TextMeasurer.java - Copyright (C) 2003 Free Software Foundation, Inc. + Copyright (C) 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,67 +38,154 @@ exception statement from your version. */ package java.awt.font; -import gnu.classpath.NotImplementedException; - import java.text.AttributedCharacterIterator; +import java.text.AttributedString; +import java.awt.Shape; /** - * @author Michael Koch + * TextMeasurer is a small utility class for measuring the length of laid-out + * text objects. + * + * @author Sven de Marothy * @since 1.3 */ public final class TextMeasurer implements Cloneable { - private AttributedCharacterIterator ci; + private AttributedCharacterIterator text; private FontRenderContext frc; - + private TextLayout totalLayout; + private int numChars; + + /** + * Creates a TextMeasurer from a given text in the form of an + * <code>AttributedCharacterIterator</code> and a + * <code>FontRenderContext</code>. + */ public TextMeasurer (AttributedCharacterIterator text, FontRenderContext frc) { - this.ci = text; + this.text = text; this.frc = frc; + totalLayout = new TextLayout( text, frc ); + numChars = totalLayout.getCharacterCount(); } + /** + * Clones the TextMeasurer object + */ protected Object clone () { - try - { - return super.clone (); - } - catch (CloneNotSupportedException e) - { - // This may never occur - throw new InternalError (); - } + return new TextMeasurer( text, frc ); } + /** + * Update the text if a character is deleted at the position deletePos + * @param newParagraph - the updated paragraph. + * @param deletePos - the deletion position + */ public void deleteChar (AttributedCharacterIterator newParagraph, int deletePos) - throws NotImplementedException { - throw new Error ("not implemented"); + totalLayout = new TextLayout(newParagraph, frc); + if( deletePos < 0 || deletePos > totalLayout.getCharacterCount() ) + throw new NullPointerException("Invalid deletePos:"+deletePos); + numChars = totalLayout.getCharacterCount(); + text = newParagraph; + } + + /** + * Update the text if a character is inserted at the position insertPos + * @param newParagraph - the updated paragraph. + * @param insertPos - the insertion position + */ + public void insertChar (AttributedCharacterIterator newParagraph, + int insertPos) + { + totalLayout = new TextLayout(newParagraph, frc); + if( insertPos < 0 || insertPos > totalLayout.getCharacterCount() ) + throw new NullPointerException("Invalid insertPos:"+insertPos); + numChars = totalLayout.getCharacterCount(); + text = newParagraph; } + /*** + * Returns the total advance between two positions in the paragraph. + * Characters from start to limit-1 (inclusive) are included in this count. + * + * @param start - the starting character index. + * @param limit - the limiting index. + */ public float getAdvanceBetween (int start, int limit) - throws NotImplementedException { - throw new Error ("not implemented"); + Shape s = totalLayout.getLogicalHighlightShape( start, limit ); + return (float)s.getBounds2D().getWidth(); } + /** + * Returns a <code>TextLayout</code> object corresponding to the characters + * from text to limit. + * @param start - the starting character index. + * @param limit - the limiting index. + */ public TextLayout getLayout (int start, int limit) - throws NotImplementedException { - throw new Error ("not implemented"); + if( start >= limit ) + throw new IllegalArgumentException("Start position must be < limit."); + return new TextLayout( totalLayout, start, limit ); } + /** + * Returns the line-break index from a given starting index and a maximum + * advance. The index returned is the first character outside the given + * advance (or the limit of the string, if all remaining characters fit.) + * + * @param start - the starting index. + * @param maxAdvance - the maximum advance allowed. + * @return the index of the first character beyond maxAdvance, or the + * index of the last character + 1. + */ public int getLineBreakIndex (int start, float maxAdvance) - throws NotImplementedException - { - throw new Error ("not implemented"); - } + { + if( start < 0 ) + throw new IllegalArgumentException("Start parameter must be > 0."); + + double remainingLength = getAdvanceBetween( start, numChars ); + + int guessOffset = (int)( ( (double)maxAdvance / (double)remainingLength) + * ( (double)numChars - (double)start ) ); + guessOffset += start; + if( guessOffset > numChars ) + guessOffset = numChars; + + double guessLength = getAdvanceBetween( start, guessOffset ); + boolean makeSmaller = ( guessLength > maxAdvance ); + int inc = makeSmaller ? -1 : 1; + boolean keepGoing = true; + + do + { + guessOffset = guessOffset + inc; + if( guessOffset <= start || guessOffset > numChars ) + { + keepGoing = false; + } + else + { + guessLength = getAdvanceBetween( start, guessOffset ); + if( makeSmaller && ( guessLength <= maxAdvance) ) + keepGoing = false; + if( !makeSmaller && ( guessLength >= maxAdvance) ) + keepGoing = false; + } + } + while( keepGoing ); - public void insertChar (AttributedCharacterIterator newParagraph, - int insertPos) - throws NotImplementedException - { - throw new Error ("not implemented"); + // Return first index that doesn't fit. + if( !makeSmaller ) + guessOffset--; + + if( guessOffset > numChars ) + return numChars; + + return guessOffset; } } |