Showing the changes to java/pkg/sun/font/FontDesignMetrics.java (1 of 18352 files). Show all
Unified · Split
| @@ -0,0 +1,590 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. | ||
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| 4 | * | ||
| 5 | * This code is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms of the GNU General Public License version 2 only, as | ||
| 7 | * published by the Free Software Foundation. Oracle designates this | ||
| 8 | * particular file as subject to the "Classpath" exception as provided | ||
| 9 | * by Oracle in the LICENSE file that accompanied this code. | ||
| 10 | * | ||
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 14 | * version 2 for more details (a copy is included in the LICENSE file that | ||
| 15 | * accompanied this code). | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License version | ||
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, | ||
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 20 | * | ||
| 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
| 22 | * or visit www.oracle.com if you need additional information or have any | ||
| 23 | * questions. | ||
| 24 | */ | ||
| 25 | |||
| 26 | package sun.font; | ||
| 27 | |||
| 28 | import java.lang.ref.ReferenceQueue; | ||
| 29 | import java.lang.ref.SoftReference; | ||
| 30 | |||
| 31 | import java.awt.FontMetrics; | ||
| 32 | import java.awt.Font; | ||
| 33 | import java.awt.GraphicsEnvironment; | ||
| 34 | import java.awt.geom.AffineTransform; | ||
| 35 | import java.awt.geom.NoninvertibleTransformException; | ||
| 36 | import java.awt.font.FontRenderContext; | ||
| 37 | import java.awt.font.TextLayout; | ||
| 38 | |||
| 39 | import java.io.IOException; | ||
| 40 | import java.io.ObjectInputStream; | ||
| 41 | import java.io.ObjectOutputStream; | ||
| 42 | |||
| 43 | import java.util.concurrent.ConcurrentHashMap; | ||
| 44 | |||
| 45 | import sun.java2d.Disposer; | ||
| 46 | import sun.java2d.DisposerRecord; | ||
| 47 | |||
| 48 | /* | ||
| 49 | * This class provides a summary of the glyph measurements for a Font | ||
| 50 | * and a set of hints that guide their display. It provides more metrics | ||
| 51 | * information for the Font than the java.awt.FontMetrics class. There | ||
| 52 | * is also some redundancy with that class. | ||
| 53 | * <p> | ||
| 54 | * The design metrics for a Font are obtained from Font.getDesignMetrics(). | ||
| 55 | * The FontDesignMetrics object returned will be independent of the | ||
| 56 | * point size of the Font. | ||
| 57 | * Most users are familiar with the idea of using <i>point size</i> to | ||
| 58 | * specify the size of glyphs in a font. This point size defines a | ||
| 59 | * measurement between the baseline of one line to the baseline of the | ||
| 60 | * following line in a single spaced text document. The point size is | ||
| 61 | * based on <i>typographic points</i>, approximately 1/72 of an inch. | ||
| 62 | * <p> | ||
| 63 | * The Java2D API adopts the convention that one point is equivalent | ||
| 64 | * to one unit in user coordinates. When using a normalized transform | ||
| 65 | * for converting user space coordinates to device space coordinates (see | ||
| 66 | * GraphicsConfiguration.getDefaultTransform() and | ||
| 67 | * GraphicsConfiguration.getNormalizingTransform()), 72 user space units | ||
| 68 | * equal 1 inch in device space. In this case one point is 1/72 of an inch. | ||
| 69 | * <p> | ||
| 70 | * The FontDesignMetrics class expresses font metrics in terms of arbitrary | ||
| 71 | * <i>typographic units</i> (not points) chosen by the font supplier | ||
| 72 | * and used in the underlying platform font representations. These units are | ||
| 73 | * defined by dividing the em-square into a grid. The em-sqaure is the | ||
| 74 | * theoretical square whose dimensions are the full body height of the | ||
| 75 | * font. A typographic unit is the smallest measurable unit in the | ||
| 76 | * em-square. The number of units-per-em is determined by the font | ||
| 77 | * designer. The greater the units-per-em, the greater the precision | ||
| 78 | * in metrics. For example, Type 1 fonts divide the em-square into a | ||
| 79 | * 1000 x 1000 grid, while TrueType fonts typically use a 2048 x 2048 | ||
| 80 | * grid. The scale of these units can be obtained by calling | ||
| 81 | * getUnitsPerEm(). | ||
| 82 | * <p> | ||
| 83 | * Typographic units are relative -- their absolute size changes as the | ||
| 84 | * size of the of the em-square changes. An em-square is 9 points high | ||
| 85 | * in a 9-point font. Because typographic units are relative to the | ||
| 86 | * em-square, a given location on a glyph will have the same coordinates | ||
| 87 | * in typographic units regardless of the point size. | ||
| 88 | * <p> | ||
| 89 | * Converting typographic units to pixels requires computing pixels-per-em | ||
| 90 | * (ppem). This can be computed as: | ||
| 91 | * <pre> | ||
| 92 | ppem = device_resolution * (inches-per-point) * pointSize | ||
| 93 | * </pre> | ||
| 94 | * where device resolution could be measured in pixels/inch and the point | ||
| 95 | * size of a font is effectively points/em. Using a normalized transform | ||
| 96 | * from user space to device space (see above), results in 1/72 inch/point. | ||
| 97 | * In this case, ppem is equal to the point size on a 72 dpi monitor, so | ||
| 98 | * that an N point font displays N pixels high. In general, | ||
| 99 | * <pre> | ||
| 100 | pixel_units = typographic_units * (ppem / units_per_em) | ||
| 101 | * </pre> | ||
| 102 | * @see java.awt.Font | ||
| 103 | * @see java.awt.GraphicsConfiguration#getDefaultTransform | ||
| 104 | * @see java.awt.GraphicsConfiguration#getNormalizingTransform | ||
| 105 | */ | ||
| 106 | |||
| 107 | public final class FontDesignMetrics extends FontMetrics { | ||
| 108 | |||
| 109 | static final long serialVersionUID = 4480069578560887773L; | ||
| 110 | |||
| 111 | private static final float UNKNOWN_WIDTH = -1; | ||
| 112 | private static final int CURRENT_VERSION = 1; | ||
| 113 | |||
| 114 | // height, ascent, descent, leading are reported to the client | ||
| 115 | // as an integer this value is added to the true fp value to | ||
| 116 | // obtain a value which is usually going to result in a round up | ||
| 117 | // to the next integer except for very marginal cases. | ||
| 118 | private static float roundingUpValue = 0.95f; | ||
| 119 | |||
| 120 | // These fields are all part of the old serialization representation | ||
| 121 | private Font font; | ||
| 122 | private float ascent; | ||
| 123 | private float descent; | ||
| 124 | private float leading; | ||
| 125 | private float maxAdvance; | ||
| 126 | private double[] matrix; | ||
| 127 | private int[] cache; // now unused, still here only for serialization | ||
| 128 | // End legacy serialization fields | ||
| 129 | |||
| 130 | private int serVersion = 0; // If 1 in readObject, these fields are on the input stream: | ||
| 131 | private boolean isAntiAliased; | ||
| 132 | private boolean usesFractionalMetrics; | ||
| 133 | private AffineTransform frcTx; | ||
| 134 | |||
| 135 | private transient float[] advCache; // transient since values could change across runtimes | ||
| 136 | private transient int height = -1; | ||
| 137 | |||
| 138 | private transient FontRenderContext frc; | ||
| 139 | |||
| 140 | private transient double[] devmatrix = null; | ||
| 141 | |||
| 142 | private transient FontStrike fontStrike; | ||
| 143 | |||
| 144 | private static FontRenderContext DEFAULT_FRC = null; | ||
| 145 | |||
| 146 | private static FontRenderContext getDefaultFrc() { | ||
| 147 | |||
| 148 | if (DEFAULT_FRC == null) { | ||
| 149 | AffineTransform tx; | ||
| 150 | if (GraphicsEnvironment.isHeadless()) { | ||
| 151 | tx = new AffineTransform(); | ||
| 152 | } else { | ||
| 153 | tx = GraphicsEnvironment | ||
| 154 | .getLocalGraphicsEnvironment() | ||
| 155 | .getDefaultScreenDevice() | ||
| 156 | .getDefaultConfiguration() | ||
| 157 | .getDefaultTransform(); | ||
| 158 | } | ||
| 159 | DEFAULT_FRC = new FontRenderContext(tx, false, false); | ||
| 160 | } | ||
| 161 | return DEFAULT_FRC; | ||
| 162 | } | ||
| 163 | |||
| 164 | /* Strongly cache up to 5 most recently requested FontMetrics objects, | ||
| 165 | * and softly cache as many as GC allows. In practice this means we | ||
| 166 | * should keep references around until memory gets low. | ||
| 167 | * We key the cache either by a Font or a combination of the Font and | ||
| 168 | * and FRC. A lot of callers use only the font so although there's code | ||
| 169 | * duplication, we allow just a font to be a key implying a default FRC. | ||
| 170 | * Also we put the references on a queue so that if they do get nulled | ||
| 171 | * out we can clear the keys from the table. | ||
| 172 | */ | ||
| 173 | private static class KeyReference extends SoftReference | ||
| 174 | implements DisposerRecord, Disposer.PollDisposable { | ||
| 175 | |||
| 176 | static ReferenceQueue queue = Disposer.getQueue(); | ||
| 177 | |||
| 178 | Object key; | ||
| 179 | |||
| 180 | KeyReference(Object key, Object value) { | ||
| 181 | super(value, queue); | ||
| 182 | this.key = key; | ||
| 183 | Disposer.addReference(this, this); | ||
| 184 | } | ||
| 185 | |||
| 186 | /* It is possible that since this reference object has been | ||
| 187 | * enqueued, that a new metrics has been put into the table | ||
| 188 | * for the same key value. So we'll test to see if the table maps | ||
| 189 | * to THIS reference. If its a new one, we'll leave it alone. | ||
| 190 | * It is possible that a new entry comes in after our test, but | ||
| 191 | * it is unlikely and if this were a problem we would need to | ||
| 192 | * synchronize all 'put' and 'remove' accesses to the cache which | ||
| 193 | * I would prefer not to do. | ||
| 194 | */ | ||
| 195 | public void dispose() { | ||
| 196 | if (metricsCache.get(key) == this) { | ||
| 197 | metricsCache.remove(key); | ||
| 198 | } | ||
| 199 | } | ||
| 200 | } | ||
| 201 | |||
| 202 | private static class MetricsKey { | ||
| 203 | Font font; | ||
| 204 | FontRenderContext frc; | ||
| 205 | int hash; | ||
| 206 | |||
| 207 | MetricsKey() { | ||
| 208 | } | ||
| 209 | |||
| 210 | MetricsKey(Font font, FontRenderContext frc) { | ||
| 211 | init(font, frc); | ||
| 212 | } | ||
| 213 | |||
| 214 | void init(Font font, FontRenderContext frc) { | ||
| 215 | this.font = font; | ||
| 216 | this.frc = frc; | ||
| 217 | this.hash = font.hashCode() + frc.hashCode(); | ||
| 218 | } | ||
| 219 | |||
| 220 | public boolean equals(Object key) { | ||
| 221 | if (!(key instanceof MetricsKey)) { | ||
| 222 | return false; | ||
| 223 | } | ||
| 224 | return | ||
| 225 | font.equals(((MetricsKey)key).font) && | ||
| 226 | frc.equals(((MetricsKey)key).frc); | ||
| 227 | } | ||
| 228 | |||
| 229 | public int hashCode() { | ||
| 230 | return hash; | ||
| 231 | } | ||
| 232 | |||
| 233 | /* Synchronize access to this on the class */ | ||
| 234 | static final MetricsKey key = new MetricsKey(); | ||
| 235 | } | ||
| 236 | |||
| 237 | /* All accesses to a CHM do not in general need to be synchronized, | ||
| 238 | * as incomplete operations on another thread would just lead to | ||
| 239 | * harmless cache misses. | ||
| 240 | */ | ||
| 241 | private static final ConcurrentHashMap<Object, KeyReference> | ||
| 242 | metricsCache = new ConcurrentHashMap<Object, KeyReference>(); | ||
| 243 | |||
| 244 | private static final int MAXRECENT = 5; | ||
| 245 | private static final FontDesignMetrics[] | ||
| 246 | recentMetrics = new FontDesignMetrics[MAXRECENT]; | ||
| 247 | private static int recentIndex = 0; | ||
| 248 | |||
| 249 | public static FontDesignMetrics getMetrics(Font font) { | ||
| 250 | return getMetrics(font, getDefaultFrc()); | ||
| 251 | } | ||
| 252 | |||
| 253 | public static FontDesignMetrics getMetrics(Font font, | ||
| 254 | FontRenderContext frc) { | ||
| 255 | |||
| 256 | |||
| 257 | /* When using alternate composites, can't cache based just on | ||
| 258 | * the java.awt.Font. Since this is rarely used and we can still | ||
| 259 | * cache the physical fonts, its not a problem to just return a | ||
| 260 | * new instance in this case. | ||
| 261 | * Note that currently Swing native L&F composites are not handled | ||
| 262 | * by this code as they use the metrics of the physical anyway. | ||
| 263 | */ | ||
| 264 | SunFontManager fm = SunFontManager.getInstance(); | ||
| 265 | if (fm.maybeUsingAlternateCompositeFonts() && | ||
| 266 | FontUtilities.getFont2D(font) instanceof CompositeFont) { | ||
| 267 | return new FontDesignMetrics(font, frc); | ||
| 268 | } | ||
| 269 | |||
| 270 | FontDesignMetrics m = null; | ||
| 271 | KeyReference r; | ||
| 272 | |||
| 273 | /* There are 2 possible keys used to perform lookups in metricsCache. | ||
| 274 | * If the FRC is set to all defaults, we just use the font as the key. | ||
| 275 | * If the FRC is non-default in any way, we construct a hybrid key | ||
| 276 | * that combines the font and FRC. | ||
| 277 | */ | ||
| 278 | boolean usefontkey = frc.equals(getDefaultFrc()); | ||
| 279 | |||
| 280 | if (usefontkey) { | ||
| 281 | r = metricsCache.get(font); | ||
| 282 | } else /* use hybrid key */ { | ||
| 283 | // NB synchronization is not needed here because of updates to | ||
| 284 | // the metrics cache but is needed for the shared key. | ||
| 285 | synchronized (MetricsKey.class) { | ||
| 286 | MetricsKey.key.init(font, frc); | ||
| 287 | r = metricsCache.get(MetricsKey.key); | ||
| 288 | } | ||
| 289 | } | ||
| 290 | |||
| 291 | if (r != null) { | ||
| 292 | m = (FontDesignMetrics)r.get(); | ||
| 293 | } | ||
| 294 | |||
| 295 | if (m == null) { | ||
| 296 | /* either there was no reference, or it was cleared. Need a new | ||
| 297 | * metrics instance. The key to use in the map is a new | ||
| 298 | * MetricsKey instance when we've determined the FRC is | ||
| 299 | * non-default. Its constructed from local vars so we are | ||
| 300 | * thread-safe - no need to worry about the shared key changing. | ||
| 301 | */ | ||
| 302 | m = new FontDesignMetrics(font, frc); | ||
| 303 | if (usefontkey) { | ||
| 304 | metricsCache.put(font, new KeyReference(font, m)); | ||
| 305 | } else /* use hybrid key */ { | ||
| 306 | MetricsKey newKey = new MetricsKey(font, frc); | ||
| 307 | metricsCache.put(newKey, new KeyReference(newKey, m)); | ||
| 308 | } | ||
| 309 | } | ||
| 310 | |||
| 311 | /* Here's where we keep the recent metrics */ | ||
| 312 | for (int i=0; i<recentMetrics.length; i++) { | ||
| 313 | if (recentMetrics[i]==m) { | ||
| 314 | return m; | ||
| 315 | } | ||
| 316 | } | ||
| 317 | |||
| 318 | synchronized (recentMetrics) { | ||
| 319 | recentMetrics[recentIndex++] = m; | ||
| 320 | if (recentIndex == MAXRECENT) { | ||
| 321 | recentIndex = 0; | ||
| 322 | } | ||
| 323 | } | ||
| 324 | return m; | ||
| 325 | } | ||
| 326 | |||
| 327 | /* | ||
| 328 | * Constructs a new FontDesignMetrics object for the given Font. | ||
| 329 | * Its private to enable caching - call getMetrics() instead. | ||
| 330 | * @param font a Font object. | ||
| 331 | */ | ||
| 332 | |||
| 333 | private FontDesignMetrics(Font font) { | ||
| 334 | |||
| 335 | this(font, getDefaultFrc()); | ||
| 336 | } | ||
| 337 | |||
| 338 | /* private to enable caching - call getMetrics() instead. */ | ||
| 339 | private FontDesignMetrics(Font font, FontRenderContext frc) { | ||
| 340 | super(font); | ||
| 341 | this.font = font; | ||
| 342 | this.frc = frc; | ||
| 343 | |||
| 344 | this.isAntiAliased = frc.isAntiAliased(); | ||
| 345 | this.usesFractionalMetrics = frc.usesFractionalMetrics(); | ||
| 346 | |||
| 347 | frcTx = frc.getTransform(); | ||
| 348 | |||
| 349 | matrix = new double[4]; | ||
| 350 | initMatrixAndMetrics(); | ||
| 351 | |||
| 352 | initAdvCache(); | ||
| 353 | } | ||
| 354 | |||
| 355 | private void initMatrixAndMetrics() { | ||
| 356 | |||
| 357 | Font2D font2D = FontUtilities.getFont2D(font); | ||
| 358 | fontStrike = font2D.getStrike(font, frc); | ||
| 359 | StrikeMetrics metrics = fontStrike.getFontMetrics(); | ||
| 360 | this.ascent = metrics.getAscent(); | ||
| 361 | this.descent = metrics.getDescent(); | ||
| 362 | this.leading = metrics.getLeading(); | ||
| 363 | this.maxAdvance = metrics.getMaxAdvance(); | ||
| 364 | |||
| 365 | devmatrix = new double[4]; | ||
| 366 | frcTx.getMatrix(devmatrix); | ||
| 367 | } | ||
| 368 | |||
| 369 | private void initAdvCache() { | ||
| 370 | advCache = new float[256]; | ||
| 371 | // 0 is a valid metric so force it to -1 | ||
| 372 | for (int i = 0; i < 256; i++) { | ||
| 373 | advCache[i] = UNKNOWN_WIDTH; | ||
| 374 | } | ||
| 375 | } | ||
| 376 | |||
| 377 | private void readObject(ObjectInputStream in) throws IOException, | ||
| 378 | ClassNotFoundException { | ||
| 379 | |||
| 380 | in.defaultReadObject(); | ||
| 381 | if (serVersion != CURRENT_VERSION) { | ||
| 382 | frc = getDefaultFrc(); | ||
| 383 | isAntiAliased = frc.isAntiAliased(); | ||
| 384 | usesFractionalMetrics = frc.usesFractionalMetrics(); | ||
| 385 | frcTx = frc.getTransform(); | ||
| 386 | } | ||
| 387 | else { | ||
| 388 | frc = new FontRenderContext(frcTx, isAntiAliased, usesFractionalMetrics); | ||
| 389 | } | ||
| 390 | |||
| 391 | // when deserialized, members are set to their default values for their type-- | ||
| 392 | // not to the values assigned during initialization before the constructor | ||
| 393 | // body! | ||
| 394 | height = -1; | ||
| 395 | |||
| 396 | cache = null; | ||
| 397 | |||
| 398 | initMatrixAndMetrics(); | ||
| 399 | initAdvCache(); | ||
| 400 | } | ||
| 401 | |||
| 402 | private void writeObject(ObjectOutputStream out) throws IOException { | ||
| 403 | |||
| 404 | cache = new int[256]; | ||
| 405 | for (int i=0; i < 256; i++) { | ||
| 406 | cache[i] = -1; | ||
| 407 | } | ||
| 408 | serVersion = CURRENT_VERSION; | ||
| 409 | |||
| 410 | out.defaultWriteObject(); | ||
| 411 | |||
| 412 | cache = null; | ||
| 413 | } | ||
| 414 | |||
| 415 | private float handleCharWidth(int ch) { | ||
| 416 | return fontStrike.getCodePointAdvance(ch); // x-component of result only | ||
| 417 | } | ||
| 418 | |||
| 419 | // Uses advCache to get character width | ||
| 420 | // It is incorrect to call this method for ch > 255 | ||
| 421 | private float getLatinCharWidth(char ch) { | ||
| 422 | |||
| 423 | float w = advCache[ch]; | ||
| 424 | if (w == UNKNOWN_WIDTH) { | ||
| 425 | w = handleCharWidth(ch); | ||
| 426 | advCache[ch] = w; | ||
| 427 | } | ||
| 428 | return w; | ||
| 429 | } | ||
| 430 | |||
| 431 | |||
| 432 | /* Override of FontMetrics.getFontRenderContext() */ | ||
| 433 | public FontRenderContext getFontRenderContext() { | ||
| 434 | return frc; | ||
| 435 | } | ||
| 436 | |||
| 437 | public int charWidth(char ch) { | ||
| 438 | // default metrics for compatibility with legacy code | ||
| 439 | float w; | ||
| 440 | if (ch < 0x100) { | ||
| 441 | w = getLatinCharWidth(ch); | ||
| 442 | } | ||
| 443 | else { | ||
| 444 | w = handleCharWidth(ch); | ||
| 445 | } | ||
| 446 | return (int)(0.5 + w); | ||
| 447 | } | ||
| 448 | |||
| 449 | public int charWidth(int ch) { | ||
| 450 | if (!Character.isValidCodePoint(ch)) { | ||
| 451 | ch = 0xffff; | ||
| 452 | } | ||
| 453 | |||
| 454 | float w = handleCharWidth(ch); | ||
| 455 | |||
| 456 | return (int)(0.5 + w); | ||
| 457 | } | ||
| 458 | |||
| 459 | public int stringWidth(String str) { | ||
| 460 | |||
| 461 | float width = 0; | ||
| 462 | if (font.hasLayoutAttributes()) { | ||
| 463 | /* TextLayout throws IAE for null, so throw NPE explicitly */ | ||
| 464 | if (str == null) { | ||
| 465 | throw new NullPointerException("str is null"); | ||
| 466 | } | ||
| 467 | if (str.length() == 0) { | ||
| 468 | return 0; | ||
| 469 | } | ||
| 470 | width = new TextLayout(str, font, frc).getAdvance(); | ||
| 471 | } else { | ||
| 472 | int length = str.length(); | ||
| 473 | for (int i=0; i < length; i++) { | ||
| 474 | char ch = str.charAt(i); | ||
| 475 | if (ch < 0x100) { | ||
| 476 | width += getLatinCharWidth(ch); | ||
| 477 | } else if (FontUtilities.isNonSimpleChar(ch)) { | ||
| 478 | width = new TextLayout(str, font, frc).getAdvance(); | ||
| 479 | break; | ||
| 480 | } else { | ||
| 481 | width += handleCharWidth(ch); | ||
| 482 | } | ||
| 483 | } | ||
| 484 | } | ||
| 485 | |||
| 486 | return (int) (0.5 + width); | ||
| 487 | } | ||
| 488 | |||
| 489 | public int charsWidth(char data[], int off, int len) { | ||
| 490 | |||
| 491 | float width = 0; | ||
| 492 | if (font.hasLayoutAttributes()) { | ||
| 493 | if (len == 0) { | ||
| 494 | return 0; | ||
| 495 | } | ||
| 496 | String str = new String(data, off, len); | ||
| 497 | width = new TextLayout(str, font, frc).getAdvance(); | ||
| 498 | } else { | ||
| 499 | /* Explicit test needed to satisfy superclass spec */ | ||
| 500 | if (len < 0) { | ||
| 501 | throw new IndexOutOfBoundsException("len="+len); | ||
| 502 | } | ||
| 503 | int limit = off + len; | ||
| 504 | for (int i=off; i < limit; i++) { | ||
| 505 | char ch = data[i]; | ||
| 506 | if (ch < 0x100) { | ||
| 507 | width += getLatinCharWidth(ch); | ||
| 508 | } else if (FontUtilities.isNonSimpleChar(ch)) { | ||
| 509 | String str = new String(data, off, len); | ||
| 510 | width = new TextLayout(str, font, frc).getAdvance(); | ||
| 511 | break; | ||
| 512 | } else { | ||
| 513 | width += handleCharWidth(ch); | ||
| 514 | } | ||
| 515 | } | ||
| 516 | } | ||
| 517 | |||
| 518 | return (int) (0.5 + width); | ||
| 519 | } | ||
| 520 | |||
| 521 | /** | ||
| 522 | * Gets the advance widths of the first 256 characters in the | ||
| 523 | * <code>Font</code>. The advance is the | ||
| 524 | * distance from the leftmost point to the rightmost point on the | ||
| 525 | * character's baseline. Note that the advance of a | ||
| 526 | * <code>String</code> is not necessarily the sum of the advances | ||
| 527 | * of its characters. | ||
| 528 | * @return an array storing the advance widths of the | ||
| 529 | * characters in the <code>Font</code> | ||
| 530 | * described by this <code>FontMetrics</code> object. | ||
| 531 | */ | ||
| 532 | // More efficient than base class implementation - reuses existing cache | ||
| 533 | public int[] getWidths() { | ||
| 534 | int[] widths = new int[256]; | ||
| 535 | for (char ch = 0 ; ch < 256 ; ch++) { | ||
| 536 | float w = advCache[ch]; | ||
| 537 | if (w == UNKNOWN_WIDTH) { | ||
| 538 | w = advCache[ch] = handleCharWidth(ch); | ||
| 539 | } | ||
| 540 | widths[ch] = (int) (0.5 + w); | ||
| 541 | } | ||
| 542 | return widths; | ||
| 543 | } | ||
| 544 | |||
| 545 | public int getMaxAdvance() { | ||
| 546 | return (int)(0.99f + this.maxAdvance); | ||
| 547 | } | ||
| 548 | |||
| 549 | /* | ||
| 550 | * Returns the typographic ascent of the font. This is the maximum distance | ||
| 551 | * glyphs in this font extend above the base line (measured in typographic | ||
| 552 | * units). | ||
| 553 | */ | ||
| 554 | public int getAscent() { | ||
| 555 | return (int)(roundingUpValue + this.ascent); | ||
| 556 | } | ||
| 557 | |||
| 558 | /* | ||
| 559 | * Returns the typographic descent of the font. This is the maximum distance | ||
| 560 | * glyphs in this font extend below the base line. | ||
| 561 | */ | ||
| 562 | public int getDescent() { | ||
| 563 | return (int)(roundingUpValue + this.descent); | ||
| 564 | } | ||
| 565 | |||
| 566 | public int getLeading() { | ||
| 567 | // nb this ensures the sum of the results of the public methods | ||
| 568 | // for leading, ascent & descent sum to height. | ||
| 569 | // if the calculations in any other methods change this needs | ||
| 570 | // to be changed too. | ||
| 571 | // the 0.95 value used here and in the other methods allows some | ||
| 572 | // tiny fraction of leeway before rouding up. A higher value (0.99) | ||
| 573 | // caused some excessive rounding up. | ||
| 574 | return | ||
| 575 | (int)(roundingUpValue + descent + leading) - | ||
| 576 | (int)(roundingUpValue + descent); | ||
| 577 | } | ||
| 578 | |||
| 579 | // height is calculated as the sum of two separately rounded up values | ||
| 580 | // because typically clients use ascent to determine the y location to | ||
| 581 | // pass to drawString etc and we need to ensure that the height has enough | ||
| 582 | // space below the baseline to fully contain any descender. | ||
| 583 | public int getHeight() { | ||
| 584 | |||
| 585 | if (height < 0) { | ||
| 586 | height = getAscent() + (int)(roundingUpValue + descent + leading); | ||
| 587 | } | ||
| 588 | return height; | ||
| 589 | } | ||
| 590 | } | ||