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