001: /* *****************************************************************************
002: * Font.java
003: * ****************************************************************************/
004:
005: /* J_LZ_COPYRIGHT_BEGIN *******************************************************
006: * Copyright 2001-2006 Laszlo Systems, Inc. All Rights Reserved. *
007: * Use is subject to license terms. *
008: * J_LZ_COPYRIGHT_END *********************************************************/
009:
010: /*
011: * $Id: Font.java,v 1.3 2002/07/12 07:46:51 skavish Exp $
012: *
013: * ==========================================================================
014: *
015: * The JGenerator Software License, Version 1.0
016: *
017: * Copyright (c) 2000 Dmitry Skavish (skavish@usa.net). All rights reserved.
018: *
019: * Redistribution and use in source and binary forms, with or without
020: * modification, are permitted provided that the following conditions are met:
021: *
022: * 1. Redistributions of source code must retain the above copyright
023: * notice, this list of conditions and the following disclaimer.
024: *
025: * 2. Redistributions in binary form must reproduce the above copyright
026: * notice, this list of conditions and the following disclaimer in
027: * the documentation and/or other materials provided with the
028: * distribution.
029: *
030: * 3. The end-user documentation included with the redistribution, if
031: * any, must include the following acknowlegement:
032: * "This product includes software developed by Dmitry Skavish
033: * (skavish@usa.net, http://www.flashgap.com/)."
034: * Alternately, this acknowlegement may appear in the software itself,
035: * if and wherever such third-party acknowlegements normally appear.
036: *
037: * 4. The name "The JGenerator" must not be used to endorse or promote
038: * products derived from this software without prior written permission.
039: * For written permission, please contact skavish@usa.net.
040: *
041: * 5. Products derived from this software may not be called "The JGenerator"
042: * nor may "The JGenerator" appear in their names without prior written
043: * permission of Dmitry Skavish.
044: *
045: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
046: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
047: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
048: * DISCLAIMED. IN NO EVENT SHALL DMITRY SKAVISH OR THE OTHER
049: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
050: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
051: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
052: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
053: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
054: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
055: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
056: * SUCH DAMAGE.
057: *
058: */
059:
060: package org.openlaszlo.iv.flash.api.text;
061:
062: import org.openlaszlo.iv.flash.parser.Parser;
063: import org.openlaszlo.iv.flash.util.*;
064: import org.openlaszlo.iv.flash.cache.*;
065: import org.openlaszlo.iv.flash.api.*;
066: import org.openlaszlo.iv.flash.api.shape.*;
067: import java.io.*;
068: import java.awt.geom.Rectangle2D;
069:
070: /**
071: * This class defines flash font.
072: * <P>
073: * Flash text has been designed to be completely device independent.
074: * Text is guaranteed to look exactly the same on every device, regardless
075: * of which fonts are installed on the client machine.
076: * The SWF format achieves this by including the exact shape of every letter,
077: * number (or other text character) used in the movie. These character shape
078: * definitions are called glyphs.
079: * <P>
080: * Defining each and every glyph increases the size of a SWF file,
081: * particularly if the font is complex. However, it is a necessary tradeoff.
082: * At design time, Flash knows nothing about the capabilities of the client device,
083: * therefore glyphs must always be included in the SWF file, even if the desired font
084: * is already on the client machine.
085: * <P>
086: * To guarantee text is reproduced correctly, SWF also includes the exact position
087: * of every character in a text block. Again, this adds to the file size, but allows
088: * sophisticated text layout effects (like kerning and text wrapping) without requiring
089: * a complex layout engine built into the Flash player.
090: * <P>
091: *
092: * <h4>Glyph Definitions</h4>
093: * <P>
094: * Glyphs are defined once in a standard coordinate
095: * space called the EM square. The same set of glyphs are
096: * used for every point size of a given font. To render a glyph
097: * at different point sizes, the Flash player scales the glyph
098: * from EM coordinates to point-size coordinates.
099: * <P>
100: * Flash fonts do not include any hinting information for improving
101: * the quality of small font sizes. However, antialiasing dramatically
102: * improves the legibility of down-scaled text. Flash text remains
103: * legible down to about 12-points (viewed at 100%). Below that, text
104: * may appear fuzzy and blurred. In any case, it is rare for Flash movies
105: * to be used for large bodies of text with small point sizes.
106: * <P>
107: * TrueType fonts can be readily converted to SWF glyphs. A simple
108: * algorithm can replace the Quadratic B-splines (used by TrueType) with
109: * Quadratic Bezier curves (used by SWF).
110: * <P>
111: *
112: * <H4>The EM Square</H4>
113: * <P>
114: * The EM square is an imaginary square that
115: * is used to size and align glyphs. The EM square is
116: * generally large enough to completely contain all glyphs,
117: * including accented glyphs. It includes the font's ascent,
118: * descent, and some extra spacing to prevent lines of text from
119: * colliding.
120: * <P>
121: * SWF glyphs are always defined on an EM square of 1024 by 1024
122: * units. Glyphs from other sources (such as TrueType fonts) may
123: * be defined on a different EM square. To use these glyphs in SWF,
124: * they should be scaled to fit an EM square of 1024.
125: * <P>
126: *
127: * <H4>Kerning and Advance Values</H4>
128: * <P>
129: * Kerning defines the horizontal distance between two glyphs.
130: * This distance may be smaller or larger than the width of the
131: * left-hand glyph. Some kerning pairs are more aesthetically
132: * pleasing if they are moved closer together.
133: * <P>
134: * SWF stores kerning information as an advance value.
135: * That is, the horizontal advance from one glyph to another.
136: * SWF stores an advance value for every character in a text block.
137: * <P>
138: *
139: * <H4>The DefineFont Tag</H4>
140: * <P>
141: * The <CODE>DefineFont</CODE> tag defines the shape outlines of
142: * each glyph used in a particular font. Only the glyphs that are
143: * used by subsequent <CODE>DefineText</CODE> tags are actually defined.
144: * <P>
145: * The FontId uniquely identifies the font. It can be used by subsequent
146: * <CODE>DefineText</CODE> tags to select the font.
147: * <P>
148: * The offset table and shape table are used together. These tables
149: * have the same number of entries, and there is a one-to-one ordering match
150: * between the order of the offsets, and the order of the shapes.
151: * The offset table points to locations in the shape table. Each offset entry
152: * stores the difference (in bytes) between the start of the offset table and
153: * the location of the corresponding shape:
154: * <CODE><pre>
155: * Location of ShapeRecord[n] = StartOfOffsetTable + OffsetTable[n]
156: * </PRE></CODE>
157: * Because the <CODE>ShapeTable</CODE> immediately follows the <CODE>OffsetTable</CODE>,
158: * the number of entries in both tables can be inferred by dividing
159: * the first offset by two:
160: * <CODE><PRE>
161: * Shape count = OffsetTable[0] / 2
162: * </PRE></CODE>
163: * <P>
164: *
165: * <H4>Mapping to Native Fonts</H4>
166: * <P>
167: * SWF also supports the use of native fonts. Rather than using the
168: * glyph outlines in the DefineFont tag, fonts can be rendered using
169: * the client machine's font engine. Since most native font engines
170: * include hinting techniques, they may produce better results at very
171: * small point sizes.
172: * <P>
173: * The DefineFontInfo tag defines the mapping of a Flash font to a native font.
174: * It includes the font name, font style - bold, italic, or plain, and the
175: * encoding scheme used - ANSI, Unicode or ShiftJIS. It also defines a mapping
176: * of glyph indices to character codes. Thus if 'a' were the first character in
177: * your DefineFont tag, the DefineFontInfo tag would map index zero to the character
178: * code for 'a'.
179: * <P>
180: *
181: * <H4>DefineFont2</H4>
182: * <P>
183: * The DefineFont2 tag extends the functionality of DefineFont.
184: * Enhancements include:
185: * <UL>
186: * <LI>32-bit entries in the OffsetTable, for fonts with more than 64K glyphs.
187: * <li>Mapping to native fonts, by incorporating all the functionality of DefineFontInfo.
188: * <LI>Ascent, descent and leading information.
189: * <LI>An advance table that defines the advance for each glyph (in EM square coordinates).
190: * <LI>A bounds table that defines the bounding-box of each glyph (in EM square coordinates).
191: * <LI>A table of kerning pairs that defines the distance between pairs of glyphs
192: * </UL>
193: * <P>
194: *
195: * <H4>Kerning Record</H4>
196: * <P>
197: * A Kerning Record defines the distance between two glyphs in EM
198: * square coordinates. Certain pairs of glyphs appear more aesthetically
199: * pleasing if they are moved closer together, or farther apart.
200: * The FontKerningCode1 and FontKerningCode2 fields are the character codes
201: * for the left and right characters. The FontKerningAdjustment field is a
202: * signed integer that defines the offset from the advance value of the
203: * left-hand character. The distance between two characters can be calculated
204: * like this:
205: * <CODE><PRE>
206: * Distance = FontAdvanceTable[ord(FontKerningCode1)] + FontKerningAdjustment
207: * </PRE></CODE>
208: *
209: * @author Dmitry Skavish
210: */
211: public final class Font {
212:
213: public static final int HAS_LAYOUT = 0x0080;
214: public static final int SHIFT_JIS = 0x0040;
215: public static final int UNICODE = 0x0020;
216: public static final int ANSI = 0x0010;
217: public static final int WIDE_OFFSETS = 0x0008;
218: public static final int WIDE_CODES = 0x0004;
219: public static final int ITALIC = 0x0002;
220: public static final int BOLD = 0x0001;
221:
222: public int flags; // flags
223: public String fontName; // font name
224: public String fontKey; // font key in cache (constructed from name and italic and bold flags)
225: public boolean cached = false; // true if font is cached
226: public byte[] fileBuffer; // glyph buffer (usually .swt file buffer)
227: public int[] glyphOffsets; // offsets of glyphs in glyph buffer
228: private BareShape[] glyphs; // shapes of all glyphs (these shapes are parsed from glyph buffer on demand)
229: public int[] codeTable; // array of codes of all characters of the font, index in this array is index in every other array
230: public int blankPos = 0; // index of 'space' (optimization)
231: public int[] advanceTable; // advance values for coresponding characters
232: public int ascent; // ascent of the font
233: public int descent; // descent of the font
234: public int leading; // leading of the font
235: // kerning tables
236: public int[] kernLeftCodes; // left characters in kerning table
237: public int[] kernRightCodes; // right characters in kerning table
238: public int[] kernAdjustment; // adjustment for left-right characters pair in kerning table
239: // not used stuff
240: public byte[] boundsBuffer; // bounds buffer
241: public int boundsOffset; // offset of bounds table in bounds buffer
242: public int boundsLength; // length of bounds table in bounds buffer
243: private Rectangle2D[] bounds; // parsed bounds (parsed from bounds buffer on demand)
244:
245: public Font() {
246: init(null);
247: }
248:
249: /**
250: * Creates empty font of given name.
251: *
252: * @param fontName name of the created font
253: * @return created empty font
254: */
255: public static Font createDummyFont(String fontName) {
256: Font f = new Font();
257: f.init(fontName);
258: return f;
259: }
260:
261: private void init(String aFontName) {
262: flags = HAS_LAYOUT | ANSI;
263: fontName = aFontName;
264: codeTable = new int[] { ' ' };
265: advanceTable = new int[] { 0 };
266: kernRightCodes = kernAdjustment = kernLeftCodes = new int[] { 0 };
267: fileBuffer = new byte[0];
268: glyphOffsets = new int[] { 0, 0 };
269: }
270:
271: /**
272: * Returns size of glyphs table.
273: * <P>
274: * Since glyph table has one additional element to hold
275: * offset after the last element then number of glyphs of the font
276: * is one less than the size of the glyph table.
277: *
278: * @return number of glyphs + 1
279: */
280: public int getGlyphTableSize() {
281: return glyphOffsets.length;
282: }
283:
284: /**
285: * Returns number of glyphs in the font
286: *
287: * @return number of glyphs
288: */
289: public int getNumGlyph() {
290: return glyphOffsets.length - 1;
291: }
292:
293: /**
294: * Returns index of specified character in codetable.
295: *
296: * @param ch specified character which index is to be returned
297: * @return index of specified character or -1 if there is no such character
298: */
299: public int getIndex(int ch) {
300: try {
301: int idx = ch - ' ' + blankPos;
302: if (codeTable[idx] == ch)
303: return idx;
304: } catch (ArrayIndexOutOfBoundsException e) {
305: }
306:
307: for (int i = 0; i < codeTable.length; i++) {
308: if (ch == codeTable[i])
309: return i;
310: }
311: return -1;
312: }
313:
314: /**
315: * Returns font name
316: *
317: * @return font name
318: */
319: public String getFontName() {
320: return fontName;
321: }
322:
323: /**
324: * Returns advance value for the character at specified index.
325: *
326: * @param idx index of character which advance value is to be returned
327: * @return advance value at specified index
328: */
329: public int getAdvanceValue(int idx) {
330: try {
331: return advanceTable[idx];
332: } catch (Exception e) {
333: return 0;
334: }
335: }
336:
337: /**
338: * Return adjustment from kerning table for specified pair of characters
339: * <p>
340: * MAY BE IMPROVED BY USING BINARY SEARCH !!!!!!!!!!!!
341: *
342: * @param ch_left left character
343: * @param ch_right right character
344: * @return adjustment value for the pair of character
345: */
346: public int getKerning(int ch_left, int ch_right) {
347: for (int i = 0; i < kernLeftCodes.length; i++) {
348: if (ch_left == kernLeftCodes[i]
349: && ch_right == kernRightCodes[i]) {
350: return kernAdjustment[i];
351: }
352: }
353: return 0;
354: }
355:
356: /**
357: * Returns array of shapes of all glyphs of this font
358: * <P>
359: * Shapes parsing can be delayed until they are requested by this call
360: *
361: * @return array of font glyphs
362: */
363: public BareShape[] getGlyphs() {
364: if (glyphs == null) {
365: Parser p = new Parser();
366: glyphs = new BareShape[getNumGlyph()];
367: for (int i = 0; i < glyphs.length; i++) {
368: p.init(fileBuffer, glyphOffsets[i], fileBuffer.length);
369: glyphs[i] = BareShape.parseBareShape(p);
370: }
371: }
372: return glyphs;
373: }
374:
375: /**
376: * Returns array of bounds of all glyphs of this font
377: *
378: * @return array of glyphs bounds
379: */
380: public Rectangle2D[] getGlyphBounds() {
381: if (bounds == null && boundsBuffer != null) {
382: Parser p = new Parser();
383: bounds = new Rectangle2D[getNumGlyph()];
384: p.init(boundsBuffer, boundsOffset, boundsBuffer.length);
385: for (int i = 0; i < bounds.length; i++) {
386: bounds[i] = p.getRect();
387: }
388: }
389: return bounds;
390: }
391:
392: /**
393: * Returns true if this font is large then the specified one
394: * <P>
395: * Font is considered large if it has more glyphs and layout
396: *
397: * @param f font to be compared with
398: * @return true if this font is large then the specified one
399: */
400: public boolean isLargeThan(Font f) {
401: if (getNumGlyph() > f.getNumGlyph())
402: return true;
403: if (getNumGlyph() < f.getNumGlyph())
404: return false;
405:
406: if ((f.flags & Font.HAS_LAYOUT) != 0
407: && (flags & Font.HAS_LAYOUT) == 0)
408: return false;
409: if ((flags & Font.HAS_LAYOUT) != 0
410: && (f.flags & Font.HAS_LAYOUT) == 0)
411: return true;
412:
413: return getFontSize() > f.getFontSize();
414: }
415:
416: /**
417: * Returns approximate size in bytes occupied by this font
418: * <p>
419: * Used mainly in caching algorithm.
420: *
421: * @return size of this font in bytes
422: */
423: public int getFontSize() {
424: int size = fileBuffer.length;
425: if (codeTable != null)
426: size += codeTable.length * 4;
427: if (advanceTable != null)
428: size += advanceTable.length * 4;
429: if (kernLeftCodes != null)
430: size += kernLeftCodes.length * 4;
431: if (kernRightCodes != null)
432: size += kernRightCodes.length * 4;
433: if (kernAdjustment != null)
434: size += kernAdjustment.length * 4;
435: if (boundsBuffer != null && boundsBuffer != fileBuffer)
436: size += boundsBuffer.length;
437: return size;
438: }
439:
440: public void printContent(PrintStream out, String indent, int id) {
441: out.println(indent + "Font: id=" + id + ", fontName="
442: + fontName + ", flags=" + Util.w2h(flags) + ", nGlyph="
443: + (glyphOffsets.length - 1));
444: /*
445: out.println( indent+" Glyphs:" );
446: BareShape[] shapes = getGlyphs();
447: for( int i=0; i<shapes.length; i++ ) {
448: out.println( indent+" i="+i+", code="+codeTable[i]);
449: shapes[i].printContent(out, indent+" ");
450: }
451: */
452: out.print(indent + " CodeTable:");
453: for (int i = 0; i < codeTable.length; i++) {
454: if ((i % 10) == 0) {
455: out.println();
456: out.print(indent + " ");
457: }
458: if ((flags & WIDE_CODES) != 0) {
459: out.print(Util.b2h(i) + "["
460: + Util.toPrint((char) codeTable[i]) + "(0x"
461: + Util.w2h(codeTable[i]) + ")] ");
462: } else {
463: out.print(Util.b2h(i) + "["
464: + Util.toPrint((char) codeTable[i]) + "(0x"
465: + Util.b2h(codeTable[i]) + ")] ");
466: }
467: }
468: out.println();
469: if ((flags & HAS_LAYOUT) != 0) {
470: out.println(indent + " hasLayout: ascent=" + ascent
471: + ", descent=" + descent + ", leading=" + leading);
472:
473: out.println(indent + " bounds: ");
474: Rectangle2D[] bounds = getGlyphBounds();
475: for (int i = 0; i < bounds.length; i++) {
476: out.println(indent + " index=" + i + ", rect: "
477: + bounds[i].toString());
478: }
479: out.print(indent + " AdvanceTable:");
480: for (int i = 0; i < advanceTable.length; i++) {
481: if ((i % 10) == 0) {
482: out.println();
483: out.print(indent + " ");
484: }
485: out.print(Util.b2h(i) + "[" + advanceTable[i] + "] ");
486: }
487: out.println();
488: out.println(indent + " KerningTable:");
489: for (int i = 0; i < kernLeftCodes.length; i++) {
490: int lc = kernLeftCodes[i];
491: int rc = kernRightCodes[i];
492: int ad = kernAdjustment[i];
493: if ((flags & WIDE_CODES) != 0) {
494: out.println(indent + " " + "["
495: + Util.toPrint((char) lc) + "(0x"
496: + Util.w2h(lc) + ")] --> ["
497: + Util.toPrint((char) rc) + "(0x"
498: + Util.w2h(rc) + ")] = " + ad);
499: } else {
500: out.println(indent + " " + "["
501: + Util.toPrint((char) lc) + "(0x"
502: + Util.b2h(lc) + ")] --> ["
503: + Util.toPrint((char) rc) + "(0x"
504: + Util.b2h(rc) + ")] = " + ad);
505: }
506: }
507: }
508: }
509:
510: /**
511: * Copy all data from this font to the specified one
512: *
513: * @param font specified font copy all the data to
514: */
515: public void copyTo(Font font) {
516: font.flags = flags;
517: font.fontName = fontName;
518: font.fontKey = fontKey;
519: font.fileBuffer = fileBuffer;
520: font.glyphOffsets = glyphOffsets;
521: font.glyphs = glyphs;
522: font.codeTable = codeTable;
523: font.blankPos = blankPos;
524: font.advanceTable = advanceTable;
525: font.boundsBuffer = boundsBuffer;
526: font.boundsOffset = boundsOffset;
527: font.boundsLength = boundsLength;
528: font.bounds = bounds;
529: font.ascent = ascent;
530: font.descent = descent;
531: font.leading = leading;
532: font.kernLeftCodes = kernLeftCodes;
533: font.kernRightCodes = kernRightCodes;
534: font.kernAdjustment = kernAdjustment;
535: /*
536: font.glyphOffsets = new int[glyphOffsets.length];
537: System.arraycopy(glyphOffsets, 0, font.glyphOffsets, 0, glyphOffsets.length);
538: font.codeTable = new int[codeTable.length];
539: System.arraycopy(codeTable, 0, font.codeTable, 0, codeTable.length);
540: font.advanceTable = new int[advanceTable.length];
541: System.arraycopy(advanceTable, 0, font.advanceTable, 0, advanceTable.length);
542: font.kernLeftCodes = new int[kernLeftCodes.length];
543: System.arraycopy(kernLeftCodes, 0, font.kernLeftCodes, 0, kernLeftCodes.length);
544: font.kernRightCodes = new int[kernRightCodes.length];
545: System.arraycopy(kernRightCodes, 0, font.kernRightCodes, 0, kernRightCodes.length);
546: font.kernAdjustment = new int[kernAdjustment.length];
547: System.arraycopy(kernAdjustment, 0, font.kernAdjustment, 0, kernAdjustment.length);*/
548: }
549:
550: }
|