summaryrefslogtreecommitdiff
path: root/libjava/classpath/java/awt/font
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/java/awt/font')
-rw-r--r--libjava/classpath/java/awt/font/FontRenderContext.java10
-rw-r--r--libjava/classpath/java/awt/font/LineBreakMeasurer.java107
-rw-r--r--libjava/classpath/java/awt/font/TextLayout.java232
-rw-r--r--libjava/classpath/java/awt/font/TextMeasurer.java149
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;
}
}