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 }