Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/anotherbigidea/flash/movie/Font.java


1   /****************************************************************
2    * Copyright (c) 2001, David N. Main, All rights reserved.
3    * 
4    * Redistribution and use in source and binary forms, with or
5    * without modification, are permitted provided that the 
6    * following conditions are met:
7    *
8    * 1. Redistributions of source code must retain the above 
9    * copyright notice, this list of conditions and the following 
10   * disclaimer. 
11   * 
12   * 2. Redistributions in binary form must reproduce the above 
13   * copyright notice, this list of conditions and the following 
14   * disclaimer in the documentation and/or other materials 
15   * provided with the distribution.
16   * 
17   * 3. The name of the author may not be used to endorse or 
18   * promote products derived from this software without specific 
19   * prior written permission. 
20   * 
21   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY 
22   * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
23   * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
24   * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
25   * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
26   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
27   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
28   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
29   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
30   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
31   * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
32   * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33   ****************************************************************/
34  package com.anotherbigidea.flash.movie;
35  
36  import java.io.IOException;
37  import java.util.ArrayList;
38  import java.util.HashMap;
39  import java.util.Iterator;
40  import java.util.List;
41  
42  import com.anotherbigidea.flash.SWFConstants;
43  import com.anotherbigidea.flash.interfaces.SWFTagTypes;
44  import com.anotherbigidea.flash.interfaces.SWFVectors;
45  import com.anotherbigidea.flash.structs.Rect;
46  
47  /**
48   * A Font Symbol.
49   * 
50   * The Font references a FontDefinition object from which it takes the glyph
51   * definitions it needs.
52   */
53  public class Font extends Symbol 
54  {
55      public class NoGlyphException extends Exception
56      {
57          public int code;
58          
59          public NoGlyphException( int code )
60          {
61              super( "The font does not have a glyph definition for code " + code );
62              this.code = code;
63          }
64      }
65      
66      /**
67       * A set of contiguous characters in one font.
68       */
69      public class Chars
70      {
71          protected String chars;
72          protected double size;
73          protected int[] indices;
74          protected int[] advances;
75          
76          protected double totalAdvance; //total advance
77          protected double ascent;
78          protected double descent;
79          protected double leftMargin;
80          protected double rightMargin;
81          
82          public String toString()        { return chars; }
83          public Font   getFont()         { return Font.this; }
84          public double getSize()         { return size; }
85          public double getTotalAdvance() { return totalAdvance; }
86          public double getAscent()       { return ascent; }
87          public double getDescent()      { return descent; }
88          
89          /**
90           * The left margin is the difference between the origin of the
91           * first glyph and the left edge of its geometry
92           */
93          public double getLeftMargin()   { return leftMargin; }
94          
95          /**
96           * The right margin is the different between the total advance and
97           * the right edge of the geometry of the last glyph
98           */
99          public double getRightMargin()  { return rightMargin; }
100         
101         /**
102          * @param chars the characters to display (displayable chars only - i.e. no newlines, tabs etc..)
103          * @param font may be null if no change of font is required
104          * @param size point-size - only relevant if font is not null
105          * @param color may be null if no color change is required.  May be AlphaColor.
106          * @param x new X position for text - only valid if hasX is true
107          * @param y new Y position for text - only valid if hasY is true
108          */
109         protected Chars( String chars, double size ) throws NoGlyphException 
110         {
111             this.chars = chars;
112             this.size  = size;
113             init();
114         }        
115         
116         protected void init() throws NoGlyphException 
117         {            
118             //--Figure out the indices and advances
119             char[] codes = chars.toCharArray();
120             indices  = new int[ codes.length ];
121             advances = new int[ codes.length ];
122 
123             double maxAscent  = 0.0;
124             double maxDescent = 0.0;
125             
126             double scale = size * SWFConstants.TWIPS / 1024.0;
127         
128             for( int i = 0; i < codes.length; i++ )
129             {
130                 int code = (int)codes[i];
131                 
132                 int[] index = new int[1];
133                 FontDefinition.Glyph glyph = getGlyph( code, index );
134                 
135                 indices[i] = index[0];
136                 
137                 if( glyph != null )
138                 {
139                     Shape shape = glyph.getShape();
140                     
141                     double[] outline = shape.getBoundingRectangle();
142                     double x1 = outline[0] * scale;
143                     double y1 = outline[1] * scale;
144                     double x2 = outline[2] * scale;
145                     double y2 = outline[3] * scale;      
146 
147                     if( maxAscent  < -y1 ) maxAscent  = -y1;
148                     if( maxDescent <  y2 ) maxDescent =  y2;
149                     
150                     double advance = glyph.getAdvance() * scale;
151                     if( advance == 0 ) advance = x2 - x1;
152 
153                     //Kerning adjustment
154                     if( i < codes.length-1 )
155                     {
156                         advance += (fontDef.getKerningOffset( code, (int)codes[i+1] ) * scale );
157                     }
158                     
159                     totalAdvance += advance;
160                                     
161                     advances[i] = (int)(advance * SWFConstants.TWIPS); 
162                     
163                     if( i == 0 ) leftMargin = -y1;
164                     if( i == codes.length - 1 ) rightMargin = x2 - advance;
165                 }
166             }
167 
168             ascent = fontDef.getAscent() * scale;
169             if( ascent == 0.0 ) ascent = maxAscent;
170             
171             descent = fontDef.getDescent() * scale;
172             if( descent == 0.0 ) descent = maxDescent;            
173         }        
174     }    
175     
176     protected Object font1Key = new Object(); //used in movie defined symbols lookup
177     protected Object font2Key = new Object(); //used in movie defined symbols lookup
178     
179     protected FontDefinition fontDef;
180     protected HashMap glyphs  = new HashMap(); //glyphs used by this font
181     protected HashMap indices = new HashMap(); //glyph indices
182     protected ArrayList glyphList = new ArrayList();
183     protected int languageCode = SWFConstants.LANGUAGE_CODE_NONE;
184     
185     public int getLanguageCode() { return languageCode; }
186     public void setLanguageCode( int code ) { languageCode = code; }
187     
188     public FontDefinition getDefinition() { return fontDef; }
189     
190     public Font( FontDefinition fontDef )
191     {
192         this.fontDef = fontDef;
193     }
194     
195     public List getGlyphList() { return glyphList; }
196     
197     /**
198      * Load the glyphs for the characters in the given string from the
199      * FontDefinition into this font.
200      * @exception NoGlyphException
201      */
202     public void loadGlyphs( String chars ) throws NoGlyphException 
203     {
204         char[] chs = chars.toCharArray();
205         for( int i = 0; i < chs.length; i++ )
206         {
207             getGlyph( chs[i], null );
208         }
209     }
210 
211     /**
212      * Load all glyphs from the font definition
213      */
214     public void loadAllGlyphs()
215     {
216         ArrayList list = fontDef.getGlyphList();
217         
218         for( Iterator it = list.iterator(); it.hasNext(); )
219         {
220             FontDefinition.Glyph g = (FontDefinition.Glyph)it.next();
221             
222             addGlyph( g );
223         }
224     }
225     
226     /**
227      * Get the Chars instance for the given string at the given font size
228      */
229     public Chars chars( String chars, double fontSize )
230         throws NoGlyphException 
231     {
232         return new Chars( chars, fontSize );
233     }
234     
235     protected FontDefinition.Glyph getGlyph( int code, int[] index )
236         throws NoGlyphException 
237     {
238         Integer codeI = new Integer(code);
239         FontDefinition.Glyph g = (FontDefinition.Glyph)glyphs.get( codeI );
240         
241         if( g != null ) 
242         {
243             if( index != null )
244             {
245                 Integer idx = (Integer)indices.get( codeI );
246                 index[0] = idx.intValue();
247             }
248             
249             return g;
250         }
251         
252         g = fontDef.getGlyph( code );
253         
254         if( g == null ) throw new NoGlyphException( code );
255         
256         int idx = addGlyph( g );
257         if( index != null ) index[0] = idx;
258 
259         return g;
260     }
261 
262     /**
263      * Add a glyph and return the index
264      */
265     public int addGlyph( FontDefinition.Glyph glyph )
266     {
267         int idx = glyphs.size();
268         
269         if( glyph.getCode() > 0 )
270         {
271             Integer codeI = new Integer( glyph.getCode() );
272             indices.put( codeI, new Integer( idx ));
273             glyphs.put( codeI, glyph );
274         }
275         
276         glyphList.add( glyph );
277         
278         return idx;
279     }
280     
281     /**
282      * Set the code for the glyph at the given index
283      */
284     public void setCode( int index, int code )
285     {
286         if( index >= glyphList.size() ) return;
287 
288         FontDefinition.Glyph g = (FontDefinition.Glyph)glyphList.get( index );
289         g.setCode( code );
290         
291         Integer codeI = new Integer( code );
292         indices.put( codeI, new Integer( index ));
293         glyphs.put( codeI, g );        
294     }
295     
296     protected int define( boolean textFont, Movie movie, SWFTagTypes tagwriter )
297         throws IOException
298     {
299         Integer integerId = textFont ? 
300                                 (Integer)movie.definedSymbols.get( font1Key ) :
301                                 (Integer)movie.definedSymbols.get( font2Key );
302                 
303         if( integerId == null )
304         {
305             if( textFont )
306             {
307                 integerId = new Integer( defineFont1( movie, tagwriter ) );
308                 movie.definedSymbols.put( font1Key, integerId );
309             }
310             else
311             {
312                 integerId = new Integer( defineFont2( movie, tagwriter ) );
313                 movie.definedSymbols.put( font2Key, integerId );
314             }
315         }
316 
317         id = integerId.intValue();
318         return id;        
319     }
320 
321     protected int defineFont1( Movie movie, SWFTagTypes tagwriter ) 
322         throws IOException
323     {
324         int id = getNextId( movie );
325         
326         SWFVectors vecs = tagwriter.tagDefineFont( id, glyphList.size() );
327         
328         for( Iterator it = glyphList.iterator(); it.hasNext(); )
329         {
330             FontDefinition.Glyph g = (FontDefinition.Glyph)it.next();
331             
332             Shape s = g.getShape();
333             
334             s.writeGlyph( vecs );
335         }
336       
337         if( fontDef.getName() != null )
338         {
339             int flags = 0;
340 
341             if( fontDef.isUnicode()  ) flags |= SWFConstants.FONT_UNICODE;
342             if( fontDef.isShiftJIS() ) flags |= SWFConstants.FONT_SHIFTJIS;
343             if( fontDef.isAnsi()     ) flags |= SWFConstants.FONT_ANSI;
344             if( fontDef.isItalic()   ) flags |= SWFConstants.FONT_ITALIC;
345             if( fontDef.isBold()     ) flags |= SWFConstants.FONT_BOLD;
346         
347           //write fontInfo2 for Flash MX+, if needed
348             if( movie.getVersion() >= SWFConstants.FLASH_MX_VERSION 
349              && languageCode != SWFConstants.LANGUAGE_CODE_NONE ) {               
350         tagwriter.tagDefineFontInfo2( id, fontDef.getName(), flags, getCodes(), languageCode );
351             } else {              
352         tagwriter.tagDefineFontInfo( id, fontDef.getName(), flags, getCodes() );              
353             }            
354         }
355         
356         return id;
357     }
358     
359     protected int defineFont2( Movie movie, SWFTagTypes tagwriter ) 
360         throws IOException
361     {
362         int id = getNextId( movie );
363 
364         int glyphCount  = glyphList.size();
365         int[]  codes    = new int [ glyphCount ];
366         Rect[] bounds   = new Rect[ glyphCount ];
367         int[]  advances = new int [ glyphCount ];
368         
369         //--Gather glyph info
370         int i = 0;
371         for( Iterator it = glyphList.iterator(); it.hasNext(); )
372         {
373             FontDefinition.Glyph g = (FontDefinition.Glyph)it.next();
374             
375             codes[i]    = g.getCode();
376             advances[i] = (int)(g.getAdvance() * SWFConstants.TWIPS);
377             
378             double[] bound = g.getShape().getBoundingRectangle();
379             
380             bounds[i] = new Rect( (int)(bound[0] * SWFConstants.TWIPS),
381                                   (int)(bound[1] * SWFConstants.TWIPS),
382                                   (int)(bound[2] * SWFConstants.TWIPS),
383                                   (int)(bound[3] * SWFConstants.TWIPS) );
384             
385             i++;
386         }
387         
388         //--Gather kerning info
389         ArrayList kerns = fontDef.getKerningPairList();
390         int kernCount = kerns.size();
391         int[] kern1   = new int[ kernCount ];
392         int[] kern2   = new int[ kernCount ];
393         int[] kernOff = new int[ kernCount ];
394         
395         i = 0;
396         for( Iterator it = kerns.iterator(); it.hasNext(); )
397         {
398             FontDefinition.KerningPair pair = (FontDefinition.KerningPair)it.next();
399             
400             kern1[i]   = pair.getCode1();
401             kern2[i]   = pair.getCode2();
402             kernOff[i] = (int)(pair.getAdjustment() * SWFConstants.TWIPS );
403                 
404             i++;
405         }
406         
407         int flags = 0;
408         if( fontDef.hasMetrics() ) flags |= SWFConstants.FONT2_HAS_LAYOUT;
409         if( fontDef.isShiftJIS() ) flags |= SWFConstants.FONT2_SHIFTJIS;
410         if( fontDef.isUnicode()  ) flags |= SWFConstants.FONT2_UNICODE;
411         if( fontDef.isAnsi()     ) flags |= SWFConstants.FONT2_ANSI;
412         if( fontDef.isItalic()   ) flags |= SWFConstants.FONT2_ITALIC;
413         if( fontDef.isBold()     ) flags |= SWFConstants.FONT2_BOLD; 
414         
415         SWFVectors vecs = tagwriter.tagDefineFont2( 
416                                   id, flags, fontDef.getName(), glyphCount,
417                                   (int)(fontDef.getAscent() * SWFConstants.TWIPS),
418                                   (int)(fontDef.getDescent() * SWFConstants.TWIPS),
419                                   (int)(fontDef.getLeading() * SWFConstants.TWIPS),
420                                   codes, advances, bounds, kern1, kern2, kernOff );
421 
422         for( Iterator it = glyphList.iterator(); it.hasNext(); )
423         {
424             FontDefinition.Glyph g = (FontDefinition.Glyph)it.next();
425             
426             Shape s = g.getShape();
427             
428             s.writeGlyph( vecs );
429         }
430         
431         return id;
432     }
433     
434     /**
435      * Get the codes of the current set of glyphs
436      */
437     protected int[] getCodes()
438     {
439         int[] codes = new int[glyphList.size()];
440         
441         for( int i = 0; i < codes.length; i++ )
442         {
443             FontDefinition.Glyph g = (FontDefinition.Glyph)glyphList.get(i);
444             codes[i] = g.getCode();
445         }
446         
447         return codes;
448     }
449     
450     protected int defineSymbol( Movie movie, 
451                                 SWFTagTypes timelineWriter,
452                                 SWFTagTypes definitionwriter ) throws IOException
453     {
454         return id;
455     }
456 }