001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package com.sun.perseus.model;
028:
029: import com.sun.perseus.util.SVGConstants;
030:
031: import com.sun.perseus.j2d.RenderGraphics;
032:
033: import org.w3c.dom.DOMException;
034:
035: /**
036: * A <tt>FontFace</tt> node corresponds to an SVG
037: * <tt><font-face></tt> element.
038: * <br />
039: * Like many other classes, <tt>FontFace</tt> extends <tt>ElementNode</tt>
040: * so that we can support mixed namespaces (i.e., unexpected content
041: * in our model).
042: * <br />
043: *
044: * @version $Id: FontFace.java,v 1.4 2006/04/21 06:37:12 st125089 Exp $
045: */
046: public class FontFace extends ElementNode {
047: /**
048: * Values when all the font weights are used
049: */
050: public static final int FONT_WEIGHT_ALL = TextNode.FONT_WEIGHT_100
051: | TextNode.FONT_WEIGHT_200 | TextNode.FONT_WEIGHT_300
052: | TextNode.FONT_WEIGHT_400 | TextNode.FONT_WEIGHT_500
053: | TextNode.FONT_WEIGHT_600 | TextNode.FONT_WEIGHT_700
054: | TextNode.FONT_WEIGHT_800 | TextNode.FONT_WEIGHT_900;
055:
056: /**
057: * Value whena all the font styles are used
058: */
059: public static final int FONT_STYLE_ALL = TextNode.FONT_STYLE_NORMAL
060: | TextNode.FONT_STYLE_ITALIC | TextNode.FONT_STYLE_OBLIQUE;
061:
062: /**
063: * Default value for units per em
064: */
065: public static final float UNITS_PER_EM_DEFAULT = 1000;
066:
067: /**
068: * Value when all the font sizes match
069: */
070: public static final float[] FONT_SIZE_ALL = null;
071:
072: /**
073: * List of font families that constitute a match
074: */
075: protected String[] fontFamilies;
076:
077: /**
078: * Describes the styles that constitute a match for this
079: * font. This can be a combination (or'd) of the various
080: * FONT_STYLE_XXX values.
081: */
082: protected int fontStyles = FONT_STYLE_ALL;
083:
084: /**
085: * Describes the weights that constitute a match for this
086: * font. This can be a combination (or'd) of the various
087: * FONT_WEIGHT_XXX values.
088: * The values FONT_WEIGHT_BOLDER and FONT_WEIGHT_LIGHTER
089: * are not allowed.
090: */
091: protected int fontWeights = FONT_WEIGHT_ALL;
092:
093: /**
094: * Font sizes that constitute a match for this font during
095: * font selection. A null value means that all font-sizes
096: * can be matched by this font
097: */
098: protected float[] fontSizes;
099:
100: /**
101: * Number of coordinate units on the em square.
102: * Defaults to 1000.
103: */
104: protected float unitsPerEm = UNITS_PER_EM_DEFAULT;
105:
106: /**
107: * Parent Font
108: * @see #setParent
109: */
110: protected Font font;
111:
112: /**
113: * The main scale factor needed to convert from the em square
114: * grid to the text's current position coordinate system.
115: * The transform built from the returned value is:
116: * <pre>
117: * scale 0 0
118: * 0 -scale 0
119: * 0 0 1
120: * </pre>
121: */
122: protected float emSquareScale = 1;
123:
124: /**
125: * @return the SVGConstants.SVG_FONT_FACE_TAG value
126: */
127:
128: public String getLocalName() {
129: return SVGConstants.SVG_FONT_FACE_TAG;
130: }
131:
132: /**
133: * Used by <code>DocumentNode</code> to create a new instance from
134: * a prototype <code>FontFace</code>.
135: *
136: * @param doc the <code>DocumentNode</code> for which a new node is
137: * should be created.
138: * @return a new <code>FontFace</code> for the requested document.
139: */
140: public ElementNode newInstance(final DocumentNode doc) {
141: return new FontFace(doc);
142: }
143:
144: /**
145: * @param newFontFamilies the new set of font families matched by this
146: * node. Note that the input array is used by reference after
147: * this call
148: */
149: public void setFontFamilies(final String[] newFontFamilies) {
150: if (equal(fontFamilies, newFontFamilies)) {
151: return;
152: }
153: modifyingNode();
154: this .fontFamilies = newFontFamilies;
155: modifiedNode();
156: }
157:
158: /**
159: * @return the array of font families matched by this node
160: */
161: public String[] getFontFamilies() {
162: return fontFamilies;
163: }
164:
165: /**
166: * @param newFontStyles the set of font styles mateched by this node.
167: */
168: public void setFontStyles(final int newFontStyles) {
169: if (newFontStyles == fontStyles) {
170: return;
171: }
172: modifyingNode();
173: this .fontStyles = newFontStyles;
174: modifiedNode();
175: }
176:
177: /**
178: * @return the set of font styles matched by this node. To test
179: * if a particular font style is supported, use the '&'
180: * operator.
181: */
182: public int getFontStyles() {
183: return fontStyles;
184: }
185:
186: /**
187: * @param newFontSizes the new set of font sizes matched by this node
188: */
189: public void setFontSizes(final float[] newFontSizes) {
190: if (equal(newFontSizes, fontSizes)) {
191: return;
192: }
193: modifyingNode();
194: this .fontSizes = newFontSizes;
195: modifiedNode();
196: }
197:
198: /**
199: * @return the array of font sizes matched by this node
200: */
201: public float[] getFontSizes() {
202: return fontSizes;
203: }
204:
205: /**
206: * @return the set of font weights matched by this node
207: */
208: public int getFontWeights() {
209: return fontWeights;
210: }
211:
212: /**
213: * @param newFontWeights the set of font weight value matched by this
214: * <tt>FontFace</tt>
215: */
216: public void setFontWeights(final int newFontWeights) {
217: if (newFontWeights == fontWeights) {
218: return;
219: }
220: modifyingNode();
221: this .fontWeights = newFontWeights;
222: modifiedNode();
223: }
224:
225: /**
226: * @param newUnitsPerEm this node's new <tt>unitsPerEm</tt> property
227: */
228: public void setUnitsPerEm(final float newUnitsPerEm) {
229: if (newUnitsPerEm == unitsPerEm) {
230: return;
231: }
232: modifyingNode();
233: this .unitsPerEm = newUnitsPerEm;
234: computeEmSquareScale();
235: modifiedNode();
236: }
237:
238: /**
239: * @return this node's <tt>unitsPerEm</tt> property
240: */
241: public float getUnitsPerEm() {
242: return unitsPerEm;
243: }
244:
245: /**
246: * The constructor computes the inital emSquareScale
247: *
248: * @param ownerDocument this element's owner <code>DocumentNode</code>
249: */
250: public FontFace(final DocumentNode ownerDocument) {
251: super (ownerDocument);
252: computeEmSquareScale();
253: }
254:
255: /**
256: * @return the current scale factor from the em grid
257: * to the text coordinate system. The scale is
258: * recomputed every time the unitsPerEm value changes.
259: * <pre>
260: * scale 0 0
261: * 0 -scale 0
262: * 0 0 1
263: * </pre>
264: */
265: public float getEmSquareScale() {
266: return emSquareScale;
267: }
268:
269: /**
270: * Computes the scale that should be applied to glyphs to
271: * transform glyph coordinates into the text coordiate system.
272: */
273: protected void computeEmSquareScale() {
274: // The number of units per em defines the number of
275: // units in 1x1 em square grid. Hence, the 1/unitsPerEm
276: // scale factor.
277: // The Y axis points up for the em square grid, which
278: // is why the Y axis scale is negative.
279: emSquareScale = 1f / unitsPerEm;
280: }
281:
282: /**
283: * If the parent is a <tt>Font</tt> object, keep track of it in the
284: * font member.
285: * @param parent this node's new parent
286: */
287: public void setParent(final ModelNode parent) {
288: super .setParent(parent);
289:
290: if (parent instanceof Font) {
291: this .font = (Font) parent;
292: } else {
293: this .font = null;
294: }
295: }
296:
297: /**
298: * @param s the character string
299: * @param index the index of the character for which to
300: * find a <tt>Glyph</tt>
301: * @return a Glyph if the FontFace can display the
302: * character at the requested index. Returns null
303: * if no matching glyph can be found.
304: */
305: Glyph canDisplay(final char[] s, final int index) {
306: return font.canDisplay(s, index);
307: }
308:
309: /**
310: * @return the Glyph that represents missing glyphs.
311: * This should *not* be null.
312: */
313: Glyph getMissingGlyph() {
314: return font.getMissingGlyph();
315: }
316:
317: /**
318: * The distance between the input <tt>refFontWeight</tt> and this
319: * <tt>FontFace</tt>'s <tt>fontWeights</tt>
320: *
321: * @param refFontWeight this method computes the distance between this
322: * <tt>FontFace</tt>'s fontWeight and the input <tt>fontWeight</tt>
323: * @return the distance to the input <tt>fontWeight</tt>
324: */
325: public int fontWeightDistance(final int refFontWeight) {
326: // This FontFace matches all, this is a zero distance
327: if (fontWeights == FONT_WEIGHT_ALL) {
328: return 0;
329: }
330:
331: // This FontFace matches the requested fontWeight,
332: // this is a zero distance.
333: if ((fontWeights & refFontWeight) != 0) {
334: return 0;
335: }
336:
337: // The requested fontWeight is not part of the ones
338: // matching exactly this FontFace. Compute how far is
339: // the closes match.
340: int dA = fontWeights;
341: int i = 0;
342: while ((dA & 0x01) == 0) {
343: dA >>= 1;
344: i++;
345: }
346:
347: dA = i;
348:
349: i = 0;
350: int curD = 100; // Infinity in this algorithm
351: int d = 100;
352:
353: for (i = 0; i < 9; i++) {
354: if (((refFontWeight >> i) & 0x01) != 0) {
355: d = (dA - i) * (dA - i);
356: if (d < curD) {
357: curD = d;
358: }
359: }
360: }
361:
362: return curD;
363: }
364:
365: /**
366: * FontFace handles the font-family, font-style, font-weight, font-size,
367: * and unitsPerEm attributes.
368: *
369: * @param traitName the name of the trait which the element may support.
370: * @return true if this element supports the given trait in one of the
371: * trait accessor methods.
372: */
373: boolean supportsTrait(final String traitName) {
374: if (SVGConstants.SVG_FONT_FAMILY_ATTRIBUTE == traitName
375: || SVGConstants.SVG_FONT_STYLE_ATTRIBUTE == traitName
376: || SVGConstants.SVG_FONT_WEIGHT_ATTRIBUTE == traitName
377: || SVGConstants.SVG_FONT_SIZE_ATTRIBUTE == traitName
378: || SVGConstants.SVG_UNITS_PER_EM_ATTRIBUTE == traitName) {
379: return true;
380: }
381:
382: return super .supportsTrait(traitName);
383: }
384:
385: /**
386: * FontFace handles the font-family, font-style, font-weight, font-size,
387: * and unitsPerEm attributes.
388: * Other traits are handled by the super class.
389: *
390: * @param name the requested trait name (e.g., "horiz-adv-x")
391: * @return the trait's value, as a string.
392: *
393: * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested
394: * trait is not supported on this element or null.
395: * @throws DOMException with error code TYPE_MISMATCH_ERR if requested
396: * trait's computed value cannot be converted to a String (SVG Tiny only).
397: */
398: public String getTraitImpl(final String name) throws DOMException {
399: if (SVGConstants.SVG_FONT_FAMILY_ATTRIBUTE == name) {
400: return toStringTraitQuote(getFontFamilies());
401: } else if (SVGConstants.SVG_FONT_STYLE_ATTRIBUTE == name) {
402: return fontStylesToStringTrait(getFontStyles());
403: } else if (SVGConstants.SVG_FONT_WEIGHT_ATTRIBUTE == name) {
404: return fontWeightsToStringTrait(getFontWeights());
405: } else if (SVGConstants.SVG_FONT_SIZE_ATTRIBUTE == name) {
406: if (getFontSizes() == FONT_SIZE_ALL) {
407: return SVGConstants.CSS_ALL_VALUE;
408: }
409: return toStringTrait(getFontSizes());
410: } else if (SVGConstants.SVG_UNITS_PER_EM_ATTRIBUTE == name) {
411: return Float.toString(getUnitsPerEm());
412: } else {
413: return super .getTraitImpl(name);
414: }
415: }
416:
417: /**
418: * FontFace handles the font-family, font-style, font-weight, font-size,
419: * and unitsPerEm attributes.
420: * Other traits are handled by the super class.
421: *
422: * @param name the trait's name (e.g., "units-per-em")
423: * @param value the new trait string value (e.g., "1000")
424: *
425: * @throws DOMException with error code NOT_SUPPORTED_ERROR if the requested
426: * trait is not supported on this element or null.
427: * @throws DOMException with error code TYPE_MISMATCH_ERR if the requested
428: * trait's value cannot be specified as a String
429: * @throws DOMException with error code INVALID_ACCESS_ERR if the input
430: * value is an invalid value for the given trait or null.
431: * @throws DOMException with error code NO_MODIFICATION_ALLOWED_ERR: if
432: * attempt is made to change readonly trait.
433: */
434: public void setTraitImpl(final String name, final String value)
435: throws DOMException {
436: if (SVGConstants.SVG_FONT_FAMILY_ATTRIBUTE == name) {
437: checkWriteLoading(name);
438: setFontFamilies(parseFontFamilyTrait(name, value));
439: } else if (SVGConstants.SVG_FONT_STYLE_ATTRIBUTE == name) {
440: checkWriteLoading(name);
441: setFontStyles(parseFontStylesTrait(name, value));
442: } else if (SVGConstants.SVG_FONT_WEIGHT_ATTRIBUTE == name) {
443: checkWriteLoading(name);
444: setFontWeights(parseFontWeightsTrait(name, value));
445: } else if (SVGConstants.SVG_FONT_SIZE_ATTRIBUTE == name) {
446: checkWriteLoading(name);
447: if (SVGConstants.CSS_ALL_VALUE.equals(value)) {
448: setFontSizes(FONT_SIZE_ALL);
449: } else {
450: setFontSizes(parseFloatArrayTrait(name, value));
451: }
452: } else if (SVGConstants.SVG_UNITS_PER_EM_ATTRIBUTE == name) {
453: checkWriteLoading(name);
454: setUnitsPerEm(parseFloatTrait(name, value));
455: } else {
456: super .setTraitImpl(name, value);
457: }
458: }
459:
460: // =========================================================================
461:
462: /**
463: * The FontFace.Match class is used to return a chain of
464: * sorted FontFace, according to the CSS2 Font Matching
465: * algorithm.
466: *
467: * @see DocumentNode#resolveFontFaces
468: */
469: static class Match {
470: /**
471: * The matching <code>FontFace</code>.
472: */
473: public FontFace fontFace;
474:
475: /**
476: * The font-weight distance. See the font matching algorithm
477: * in <code>DocumentNode</code>
478: */
479: public int distance;
480:
481: /**
482: * The next match in the chain.
483: */
484: public Match next;
485:
486: /**
487: * Constructor
488: *
489: * @param fontFace the matching <code>FontFace</code>
490: */
491: public Match(final FontFace fontFace) {
492: this .fontFace = fontFace;
493: }
494: }
495:
496: // =========================================================================
497:
498: }
|