KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openlaszlo > compiler > SWFWriter


1 /*****************************************************************************
2  * SWFWriter.java
3  * ****************************************************************************/

4
5 /* J_LZ_COPYRIGHT_BEGIN *******************************************************
6 * Copyright 2001-2004 Laszlo Systems, Inc. All Rights Reserved. *
7 * Use is subject to license terms. *
8 * J_LZ_COPYRIGHT_END *********************************************************/

9
10 package org.openlaszlo.compiler;
11
12 import org.openlaszlo.sc.ScriptCompiler;
13 import org.openlaszlo.server.LPS;
14 import org.openlaszlo.utils.ChainedException;
15 import org.openlaszlo.utils.FileUtils;
16 import org.openlaszlo.utils.ListFormat;
17 import org.openlaszlo.iv.flash.api.*;
18 import org.openlaszlo.iv.flash.api.action.*;
19 import org.openlaszlo.iv.flash.api.button.*;
20 import org.openlaszlo.iv.flash.api.image.*;
21 import org.openlaszlo.iv.flash.api.sound.*;
22 import org.openlaszlo.iv.flash.api.shape.*;
23 import org.openlaszlo.iv.flash.api.text.*;
24 import org.openlaszlo.iv.flash.util.*;
25 import org.openlaszlo.iv.flash.cache.*;
26
27 import org.openlaszlo.compiler.CompilationEnvironment;
28
29 import org.openlaszlo.media.*;
30
31 import java.io.*;
32 import java.util.*;
33 import java.lang.Math JavaDoc;
34 import java.lang.Character JavaDoc;
35
36 import org.jdom.Element;
37
38 // jgen 1.4
39
import java.awt.geom.Rectangle2D JavaDoc;
40
41 import org.apache.log4j.*;
42
43 /** Accumulates code, XML, and assets to a SWF object file.
44  *
45  * Once there's a second target, the methods here can be lifted into
46  * an API that SWFWriter implements. Rationale for not doing this
47  * now: It's easier to generalize from more than one instance, and
48  * having to change signatures twice for every refactoring is an
49  * unnecessary burden during early development.
50  *
51  * Make heavy use of JGenerator API.
52  *
53  * Properties documented in Compiler.getProperties.
54  */

