001 /* TextMeasurer.java
002 Copyright (C) 2006 Free Software Foundation, Inc.
003
004 This file is part of GNU Classpath.
005
006 GNU Classpath is free software; you can redistribute it and/or modify
007 it under the terms of the GNU General Public License as published by
008 the Free Software Foundation; either version 2, or (at your option)
009 any later version.
010
011 GNU Classpath is distributed in the hope that it will be useful, but
012 WITHOUT ANY WARRANTY; without even the implied warranty of
013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 General Public License for more details.
015
016 You should have received a copy of the GNU General Public License
017 along with GNU Classpath; see the file COPYING. If not, write to the
018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019 02110-1301 USA.
020
021 Linking this library statically or dynamically with other modules is
022 making a combined work based on this library. Thus, the terms and
023 conditions of the GNU General Public License cover the whole
024 combination.
025
026 As a special exception, the copyright holders of this library give you
027 permission to link this library with independent modules to produce an
028 executable, regardless of the license terms of these independent
029 modules, and to copy and distribute the resulting executable under
030 terms of your choice, provided that you also meet, for each linked
031 independent module, the terms and conditions of the license of that
032 module. An independent module is a module which is not derived from
033 or based on this library. If you modify this library, you may extend
034 this exception to your version of the library, but you are not
035 obligated to do so. If you do not wish to do so, delete this
036 exception statement from your version. */
037
038
039 package java.awt.font;
040
041 import java.text.AttributedCharacterIterator;
042 import java.text.AttributedString;
043 import java.awt.Shape;
044
045 /**
046 * TextMeasurer is a small utility class for measuring the length of laid-out
047 * text objects.
048 *
049 * @author Sven de Marothy
050 * @since 1.3
051 */
052 public final class TextMeasurer implements Cloneable
053 {
054 private AttributedCharacterIterator text;
055 private FontRenderContext frc;
056 private TextLayout totalLayout;
057 private int numChars;
058
059 /**
060 * Creates a TextMeasurer from a given text in the form of an
061 * <code>AttributedCharacterIterator</code> and a
062 * <code>FontRenderContext</code>.
063 */
064 public TextMeasurer (AttributedCharacterIterator text, FontRenderContext frc)
065 {
066 this.text = text;
067 this.frc = frc;
068 totalLayout = new TextLayout( text, frc );
069 numChars = totalLayout.getCharacterCount();
070 }
071
072 /**
073 * Clones the TextMeasurer object
074 */
075 protected Object clone ()
076 {
077 return new TextMeasurer( text, frc );
078 }
079
080 /**
081 * Update the text if a character is deleted at the position deletePos
082 * @param newParagraph - the updated paragraph.
083 * @param deletePos - the deletion position
084 */
085 public void deleteChar (AttributedCharacterIterator newParagraph,
086 int deletePos)
087 {
088 totalLayout = new TextLayout(newParagraph, frc);
089 if( deletePos < 0 || deletePos > totalLayout.getCharacterCount() )
090 throw new NullPointerException("Invalid deletePos:"+deletePos);
091 numChars = totalLayout.getCharacterCount();
092 text = newParagraph;
093 }
094
095 /**
096 * Update the text if a character is inserted at the position insertPos
097 * @param newParagraph - the updated paragraph.
098 * @param insertPos - the insertion position
099 */
100 public void insertChar (AttributedCharacterIterator newParagraph,
101 int insertPos)
102 {
103 totalLayout = new TextLayout(newParagraph, frc);
104 if( insertPos < 0 || insertPos > totalLayout.getCharacterCount() )
105 throw new NullPointerException("Invalid insertPos:"+insertPos);
106 numChars = totalLayout.getCharacterCount();
107 text = newParagraph;
108 }
109
110 /***
111 * Returns the total advance between two positions in the paragraph.
112 * Characters from start to limit-1 (inclusive) are included in this count.
113 *
114 * @param start - the starting character index.
115 * @param limit - the limiting index.
116 */
117 public float getAdvanceBetween (int start, int limit)
118 {
119 Shape s = totalLayout.getLogicalHighlightShape( start, limit );
120 return (float)s.getBounds2D().getWidth();
121 }
122
123 /**
124 * Returns a <code>TextLayout</code> object corresponding to the characters
125 * from text to limit.
126 * @param start - the starting character index.
127 * @param limit - the limiting index.
128 */
129 public TextLayout getLayout (int start, int limit)
130 {
131 if( start >= limit )
132 throw new IllegalArgumentException("Start position must be < limit.");
133 return new TextLayout( totalLayout, start, limit );
134 }
135
136 /**
137 * Returns the line-break index from a given starting index and a maximum
138 * advance. The index returned is the first character outside the given
139 * advance (or the limit of the string, if all remaining characters fit.)
140 *
141 * @param start - the starting index.
142 * @param maxAdvance - the maximum advance allowed.
143 * @return the index of the first character beyond maxAdvance, or the
144 * index of the last character + 1.
145 */
146 public int getLineBreakIndex (int start, float maxAdvance)
147 {
148 if( start < 0 )
149 throw new IllegalArgumentException("Start parameter must be > 0.");
150
151 double remainingLength = getAdvanceBetween( start, numChars );
152
153 int guessOffset = (int)( ( (double)maxAdvance / (double)remainingLength)
154 * ( (double)numChars - (double)start ) );
155 guessOffset += start;
156 if( guessOffset > numChars )
157 guessOffset = numChars;
158
159 double guessLength = getAdvanceBetween( start, guessOffset );
160 boolean makeSmaller = ( guessLength > maxAdvance );
161 int inc = makeSmaller ? -1 : 1;
162 boolean keepGoing = true;
163
164 do
165 {
166 guessOffset = guessOffset + inc;
167 if( guessOffset <= start || guessOffset > numChars )
168 {
169 keepGoing = false;
170 }
171 else
172 {
173 guessLength = getAdvanceBetween( start, guessOffset );
174 if( makeSmaller && ( guessLength <= maxAdvance) )
175 keepGoing = false;
176 if( !makeSmaller && ( guessLength >= maxAdvance) )
177 keepGoing = false;
178 }
179 }
180 while( keepGoing );
181
182 // Return first index that doesn't fit.
183 if( !makeSmaller )
184 guessOffset--;
185
186 if( guessOffset > numChars )
187 return numChars;
188
189 return guessOffset;
190 }
191 }