FileDocCategorySizeDatePackage
FontFace.javaAPI DocphoneME MR2 API (J2ME)15916Wed May 02 18:00:36 BST 2007com.sun.perseus.model

FontFace.java

/*
 *
 *
 * Copyright  1990-2007 Sun Microsystems, Inc. All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * 
 * This program 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.
 * 
 * This program 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 at /legal/license.txt).
 * 
 * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa
 * Clara, CA 95054 or visit www.sun.com if you need additional
 * information or have any questions.
 */

package com.sun.perseus.model;

import com.sun.perseus.util.SVGConstants;

import com.sun.perseus.j2d.RenderGraphics;

import org.w3c.dom.DOMException;

/**
 * A <tt>FontFace</tt> node corresponds to an SVG
 * <tt><font-face></tt> element.
 * <br />
 * Like many other classes, <tt>FontFace</tt> extends <tt>ElementNode</tt>
 * so that we can support mixed namespaces (i.e., unexpected content
 * in our model).
 * <br />
 *
 * @version $Id: FontFace.java,v 1.4 2006/04/21 06:37:12 st125089 Exp $
 */
public class FontFace extends ElementNode {
    /**
     * Values when all the font weights are used
     */
    public static final int FONT_WEIGHT_ALL =
        TextNode.FONT_WEIGHT_100 
        | TextNode.FONT_WEIGHT_200 
        | TextNode.FONT_WEIGHT_300 
        | TextNode.FONT_WEIGHT_400 
        | TextNode.FONT_WEIGHT_500 
        | TextNode.FONT_WEIGHT_600 
        | TextNode.FONT_WEIGHT_700 
        | TextNode.FONT_WEIGHT_800 
        | TextNode.FONT_WEIGHT_900;

    /**
     * Value whena all the font styles are used
     */
    public static final int FONT_STYLE_ALL =
        TextNode.FONT_STYLE_NORMAL 
        | TextNode.FONT_STYLE_ITALIC 
        | TextNode.FONT_STYLE_OBLIQUE;

    /**
     * Default value for units per em
     */
    public static final float UNITS_PER_EM_DEFAULT = 1000;

    /**
     * Value when all the font sizes match
     */
    public static final float[] FONT_SIZE_ALL = null;

    /**
     * List of font families that constitute a match
     */
    protected String[] fontFamilies;

    /**
     * Describes the styles that constitute a match for this
     * font. This can be a combination (or'd) of the various
     * FONT_STYLE_XXX values.
     */
    protected int fontStyles = FONT_STYLE_ALL;

    /**
     * Describes the weights that constitute a match for this
     * font. This can be a combination (or'd) of the various
     * FONT_WEIGHT_XXX values.
     * The values FONT_WEIGHT_BOLDER and FONT_WEIGHT_LIGHTER
     * are not allowed.
     */
    protected int fontWeights = FONT_WEIGHT_ALL;

    /**
     * Font sizes that constitute a match for this font during 
     * font selection. A null value means that all font-sizes
     * can be matched by this font
     */
    protected float[] fontSizes;

    /**
     * Number of coordinate units on the em square.
     * Defaults to 1000.
     */
    protected float unitsPerEm = UNITS_PER_EM_DEFAULT;

    /**
     * Parent Font
     * @see #setParent
     */
    protected Font font;

    /**
     * The main scale factor needed to convert from the em square
     * grid to the text's current position coordinate system.
     * The transform built from the returned value is:
     * <pre>
     * scale 0     0
     * 0    -scale 0
     * 0     0     1
     * </pre>
     */
    protected float emSquareScale 
        = 1;

    /**
     * @return the SVGConstants.SVG_FONT_FACE_TAG value
     */

    public String getLocalName() {
        return SVGConstants.SVG_FONT_FACE_TAG;
    }

    /**
     * Used by <code>DocumentNode</code> to create a new instance from
     * a prototype <code>FontFace</code>.
     *
     * @param doc the <code>DocumentNode</code> for which a new node is
     *        should be created.
     * @return a new <code>FontFace</code> for the requested document.
     */
    public ElementNode newInstance(final DocumentNode doc) {
        return new FontFace(doc);
    }

    /**
     * @param newFontFamilies the new set of font families matched by this
     *        node. Note that the input array is used by reference after
     *        this call
     */
    public void setFontFamilies(final String[] newFontFamilies) {
        if (equal(fontFamilies, newFontFamilies)) {
            return;
        }
        modifyingNode();
        this.fontFamilies = newFontFamilies;
        modifiedNode();
    }
    
    /**
     * @return the array of font families matched by this node
     */
    public String[] getFontFamilies() {
        return fontFamilies;
    }

    /**
     * @param newFontStyles the set of font styles mateched by this node.
     */
    public void setFontStyles(final int newFontStyles) {
        if (newFontStyles == fontStyles) {
            return;
        }
        modifyingNode();
        this.fontStyles = newFontStyles;
        modifiedNode();
    }

