1 /*
2  * Copyright (c) 2003, 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.geom.AffineTransform;
29 import java.awt.geom.Point2D;
30 
31 /* These are font metrics: they are in user space, not device space.
32  * Hence they are not truly "strike" metrics. However it is convenient to
33  * treat them as such since we need to have a scaler context to obtain them
34  * and also to cache them. The old implementation obtained a C++ strike object
35  * that matched the Font TX + pt size only. It was wasteful of strike objects.
36  * This new implementation still has separate StrikeMetrics for 2 fonts that
37  * are really the same but are used in different device transforms, but at
38  * least it doesn't create a whole new strike just to get the metrics for
39  * a strike in a transformed graphics.
40  * So these metrics do not take into account the device transform. They
41  * are considered inherent properties of the font. Hence it may be that we
42  * should use the device transform to obtain the most accurate metrics, but
43  * typically 1.1 APIs do not provide for this. So some APIs may want to
44  * ignore the dev. tx and others may want to use it, and then apply an
45  * inverse transform. For now we ignore the dev. tx.
46  * "Font" metrics are representative of a typical glyph in the font.
47  * Generally speaking these values are the choice of the font designer and
48  * are stored in the font, from which we retrieve the values. They do
49  * not necessarily equate to the maximum bounds of all glyphs in the font.
50  * Note that the ascent fields are typically a -ve value as we use a top-left
51  * origin user space, and text is positioned relative to its baseline.
52  */
53 public final class StrikeMetrics {
54 
55     public float ascentX;
56     public float ascentY;
57     public float descentX;
58     public float descentY;
59     public float baselineX;
60     public float baselineY;
61     public float leadingX;
62     public float leadingY;
63     public float maxAdvanceX;
64     public float maxAdvanceY;
65 
66 
67     /* The no-args constructor is used by CompositeStrike, which then
68      * merges in the metrics of physical fonts.
69      * The approach here is the same as earlier releases but it is flawed
70      * take for example the following which ignores leading for simplicity.
71      * Say we have a composite with an element asc=-9, dsc=2, and another with
72      * asc=-7, dsc=3.  The merged font is (-9,3) for height of -(-9)+3=12.
73      * Suppose this same font has been derived with a 180% rotation
74      * Now its signs for ascent/descent are reversed. Its (9,-2) and (7,-3)
75      * Its merged values are (using the code in this class) (7,-2) for
76      * a height of -(7)+-2 = =-9!
77      * We need to have a more intelligent merging algorithm,
78      * which so far as I can see needs to apply an inverse of the font
79      * tx, do its merging, and then reapply the font tx.
80      * This wouldn't often be a problem as there rarely is a font TX, and
81      * the tricky part is getting the information. Probably the no-args
82      * constructor needs to pass a TX in to be applied to all merges.
83      * CompositeStrike would be left with the problem of figuring out what
84      * tx to use.
85      * But at least for now we are probably no worse than 1.4 ...
86      * REMIND: FIX THIS.
87      */
StrikeMetrics()88     StrikeMetrics() {
89         ascentX = ascentY = Integer.MAX_VALUE;
90         descentX = descentY = leadingX = leadingY = Integer.MIN_VALUE;
91         baselineX = baselineX = maxAdvanceX = maxAdvanceY = Integer.MIN_VALUE;
92     }
93 
StrikeMetrics(float ax, float ay, float dx, float dy, float bx, float by, float lx, float ly, float mx, float my)94     StrikeMetrics(float ax, float ay, float dx, float dy, float bx, float by,
95                   float lx, float ly, float mx, float my) {
96        ascentX = ax;
97        ascentY = ay;
98        descentX = dx;
99        descentY = dy;
100        baselineX = bx;
101        baselineY = by;
102        leadingX = lx;
103        leadingY = ly;
104        maxAdvanceX = mx;
105        maxAdvanceY = my;
106     }
107 
getAscent()108     public float getAscent() {
109         return -ascentY;
110     }
111 
getDescent()112     public float getDescent() {
113         return descentY;
114     }
115 
getLeading()116     public float getLeading() {
117         return leadingY;
118     }
119 
getMaxAdvance()120     public float getMaxAdvance() {
121         return maxAdvanceX;
122     }
123 
124     /*
125      * Currently only used to merge together slot metrics to create
126      * the metrics for a composite font.
127      */
merge(StrikeMetrics other)128      void merge(StrikeMetrics other) {
129          if (other == null) {
130              return;
131          }
132          if (other.ascentX < ascentX) {
133              ascentX = other.ascentX;
134          }
135          if (other.ascentY < ascentY) {
136              ascentY = other.ascentY;
137          }
138          if (other.descentX > descentX) {
139              descentX = other.descentX;
140          }
141          if (other.descentY > descentY) {
142              descentY = other.descentY;
143          }
144          if (other.baselineX > baselineX) {
145              baselineX = other.baselineX;
146          }
147          if (other.baselineY > baselineY) {
148              baselineY = other.baselineY;
149          }
150          if (other.leadingX > leadingX) {
151              leadingX = other.leadingX;
152          }
153          if (other.leadingY > leadingY) {
154              leadingY = other.leadingY;
155          }
156          if (other.maxAdvanceX > maxAdvanceX) {
157              maxAdvanceX = other.maxAdvanceX;
158          }
159          if (other.maxAdvanceY > maxAdvanceY) {
160              maxAdvanceY = other.maxAdvanceY;
161          }
162     }
163 
164     /* Used to transform the values back into user space.
165      * This is done ONCE by the strike so clients should not need
166      * to worry about this
167      */
convertToUserSpace(AffineTransform invTx)168     void convertToUserSpace(AffineTransform invTx) {
169         Point2D.Float pt2D = new Point2D.Float();
170 
171         pt2D.x = ascentX; pt2D.y = ascentY;
172         invTx.deltaTransform(pt2D, pt2D);
173         ascentX = pt2D.x; ascentY = pt2D.y;
174 
175         pt2D.x = descentX; pt2D.y = descentY;
176         invTx.deltaTransform(pt2D, pt2D);
177         descentX = pt2D.x; descentY = pt2D.y;
178 
179         pt2D.x = baselineX; pt2D.y = baselineY;
180         invTx.deltaTransform(pt2D, pt2D);
181         baselineX = pt2D.x; baselineY = pt2D.y;
182 
183         pt2D.x = leadingX; pt2D.y = leadingY;
184         invTx.deltaTransform(pt2D, pt2D);
185         leadingX = pt2D.x; leadingY = pt2D.y;
186 
187         pt2D.x = maxAdvanceX; pt2D.y = maxAdvanceY;
188         invTx.deltaTransform(pt2D, pt2D);
189         maxAdvanceX = pt2D.x; maxAdvanceY = pt2D.y;
190     }
191 
toString()192     public String toString() {
193         return "ascent:x="      + ascentX +     " y=" + ascentY +
194                " descent:x="    + descentX +    " y=" + descentY +
195                " baseline:x="   + baselineX +   " y=" + baselineY +
196                " leading:x="    + leadingX +    " y=" + leadingY +
197                " maxAdvance:x=" + maxAdvanceX + " y=" + maxAdvanceY;
198     }
199 }
200