/* * $RCSfile: HKern.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 <code>HKern</code> corresponds to an SVG <code><hkern></code> * element. <br /> * * @version $Id: HKern.java,v 1.5 2006/06/29 10:47:32 ln156897 Exp $ */ public class HKern extends ElementNode { /** * Only the k trait is required on <hkern> */ static final String[] REQUIRED_TRAITS = {SVGConstants.SVG_K_ATTRIBUTE}; /** * Unicode ranges */ protected int[][] u1, u2; /** * Name matches */ protected String[] g1, g2; /** * Adjusted horizontal advance */ protected float k; /** * For use by <code>Font</code> */ protected HKern nextHKern; /** * Constructor. * * @param ownerDocument this element's owner <code>DocumentNode</code> */ public HKern(final DocumentNode ownerDocument) { super(ownerDocument); } /** * @return the SVGConstants.SVG_HKERN_TAG value */ public String getLocalName() { return SVGConstants.SVG_HKERN_TAG; } /** * Used by <code>DocumentNode</code> to create a new instance from * a prototype <code>HKern</code>. * * @param doc the <code>DocumentNode</code> for which a new node is * should be created. * @return a new <code>HKern</code> for the requested document. */ public ElementNode newInstance(final DocumentNode doc) { return new HKern(doc); } /** * Sets this kerning entry's first match * * @param u1 the new matching unicode ranges. * * @throws IllegalArgumentException if the input array has an entry which * is not of length 2. */ public void setU1(final int[][] u1) { if (u1 != null) { for (int i = 0; i < u1.length; i++) { if (u1[i] == null || u1[i].length != 2) { throw new IllegalArgumentException(); } } } if (equal(u1, this.u1)) { return; } modifyingNode(); this.u1 = u1; modifiedNode(); } /** * Sets this kerning entry's second match * * @param u2 the new matching unicode ranges. * * @throws IllegalArgumentException if the input array has an entry which * is not of length 2. */ public void setU2(final int[][] u2) { if (u2 != null) { for (int i = 0; i < u2.length; i++) { if (u2[i] == null || u2[i].length != 2) { throw new IllegalArgumentException(); } } } if (equal(u2, this.u2)) { return; } modifyingNode(); this.u2 = u2; modifiedNode(); } /** * Sets this kerning entry first glyph names matches. * * @param g1 the list of matching names. */ public void setG1(final String[] g1) { if (equal(g1, this.g1)) { return; } modifyingNode(); this.g1 = g1; modifiedNode(); } /** * Sets this kerning entry second glyph names matches. * * @param g2 the list of matching names. */ public void setG2(final String[] g2) { if (equal(g2, this.g2)) { return; } modifyingNode(); this.g2 = g2; modifiedNode(); } /** * Sets this kernixng entry's advance. * * @param k the new kerning advance */ public void setK(final float k) { if (this.k == k) { return; } modifyingNode(); this.k = k; modifiedNode(); } /** * @return this kerning entry's advance. */ public float getK() { return k; } /** * @return this node's matching unicode ranges for the first glyph */ public int[][] getU1() { return u1; } /** * @return this node's matching unicode ranges for the second glyph */ public int[][] getU2() { return u2; } /** * @return this node's matching glyphs names for the first glyph */ public String[] getG1() { return g1; } /** * @return this node's matching glyph names for the second glyph */ public String[] getG2() { return g2; } /** * Checks if the input glyph is a match for u1 or g1. * * @param g the glyph to match. * @return true if the unicode range or glyph name of the input * <code>Glyph</code> matches the kerning's u1 or g1 * setting. */ public boolean matchesFirst(final Glyph g) { return matches(g, u1, g1); } /** * Checks if the input glyph is a match for u2 or g2. * * @param g the glyph to match. * @return true if the unicode range or glyph name of the input * <code>Glyph</code> matches the kerning's u2 or g2 * setting. */ public boolean matchesSecond(final Glyph g) { return matches(g, u2, g2); } /** * Checks if the input glyph is a match for the input * set of unicode ranges and/or glyph names. * * @param glyph the glyph to match. * @param u the set of unicode ranges * @param g the set of glyph names. * @return true if there is a match. */ boolean matches(final Glyph glyph, final int[][] u, final String[] g) { String gu = glyph.getUnicode(); String[] gg = glyph.getGlyphName(); if (u != null && gu != null && gu.length() == 1) { char c = gu.charAt(0); for (int i = 0; i < u.length; i++) { if (c >= u[i][0] && c <= u[i][1]) { return true; } } } if (gg != null && g != null) { for (int i = 0; i < gg.length; i++) { String gn = gg[i]; for (int j = 0; j < g.length; j++) { if (g[j].equals(gn)) { return true; } } } } return false; } /** * HKern handles the u1, u2, g1, g2 and k traits. * * @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_U1_ATTRIBUTE == traitName || SVGConstants.SVG_U2_ATTRIBUTE == traitName || SVGConstants.SVG_G1_ATTRIBUTE == traitName || SVGConstants.SVG_G2_ATTRIBUTE == traitName || SVGConstants.SVG_K_ATTRIBUTE == traitName) { return true; } return super.supportsTrait(traitName); } /** * @return an array of traits that are required by this element. */ public String[] getRequiredTraits() { return REQUIRED_TRAITS; } /** * HKern handles the u1, u2, g1, g2 and k traits. * * @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_U1_ATTRIBUTE == name) { return unicodeRangeToStringTrait(u1); } else if (SVGConstants.SVG_U2_ATTRIBUTE == name) { return unicodeRangeToStringTrait(u2); } else if (SVGConstants.SVG_G1_ATTRIBUTE == name) { return toStringTrait(g1); } else if (SVGConstants.SVG_G2_ATTRIBUTE == name) { return toStringTrait(g2); } else if (SVGConstants.SVG_K_ATTRIBUTE == name) { return Float.toString(k); } else { return super.getTraitImpl(name); } } /** * HKern handles the u1, u2, g1, g2 and k traits. * * @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_U1_ATTRIBUTE == name) { checkWriteLoading(name); setU1(parseUnicodeRangeTrait(name, value)); } else if (SVGConstants.SVG_U2_ATTRIBUTE == name) { checkWriteLoading(name); setU2(parseUnicodeRangeTrait(name, value)); } else if (SVGConstants.SVG_G1_ATTRIBUTE == name) { checkWriteLoading(name); setG1(parseStringArrayTrait(name, value, SVGConstants.COMMA_STR)); } else if (SVGConstants.SVG_G2_ATTRIBUTE == name) { checkWriteLoading(name); setG2(parseStringArrayTrait(name, value, SVGConstants.COMMA_STR)); } else if (SVGConstants.SVG_K_ATTRIBUTE == name) { checkWriteLoading(name); setK(parseFloatTrait(name, value)); } else { super.setTraitImpl(name, value); } } }