55 class SWFWriter {
56     /** Stream to write to. */
57     private OutputStream mStream = null;
58     /** Movie being constructed. */
59     private SWFFile mFlashFile;
60     /** Fonts being collected. */
61     private FontsCollector mFontsCollector = new FontsCollector();
62     private FontsCollector mPreloaderFontsCollector = new FontsCollector();
63     private FontManager mFontManager = new FontManager();
64     /** True iff close() has been called. */
65     private boolean mCloseCalled = false;
66
67     private final CompilationEnvironment mEnv;
68
69     /** Set of resoures we're importing into the output SWF */
70     private final HashSet mMultiFrameResourceSet = new HashSet();
71
72     /** Constant */
73     private final int TWIP = 20;
74     
75     /** Total number of frames in the movie **/
76     private int mLastFrame = 0;
77     
78     /** Unique name supply for clip/js names */
79     protected SymbolGenerator mNameSupply = new SymbolGenerator("$LZ");
80
81     /** Properties */
82     private Properties mProperties;
83
84     /** Input text fontinfo map */
85     private final TreeMap mInputTextSet = new TreeMap();
86
87     /** Index of font table in the first frame */
88     private int mFontTableIndex = -1;
89
90     /** Index of resource table in the first frame */
91     private int mResourceTableIndex = -1;
92
93     /** Default font */
94     private Font mDefaultFont = null;
95     private String JavaDoc mDefaultFontName = null;
96     private String JavaDoc mDefaultFontFileName = null;
97     private String JavaDoc mDefaultBoldFontFileName = null;
98     private String JavaDoc mDefaultItalicFontFileName = null;
99     private String JavaDoc mDefaultBoldItalicFontFileName = null;
100     // TODO: [2003-12-08 bloch] need logic to set this to true
101
private boolean mDefaultFontUsedForMeasurement = false;
102
103     /** Flash format version */
104     private int mFlashVersion = 6;
105
106     /** frame rate of movie */
107     private int mFrameRate = 30;
108
109     /** Leading for text and input text */
110     private int mTextLeading = 2;
111
112     /** media cache for transcoding */
113     private CompilerMediaCache mCache = null;
114
115     /** <String,Resource> maps resource files to the Resources
116      * definition in the swf file. */

117     protected Map mResourceMap = new HashMap();
118     protected Map mClickResourceMap = new HashMap();
119     protected Map mMultiFrameResourceMap = new HashMap();
120
121     private Map mDeviceFontTable = new HashMap();
122
123     /** Logger */
124     private static Logger mLogger = org.apache.log4j.Logger.getLogger(SWFWriter.class);
125
126     /** Height for generated advance (width) table */
127     public static final int DEFAULT_SIZE = 8;
128
129     /**
130      * Initialize jgenerator
131      */

132     static {
133         org.openlaszlo.iv.flash.util.Util.init(LPS.getConfigDirectory());
134     }
135
136     /**
137      * Compiled ExpiryProgram
138      */

139     private static Program mExpiryProgram = null;
140
141     /** Canvas Height */
142     private int mHeight = 0;
143
144     /** Canvas Width */
145     private int mWidth = 0;
146
147     /** Width of the "root" text output object. Defaults to canvas width. */
148     private int mMaxTextWidth = 0;
149
150
151     /** Height of the "root" text output object. Defaults to canvas height. */
152     private int mMaxTextHeight = 0;
153
154     /** Has the preloader been added? */
155     private boolean mPreloaderAdded = false;
156
157     /** InfoXML */
158     private Element mInfo = new Element("swf");
159
160     /** Logger for jgenerator */
161     /**
162      * Initializes a SWFWriter with an OutputStream to which a new SWF
163      * will be written when <code>SWFWriter.close()</code> is called.
164      *
165      * @param stream A <code>java.io.OutputStream</code> that the
166      * movie will be written to.
167      * @param props list of properties
168      * @param cache media cache
169      * @param importLibrary If true, the compiler will add in the LaszloLibrary.
170      */

171     SWFWriter(Properties props, OutputStream stream,
172               CompilerMediaCache cache,
173               boolean importLibrary,
174               CompilationEnvironment env) {
175         this.mProperties = props;
176         this.mCache = cache;
177         this.mStream = stream;
178         this.mEnv = env;
179
180         String JavaDoc s;
181
182         s = mProperties.getProperty("swf.frame.rate", "30");
183         mFrameRate = Integer.parseInt(s);
184
185         s = mProperties.getProperty("swf.text.leading", "2");
186         mTextLeading = Integer.parseInt(s);
187
188         mFlashVersion = env.getSWFVersionInt();
189
190         try {
191             if (!importLibrary) {
192                 mFlashFile = new SWFFile(props);
193                 mFlashFile.setVersion(mFlashVersion);
194                 mFlashFile.setMainScript(new Script(1));
195             }
196
197         } catch (CompilationError e) {
198             throw new ChainedException(e);
199         }
200     }
201
202     public void importBaseLibrary(String JavaDoc library, CompilationEnvironment env) {
203
204         try {
205             File f = env.resolve(library, "");
206             mFlashFile = new SWFFile(f.getAbsolutePath(), mProperties);
207             mFlashFile.setVersion(mFlashVersion);
208             // Set the frame rate (shifted)
209
mFlashFile.setFrameRate(mFrameRate << 8);
210             
211             // mFlashFile.printContent(System.out);
212

213             if (LPS.isInternalBuild()) {
214                 long lfcModTime = f.lastModified();
215                 List newerFiles = new Vector(); // List<File>
216
List sourceDirs = new Vector(); // List<File>
217
sourceDirs.add(f.getParentFile());
218                 while (!sourceDirs.isEmpty()) {
219                     File ff = (File) sourceDirs.get(0);
220                     sourceDirs.remove(0);
221                     if (ff.isDirectory()) {
222                         sourceDirs.addAll(Arrays.asList(ff.listFiles()));
223                     } else if (ff.isFile() && ff.getName().endsWith(".as")) {
224                         long modTime = ff.lastModified();
225                         if (modTime > lfcModTime)
226                             newerFiles.add(ff.getName());
227                     }
228                 }
229                 if (!newerFiles.isEmpty()) {
230                     env.warn(f.getName() + " is older than " +
231                              new ListFormat().format(newerFiles));
232                 }
233             }
234         } catch (FileNotFoundException e) {
235             throw new ChainedException(e);
236         }
237     }
238     
239     FontManager getFontManager() {
240         return mFontManager;
241     }
242
243     public boolean isDeviceFont(String JavaDoc face) {
244         return (mDeviceFontTable.get(face) != null);
245     }
246
247     public void setDeviceFont(String JavaDoc face) {
248         mDeviceFontTable.put(face,"true");
249     }
250
251     public void setFontManager(FontManager fm) {
252         this.mFontManager = fm;
253     }
254
255     void addPreloaderScript(String JavaDoc script) {
256         if (mPreloaderAdded != true)
257             throw new RuntimeException JavaDoc("Preloader not added yet.");
258
259         addScript(script, 0);
260     }
261         
262     void addPreloader(CompilationEnvironment env) {
263         if (mPreloaderAdded == true)
264             throw new RuntimeException JavaDoc("Preloader already added.");
265
266         // TODO: [2004-03-04 bloch] maybe someday we have different versions of
267
// preloader.lzl (e.g. debug, profile).
268
File f;
269         try {
270             f = env.resolve("lzpreloader.lzl", "");
271         } catch (FileNotFoundException e) {
272             throw new ChainedException(e);
273         }
274         mFlashFile.addPreloaderFrame(f.getAbsolutePath());
275         mLastFrame = 1;
276
277         mPreloaderAdded = true;
278     }
279
280     /**
281      * Sets the canvas for the movie
282      *
283      * @param canvas
284      *
285      */

286     void setCanvas(Canvas canvas, String JavaDoc canvasConstructor) {
287         Rectangle2D JavaDoc r = new Rectangle2D.Double JavaDoc(
288             0, 0,
289             (canvas.getWidth()*TWIP),
290             (canvas.getHeight()*TWIP));
291
292         mFlashFile.setFrameSize(r);
293
294         int bgc = canvas.getBGColor();
295         int red = (bgc >> 16) & 0xff;
296         int green = (bgc >> 8) & 0xff;
297         int blue = (bgc) & 0xff;
298         Color c = new Color(red, green, blue);
299         SetBackgroundColor setbgc = new SetBackgroundColor(c);
300         mFlashFile.getMainScript().setBackgroundColor(setbgc);
301         
302         // NOTE: disable constant pool when compiling canvas constructor
303
// so that build id etc... are easy for qa to pick out.
304
Properties props = (Properties)mProperties.clone();
305         props.setProperty("disableConstantPool", "1");
306         byte[] action = ScriptCompiler.compileToByteArray(canvasConstructor, props);
307         Program program = new Program(action, 0, action.length);
308         mLogger.debug(" Adding a program of " + action.length + " bytes.");
309         addProgram(program);
310
311         // Set width and height properties for preloader...
312
mWidth = canvas.getWidth();
313         mHeight = canvas.getHeight();
314
315         mMaxTextWidth = canvas.getMaxTextWidth();
316         // If zero, default to canvas width
317
if (mMaxTextWidth == 0) {
318             mMaxTextWidth = mWidth;
319         }
320
321         mMaxTextHeight = canvas.getMaxTextHeight();
322         // If zero, default to canvas height
323
if (mMaxTextHeight == 0) {
324             mMaxTextHeight = mHeight;
325         }
326
327         // Get default font info
328
FontInfo fontInfo = canvas.getFontInfo();
329
330         mDefaultFontName = canvas.defaultFont;
331         mDefaultFontFileName = canvas.defaultFontFilename;
332         mDefaultBoldFontFileName = canvas.defaultBoldFontFilename;
333         mDefaultItalicFontFileName = canvas.defaultItalicFontFilename;
334         mDefaultBoldItalicFontFileName = canvas.defaultBoldItalicFontFilename;
335
336         Frame frame = mFlashFile.getMainScript().getFrameAt(mLastFrame);
337
338         // Make a bogus object for us to replace with the font and resource table
339
// actionscript at the end of compilation.
340
mFontTableIndex = frame.size();
341         frame.addFlashObject(new SetBackgroundColor(new Color(0, 0, 0)));
342         mResourceTableIndex = frame.size();
343         frame.addFlashObject(new SetBackgroundColor(new Color(0, 0, 0)));
344
345         mEnv.getCanvas().addInfo(mInfo);
346     }
347
348
349     /** Get default fonts and stuff from canvas; used for snippet compilation */
350     void setCanvasDefaults(Canvas canvas, CompilerMediaCache mc) {
351         Rectangle2D JavaDoc r = new Rectangle2D.Double JavaDoc(
352             0, 0,
353             (canvas.getWidth()*TWIP),
354             (canvas.getHeight()*TWIP));
355
356         mFlashFile.setFrameSize(r);
357         this.mCache = mc;
358         mWidth = canvas.getWidth();
359         mHeight = canvas.getHeight();
360         // Get default font info
361
FontInfo fontInfo = canvas.getFontInfo();
362         mDefaultFontName = canvas.defaultFont;
363         mDefaultFontFileName = canvas.defaultFontFilename;
364         mDefaultBoldFontFileName = canvas.defaultBoldFontFilename;
365         mDefaultItalicFontFileName = canvas.defaultItalicFontFilename;
366         mDefaultBoldItalicFontFileName = canvas.defaultBoldItalicFontFilename;
367     }
368
369
370     /** Create a program from the given script */
371     Program program(String JavaDoc script) {
372          byte[] action = ScriptCompiler.compileToByteArray(script, mProperties);
373          return new Program(action, 0, action.length);
374     }
375
376     /** Compiles the specified script to bytecodes
377      * and add its bytecodes to the current frame in this movie.
378      *
379      * @param script the script to be compiled
380      * @return the number of bytes
381      */

382     int addScript(String JavaDoc script) {
383          byte[] action = ScriptCompiler.compileToByteArray(script, mProperties);
384          Program program = new Program(action, 0, action.length);
385          mLogger.debug(" Adding a program of " + action.length + " bytes.");
386          addProgram(program);
387          return action.length;
388     }
389
390     /** Compiles the specified script to bytecodes
391      * and add its bytecodes to the current frame in this movie.
392      *
393      * @param script the script to be compiled
394      * @param offset of frame to add to
395      */

396     private void addScript(String JavaDoc script, int offset) {
397          byte[] action = ScriptCompiler.compileToByteArray(script, mProperties);
398          Program program = new Program(action, 0, action.length);
399          mLogger.debug(" Adding a program of " + action.length + " bytes.");
400          addProgram(program, offset);
401     }
402
403     /**
404      * Adds the program to the next frame
405      *
406      * @param program to be added
407      */

408     void addProgram(Program program) {
409          Frame frame = mFlashFile.getMainScript().getFrameAt(mLastFrame);
410          frame.addFlashObject(new DoAction(program));
411     }
412     
413     /**
414      * Adds the program to the specified frame
415      *
416      * @param program to be added
417      * @param offset of frame to add to
418      */

419     private void addProgram(Program program, int offset) {
420          Frame frame = mFlashFile.getMainScript().getFrameAt(offset);
421          frame.addFlashObject(new DoAction(program));
422     }
423
424
425     class ImportResourceError extends CompilationError {
426
427         ImportResourceError(String JavaDoc fileName, CompilationEnvironment env) {
428             super("Can't import " + stripBaseName(fileName, env));
429         }
430         ImportResourceError(String JavaDoc fileName, String JavaDoc type, CompilationEnvironment env) {
431             super("Can't import " + type + " " + stripBaseName(fileName, env));
432         }
433         ImportResourceError(String JavaDoc fileName, Exception JavaDoc e, CompilationEnvironment env) {
434             super("Can't import " + stripBaseName(fileName, env) + ": " + e.getMessage());
435         }
436         ImportResourceError(String JavaDoc fileName, Exception JavaDoc e, String JavaDoc type, CompilationEnvironment env) {
437             super("Can't import " + type + " " + stripBaseName(fileName, env) + ": " + e.getMessage());
438         }
439
440     }
441
442     public static String JavaDoc stripBaseName (String JavaDoc fileName, CompilationEnvironment env) {
443         try {
444             fileName = (new File(fileName)).getCanonicalFile().toString();
445         } catch (java.io.IOException JavaDoc e) {
446         }
447         String JavaDoc base = env.getErrorHandler().fileBase;
448         if (base == null || "".equals(base)) {
449             return fileName;
450         }
451         base = (new File(base)).getAbsolutePath() + File.separator;
452         if (base != null) {
453             int i = 1;
454             // Find longest common substring
455
while (i < base.length() && fileName.startsWith(base.substring(0, i))) { i++; }
456             // remove base string prefix
457
return fileName.substring(i-1);
458         } else {
459             return fileName;
460         }
461     }
462
463    /** Find a resource for importing into a movie and return a flashdef.
464      * Doesn't includes stop action.
465      *
466      * @param fileName file name of the resource
467      * @param name name of the resource
468      */

469     private Resource getMultiFrameResource(String JavaDoc fileName, String JavaDoc name, int fNum)
470         throws ImportResourceError
471     {
472         Resource res = (Resource)mMultiFrameResourceMap.get(fileName);
473         if (res != null) {
474             return res;
475         }
476
477         res = getResource(fileName, "$" + name + "_lzf_" + (fNum+1), false);
478
479         mMultiFrameResourceMap.put(fileName, res);
480         return res;
481     }
482
483    /** Find a resource for importing into a movie and return a flashdef.
484      * Includes stop action.
485      *
486      * @param fileName file name of the resource
487      * @param name name of the resource
488      */

489     private Resource getResource(String JavaDoc fileName, String JavaDoc name)
490         throws ImportResourceError
491     {
492         return getResource(fileName, name, true);
493     }
494
495    /** Find a resource for importing into a movie and return a flashdef.
496      *
497      * @param name name of the resource
498      * @param fileName file name of the resource
499      * @param stop include stop action if true
500      */

501     private Resource getResource(String JavaDoc fileName, String JavaDoc name, boolean stop)
502         throws ImportResourceError
503     {
504         try {
505             String JavaDoc inputMimeType = MimeType.fromExtension(fileName);
506             if (!Transcoder.canTranscode(inputMimeType, MimeType.SWF)
507                 && !inputMimeType.equals(MimeType.SWF)) {
508                 inputMimeType = Transcoder.guessSupportedMimeTypeFromContent(fileName);
509                 if (inputMimeType == null || inputMimeType.equals("")) {
510                     throw new ImportResourceError(fileName, new Exception JavaDoc("bad mime type"), mEnv);
511                 }
512             }
513             // No need to get these from the cache since they don't need to be
514
// transcoded and we usually keep the cmcache on disk.
515
if (inputMimeType.equals(MimeType.SWF)) {
516         
517                 long fileSize = FileUtils.getSize(new File(fileName));
518     
519                 Element elt = new Element("resource");
520                     elt.setAttribute("name", name);
521                     elt.setAttribute("mime-type", inputMimeType);
522                     elt.setAttribute("source", fileName);
523                     elt.setAttribute("filesize", "" + fileSize);
524                 mInfo.addContent(elt);
525     
526                 return importSWF(fileName, name, false);
527             }
528
529             // TODO: [2002-12-3 bloch] use cache for mp3s; for now we're skipping it
530
// arguably, this is a fixme
531
if (inputMimeType.equals(MimeType.MP3) ||
532                 inputMimeType.equals(MimeType.XMP3)) {
533                 return importMP3(fileName, name);
534             }
535     
536             File inputFile = new File(fileName);
537             File outputFile = mCache.transcode(inputFile, inputMimeType, MimeType.SWF);
538             mLogger.debug("importing: " + fileName + " as " + name +
539                           " from cache; size: " + outputFile.length());
540
541             long fileSize = FileUtils.getSize(outputFile);
542
543             Element elt = new Element("resource");
544                 elt.setAttribute("name", name);
545                 elt.setAttribute("mime-type", inputMimeType);
546                 elt.setAttribute("source", fileName);
547                 elt.setAttribute("filesize", "" + fileSize);
548             mInfo.addContent(elt);
549
550             return importSWF(outputFile.getPath(), name, stop);
551         } catch (Exception JavaDoc e) {
552             mLogger.error("Can't get resource " + fileName);
553             throw new ImportResourceError(fileName, e, mEnv);
554         }
555
556     }
557
558     /** Import a resource file into the preloader movie.
559      * Using a name that already exists clobbers the
560      * old resource (for now).
561      *
562      * @param fileName file name of the resource
563      * @param name name of the MovieClip/Sprite
564      * @throws CompilationError
565      */

566     public void importPreloadResource(String JavaDoc fileName, String JavaDoc name)
567         throws ImportResourceError
568     {
569         if (name.equals(""))
570             name = createName();
571         importResource(fileName, name, 0, mPreloaderFontsCollector);
572     }
573
574     /** Import a multiframe resource into the current movie. Using a
575      * name that already exists clobbers the old resource (for now).
576      */

577     public void importPreloadResource(List sources, String JavaDoc name, File parent)
578         throws ImportResourceError
579     {
580         if (name.equals(""))
581             name = createName();
582         importResource(sources, name, parent, 0, mPreloaderFontsCollector);
583     }
584
585     /** Import a resource file into the current movie.
586      * Using a name that already exists clobbers the
587      * old resource (for now).
588      *
589      * @param fileName file name of the resource
590      * @param name name of the MovieClip/Sprite
591      * @throws CompilationError
592      */

593     public void importResource(String JavaDoc fileName, String JavaDoc name)
594         throws ImportResourceError
595     {
596         importResource(fileName, name, -1);
597     }
598
599     /** Import a resource file into the current movie.
600      * Using a name that already exists clobbers the
601      * old resource (for now).
602      *
603      * @param fileName file name of the resource
604      * @param name name of the MovieClip/Sprite
605      * @param frameNum frame offset to add to
606      * @throws CompilationError
607      */

608     public void importResource(String JavaDoc fileName, String JavaDoc name, int frameNum)
609         throws CompilationError
610     {
611         importResource(fileName, name, frameNum, null);
612     }
613
614
615     /** Import a resource file into the current movie.
616      * Using a name that already exists clobbers the
617      * old resource (for now).
618      *
619      * @param fileName file name of the resource
620      * @param name name of the MovieClip/Sprite
621      * @param frameNum frame offset to add to
622      * @param fontsCollector fonts collector for resource (used by preloader)
623      * @throws CompilationError
624      */

625     public void importResource(String JavaDoc fileName, String JavaDoc name, int frameNum,
626                                FontsCollector fontsCollector)
627         throws CompilationError
628     {
629         mLogger.debug(" Importing resource " + name);
630
631         try {
632             fileName = new File(fileName).getCanonicalPath();
633         } catch (java.io.IOException JavaDoc e) {
634             throw new ImportResourceError(fileName, e, mEnv);
635         }
636         FlashDef def = null;
637
638         Resource res = (Resource)mResourceMap.get(fileName);
639         boolean oldRes = (res != null);
640         if (!oldRes) {
641             // Get the resource and put in the map
642
res = getResource(fileName, name);
643             mResourceMap.put(fileName, res);
644
645             def = res.getFlashDef();
646             if (fontsCollector != null) def.collectFonts(fontsCollector);
647             mFlashFile.addDefToLibrary(name, def);
648             def.setName(name);
649         
650         } else {
651             def = res.getFlashDef();
652             if (fontsCollector != null) def.collectFonts(fontsCollector);
653             // Add an element with 0 size, since it's already there.
654
Element elt = new Element("resource");
655                 elt.setAttribute("name", name);
656                 // elt.setAttribute("mime-type", MimeType.MP3);
657
elt.setAttribute("source", fileName);
658                 elt.setAttribute("filesize", "0");
659             mInfo.addContent(elt);
660         }
661         
662     
663         ExportAssets ea = new ExportAssets();
664         ea.addAsset(name, def);
665         Timeline timeline = mFlashFile.getMainScript().getTimeline();
666         if (frameNum == -1) {
667             frameNum = timeline.getFrameCount() - 1;
668         }
669         Frame frame = timeline.getFrameAt(frameNum);
670         frame.addFlashObject(ea);
671     }
672     
673     /** Imports file, if it has not previously been imported, and
674      * returns in any case the name of the clip that refers to it.
675      * File should refer to a graphical asset. */

676     public String JavaDoc importResource(File file)
677     {
678         Resource res;
679
680         try {
681             file = file.getCanonicalFile();
682             res = (Resource) mResourceMap.get(file.getCanonicalPath());
683         } catch (java.io.IOException JavaDoc e) {
684             throw new CompilationError(e);
685         }
686         if (res == null) {
687             String JavaDoc clipName = createName();
688             importResource(file.getPath(), clipName);
689             return clipName;
690         } else {
691             return res.getName();
692         }
693     }
694
695     /** Imports this resource, if it has not previously been imported, as
696      * resource that can be used as a click region, and returns in any
697      * case the name of the clip that refers to it.
698      */

699     public String JavaDoc importClickResource(File file) throws ImportResourceError
700     {
701         String JavaDoc fileName;
702         try {
703             fileName = file.getCanonicalPath();
704         } catch (java.io.IOException JavaDoc e) {
705             throw new CompilationError(e);
706         }
707
708         String JavaDoc name = (String JavaDoc) mClickResourceMap.get(fileName);
709
710         if (name == null) {
711
712             Button2 but = new Button2();
713
714             FlashDef def;
715             Rectangle2D JavaDoc bounds;
716
717             // FIXME: [2004-06-29 bloch]
718
// For each instance in the first frame, add a button record.
719
// Get bounds for entire clip; should only get bounds for first frame!
720
// Should only allow swf resources as click resources.
721
try {
722                 Script script = FlashFile.parse(fileName).getMainScript();
723                 Frame frame = script.getFrameAt(0);
724                 bounds = script.getBounds();
725                 for(int i = 0; i < frame.size(); i++) {
726                     FlashObject o = (FlashObject)frame.getFlashObjectAt(i);
727                     if (o instanceof Instance) {
728                         Instance inst = (Instance)o;
729                         CXForm cxform = inst.cxform;
730                         if (cxform == null) {
731                             cxform = CXForm.newIdentity(false);
732                         }
733                         java.awt.geom.AffineTransform JavaDoc matrix = inst.matrix;
734                         if (matrix == null) {
735                             matrix = new java.awt.geom.AffineTransform JavaDoc();
736                         }
737                          
738                         but.addButtonRecord(new ButtonRecord(ButtonRecord.HitTest,
739                                                  inst.def, 1, matrix, cxform));
740                     }
741                 }
742             } catch (Exception JavaDoc e) {
743                 throw new ImportResourceError(fileName, e, mEnv);
744             }
745
746             // TODO: [2004-06029 bloch] When we merge into lps-intl2, there should
747
// be some code sharing between this and SWFFile
748
but.addActionCondition(ActionCondition.onPress(program(
749                 "_root.LzModeManager.handleMouseButton( myView, 'onmousedown')")));
750             but.addActionCondition(ActionCondition.onRelease(program(
751                 "_root.LzModeManager.handleMouseButton( myView, 'onmouseup');" +
752                 "_root.LzModeManager.handleMouseEvent( myView, 'onclick')")));
753             but.addActionCondition(ActionCondition.onReleaseOutside(program(
754                 "_root.LzModeManager.handleMouseButton( myView, 'onmouseup');" +
755                 "_root.LzModeManager.handleMouseEvent( myView, 'onmouseupoutside')")));
756             but.addActionCondition(ActionCondition.onRollOver(program(
757                 "_root.LzModeManager.handleMouseEvent( myView, 'onmouseover')")));
758             but.addActionCondition(ActionCondition.onRollOut(program(
759                 "_root.LzModeManager.handleMouseEvent( myView, 'onmouseout')")));
760             but.addActionCondition(ActionCondition.onDragOut(program(
761                 "_root.LzModeManager.handleMouseEvent( myView, 'onmouseout');" +
762                 "_root.LzModeManager.handleMouseEvent( myView, 'onmousedragout')")));
763             but.addActionCondition(ActionCondition.onDragOver(program(
764                 "_root.LzModeManager.handleMouseEvent( myView, 'onmouseover');" +
765                 "_root.LzModeManager.handleMouseEvent( myView, 'onmousedragin')")));
766             
767             name = createName();
768
769             // Scale the movieclip to 100x100 for use by LFC.
770
Script movieClip = new Script(1);
771             Frame f = movieClip.getFrameAt(0);
772             double sx = 100.0 * TWIP / (double)bounds.getWidth();
773             double sy = 100.0 * TWIP / (double)bounds.getHeight();
774             java.awt.geom.AffineTransform JavaDoc matrix = java.awt.geom.AffineTransform.getScaleInstance(sx, sy);
775             f.addInstance(but, 1, matrix, null);
776
777             mFlashFile.addDefToLibrary(name, movieClip);
778             movieClip.setName(name);
779             ExportAssets ea = new ExportAssets();
780             ea.addAsset(name, movieClip);
781             Timeline timeline = mFlashFile.getMainScript().getTimeline();
782             Frame frame = timeline.getFrameAt(timeline.getFrameCount() - 1);
783             frame.addFlashObject(ea);
784
785             mClickResourceMap.put(fileName, name);
786         }
787
788         return name;
789     }
790
791     /** Import a multiframe resource into the current movie. Using a
792      * name that already exists clobbers the old resource (for now).
793      *
794      * @param sources file names of the resources
795      * @param name name of the MovieClip/Sprite
796      * @param parent parent's File object
797      */

798     public void importResource(List sources, String JavaDoc name, File parent)
799     {
800         importResource(sources, name, parent, -1);
801     }
802
803     /** Import a multiframe resource into the current movie. Using a
804      * name that already exists clobbers the old resource (for now).
805      *
806      * @param sources file names of the resources
807      * @param name name of the MovieClip/Sprite
808      * @param parent parent's File object
809      * @param frameNum frame offset to add to
810      */

811     public void importResource(List sources, String JavaDoc name, File parent, int frameNum)
812     {
813         importResource(sources, name, parent, frameNum, null);
814     }
815
816
817     /** Import a multiframe resource into the current movie. Using a
818      * name that already exists clobbers the old resource (for now).
819      *
820      * @param sources file names of the resources
821      * @param name name of the MovieClip/Sprite
822      * @param parent parent's File object
823      * @param frameNum frame offset to add to
824      * @param fontsCollector fonts collector for resource (used by preloader)
825      */

826     public void importResource(List sources, String JavaDoc name, File parent, int frameNum,
827                                FontsCollector fontsCollector)
828     {
829         Script out = new Script(1);
830         String JavaDoc fileName = null;
831         mLogger.debug("Including multiple resources as " + name);
832         int width = 0;
833         int height = 0;
834         int fNum = 0;
835         for (Iterator e = sources.iterator() ; e.hasNext() ;) {
836             fileName = (String JavaDoc)e.next();
837             mLogger.debug(" Importing " + fileName);
838      
839             // Definition to add to the library (without stop)
840
Resource res = getMultiFrameResource(fileName, name, fNum);
841             Script scr = (Script)res.getFlashDef();
842             if (fontsCollector != null) scr.collectFonts(fontsCollector);
843             int bc = out.getFrameCount();
844             out.appendScript(scr);
845             int fc = out.getFrameCount();
846             Frame f = out.getFrameAt(fc - 1);
847             f.addStopAction();
848             mLogger.debug(" Added " + (fc - bc) + " of " + fc + "frame(s)");
849
850             int rw = res.getWidth();
851             int rh = res.getHeight();
852             if (rw > width) {
853                 width = rw;
854             }
855             if (rh > height) {
856                 height = rh;
857             }
858             // NOTE: add the ratio attribute to each frame here; this
859
// appears to be required to make a multi-frame resource that has individual
860
// frames that are swfs with nested movieclips work correctly.
861
// This was "guessed" by dumping the contents
862
// of a multi-frame SWF created by the Flash tool itself.
863
// This is weird since the docs on 'ratio' say it is only
864
// needed to cope with Morphing. Hmph. See bug 4961 for details.
865
if (fNum > 0) {
866                 for (int i = 0; i < f.size(); i++) {
867                     if (f.getFlashObjectAt(i) instanceof Instance) {
868                         Instance inst = (Instance) f.getFlashObjectAt(i);
869                         inst.ratio = fNum;
870                         break;
871                     }
872                 }
873             }
874             fNum++;
875         }
876
877         // TODO [2003-1-2 bloch]: Could optimize and only add
878
// multi-frame resources when the total size is greater than
879
// the size of the first frame
880
mMultiFrameResourceSet.add(new Resource(name, out, width, height));
881
882         FlashDef def = (FlashDef)out;
883         mFlashFile.addDefToLibrary(name, def);
884         def.setName(name);
885         ExportAssets ea = new ExportAssets();
886         ea.addAsset(name, def);
887         Timeline timeline = mFlashFile.getMainScript().getTimeline();
888         if (frameNum == -1) {
889             frameNum = timeline.getFrameCount() - 1;
890         }
891         Frame frame = timeline.getFrameAt(frameNum);
892         frame.addFlashObject(ea);
893     }
894
895     /** Returns a new unique js name. */
896     String JavaDoc createName() {
897         return mNameSupply.next();
898     }
899     
900     /**
901      * Recursively strips out the ActionScript from a
902      * given movie Script (MovieClip)
903      *
904      * Actually, it leaves the actionscript blocks in,
905      * but turns them into programs that do nothing.
906      */

907     private void stripActions(Script s) {
908         Timeline t = s.getTimeline();
909         int n = t.getFrameCount();
910
911         boolean didStop = false;
912
913         for(int i = 0; i < n; i++ ) {
914             Frame f = s.getFrameAt(i);
915
916             for (int j = 0; j < f.size(); j++) {
917                 FlashObject o = f.getFlashObjectAt(j);
918                 if (o instanceof Script) {
919                     stripActions((Script)o);
920                 } else if (o instanceof DoAction) {
921                     DoAction doa = (DoAction) o;
922                     Program p = new Program();
923                     p.none();
924                     doa.setProgram(p);
925                 }
926             }
927         }
928     }
929
930     /**
931      * @param fileName
932      * @param name
933      * @param addStop if true, add stop action to last frame
934      */

935     private Resource importSWF(String JavaDoc fileName, String JavaDoc name, boolean addStop)
936         throws IVException, FileNotFoundException {
937
938         FlashFile f = FlashFile.parse(fileName);
939         Script s = f.getMainScript();
940         collectFonts(s);
941         if (addStop) {
942             Frame frame = s.getFrameAt(s.getFrameCount() - 1);
943             frame.addStopAction();
944         }
945
946         Rectangle2D JavaDoc rect = s.getBounds();
947         int mw = (int)(rect.getMaxX()/TWIP);
948         int mh = (int)(rect.getMaxY()/TWIP);
949
950         Resource res = new Resource(name, s, mw, mh);
951
952         // Add multi-frame SWF resources that have bounds that
953
// are different than their first frame to the resource table.
954
if (s.getFrameCount() > 1) {
955
956             Rectangle2D JavaDoc f1Rect = new Rectangle2D.Double JavaDoc();
957             s.getFrameAt(0).addBounds(f1Rect);
958             int f1w = (int)(f1Rect.getMaxX()/TWIP);
959             int f1h = (int)(f1Rect.getMaxY()/TWIP);
960             if (f1w < mw || f1h < mh) {
961                 mMultiFrameResourceSet.add(res);
962             }
963         }
964  
965         return res;
966     }
967
968     /**
969      * collect fonts for later use
970      */

971     private void collectFonts(Script s) {
972
973         Timeline tl = s.getTimeline();
974         // If preloader is added, skip frame 0. Assume that preloader is only
975
// one frame long starting at frame 0.
976
for(int i=0; i<tl.getFrameCount(); i++ ) {
977             Frame frame = tl.getFrameAt(i);
978             for( int k=0; k<frame.size(); k++ ) {
979                 FlashObject fo = frame.getFlashObjectAt(k);
980                 //mLogger.debug("collecting from a " + fo.getClass().getName());
981
fo.collectFonts( mFontsCollector );
982                 //mLogger.debug("FONTS size "
983
//+ mFontsCollector.getFonts().size());
984
}
985         }
986     }
987
988     /**
989      * @param fileName
990      * @param name
991      */

992     private Resource importMP3(String JavaDoc fileName, String JavaDoc name)
993         throws IVException, IOException {
994
995         long fileSize = FileUtils.getSize(new File(fileName));
996         FileInputStream stream = new FileInputStream(fileName);
997         FlashBuffer buffer = new FlashBuffer(stream);
998         Sound sound = MP3Sound.newMP3Sound(buffer);
999
1000        Element elt = new Element("resource");
1001            elt.setAttribute("name", name);
1002            elt.setAttribute("mime-type", MimeType.MP3);
1003            elt.setAttribute("source", fileName);
1004            elt.setAttribute("filesize", "" + fileSize);
1005        mInfo.addContent(elt);
1006
1007        stream.close();
1008
1009        return new Resource(sound);
1010    }
1011
1012    /** Writes the SWF to the <code>OutputStream</code> that was
1013     * supplied to the SWFWriter's constructor.
1014     * @throws IOException if an error occurs
1015     */

1016    public void close() throws IOException {
1017
1018        if (mCloseCalled) {
1019            throw new IllegalStateException JavaDoc("SWFWriter.close() called twice");
1020        }
1021        
1022        // Create the textfield movieclip; name must match that used in LFC
1023
if ((mFlashVersion == 5) && (mDefaultFont == null)) {
1024            try {
1025                File f = mEnv.resolve(mDefaultFontFileName, "");
1026                mDefaultFont = importFontStyle(f.getAbsolutePath(), mDefaultFontName, "plain", mEnv);
1027            } catch (FileNotFoundException fnfe) {
1028                throw new CompilationError("default font "
1029                        + mDefaultFontFileName + " missing " + fnfe);
1030            }
1031        }
1032
1033        if (mFlashVersion == 5) {
1034            addTextAsset("newtext", mDefaultFont);
1035        }
1036
1037        // Add font information
1038
addFontTable();
1039
1040        // Add resource information to canvas.
1041
addResourceTable();
1042
1043        if (mFlashVersion == 5) {
1044            // Add list of input text resource names
1045
addInputTextResourceNames();
1046        }
1047
1048        addDefaultTextWidth();
1049
1050        if (mPreloaderAdded == true) {
1051            // Add script to hide the preloader after finishing instantiation
1052
String JavaDoc finalobj = "_root.lzpreloader.done();";
1053            addScript(finalobj);
1054        }
1055
1056        // Flag says to post a copy of all debug.write() calls back to the server
1057
if (mProperties.getProperty("logdebug", "false").equals("true")) {
1058            addScript("_dbg_log_all_writes = true;");
1059        }
1060
1061        boolean remotedebug = (mProperties.getProperty(mEnv.REMOTEDEBUG_PROPERTY, "").length() > 0);
1062        boolean debug = mProperties.getProperty("debug", "false").equals("true");
1063
1064        // Tell the debugger we're done loading.
1065
if (debug && !remotedebug && !mEnv.getBooleanProperty(mEnv.USER_DEBUG_WINDOW)) {
1066            addScript("LzInstantiateView({attrs: {}, name: \"LzDebugWindow\"})");
1067            addScript("Debug.loaded()");
1068        }
1069
1070        boolean kranking = "true".equals(mEnv.getProperty("krank"));
1071
1072        // Bake the krank TCP server port into the app
1073
if (kranking) {
1074            addScript("_root.LzSerializer.krankport = "+LPS.getKrankPort()+";");
1075        }
1076        // Tell the canvas we're done loading.
1077
addScript("canvas.initDone()");
1078
1079        // Make sure we stop
1080
Program program = new Program();
1081        program.stop();
1082        program.none();
1083        addProgram(program);
1084
1085        // Don't compress flash5 files or krank files
1086
if (mFlashVersion != 5 && !kranking) {
1087            mFlashFile.setCompressed(true);
1088        }
1089     
1090        try {
1091            InputStream input;
1092            input = mFlashFile.generate(mEnv.getEmbedFonts() ? mFontsCollector : new FontsCollector(),
1093                                        mEnv.getEmbedFonts() ? mPreloaderFontsCollector : new FontsCollector(),
1094                                        mPreloaderAdded).getInputStream();
1095            FileUtils.send(input, mStream);
1096        } catch (IVException e) {
1097            throw new ChainedException(e);
1098        }
1099
1100        mCloseCalled = true;
1101    }
1102
1103    public void openSnippet() throws IOException {
1104        // How do we make sure an initial frame exists? Does this do it?
1105
Frame frame = mFlashFile.getMainScript().getFrameAt(mLastFrame);
1106        // if we don't have any frame, then code which adds resources gets
1107
// an error. This happens if you have a resource declared before any code.
1108
}
1109
1110    public void closeSnippet() throws IOException {
1111
1112        if (mCloseCalled) {
1113            throw new IllegalStateException JavaDoc("SWFWriter.closeSnippet() called twice");
1114        }
1115
1116        mFlashFile.setCompressed(true);
1117
1118        // TODO [hqm 2004-09-29] Add callback for snippet loader onload event
1119
// Make sure we stop
1120
addScript("this._parent.loader.snippetLoaded(this._parent, null)");
1121        Program program = new Program();
1122        program.stop();
1123        program.none();
1124        addProgram(program);
1125
1126
1127        try {
1128            InputStream input;
1129            input = mFlashFile.generate(mEnv.getEmbedFonts() ? mFontsCollector : new FontsCollector(),
1130                                        mEnv.getEmbedFonts() ? mPreloaderFontsCollector : new FontsCollector(),
1131                                        mPreloaderAdded).getInputStream();
1132            FileUtils.send(input, mStream);
1133        } catch (IVException e) {
1134            throw new ChainedException(e);
1135        }
1136
1137        mCloseCalled = true;
1138    }
1139
1140
1141
1142    /**
1143     * Generate a warning message
1144     */

1145    void warn(CompilationEnvironment env, String JavaDoc msg) {
1146        CompilationError cerr;
1147        env.warn(msg);
1148    }
1149
1150    /**
1151     * Import a font of a given style into the SWF we are writing.
1152     *
1153     * @param fileName filename for font in LZX
1154     * @param face face name of font
1155     * @param style style of font
1156     */

1157    Font importFontStyle(String JavaDoc fileName, String JavaDoc face, String JavaDoc style,
1158            CompilationEnvironment env)
1159        throws FileNotFoundException, CompilationError {
1160
1161        int styleBits = FontInfo.styleBitsFromString(style);
1162
1163        mLogger.debug("importing " + face + " of style " + style);
1164
1165        FontInfo fontInfo = mEnv.getCanvas().getFontInfo();
1166        boolean isDefault = false;
1167
1168        Font font = importFont(fileName, face, styleBits, false);
1169
1170        if (fontInfo.getName().equals(face)) {
1171            if (styleBits == FontInfo.PLAIN) {
1172                isDefault = true;
1173                mDefaultFont = font;
1174            }
1175        }
1176
1177        FontFamily family = mFontManager.getFontFamily(face, true);
1178
1179        switch (styleBits) {
1180            case FontInfo.PLAIN:
1181                if (family.plain != null) {
1182                    if (!isDefault || mDefaultFontUsedForMeasurement) {
1183                        warn(env, "Redefined plain style of font: " + face);
1184                    }
1185                }
1186                family.plain = font; break;
1187            case FontInfo.BOLD:
1188                if (family.bold != null) {
1189                    warn(env, "Redefined bold style of font: " + face);
1190                }
1191                family.bold = font; break;
1192            case FontInfo.ITALIC:
1193                if (family.italic != null) {
1194                    warn(env, "Redefined italic style of font: " + face);
1195                }
1196                family.italic = font; break;
1197            case FontInfo.BOLDITALIC:
1198                if (family.bitalic != null) {
1199                    warn(env, "Redefined bold italic style of font: " + face);
1200                }
1201                family.bitalic = font; break;
1202            default:
1203                throw new ChainedException("Unexpected style");
1204        }
1205
1206        return font;
1207    }
1208
1209    /**
1210     * Import a font into the SWF we are writing
1211     *
1212     * @param name of font in LZX
1213     * @param fileName name of font file
1214     */

1215    private Font importFont(String JavaDoc fileName, String JavaDoc face, int styleBits,
1216        boolean replace)
1217        throws FileNotFoundException, CompilationError {
1218
1219        if (isDeviceFont(face)) {
1220            return Font.createDummyFont(face);
1221        }
1222
1223        mLogger.debug(" Importing font " + face + " from " + fileName);
1224
1225        String JavaDoc fromType = FontType.fromName(fileName);
1226        String JavaDoc location = null;
1227        try {
1228            File fontFile = mCache.transcode(new File(fileName), fromType,
1229                    FontType.FFT);
1230            location = fontFile.getAbsolutePath();
1231        } catch (TranscoderException e) {
1232            throw new CompilationError(e);
1233        } catch (FileNotFoundException e) {
1234            throw e;
1235        } catch (IOException e) {
1236            throw new CompilationError(e);
1237        }
1238
1239        Font font = Font.createDummyFont(face);
1240
1241        long fileSize = FileUtils.getSize(new File(location));
1242
1243        // FIXME: [2004-05-31 bloch] embed fonts shouldn't be global
1244
if (mFlashVersion == 5 || mEnv.getEmbedFonts()) {
1245            Element elt = new Element("font");
1246                elt.setAttribute("face", face);
1247                    elt.setAttribute("style", FontInfo.styleBitsToString(styleBits, true));
1248                elt.setAttribute("location", location);
1249                elt.setAttribute("source", fileName);
1250                elt.setAttribute("filesize", "" + fileSize);
1251            mInfo.addContent(elt);
1252        }
1253
1254        try {
1255
1256            // Parse the font
1257
mLogger.debug("Font file name " + location);
1258            FlashFile fontFile = FlashFile.parse( location );
1259            Enumeration defs = fontFile.definitions();
1260            FontDef fontDef = (FontDef)defs.nextElement();
1261
1262            // Copy the font
1263
// For now, add the entire font (including "layout" info)
1264
fontDef.getFont().copyTo(font);
1265
1266            if ((font.flags & Font.SHIFT_JIS) != 0) {
1267                throw new CompilationError("Can't handle SHIFT_JIS font: "
1268                        + fileName);
1269            }
1270            // Make sure font has LAYOUT info
1271
if ((font.flags & Font.HAS_LAYOUT) == 0) {
1272                throw new CompilationError(fileName +
1273                        " has no layout information.");
1274            }
1275
1276            // Put in our face name
1277
font.fontName = face;
1278
1279            // Clean out existing styles.
1280
font.flags &= ~(Font.BOLD | Font.ITALIC);
1281
1282            // Write in ours.
1283
if ((styleBits & FontInfo.BOLD) != 0) {
1284                font.flags |= Font.BOLD;
1285            }
1286            if ((styleBits & FontInfo.ITALIC) != 0) {
1287                font.flags |= Font.ITALIC;
1288            }
1289
1290            // FIXME: [OLD bloch] debugging shouldn't go to system.err; should log!
1291
// need adapter class for logger for that
1292
if (mProperties.getProperty("trace.fonts", "false").equals("true")) {
1293                 font.printContent(System.err, " ", 0);
1294            }
1295
1296            // Add to the list of fonts
1297
FontDef fdef = mFontsCollector.addFont(font, null);
1298            // For now, write all characters.
1299
fdef.setWriteAllChars(true);
1300            fdef.setWriteLayout(true);
1301
1302        } catch (IVException e) {
1303            throw new ChainedException(e);
1304        }
1305
1306        return font;
1307    }
1308
1309    /**
1310     * Import all action script blocks
1311     */

1312     void importActions(String JavaDoc fileName)
1313         throws FileNotFoundException, IVException {
1314
1315         Timeline t = FlashFile.parse(fileName).getMainScript().getTimeline();
1316
1317         for(int i = 0; i < t.getFrameCount(); i++) {
1318             Frame frame = t.getFrameAt(i);
1319             for(int j = 0; j < frame.size(); j++) {
1320                 FlashObject o = frame.getFlashObjectAt(j);
1321                 if (o instanceof DoAction) {
1322                     DoAction action = (DoAction)o;
1323                     addProgram(action.getProgram());
1324                 }
1325             }
1326         }
1327     }
1328
1329    /**
1330     * @return first action block
1331     */

1332     DoAction getFirstDoAction(String JavaDoc fileName)
1333         throws FileNotFoundException, IVException {
1334
1335         Timeline t = FlashFile.parse(fileName).getMainScript().getTimeline();
1336
1337         for(int i = 0; i < t.getFrameCount(); i++) {
1338             Frame frame = t.getFrameAt(i);
1339             for(int j = 0; j < frame.size(); j++) {
1340                 FlashObject o = frame.getFlashObjectAt(j);
1341                 if (o instanceof DoAction) {
1342                     return (DoAction)o;
1343                 }
1344             }
1345         }
1346         return null;
1347     }
1348
1349     /**
1350      * Adds font information to the canvas
1351      */

1352     private void addFontTable() {
1353
1354        if (mFontTableIndex == -1) {
1355            mLogger.error("No canvas. Skipping font table");
1356            return;
1357        }
1358        Enumeration fonts = mFontManager.getNames();
1359        StringBuffer JavaDoc actions = new StringBuffer JavaDoc("");
1360        while(fonts.hasMoreElements()) {
1361            // TODO: [old bloch] Add assert that the canvas has been created
1362
// before this action script is added!
1363
String JavaDoc name = (String JavaDoc)fonts.nextElement();
1364            FontFamily family = mFontManager.getFontFamily(name);
1365            mLogger.debug("Adding font family: " + name);
1366
1367            actions.append("_root.LzFontManager.addFont('" + name + "', " );
1368                    
1369            appendFont(actions, family.plain, family.getBounds(FontInfo.PLAIN));
1370            actions.append(",");
1371            appendFont(actions, family.bold, family.getBounds(FontInfo.BOLD));
1372            actions.append(",");
1373            appendFont(actions, family.italic, family.getBounds(FontInfo.ITALIC));
1374            actions.append(",");
1375            appendFont(actions, family.bitalic, family.getBounds(FontInfo.BOLDITALIC));
1376            actions.append("\n)\n");
1377        }
1378
1379        if (mProperties.getProperty("trace.fonts", "false").equals("true")) {
1380            mLogger.debug(actions.toString());
1381        }
1382
1383        byte[] action = ScriptCompiler.compileToByteArray(actions.toString(), mProperties);
1384        Program program = new Program(action, 0, action.length);
1385
1386        // Add the program right in the spot where it belongs
1387
Frame frame = mFlashFile.getMainScript().getFrameAt(mLastFrame);
1388        frame.setFlashObjectAt(new DoAction(program), mFontTableIndex);
1389    }
1390
1391     /**
1392      * Adds resource information to the canvas
1393      */

1394     private void addResourceTable() {
1395
1396         StringBuffer JavaDoc buf = new StringBuffer JavaDoc("");
1397         // Sort the keys, so that regression tests aren't sensitive to
1398
// the undefined order of iterating a (non-TreeSet) Set.
1399
SortedSet sset = new TreeSet(mMultiFrameResourceSet);
1400         Iterator resources = sset.iterator();
1401         while(resources.hasNext()) {
1402             Resource res = (Resource)resources.next();
1403             buf.append("canvas.resourcetable[\"" + res.getName() +
1404                    "\"]={ width : " + res.getWidth() + ", height :" +
1405                    res.getHeight() + "};\n");
1406         }
1407
1408         byte[] action = ScriptCompiler.compileToByteArray(buf.toString(), mProperties);
1409         Program program = new Program(action, 0, action.length);
1410
1411         // Add the program right in the spot where it belongs
1412
Frame frame = mFlashFile.getMainScript().getFrameAt(mLastFrame);
1413         frame.setFlashObjectAt(new DoAction(program), mResourceTableIndex);
1414     }
1415
1416    /**
1417     * Sets the lfc's default text width
1418     * LzAbstractText.prototype.DEFAULT_WIDTH,
1419     * from the compiler's default value
1420     */

1421    private void addDefaultTextWidth() {
1422        StringBuffer JavaDoc buf = new StringBuffer JavaDoc("");
1423        buf.append("_root.LzAbstractText.prototype.DEFAULT_WIDTH = "+mEnv.getDefaultTextWidth()+";\n");
1424        addScript(buf.toString());
1425    }
1426
1427     /**
1428      * Adds names of input text resources as a property of LzFontManager
1429      */

1430     private void addInputTextResourceNames() {
1431         StringBuffer JavaDoc buf = new StringBuffer JavaDoc("");
1432         Iterator resources = mInputTextSet.entrySet().iterator();
1433         buf.append("_root.LzFontManager.inputTextResourceNames = {};\n");
1434         while(resources.hasNext()) {
1435             Map.Entry entry = (Map.Entry) resources.next();
1436             buf.append("_root.LzFontManager.inputTextResourceNames['"+entry.getValue()+"'] = true;\n");
1437         }
1438         addScript(buf.toString());
1439     }
1440
1441    /**
1442     * @return height of fontinfo in pixels
1443     * @param fontinfo
1444     */

1445    double getFontHeight (FontInfo fontInfo) {
1446        return fontHeight(getFontFromInfo(fontInfo));
1447    }
1448
1449    /**
1450     * @return lineheight which lfc LzInputText expects for a given fontsize
1451     * @param fontinfo, int fontsize
1452     */

1453    double getLFCLineHeight (FontInfo fontInfo, int fontsize) {
1454        return lfcLineHeight(getFontFromInfo(fontInfo), fontsize);
1455    }
1456
1457    /**
1458     * Convert em units to pixels, truncated to 2 decimal places.
1459     * Slow float math... but simple code to read.
1460     *
1461     * @param units number of 1024 em units
1462     * @return pixels
1463     */

1464    private static double emUnitsToPixels(int units) {
1465        int x = (100 * units * DEFAULT_SIZE) / 1024;
1466        return (double)(x / 100.0);
1467    }
1468
1469    /**
1470     * Compute font bounding box
1471     *
1472     * @param font
1473     */

1474    static double fontHeight(Font font) {
1475        if (font == null) { return 0; }
1476        double ascent = emUnitsToPixels(font.ascent);
1477        double descent = emUnitsToPixels(font.descent);
1478        double leading = emUnitsToPixels(font.leading);
1479        double lineheight = ascent+descent+leading;
1480        return lineheight;
1481    }
1482
1483    /**
1484     * Compute font bounding box
1485     *
1486     * @param font
1487     */

1488    double lfcLineHeight(Font font, int fontsize) {
1489        double ascent = emUnitsToPixels(font.ascent);
1490        double descent = emUnitsToPixels(font.descent);
1491        //double leading = emUnitsToPixels(font.leading);
1492
double lineheight = mTextLeading + ((ascent+descent) * ((double)fontsize) / DEFAULT_SIZE);
1493        return lineheight;
1494    }
1495
1496    /**
1497     * Appends font to actionscript string buffer
1498     * @param actions string
1499     * @param font font
1500     */

1501    private static void appendFont(StringBuffer JavaDoc actions, Font font,
1502        Rectangle2D JavaDoc[] bounds) {
1503
1504        final String JavaDoc newline = "\n ";
1505        actions.append(newline);
1506
1507        if (font == null) {
1508            actions.append("null");
1509            return;
1510        }
1511
1512        double ascent = emUnitsToPixels(font.ascent);
1513        double descent = emUnitsToPixels(font.descent);
1514        double leading = emUnitsToPixels(font.leading);
1515
1516        final String JavaDoc comma = ", ";
1517
1518        actions.append("{");
1519        actions.append("ascent:");
1520        actions.append(ascent);
1521        actions.append(comma);
1522        actions.append("descent:");
1523        actions.append(descent);
1524        actions.append(comma);
1525        actions.append("leading:");
1526        actions.append(leading);
1527        actions.append(comma);
1528        actions.append("advancetable:");
1529
1530        int idx, adv;
1531            
1532        actions.append(newline);
1533        actions.append("[");
1534        // FIXME: [2003-03-19 bloch] We only support ANSI 8bit (up to
1535
// 255) char encodings. We lose the higher characters is
1536
// UNICODE and we don't support anything else.
1537

1538        for(int i = 0; i < 256; i++) {
1539            idx = font.getIndex(i);
1540            adv = font.getAdvanceValue(idx);
1541
1542            // Convert to pixels rounded to nearest 100th
1543
double advance = emUnitsToPixels(adv);
1544            actions.append(advance);
1545            if (i != 255) {
1546                actions.append(comma);
1547            }
1548
1549            if (i%10 == 9) {
1550                actions.append(newline);
1551            }
1552        }
1553        actions.append("],");
1554        actions.append(newline);
1555
1556        actions.append("lsbtable:");
1557        actions.append(newline);
1558        actions.append("[");
1559
1560        int m;
1561        int max;
1562        int adj;
1563        for(int i = 0; i < 256; i++) {
1564            idx = font.getIndex(i);
1565            try {
1566                m = (int)bounds[idx].getMinX();
1567                //max = (int)bounds[idx].getMaxX();
1568
} catch (Exception JavaDoc e) {
1569                m = 0;
1570                //max = 0;
1571
}
1572            adv = font.getAdvanceValue(idx);
1573            adj = m;
1574            if (adj < 0) adj = 0;
1575
1576            /* The following makes the lsb bigger
1577               but is strictly wrong */

1578            /*max = max - adv;
1579            if (max < 0) max = 0;
1580            
1581            if (max > adj) {
1582                adj = max;
1583            }*/

1584            
1585            // Convert to pixels rounded to nearest 100th
1586
double lsb = emUnitsToPixels(adj);
1587            actions.append(lsb);
1588            if (i != 255) {
1589                actions.append(comma);
1590            }
1591
1592            if (i%10 == 9) {
1593                actions.append(newline);
1594            }
1595        }
1596
1597        actions.append("],");
1598
1599        actions.append(newline);
1600        actions.append("rsbtable:");
1601        actions.append(newline);
1602        actions.append("[");
1603
1604        for(int i = 0; i < 256; i++) {
1605            idx = font.getIndex(i);
1606            try {
1607                m = (int)bounds[idx].getMaxX();
1608            } catch (Exception JavaDoc e) {
1609                m = 0;
1610            }
1611            adv = font.getAdvanceValue(idx);
1612            adj = m - adv;
1613            if (adj < 0) adj = 0;
1614            
1615            // Convert to pixels rounded to nearest 100th
1616
double rsb = emUnitsToPixels(adj);
1617            actions.append(rsb);
1618            if (i != 255) {
1619                actions.append(comma);
1620            }
1621
1622            if (i%10 == 9) {
1623                actions.append(newline);
1624            }
1625        }
1626
1627        actions.append("]}");
1628    }
1629
1630    /**
1631     * Add an asset to the output SWF to represent an input text
1632     * field that uses the given FontInfo
1633     * @param fontInfo
1634     */

1635    public void addInputText(FontInfo fontInfo, boolean password)
1636        throws CompilationError {
1637
1638        // We only need to add text field defs at compile time to swf5 files;
1639
// swf6 and greater create all text at runtime.
1640
if (mFlashVersion != 5) {
1641            return;
1642        }
1643
1644        String JavaDoc fontName = fontInfo.getName();
1645        Font font = getFontFromInfo(fontInfo);
1646
1647        String JavaDoc name =
1648            "lzinputtext/" +
1649            fontName + "/" +
1650            fontInfo.getSize() + "/" +
1651            fontInfo.getStyle(false) + (password ? "/passwd" : "");
1652
1653        // We've already added an movieclip that will work for this
1654
// input text
1655
if (mInputTextSet.containsKey(name)) {
1656            return;
1657        }
1658
1659        // Create a movieclip with a single frame with
1660
// a text field of the correct size.
1661
Script movieClip = new Script(1);
1662        Frame frame = movieClip.newFrame();
1663
1664        TextField input = new TextField("", "text",
1665                font, fontInfo.getSize()*TWIP, AlphaColor.black);
1666
1667        int flags = input.getFlags();
1668
1669        if (password) {
1670            flags |= TextField.PASSWORD;
1671        }
1672
1673        if (mProperties.getProperty("text.borders", "false").equals("true")) {
1674            flags |= TextField.BORDER;
1675        }
1676
1677        input.setFlags(flags
1678                           | TextField.USEOUTLINES
1679                           | TextField.HASFONT
1680                           | TextField.MULTILINE
1681                           );
1682
1683        // left, right, indent, and align don't make sense
1684
// when we do all input text wrapping ourselves.
1685
// Leading still matters though!
1686
input.setLayout(0, 0, 0, 0, mTextLeading*TWIP);
1687
1688        input.setBounds(new Rectangle2D.Double JavaDoc(0, 0, mWidth*TWIP, mHeight*TWIP));
1689
1690        mLogger.debug("Add inputtext " + name + " " + mWidth + " " + mHeight);
1691
1692        frame.addInstance(input, 1, null, null);
1693        frame.addStopAction();
1694
1695        // Add movieClip to library
1696
mFlashFile.addDefToLibrary(name, movieClip);
1697        // Export it.
1698
ExportAssets ea = new ExportAssets();
1699        ea.addAsset(name, movieClip);
1700        Timeline timeline = mFlashFile.getMainScript().getTimeline();
1701        frame = timeline.getFrameAt(timeline.getFrameCount() - 1);
1702        frame.addFlashObject(ea);
1703
1704        // Add this clip to our set of clips
1705
mInputTextSet.put(name, name);
1706    }
1707
1708
1709    /** Create a custom text input field that will correspond to a
1710     * specific lzx input text view.
1711     */

1712    public void addCustomInputText(FontInfo fontInfo, int width, int height,
1713                                   boolean multiline, boolean password)
1714        throws CompilationError {
1715
1716        // We only need to add text field defs at compile time to swf5 files;
1717
// swf6 and greater create all text at runtime.
1718
if (mFlashVersion != 5) {
1719            return;
1720        }
1721
1722        String JavaDoc fontName = fontInfo.getName();
1723        Font font = getFontFromInfo(fontInfo);
1724        // code to designate if this resource is single line or multiline
1725
String JavaDoc mstring;
1726        mstring = multiline ? "/m" : "/s";
1727
1728        String JavaDoc name =
1729            "lzinputtext/" +
1730            fontName + "/" +
1731            fontInfo.getSize() + "/" +
1732            fontInfo.getStyle(false) + "/" + width + "/" + height +
1733            (password ? "/passwd" : "") + mstring;
1734
1735        // We've already added an movieclip that will work for this
1736
// input text
1737
if (mInputTextSet.containsKey(name)) {
1738            return;
1739        }
1740
1741        // Create a movieclip with a single frame with
1742
// a text field of the correct size.
1743
Script movieClip = new Script(1);
1744        Frame frame = movieClip.newFrame();
1745
1746        TextField input = new TextField("", "text",
1747                font, fontInfo.getSize()*TWIP, AlphaColor.black);
1748
1749        int flags = input.getFlags();
1750
1751        if (password) {
1752            flags |= TextField.PASSWORD;
1753        }
1754
1755        if (mProperties.getProperty("text.borders", "false").equals("true")) {
1756            flags |= TextField.BORDER;
1757        }
1758
1759        input.setFlags(flags
1760                       | TextField.USEOUTLINES
1761                       | TextField.HASFONT
1762                       | (multiline ? TextField.MULTILINE : 0)
1763                       | (multiline ? TextField.WORDWRAP : 0)
1764                       );
1765
1766        // left, right, indent, and align don't make sense
1767
// when we do all input text wrapping ourselves.
1768
// Leading still matters though!
1769
input.setLayout(0, 0, 0, 0, mTextLeading*TWIP);
1770        input.setBounds(new Rectangle2D.Double JavaDoc(0, 0, width*TWIP, (height+mTextLeading)*TWIP));
1771        mLogger.debug("Add custom inputtext " + name + " " + width + " " + height);
1772
1773        frame.addInstance(input, 1, null, null);
1774        frame.addStopAction();
1775
1776        // Add movieClip to library
1777
mFlashFile.addDefToLibrary(name, movieClip);
1778        // Export it.
1779
ExportAssets ea = new ExportAssets();
1780        ea.addAsset(name, movieClip);
1781        Timeline timeline = mFlashFile.getMainScript().getTimeline();
1782        frame = timeline.getFrameAt(timeline.getFrameCount() - 1);
1783        frame.addFlashObject(ea);
1784
1785        // Add this clip to our set of clips
1786
mInputTextSet.put(name, name);
1787    }
1788
1789
1790
1791    /**
1792     * Add a movieclip with two frames in it,
1793     * each one with a text field
1794     */

1795    void addTextAsset(String JavaDoc name, Font font)
1796        throws CompilationError {
1797
1798        if (font == null) { return; }
1799
1800        // Create a movieclip with a 2 frames with
1801
// a text field the size of the canvas (or maxTextWidth/maxTextHeight).
1802
Script movieClip = new Script(2);
1803
1804        String JavaDoc initText =
1805            "<P ALIGN=\"LEFT\"><FONT SIZE=\"" + DEFAULT_SIZE
1806             + "\" COLOR=\"#000000\"> </FONT></P>";
1807
1808        // We only need to add text field defs at compile time to swf5 files;
1809
// swf6 and greater create all text at runtime.
1810
if (mFlashVersion != 5) {
1811            return;
1812        }
1813
1814        {
1815            TextField text = new TextField(initText, "text",
1816                    font, mMaxTextWidth*TWIP, AlphaColor.black);
1817    
1818            int flags = text.getFlags();
1819            if (mProperties.getProperty("text.borders", "false").equals("true")) {
1820                flags |= TextField.BORDER;
1821            }
1822    
1823            text.setFlags(flags
1824                               | TextField.USEOUTLINES
1825                               | TextField.HASFONT
1826                               | TextField.READONLY
1827                               | TextField.MULTILINE
1828                               | TextField.HTML
1829                               | TextField.NOSELECT
1830                               );
1831    
1832            text.setLayout(0, 0, 0, 0, mTextLeading*TWIP);
1833            text.setBounds(new Rectangle2D.Double JavaDoc(0, 0, mMaxTextWidth*TWIP, mMaxTextHeight*TWIP));
1834
1835            Frame frame = movieClip.newFrame();
1836            frame.addStopAction();
1837            frame.addInstance(text, 1, null, null);
1838        }
1839
1840        {
1841            TextField text = new TextField(initText, "text",
1842                    font, mMaxTextWidth*TWIP, AlphaColor.black);
1843    
1844            int flags = text.getFlags();
1845            if (mProperties.getProperty("text.borders", "false").equals("true")) {
1846                flags |= TextField.BORDER;
1847            }
1848    
1849            text.setFlags(flags
1850                               | TextField.USEOUTLINES
1851                               | TextField.HASFONT
1852                               | TextField.READONLY
1853                               | TextField.MULTILINE
1854                               | TextField.HTML
1855                               );
1856    
1857            text.setLayout(0, 0, 0, 0, mTextLeading*TWIP);
1858            text.setBounds(new Rectangle2D.Double JavaDoc(0, 0, mMaxTextWidth*TWIP, mMaxTextHeight*TWIP));
1859
1860            Frame frame = movieClip.newFrame();
1861            frame.removeInstance(1);
1862            frame.addInstance(text, 1, null, null);
1863        }
1864
1865        // Add movieClip to library
1866
mFlashFile.addDefToLibrary(name, movieClip);
1867
1868        // Export it.
1869
ExportAssets ea = new ExportAssets();
1870        ea.addAsset(name, movieClip);
1871        Timeline timeline = mFlashFile.getMainScript().getTimeline();
1872
1873        // Add this asset
1874
int num = mPreloaderAdded ? 1 : 0;
1875        Frame frame = timeline.getFrameAt(num);
1876        frame.addFlashObject(ea);
1877    }
1878
1879    /**
1880     * A resource we've imported
1881     */

1882    class Resource implements Comparable JavaDoc {
1883        /** Name of the resource */
1884        private String JavaDoc mName = "";
1885        /** width of the resource in pixels */
1886        private int mWidth = 0;
1887        /** height of the resource in pixels */
1888        private int mHeight = 0;
1889        /** Flash definition of this resource */
1890        private FlashDef mFlashDef = null;
1891
1892        /** Create a resource that represents this flash def
1893         * @param def
1894         */

1895        public Resource(FlashDef def) {
1896            mFlashDef = def;
1897        }
1898
1899        /** Create a resource
1900         */

1901        public Resource(String JavaDoc name, FlashDef def, int width, int height) {
1902            mName = name;
1903            mFlashDef = def;
1904            mWidth = width;
1905            mHeight = height;
1906        }
1907
1908        public String JavaDoc getName() { return mName; }
1909        public int getWidth() { return mWidth; }
1910        public int getHeight() { return mHeight; }
1911        public FlashDef getFlashDef() { return mFlashDef; }
1912
1913        public int compareTo(Object JavaDoc other) throws ClassCastException JavaDoc {
1914          Resource o = (Resource)other;
1915          return mName.compareTo(o.mName);
1916        }
1917    }
1918
1919    /**
1920     * @return font given a font info
1921     */

1922    private Font getFontFromInfo(FontInfo fontInfo) {
1923        // This will bring in the default bold ofnt if it's not here yet
1924
checkFontExists(fontInfo);
1925        String JavaDoc fontName = fontInfo.getName();
1926        FontFamily family = mFontManager.getFontFamily(fontName);
1927        String JavaDoc style = fontInfo.getStyle();
1928
1929        if (family == null) {
1930            return null;
1931            /*
1932            throw new CompilationError("Font '" + fontName +
1933                "' used but not defined");
1934            */

1935        }
1936        Font font = family.getStyle(fontInfo.styleBits);
1937        if (font == null) {
1938            throw new CompilationError("Font '"
1939                + fontName
1940                + "' style ('"
1941                + style
1942                + "') used but not defined");
1943        }
1944        return font;
1945    }
1946
1947    /**
1948     * @return true if the font exists
1949     *
1950     * If this is the default bold font and it hasn't been
1951     * declared, import it.
1952     */

1953    boolean checkFontExists(FontInfo fontInfo) {
1954
1955        // Bulletproofing...
1956
if (fontInfo.getName() == null) {
1957            return false;
1958        }
1959 
1960        boolean a = mFontManager.checkFontExists(fontInfo);
1961        if (a) {
1962            return a;
1963        }
1964
1965        if (fontInfo.getName().equals(mDefaultFontName) &&
1966            fontInfo.styleBits == FontInfo.PLAIN) {
1967            try {
1968                    File f = mEnv.resolve(mDefaultFontFileName, "");
1969                    importFontStyle(f.getAbsolutePath(), mDefaultFontName, "plain", mEnv);
1970                } catch (FileNotFoundException fnfe) {
1971                    throw new CompilationError("default font "
1972                        + mDefaultFontFileName + " missing " + fnfe);
1973            }
1974            return true;
1975        }
1976
1977        if (fontInfo.getName().equals(mDefaultFontName) &&
1978            fontInfo.styleBits == FontInfo.BOLD) {
1979            try {
1980                File f = mEnv.resolve(mDefaultBoldFontFileName, "");
1981                importFontStyle(f.getAbsolutePath(), mDefaultFontName, "bold", mEnv);
1982            } catch (FileNotFoundException fnfe) {
1983                throw new CompilationError("default bold font "
1984                    + mDefaultBoldFontFileName + " missing " + fnfe);
1985            }
1986            return true;
1987        }
1988
1989        if (fontInfo.getName().equals(mDefaultFontName) &&
1990            fontInfo.styleBits == FontInfo.ITALIC) {
1991            try {
1992                File f = mEnv.resolve(mDefaultItalicFontFileName, "");
1993                importFontStyle(f.getAbsolutePath(), mDefaultFontName, "italic", mEnv);
1994            } catch (FileNotFoundException fnfe) {
1995                throw new CompilationError("default italic font "
1996                    + mDefaultItalicFontFileName + " missing " + fnfe);
1997            }
1998            return true;
1999        }
2000
2001        if (fontInfo.getName().equals(mDefaultFontName) &&
2002            fontInfo.styleBits == FontInfo.BOLDITALIC) {
2003            try {
2004                File f = mEnv.resolve(mDefaultBoldItalicFontFileName, "");
2005                importFontStyle(f.getAbsolutePath(), mDefaultFontName, "bold italic", mEnv);
2006            } catch (FileNotFoundException fnfe) {
2007                throw new CompilationError("default bold italic font "
2008                    + mDefaultBoldItalicFontFileName + " missing " + fnfe);
2009            }
2010            return true;
2011        }
2012
2013        return false;
2014    }
2015}
2016
Popular Tags