    /**
     * @return the set of font styles matched by this node. To test
     *         if a particular font style is supported, use the '&'
     *         operator.
     */
    public int getFontStyles() {
        return fontStyles;
    }
    
    /**
     * @param newFontSizes the new set of font sizes matched by this node
     */
    public void setFontSizes(final float[] newFontSizes) {
        if (equal(newFontSizes, fontSizes)) {
            return;
        }
        modifyingNode();
        this.fontSizes = newFontSizes;
        modifiedNode();
    }

    /**
     * @return the array of font sizes matched by this node
     */
    public float[] getFontSizes() {
        return fontSizes;
    }

    /**
     * @return the set of font weights matched by this node
     */
    public int getFontWeights() {
        return fontWeights;
    }

    /**
     * @param newFontWeights the set of font weight value matched by this 
     *        <tt>FontFace</tt>
     */
    public void setFontWeights(final int newFontWeights) {
        if (newFontWeights == fontWeights) {
            return;
        }
        modifyingNode();
        this.fontWeights = newFontWeights;
        modifiedNode();
    }

    /**
     * @param newUnitsPerEm this node's new <tt>unitsPerEm</tt> property
     */
    public void setUnitsPerEm(final float newUnitsPerEm) {
        if (newUnitsPerEm == unitsPerEm) {
            return;
        }
        modifyingNode();
        this.unitsPerEm = newUnitsPerEm;
        computeEmSquareScale();
        modifiedNode();
    }

    /**
     * @return this node's <tt>unitsPerEm</tt> property
     */
    public float getUnitsPerEm() {
        return unitsPerEm;
    }

    /**
     * The constructor computes the inital emSquareScale
     *
     * @param ownerDocument this element's owner <code>DocumentNode</code>
     */
    public FontFace(final DocumentNode ownerDocument) {
        super(ownerDocument);
        computeEmSquareScale();
    }

    /**
     * @return the current scale factor from the em grid
     * to the text coordinate system. The scale is 
     * recomputed every time the unitsPerEm value changes.
     * <pre>
     * scale  0    0
     *   0  -scale 0
     *   0    0    1 
     * </pre>
     */
    public float getEmSquareScale() {
        return emSquareScale;
    }

    /**
     * Computes the scale that should be applied to glyphs to 
     * transform glyph coordinates into the text coordiate system.
     */
    protected void computeEmSquareScale() {
        // The number of units per em defines the number of 
        // units in 1x1 em square grid. Hence, the 1/unitsPerEm
        // scale factor.
        // The Y axis points up for the em square grid, which
        // is why the Y axis scale is negative.
        emSquareScale = 1f / unitsPerEm; 
    }

    /**
     * If the parent is a <tt>Font</tt> object, keep track of it in the
     * font member.
     * @param parent this node's new parent
     */
    public void setParent(final ModelNode parent) {
        super.setParent(parent);

        if (parent instanceof Font) {
            this.font = (Font) parent;
        } else {
            this.font = null;
        }
    }

    /**
     * @param s the character string
     * @param index the index of the character for which to
     *        find a <tt>Glyph</tt>
     * @return a Glyph if the FontFace can display the 
     * character at the requested index. Returns null
     * if no matching glyph can be found.
     */
    Glyph canDisplay(final char[] s, final int index) {
        return font.canDisplay(s, index);
    }

    /**
     * @return the Glyph that represents missing glyphs.
     * This should *not* be null.
     */
    Glyph getMissingGlyph() {
        return font.getMissingGlyph();
    }

    /**
     * The distance between the input <tt>refFontWeight</tt> and this
     * <tt>FontFace</tt>'s <tt>fontWeights</tt>
     * 
     * @param refFontWeight this method computes the distance between this 
     *        <tt>FontFace</tt>'s fontWeight and the input <tt>fontWeight</tt>
     * @return the distance to the input <tt>fontWeight</tt>
     */
    public int fontWeightDistance(final int refFontWeight) {
        // This FontFace matches all, this is a zero distance
        if (fontWeights == FONT_WEIGHT_ALL) {
            return 0;
        }

        // This FontFace matches the requested fontWeight,
        // this is a zero distance.
        if ((fontWeights & refFontWeight) != 0) {
            return 0;
        }

        // The requested fontWeight is not part of the ones
        // matching exactly this FontFace. Compute how far is 
        // the closes match.
        int dA = fontWeights;
        int i = 0;
        while ((dA & 0x01) == 0) {
            dA >>= 1;
            i++;
        }
        
        dA = i;

        i = 0;
        int curD = 100; // Infinity in this algorithm
        int d = 100;

        for (i = 0; i < 9; i++) {
            if (((refFontWeight >> i) & 0x01) != 0) {
                d = (dA - i) * (dA - i);
                if (d < curD) {
                    curD = d;
                }
            }
        }

        return curD;
    }

