public final class

StrikeMetrics

extends Object
/*
 * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package sun.font;

import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;

/* These are font metrics: they are in user space, not device space.
 * Hence they are not truly "strike" metrics. However it is convenient to
 * treat them as such since we need to have a scaler context to obtain them
 * and also to cache them. The old implementation obtained a C++ strike object
 * that matched the Font TX + pt size only. It was wasteful of strike objects.
 * This new implementation still has separate StrikeMetrics for 2 fonts that
 * are really the same but are used in different device transforms, but at
 * least it doesn't create a whole new strike just to get the metrics for
 * a strike in a transformed graphics.
 * So these metrics do not take into account the device transform. They
 * are considered inherent properties of the font. Hence it may be that we
 * should use the device transform to obtain the most accurate metrics, but
 * typically 1.1 APIs do not provide for this. So some APIs may want to
 * ignore the dev. tx and others may want to use it, and then apply an
 * inverse transform. For now we ignore the dev. tx.
 * "Font" metrics are representative of a typical glyph in the font.
 * Generally speaking these values are the choice of the font designer and
 * are stored in the font, from which we retrieve the values. They do
 * not necessarily equate to the maximum bounds of all glyphs in the font.
 * Note that the ascent fields are typically a -ve value as we use a top-left
 * origin user space, and text is positioned relative to its baseline.
 */
public final class StrikeMetrics {

    public float ascentX;
    public float ascentY;
    public float descentX;
    public float descentY;
    public float baselineX;
    public float baselineY;
    public float leadingX;
    public float leadingY;
    public float maxAdvanceX;
    public float maxAdvanceY;


    /* The no-args constructor is used by CompositeStrike, which then
     * merges in the metrics of physical fonts.
     * The approach here is the same as earlier releases but it is flawed
     * take for example the following which ignores leading for simplicity.
     * Say we have a composite with an element asc=-9, dsc=2, and another with
     * asc=-7, dsc=3.  The merged font is (-9,3) for height of -(-9)+3=12.
     * Suppose this same font has been derived with a 180% rotation
     * Now its signs for ascent/descent are reversed. Its (9,-2) and (7,-3)
     * Its merged values are (using the code in this class) (7,-2) for
     * a height of -(7)+-2 = =-9!
     * We need to have a more intelligent merging algorithm,
     * which so far as I can see needs to apply an inverse of the font
     * tx, do its merging, and then reapply the font tx.
     * This wouldn't often be a problem as there rarely is a font TX, and
     * the tricky part is getting the information. Probably the no-args
     * constructor needs to pass a TX in to be applied to all merges.
     * CompositeStrike would be left with the problem of figuring out what
     * tx to use.
     * But at least for now we are probably no worse than 1.4 ...
     * REMIND: FIX THIS.
     */
    StrikeMetrics() {
        ascentX = ascentY = Integer.MAX_VALUE;
        descentX = descentY = leadingX = leadingY = Integer.MIN_VALUE;
        baselineX = baselineX = maxAdvanceX = maxAdvanceY = Integer.MIN_VALUE;
    }

    StrikeMetrics(float ax, float ay, float dx, float dy, float bx, float by,
                  float lx, float ly, float mx, float my) {
       ascentX = ax;
       ascentY = ay;
       descentX = dx;
       descentY = dy;
       baselineX = bx;
       baselineY = by;
       leadingX = lx;
       leadingY = ly;
       maxAdvanceX = mx;
       maxAdvanceY = my;
    }

    public float getAscent() {
        return -ascentY;
    }

    public float getDescent() {
        return descentY;
    }

    public float getLeading() {
        return leadingY;
    }

    public float getMaxAdvance() {
        return maxAdvanceX;
    }

    /*
     * Currently only used to merge together slot metrics to create
     * the metrics for a composite font.
     */
     void merge(StrikeMetrics other) {
         if (other == null) {
             return;
         }
         if (other.ascentX < ascentX) {
             ascentX = other.ascentX;
         }
         if (other.ascentY < ascentY) {
             ascentY = other.ascentY;
         }
         if (other.descentX > descentX) {
             descentX = other.descentX;
         }
         if (other.descentY > descentY) {
             descentY = other.descentY;
         }
         if (other.baselineX > baselineX) {
             baselineX = other.baselineX;
         }
         if (other.baselineY > baselineY) {
             baselineY = other.baselineY;
         }
         if (other.leadingX > leadingX) {
             leadingX = other.leadingX;
         }
         if (other.leadingY > leadingY) {
             leadingY = other.leadingY;
         }
         if (other.maxAdvanceX > maxAdvanceX) {
             maxAdvanceX = other.maxAdvanceX;
         }
         if (other.maxAdvanceY > maxAdvanceY) {
             maxAdvanceY = other.maxAdvanceY;
         }
    }

    /* Used to transform the values back into user space.
     * This is done ONCE by the strike so clients should not need
     * to worry about this
     */
    void convertToUserSpace(AffineTransform invTx) {
        Point2D.Float pt2D = new Point2D.Float();

        pt2D.x = ascentX; pt2D.y = ascentY;
        invTx.deltaTransform(pt2D, pt2D);
        ascentX = pt2D.x; ascentY = pt2D.y;

        pt2D.x = descentX; pt2D.y = descentY;
        invTx.deltaTransform(pt2D, pt2D);
        descentX = pt2D.x; descentY = pt2D.y;

        pt2D.x = baselineX; pt2D.y = baselineY;
        invTx.deltaTransform(pt2D, pt2D);
        baselineX = pt2D.x; baselineY = pt2D.y;

        pt2D.x = leadingX; pt2D.y = leadingY;
        invTx.deltaTransform(pt2D, pt2D);
        leadingX = pt2D.x; leadingY = pt2D.y;

        pt2D.x = maxAdvanceX; pt2D.y = maxAdvanceY;
        invTx.deltaTransform(pt2D, pt2D);
        maxAdvanceX = pt2D.x; maxAdvanceY = pt2D.y;
    }

    public String toString() {
        return "ascent:x="      + ascentX +     " y=" + ascentY +
               " descent:x="    + descentX +    " y=" + descentY +
               " baseline:x="   + baselineX +   " y=" + baselineY +
               " leading:x="    + leadingX +    " y=" + leadingY +
               " maxAdvance:x=" + maxAdvanceX + " y=" + maxAdvanceY;
    }
}