/* * $RCSfile: FontFace.java,v $ * * Copyright 1990-2009 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; } } // ========================================================================= }