    /**
     * FontFace handles the font-family, font-style, font-weight, font-size,
     * and unitsPerEm attributes.
     *
     * @param traitName the name of the trait which the element may support.
     * @return true if this element supports the given trait in one of the
     *         trait accessor methods.
     */
    boolean supportsTrait(final String traitName) {
        if (SVGConstants.SVG_FONT_FAMILY_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_FONT_STYLE_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_FONT_WEIGHT_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_FONT_SIZE_ATTRIBUTE == traitName
            ||
            SVGConstants.SVG_UNITS_PER_EM_ATTRIBUTE == traitName) {
            return true;
        }
        
        return super.supportsTrait(traitName);
    }


    /**
     * FontFace handles the font-family, font-style, font-weight, font-size,
     * and unitsPerEm attributes.
     * Other traits are handled by the super class.
     *
     * @param name the requested trait name (e.g., "horiz-adv-x")
     * @return the trait's value, as a string.
     *
     * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested
     * trait is not supported on this element or null.
     * @throws DOMException with error code TYPE_MISMATCH_ERR if requested
     * trait's computed value cannot be converted to a String (SVG Tiny only).
     */
    public String getTraitImpl(final String name)
        throws DOMException {
        if (SVGConstants.SVG_FONT_FAMILY_ATTRIBUTE == name) {
            return toStringTraitQuote(getFontFamilies());
        } else if (SVGConstants.SVG_FONT_STYLE_ATTRIBUTE == name) {
            return fontStylesToStringTrait(getFontStyles());
        } else if (SVGConstants.SVG_FONT_WEIGHT_ATTRIBUTE == name) {
            return fontWeightsToStringTrait(getFontWeights());
        } else if (SVGConstants.SVG_FONT_SIZE_ATTRIBUTE == name) {
            if (getFontSizes() == FONT_SIZE_ALL) {
                return SVGConstants.CSS_ALL_VALUE;
            }
            return toStringTrait(getFontSizes());
        } else if (SVGConstants.SVG_UNITS_PER_EM_ATTRIBUTE == name) {
            return Float.toString(getUnitsPerEm());
        } else {
            return super.getTraitImpl(name);
        }
    }


    /**
     * FontFace handles the font-family, font-style, font-weight, font-size,
     * and unitsPerEm attributes.
     * Other traits are handled by the super class.
     *
     * @param name the trait's name (e.g., "units-per-em")
     * @param value the new trait string value (e.g., "1000")
     *
     * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested
     * trait is not supported on this element or null.
     * @throws DOMException with error code TYPE_MISMATCH_ERR if the requested
     * trait's value cannot be specified as a String
     * @throws DOMException with error code INVALID_ACCESS_ERR if the input
     * value is an invalid value for the given trait or null.
     * @throws DOMException with error code NO_MODIFICATION_ALLOWED_ERR: if
     * attempt is made to change readonly trait.
     */
    public void setTraitImpl(final String name, final String value)
        throws DOMException {
        if (SVGConstants.SVG_FONT_FAMILY_ATTRIBUTE == name) {
            checkWriteLoading(name);
            setFontFamilies(parseFontFamilyTrait(name, value));
        } else if (SVGConstants.SVG_FONT_STYLE_ATTRIBUTE == name) {
            checkWriteLoading(name);
            setFontStyles(parseFontStylesTrait(name, value));
        } else if (SVGConstants.SVG_FONT_WEIGHT_ATTRIBUTE == name) {
            checkWriteLoading(name);
            setFontWeights(parseFontWeightsTrait(name, value));
        } else if (SVGConstants.SVG_FONT_SIZE_ATTRIBUTE == name) {
            checkWriteLoading(name);
            if (SVGConstants.CSS_ALL_VALUE.equals(value)) {
                setFontSizes(FONT_SIZE_ALL);
            } else {
                setFontSizes(parseFloatArrayTrait(name, value));
            }
        } else if (SVGConstants.SVG_UNITS_PER_EM_ATTRIBUTE == name) {
            checkWriteLoading(name);
            setUnitsPerEm(parseFloatTrait(name, value));
        } else {
            super.setTraitImpl(name, value);
        }
    }


    // =========================================================================

    /**
     * The FontFace.Match class is used to return a chain of 
     * sorted FontFace, according to the CSS2 Font Matching
     * algorithm.
     *
     * @see DocumentNode#resolveFontFaces
     */
    static class Match {
        /**
         * The matching <code>FontFace</code>.
         */
        public FontFace fontFace;

        /**
         * The font-weight distance. See the font matching algorithm
         * in <code>DocumentNode</code>
         */
        public int distance;

        /**
         * The next match in the chain.
         */
        public Match next;
        
        /**
         * Constructor
         *
         * @param fontFace the matching <code>FontFace</code>
         */
        public Match(final FontFace fontFace) {
            this.fontFace = fontFace;
        }
    }

    // =========================================================================

}