0001: /*****************************************************************************
0002: * SWFWriter.java
0003: * ****************************************************************************/package org.openlaszlo.compiler;
0004:
0005: import org.openlaszlo.sc.ScriptCompiler;
0006: import org.openlaszlo.server.LPS;
0007: import org.openlaszlo.utils.ChainedException;
0008: import org.openlaszlo.utils.FileUtils;
0009: import org.openlaszlo.utils.ListFormat;
0010: import org.openlaszlo.iv.flash.api.*;
0011: import org.openlaszlo.iv.flash.api.action.*;
0012: import org.openlaszlo.iv.flash.api.button.*;
0013: import org.openlaszlo.iv.flash.api.image.*;
0014: import org.openlaszlo.iv.flash.api.sound.*;
0015: import org.openlaszlo.iv.flash.api.shape.*;
0016: import org.openlaszlo.iv.flash.api.text.*;
0017: import org.openlaszlo.iv.flash.util.*;
0018: import org.openlaszlo.iv.flash.cache.*;
0019: import org.openlaszlo.compiler.CompilationEnvironment;
0020:
0021: import org.openlaszlo.media.*;
0022:
0023: import java.io.*;
0024: import java.util.*;
0025: import java.lang.Math;
0026: import java.lang.Character;
0027:
0028: import org.jdom.Element;
0029:
0030: // jgen 1.4
0031: import java.awt.geom.Rectangle2D;
0032:
0033: import org.apache.log4j.*;
0034:
0035: /** Accumulates code, XML, and assets to a SWF object file.
0036: *
0037: * Make heavy use of JGenerator API.
0038: *
0039: * Properties documented in Compiler.getProperties.
0040: */
0041: class SWFWriter extends ObjectWriter {
0042:
0043: /** Movie being constructed. */
0044: private SWFFile mFlashFile;
0045:
0046: /** Fonts being collected. */
0047: private FontsCollector mPreloaderFontsCollector = new FontsCollector();
0048: private FontManager mFontManager = new FontManager();
0049: /** Have any fonts been imported into this swf movie? */
0050: private boolean mLibFontsDefined = false;
0051:
0052: /** True iff close() has been called. */
0053: private boolean mCloseCalled = false;
0054:
0055: /** Total number of frames in the movie **/
0056: private int mLastFrame = 0;
0057:
0058: /** Input text fontinfo map */
0059: private final TreeMap mInputTextSet = new TreeMap();
0060:
0061: /** Index of font table in the first frame */
0062: private int mFontTableIndex = -1;
0063:
0064: /** Index of resource table in the first frame */
0065: private int mResourceTableIndex = -1;
0066:
0067: /** Default font */
0068: private Font mDefaultFont = null;
0069: private String mDefaultFontName = null;
0070: private String mDefaultFontFileName = null;
0071: private String mDefaultBoldFontFileName = null;
0072: private String mDefaultItalicFontFileName = null;
0073: private String mDefaultBoldItalicFontFileName = null;
0074: // TODO: [2003-12-08 bloch] need logic to set this to true
0075: private boolean mDefaultFontUsedForMeasurement = false;
0076:
0077: /** Flash format version */
0078: private int mFlashVersion = 6;
0079:
0080: /** frame rate of movie */
0081: private int mFrameRate = 30;
0082:
0083: /** Leading for text and input text */
0084: private int mTextLeading = 2;
0085:
0086: private Map mDeviceFontTable = new HashMap();
0087:
0088: /** Logger */
0089: private static Logger mLogger = org.apache.log4j.Logger
0090: .getLogger(SWFWriter.class);
0091:
0092: /** Height for generated advance (width) table */
0093: public static final int DEFAULT_SIZE = 8;
0094:
0095: /** Logger for jgenerator */
0096: /**
0097: * Initializes a SWFWriter with an OutputStream to which a new SWF
0098: * will be written when <code>SWFWriter.close()</code> is called.
0099: *
0100: * @param stream A <code>java.io.OutputStream</code> that the
0101: * movie will be written to.
0102: * @param props list of properties
0103: * @param cache media cache
0104: * @param importLibrary If true, the compiler will add in the LaszloLibrary.
0105: */
0106: SWFWriter(Properties props, OutputStream stream,
0107: CompilerMediaCache cache, boolean importLibrary,
0108: CompilationEnvironment env) {
0109: super (props, stream, cache, importLibrary, env);
0110:
0111: String s;
0112:
0113: s = mProperties.getProperty("swf.frame.rate", "30");
0114: mFrameRate = Integer.parseInt(s);
0115:
0116: s = mProperties.getProperty("swf.text.leading", "2");
0117: mTextLeading = Integer.parseInt(s);
0118:
0119: mFlashVersion = env.getSWFVersionInt();
0120:
0121: try {
0122: if (!importLibrary) {
0123: mFlashFile = new SWFFile(props);
0124: mFlashFile.setVersion(mFlashVersion);
0125: mFlashFile.setMainScript(new Script(1));
0126: }
0127:
0128: } catch (CompilationError e) {
0129: throw new ChainedException(e);
0130: }
0131: }
0132:
0133: public void importBaseLibrary(String library,
0134: CompilationEnvironment env) {
0135:
0136: try {
0137: File f = env.resolveLibrary(library, "");
0138: mFlashFile = new SWFFile(f.getAbsolutePath(), mProperties);
0139: mFlashFile.setVersion(mFlashVersion);
0140: // Set the frame rate (shifted)
0141: mFlashFile.setFrameRate(mFrameRate << 8);
0142:
0143: // mFlashFile.printContent(System.out);
0144:
0145: if (LPS.isInternalBuild()) {
0146: long lfcModTime = f.lastModified();
0147: List newerFiles = new Vector(); // List<File>
0148: List sourceDirs = new Vector(); // List<File>
0149: sourceDirs.add(f.getParentFile());
0150: while (!sourceDirs.isEmpty()) {
0151: File ff = (File) sourceDirs.get(0);
0152: sourceDirs.remove(0);
0153: if (ff.isDirectory()) {
0154: sourceDirs
0155: .addAll(Arrays.asList(ff.listFiles()));
0156: } else if (ff.isFile()
0157: && ff.getName().endsWith(".as")) {
0158: long modTime = ff.lastModified();
0159: if (modTime > lfcModTime)
0160: newerFiles.add(ff.getName());
0161: }
0162: }
0163: if (!newerFiles.isEmpty()) {
0164: env
0165: .warn(
0166: /* (non-Javadoc)
0167: * @i18n.test
0168: * @org-mes=p[0] + " is older than " + p[1]
0169: */
0170: org.openlaszlo.i18n.LaszloMessages
0171: .getMessage(
0172: SWFWriter.class.getName(),
0173: "051018-235",
0174: new Object[] {
0175: f.getName(),
0176: new ListFormat()
0177: .format(newerFiles) }));
0178: }
0179: }
0180: } catch (FileNotFoundException e) {
0181: throw new ChainedException(e);
0182: }
0183: }
0184:
0185: FontManager getFontManager() {
0186: return mFontManager;
0187: }
0188:
0189: public boolean isDeviceFont(String face) {
0190: return (mDeviceFontTable.get(face) != null);
0191: }
0192:
0193: public void setDeviceFont(String face) {
0194: mDeviceFontTable.put(face, "true");
0195: }
0196:
0197: public void setFontManager(FontManager fm) {
0198: this .mFontManager = fm;
0199: }
0200:
0201: void addPreloaderScript(String script) {
0202: if (mPreloaderAdded != true)
0203: throw new RuntimeException(
0204: /* (non-Javadoc)
0205: * @i18n.test
0206: * @org-mes="Preloader not added yet."
0207: */
0208: org.openlaszlo.i18n.LaszloMessages.getMessage(
0209: SWFWriter.class.getName(), "051018-268"));
0210:
0211: addScript(script, 0);
0212: }
0213:
0214: void addPreloader(CompilationEnvironment env) {
0215: if (mPreloaderAdded == true)
0216: throw new RuntimeException(
0217: /* (non-Javadoc)
0218: * @i18n.test
0219: * @org-mes="Preloader already added."
0220: */
0221: org.openlaszlo.i18n.LaszloMessages.getMessage(
0222: SWFWriter.class.getName(), "051018-282"));
0223:
0224: // TODO: [2004-03-04 bloch] maybe someday we have different versions of
0225: // preloader.lzl (e.g. debug, profile).
0226: File f;
0227: try {
0228: f = env.resolve("lzpreloader.lzl", null);
0229: } catch (FileNotFoundException e) {
0230: throw new ChainedException(e);
0231: }
0232: mFlashFile.addPreloaderFrame(f.getAbsolutePath());
0233: mLastFrame = 1;
0234:
0235: mPreloaderAdded = true;
0236: }
0237:
0238: /**
0239: * Sets the canvas for the movie
0240: *
0241: * @param canvas
0242: *
0243: */
0244: void setCanvas(Canvas canvas, String canvasConstructor) {
0245: Rectangle2D r = new Rectangle2D.Double(0, 0,
0246: (canvas.getWidth() * TWIP), (canvas.getHeight() * TWIP));
0247:
0248: mFlashFile.setFrameSize(r);
0249:
0250: int bgc = canvas.getBGColor();
0251: int red = (bgc >> 16) & 0xff;
0252: int green = (bgc >> 8) & 0xff;
0253: int blue = (bgc) & 0xff;
0254: Color c = new Color(red, green, blue);
0255: SetBackgroundColor setbgc = new SetBackgroundColor(c);
0256: mFlashFile.getMainScript().setBackgroundColor(setbgc);
0257:
0258: // Write scriptlimits tag if requested
0259: if ((this .mRecursionLimit != 0)
0260: || (this .mExecutionTimeout != 0)) {
0261: // ScriptLimits tag, to set max recursion depth and timeout
0262: Frame frame = mFlashFile.getMainScript().getFrameAt(
0263: mLastFrame);
0264: ScriptLimits slimit = new ScriptLimits(
0265: this .mRecursionLimit, this .mExecutionTimeout);
0266: frame.addFlashObject(slimit);
0267: }
0268:
0269: // NOTE: disable constant pool when compiling canvas constructor
0270: // so that build id etc... are easy for qa to pick out.
0271: Properties props = (Properties) mProperties.clone();
0272: props.setProperty("disableConstantPool", "1");
0273: //scriptWriter.println(canvasConstructor);
0274: byte[] action = ScriptCompiler.compileToByteArray(
0275: canvasConstructor, props);
0276: Program program = new Program(action, 0, action.length);
0277: mLogger.debug(" Adding a program of " + action.length
0278: + " bytes.");
0279: addProgram(program);
0280:
0281: // Set width and height properties for preloader...
0282: mWidth = canvas.getWidth();
0283: mHeight = canvas.getHeight();
0284:
0285: // Get default font info
0286: FontInfo fontInfo = canvas.getFontInfo();
0287:
0288: mDefaultFontName = canvas.defaultFont;
0289: mDefaultFontFileName = canvas.defaultFontFilename;
0290: mDefaultBoldFontFileName = canvas.defaultBoldFontFilename;
0291: mDefaultItalicFontFileName = canvas.defaultItalicFontFilename;
0292: mDefaultBoldItalicFontFileName = canvas.defaultBoldItalicFontFilename;
0293:
0294: Frame frame = mFlashFile.getMainScript().getFrameAt(mLastFrame);
0295:
0296: // Make a bogus object for us to replace with the font and resource table
0297: // actionscript at the end of compilation.
0298: mFontTableIndex = frame.size();
0299: frame
0300: .addFlashObject(new SetBackgroundColor(new Color(0, 0,
0301: 0)));
0302: mResourceTableIndex = frame.size();
0303: frame
0304: .addFlashObject(new SetBackgroundColor(new Color(0, 0,
0305: 0)));
0306:
0307: mEnv.getCanvas().addInfo(mInfo);
0308: }
0309:
0310: /** Get default fonts and stuff from canvas; used for snippet compilation */
0311: void setCanvasDefaults(Canvas canvas, CompilerMediaCache mc) {
0312: Rectangle2D r = new Rectangle2D.Double(0, 0,
0313: (canvas.getWidth() * TWIP), (canvas.getHeight() * TWIP));
0314:
0315: mFlashFile.setFrameSize(r);
0316: this .mCache = mc;
0317: mWidth = canvas.getWidth();
0318: mHeight = canvas.getHeight();
0319: // Get default font info
0320: FontInfo fontInfo = canvas.getFontInfo();
0321: mDefaultFontName = canvas.defaultFont;
0322: mDefaultFontFileName = canvas.defaultFontFilename;
0323: mDefaultBoldFontFileName = canvas.defaultBoldFontFilename;
0324: mDefaultItalicFontFileName = canvas.defaultItalicFontFilename;
0325: mDefaultBoldItalicFontFileName = canvas.defaultBoldItalicFontFilename;
0326: }
0327:
0328: /** Create a program from the given script */
0329: Program program(String script) {
0330: byte[] action = ScriptCompiler.compileToByteArray(script,
0331: mProperties);
0332: return new Program(action, 0, action.length);
0333: }
0334:
0335: /** Compiles the specified script to bytecodes
0336: * and add its bytecodes to the current frame in this movie.
0337: *
0338: * @param script the script to be compiled
0339: * @return the number of bytes
0340: */
0341: public int addScript(String script) {
0342: byte[] action = ScriptCompiler.compileToByteArray(script,
0343: mProperties);
0344: //scriptWriter.println(script);
0345: Program program = new Program(action, 0, action.length);
0346: mLogger.debug(
0347: /* (non-Javadoc)
0348: * @i18n.test
0349: * @org-mes="Adding a program of " + p[0] + " bytes."
0350: */
0351: org.openlaszlo.i18n.LaszloMessages.getMessage(SWFWriter.class
0352: .getName(), "051018-410", new Object[] { new Integer(
0353: action.length) }));
0354: addProgram(program);
0355: return action.length;
0356: }
0357:
0358: /** Compiles the specified script to bytecodes
0359: * and add its bytecodes to the current frame in this movie.
0360: *
0361: * @param script the script to be compiled
0362: * @param offset of frame to add to
0363: */
0364: private void addScript(String script, int offset) {
0365: byte[] action = ScriptCompiler.compileToByteArray(script,
0366: mProperties);
0367: //scriptWriter.println(script);
0368: Program program = new Program(action, 0, action.length);
0369: mLogger.debug(
0370: /* (non-Javadoc)
0371: * @i18n.test
0372: * @org-mes="Adding a program of " + p[0] + " bytes."
0373: */
0374: org.openlaszlo.i18n.LaszloMessages.getMessage(SWFWriter.class
0375: .getName(), "051018-410", new Object[] { new Integer(
0376: action.length) }));
0377: addProgram(program, offset);
0378: }
0379:
0380: /**
0381: * Adds the program to the next frame
0382: *
0383: * @param program to be added
0384: */
0385: void addProgram(Program program) {
0386: Frame frame = mFlashFile.getMainScript().getFrameAt(mLastFrame);
0387: frame.addFlashObject(new DoAction(program));
0388: }
0389:
0390: /**
0391: * Adds the program to the specified frame
0392: *
0393: * @param program to be added
0394: * @param offset of frame to add to
0395: */
0396: private void addProgram(Program program, int offset) {
0397: Frame frame = mFlashFile.getMainScript().getFrameAt(offset);
0398: frame.addFlashObject(new DoAction(program));
0399: }
0400:
0401: public static String stripBaseName(String fileName,
0402: CompilationEnvironment env) {
0403: try {
0404: fileName = (new File(fileName)).getCanonicalFile()
0405: .toString();
0406: } catch (java.io.IOException e) {
0407: }
0408: String base = env.getErrorHandler().fileBase;
0409: if (base == null || "".equals(base)) {
0410: return fileName;
0411: }
0412: base = (new File(base)).getAbsolutePath() + File.separator;
0413: if (base != null) {
0414: int i = 1;
0415: // Find longest common substring
0416: while (i < base.length()
0417: && fileName.startsWith(base.substring(0, i))) {
0418: i++;
0419: }
0420: // remove base string prefix
0421: return fileName.substring(i - 1);
0422: } else {
0423: return fileName;
0424: }
0425: }
0426:
0427: /** Import a resource file into the preloader movie.
0428: * Using a name that already exists clobbers the
0429: * old resource (for now).
0430: *
0431: * @param fileName file name of the resource
0432: * @param name name of the MovieClip/Sprite
0433: * @throws CompilationError
0434: */
0435: public void importPreloadResource(String fileName, String name)
0436: throws ImportResourceError {
0437: if (name.equals(""))
0438: name = createName();
0439: importResource(fileName, name, 0, mPreloaderFontsCollector);
0440: }
0441:
0442: public void importPreloadResource(File fFileName, String name)
0443: throws ImportResourceError {
0444: if (name.equals(""))
0445: name = createName();
0446: importResource(fFileName.toString(), name, 0,
0447: mPreloaderFontsCollector);
0448: }
0449:
0450: /** Import a multiframe resource into the current movie. Using a
0451: * name that already exists clobbers the old resource (for now).
0452: */
0453: public void importPreloadResource(List sources, String name,
0454: File parent) throws ImportResourceError {
0455: if (name.equals(""))
0456: name = createName();
0457: importResource(sources, name, parent, 0,
0458: mPreloaderFontsCollector);
0459: }
0460:
0461: /** Import a multiframe resource into the current movie. Using a
0462: * name that already exists clobbers the old resource (for now).
0463: *
0464: * @param sources file names of the resources
0465: * @param name name of the MovieClip/Sprite
0466: * @param parent parent's File object
0467: */
0468: public void importResource(List sources, String name, File parent) {
0469: importResource(sources, name, parent, -1);
0470: }
0471:
0472: /** Import a multiframe resource into the current movie. Using a
0473: * name that already exists clobbers the old resource (for now).
0474: *
0475: * @param sources file names of the resources
0476: * @param name name of the MovieClip/Sprite
0477: * @param parent parent's File object
0478: * @param frameNum frame offset to add to
0479: */
0480: public void importResource(List sources, String name, File parent,
0481: int frameNum) {
0482: importResource(sources, name, parent, frameNum, null);
0483: }
0484:
0485: /** Import a resource file into the current movie.
0486: * Using a name that already exists clobbers the
0487: * old resource (for now).
0488: *
0489: * @param fileName file name of the resource
0490: * @param name name of the MovieClip/Sprite
0491: * @throws CompilationError
0492: */
0493: public void importResource(String fileName, String name)
0494: throws ImportResourceError {
0495: importResource(fileName, name, -1);
0496: }
0497:
0498: public void importResource(File fFile, String name)
0499: throws ImportResourceError {
0500: importResource(fFile.toString(), name);
0501: }
0502:
0503: /** Import a resource file into the current movie.
0504: * Using a name that already exists clobbers the
0505: * old resource (for now).
0506: *
0507: * @param fileName file name of the resource
0508: * @param name name of the MovieClip/Sprite
0509: * @param frameNum frame offset to add to
0510: * @throws CompilationError
0511: */
0512: public void importResource(String fileName, String name,
0513: int frameNum) throws CompilationError {
0514: importResource(fileName, name, frameNum, null);
0515: }
0516:
0517: /** Import a resource file into the current movie.
0518: * Using a name that already exists clobbers the
0519: * old resource (for now).
0520: *
0521: * @param fileName file name of the resource
0522: * @param name name of the MovieClip/Sprite
0523: * @param frameNum frame offset to add to
0524: * @param fontsCollector fonts collector for resource (used by preloader)
0525: * @throws CompilationError
0526: */
0527: public void importResource(String fileName, String name,
0528: int frameNum, FontsCollector fontsCollector)
0529: throws CompilationError {
0530:
0531: File inputFile = new File(fileName);
0532: if (inputFile.isDirectory()) {
0533: String[] sources = inputFile.list();
0534: ArrayList outsources = new ArrayList();
0535:
0536: for (int i = 0; i < sources.length; i++) {
0537: String fname = fileName + File.separator + sources[i];
0538: File f = new File(fname);
0539: //mLogger.debug("SWFWriter file: " + f.isFile());
0540:
0541: if (f.isFile()) {
0542: //mLogger.debug("SWFWriter adding: " + fname);
0543: outsources.add(fname);
0544: }
0545: }
0546: importResource(outsources, name, null, -1, null, false);
0547: return;
0548: }
0549:
0550: mLogger.debug(" Importing resource " + name);
0551: try {
0552: fileName = new File(fileName).getCanonicalPath();
0553: } catch (java.io.IOException e) {
0554: throw new ImportResourceError(fileName, e, mEnv);
0555: }
0556: FlashDef def = null;
0557:
0558: Resource res = (Resource) mResourceMap.get(fileName);
0559: boolean oldRes = (res != null);
0560: if (!oldRes) {
0561: // Get the resource and put in the map
0562: res = getResource(fileName, name);
0563: mResourceMap.put(fileName, res);
0564:
0565: def = res.getFlashDef();
0566: if (fontsCollector != null)
0567: def.collectFonts(fontsCollector);
0568: mFlashFile.addDefToLibrary(name, def);
0569: def.setName(name);
0570:
0571: } else {
0572: def = res.getFlashDef();
0573: if (fontsCollector != null)
0574: def.collectFonts(fontsCollector);
0575:
0576: // Add an element with 0 size, since it's already there.
0577: Element elt = new Element("resource");
0578: elt.setAttribute("name", name);
0579: // elt.setAttribute("mime-type", MimeType.MP3);
0580: elt.setAttribute("source", fileName);
0581: elt.setAttribute("filesize", "0");
0582: mInfo.addContent(elt);
0583: }
0584:
0585: ExportAssets ea = new ExportAssets();
0586: ea.addAsset(name, def);
0587: Timeline timeline = mFlashFile.getMainScript().getTimeline();
0588: if (frameNum == -1) {
0589: frameNum = timeline.getFrameCount() - 1;
0590: }
0591: Frame frame = timeline.getFrameAt(frameNum);
0592: frame.addFlashObject(ea);
0593: }
0594:
0595: /** Import a multiframe resource into the current movie. Using a
0596: * name that already exists clobbers the old resource (for now).
0597: *
0598: * @param sources file names of the resources
0599: * @param name name of the MovieClip/Sprite
0600: * @param parent parent's File object
0601: * @param frameNum frame offset to add to
0602: * @param fontsCollector fonts collector for resource (used by preloader)
0603: */
0604: public void importResource(List sources, String name, File parent,
0605: int frameNum, FontsCollector fontsCollector)
0606:
0607: {
0608: importResource(sources, name, parent, frameNum, fontsCollector,
0609: true);
0610: }
0611:
0612: /** Import a multiframe resource into the current movie. Using a
0613: * name that already exists clobbers the old resource (for now).
0614: *
0615: * @param sources file names of the resources
0616: * @param name name of the MovieClip/Sprite
0617: * @param parent parent's File object
0618: * @param frameNum frame offset to add to
0619: * @param fontsCollector fonts collector for resource (used by preloader)
0620: * @param addStop if true, add a stop frame after each imported resource
0621: */
0622: public void importResource(List sources, String name, File parent,
0623: int frameNum, FontsCollector fontsCollector, boolean addStop) {
0624: Script out = new Script(1);
0625: String fileName = null;
0626: mLogger.debug("Including multiple resources as " + name);
0627: int width = 0;
0628: int height = 0;
0629: int fNum = 0;
0630: for (Iterator e = sources.iterator(); e.hasNext();) {
0631: fileName = (String) e.next();
0632: mLogger.debug(" Importing " + fileName);
0633:
0634: // Definition to add to the library (without stop)
0635: Resource res = getMultiFrameResource(fileName, name, fNum);
0636: Script scr = (Script) res.getFlashDef();
0637: if (fontsCollector != null)
0638: scr.collectFonts(fontsCollector);
0639: int bc = out.getFrameCount();
0640: out.appendScript(scr);
0641: int fc = out.getFrameCount();
0642: Frame f = out.getFrameAt(fc - 1);
0643: if (addStop)
0644: f.addStopAction();
0645: mLogger.debug(" Added " + (fc - bc) + " of " + fc
0646: + "frame(s)");
0647:
0648: int rw = res.getWidth();
0649: int rh = res.getHeight();
0650: if (rw > width) {
0651: width = rw;
0652: }
0653: if (rh > height) {
0654: height = rh;
0655: }
0656: // NOTE: add the ratio attribute to each frame here; this
0657: // appears to be required to make a multi-frame resource that has individual
0658: // frames that are swfs with nested movieclips work correctly.
0659: // This was "guessed" by dumping the contents
0660: // of a multi-frame SWF created by the Flash tool itself.
0661: // This is weird since the docs on 'ratio' say it is only
0662: // needed to cope with Morphing. Hmph. See bug 4961 for details.
0663: if (fNum > 0) {
0664: for (int i = 0; i < f.size(); i++) {
0665: if (f.getFlashObjectAt(i) instanceof Instance) {
0666: Instance inst = (Instance) f
0667: .getFlashObjectAt(i);
0668: inst.ratio = fNum;
0669: break;
0670: }
0671: }
0672: }
0673: fNum++;
0674: }
0675:
0676: // TODO [2003-1-2 bloch]: Could optimize and only add
0677: // multi-frame resources when the total size is greater than
0678: // the size of the first frame
0679: mMultiFrameResourceSet.add(new Resource(name, out, width,
0680: height));
0681:
0682: FlashDef def = (FlashDef) out;
0683: mFlashFile.addDefToLibrary(name, def);
0684: def.setName(name);
0685: ExportAssets ea = new ExportAssets();
0686: ea.addAsset(name, def);
0687: Timeline timeline = mFlashFile.getMainScript().getTimeline();
0688: if (frameNum == -1) {
0689: frameNum = timeline.getFrameCount() - 1;
0690: }
0691: Frame frame = timeline.getFrameAt(frameNum);
0692: frame.addFlashObject(ea);
0693: }
0694:
0695: /** Imports this resource, if it has not previously been imported, as
0696: * resource that can be used as a click region, and returns in any
0697: * case the name of the clip that refers to it.
0698: */
0699: public String importClickResource(File file)
0700: throws ImportResourceError {
0701: String fileName;
0702: try {
0703: fileName = file.getCanonicalPath();
0704: } catch (java.io.IOException e) {
0705: throw new CompilationError(e);
0706: }
0707:
0708: String name = (String) mClickResourceMap.get(fileName);
0709:
0710: if (name == null) {
0711:
0712: Button2 but = new Button2();
0713:
0714: FlashDef def;
0715: Rectangle2D bounds;
0716:
0717: // FIXME: [2004-06-29 bloch]
0718: // For each instance in the first frame, add a button record.
0719: // Get bounds for entire clip; should only get bounds for first frame!
0720: // Should only allow swf resources as click resources.
0721: try {
0722: Script script = FlashFile.parse(fileName)
0723: .getMainScript();
0724: Frame frame = script.getFrameAt(0);
0725: bounds = script.getBounds();
0726: for (int i = 0; i < frame.size(); i++) {
0727: FlashObject o = (FlashObject) frame
0728: .getFlashObjectAt(i);
0729: if (o instanceof Instance) {
0730: Instance inst = (Instance) o;
0731: CXForm cxform = inst.cxform;
0732: if (cxform == null) {
0733: cxform = CXForm.newIdentity(false);
0734: }
0735: java.awt.geom.AffineTransform matrix = inst.matrix;
0736: if (matrix == null) {
0737: matrix = new java.awt.geom.AffineTransform();
0738: }
0739:
0740: but.addButtonRecord(new ButtonRecord(
0741: ButtonRecord.HitTest, inst.def, 1,
0742: matrix, cxform));
0743: }
0744: }
0745: } catch (Exception e) {
0746: throw new ImportResourceError(fileName, e, mEnv);
0747: }
0748:
0749: // TODO: [2004-06029 bloch] When we merge into lps-intl2, there should
0750: // be some code sharing between this and SWFFile
0751: but
0752: .addActionCondition(ActionCondition
0753: .onPress(program("_root.LzModeManager.handleMouseButton( myView, 'onmousedown')")));
0754: but
0755: .addActionCondition(ActionCondition
0756: .onRelease(program("_root.LzModeManager.handleMouseButton( myView, 'onmouseup');"
0757: + "_root.LzModeManager.handleMouseEvent( myView, 'onclick')")));
0758: but
0759: .addActionCondition(ActionCondition
0760: .onReleaseOutside(program("_root.LzModeManager.handleMouseButton( myView, 'onmouseup');"
0761: + "_root.LzModeManager.handleMouseEvent( myView, 'onmouseupoutside')")));
0762: but
0763: .addActionCondition(ActionCondition
0764: .onRollOver(program("_root.LzModeManager.handleMouseEvent( myView, 'onmouseover')")));
0765: but
0766: .addActionCondition(ActionCondition
0767: .onRollOut(program("_root.LzModeManager.handleMouseEvent( myView, 'onmouseout')")));
0768: but
0769: .addActionCondition(ActionCondition
0770: .onDragOut(program("_root.LzModeManager.handleMouseEvent( myView, 'onmouseout');"
0771: + "_root.LzModeManager.handleMouseEvent( myView, 'onmousedragout')")));
0772: but
0773: .addActionCondition(ActionCondition
0774: .onDragOver(program("_root.LzModeManager.handleMouseEvent( myView, 'onmouseover');"
0775: + "_root.LzModeManager.handleMouseEvent( myView, 'onmousedragin')")));
0776:
0777: name = createName();
0778:
0779: // Scale the movieclip to 100x100 for use by LFC.
0780: Script movieClip = new Script(1);
0781: Frame f = movieClip.getFrameAt(0);
0782: double sx = 100.0 * TWIP / (double) bounds.getWidth();
0783: double sy = 100.0 * TWIP / (double) bounds.getHeight();
0784: java.awt.geom.AffineTransform matrix = java.awt.geom.AffineTransform
0785: .getScaleInstance(sx, sy);
0786: f.addInstance(but, 1, matrix, null);
0787:
0788: mFlashFile.addDefToLibrary(name, movieClip);
0789: movieClip.setName(name);
0790: ExportAssets ea = new ExportAssets();
0791: ea.addAsset(name, movieClip);
0792: Timeline timeline = mFlashFile.getMainScript()
0793: .getTimeline();
0794: Frame frame = timeline
0795: .getFrameAt(timeline.getFrameCount() - 1);
0796: frame.addFlashObject(ea);
0797:
0798: mClickResourceMap.put(fileName, name);
0799: }
0800:
0801: return name;
0802: }
0803:
0804: /**
0805: * Recursively strips out the ActionScript from a
0806: * given movie Script (MovieClip)
0807: *
0808: * Actually, it leaves the actionscript blocks in,
0809: * but turns them into programs that do nothing.
0810: */
0811: private void stripActions(Script s) {
0812: Timeline t = s.getTimeline();
0813: int n = t.getFrameCount();
0814:
0815: boolean didStop = false;
0816:
0817: for (int i = 0; i < n; i++) {
0818: Frame f = s.getFrameAt(i);
0819:
0820: for (int j = 0; j < f.size(); j++) {
0821: FlashObject o = f.getFlashObjectAt(j);
0822: if (o instanceof Script) {
0823: stripActions((Script) o);
0824: } else if (o instanceof DoAction) {
0825: DoAction doa = (DoAction) o;
0826: Program p = new Program();
0827: p.none();
0828: doa.setProgram(p);
0829: }
0830: }
0831: }
0832: }
0833:
0834: /** strip compiler pragmas from script
0835: */
0836: String stripPragmas(String str) {
0837: String s2 = str;
0838: //s2 = s2.replaceAll("[\r\n]#[^\r\n]*[\r\n]", "");
0839: s2 = s2.replaceAll("#line\\s\\d*", "");
0840: s2 = s2.replaceAll("#beginAttributeStatements", "");
0841: s2 = s2.replaceAll("#endAttributeStatements", "");
0842: s2 = s2.replaceAll("#pragma\\s\\S*", "");
0843: s2 = s2.replaceAll("#beginAttribute", "");
0844: s2 = s2.replaceAll("#endAttribute", "");
0845: s2 = s2.replaceAll("#beginContent", "");
0846: s2 = s2.replaceAll("#endContent", "");
0847: s2 = s2.replaceAll("#file\\s[^ \t\"]*", "");
0848: return s2;
0849: }
0850:
0851: /** Writes the SWF to the <code>OutputStream</code> that was
0852: * supplied to the SWFWriter's constructor.
0853: * @throws IOException if an error occurs
0854: */
0855: public void close() throws IOException {
0856:
0857: if (mCloseCalled) {
0858: throw new IllegalStateException(
0859: "SWFWriter.close() called twice");
0860: }
0861:
0862: // Add font information
0863: addFontTable();
0864:
0865: // Add resource information to canvas.
0866: addResourceTable();
0867:
0868: if (mPreloaderAdded == true) {
0869: // Add script to hide the preloader after finishing instantiation
0870: String finalobj = "_root.lzpreloader.done();";
0871: addScript(finalobj);
0872: }
0873:
0874: // Flag says to post a copy of all debug.write() calls back to the server
0875: if (mProperties.getProperty("logdebug", "false").equals("true")) {
0876: addScript("_dbg_log_all_writes = true;");
0877: }
0878:
0879: boolean debug = mProperties.getProperty("debug", "false")
0880: .equals("true");
0881:
0882: // This indicates whether the user's source code already manually invoked
0883: // <debug> to create a debug window. If they didn't explicitly call for
0884: // a debugger window, instantiate one now by passing 'true' to __LzDebug.startDebugWindow()
0885: boolean makedebugwindow = !mEnv
0886: .getBooleanProperty(mEnv.USER_DEBUG_WINDOW);
0887:
0888: // Bring up a debug window if needed.
0889: if (debug && makedebugwindow) {
0890: addScript("__LzDebug.makeDebugWindow()");
0891: }
0892:
0893: // Tell the canvas we're done loading.
0894: addScript("canvas.initDone()");
0895:
0896: // Make sure we stop
0897: Program program = new Program();
0898: program.stop();
0899: program.none();
0900: addProgram(program);
0901:
0902: // Always compress
0903: mFlashFile.setCompressed(true);
0904:
0905: try {
0906:
0907: InputStream input;
0908: input = mFlashFile.generate(
0909: mEnv.getEmbedFonts() ? mFontsCollector
0910: : new FontsCollector(),
0911: mEnv.getEmbedFonts() ? mPreloaderFontsCollector
0912: : new FontsCollector(), mPreloaderAdded)
0913: .getInputStream();
0914:
0915: FileUtils.send(input, mStream);
0916:
0917: } catch (IVException e) {
0918: throw new ChainedException(e);
0919: }
0920:
0921: mCloseCalled = true;
0922: }
0923:
0924: public void openSnippet(String liburl) throws IOException {
0925: // How do we make sure an initial frame exists? Does this do it?
0926: Frame frame = mFlashFile.getMainScript().getFrameAt(mLastFrame);
0927: // if we don't have any frame, then code which adds resources gets
0928: // an error. This happens if you have a resource declared before any code.
0929: this .liburl = liburl;
0930: }
0931:
0932: public void closeSnippet() throws IOException {
0933:
0934: if (mCloseCalled) {
0935: throw new IllegalStateException(
0936: "SWFWriter.closeSnippet() called twice");
0937: }
0938:
0939: mFlashFile.setCompressed(true);
0940:
0941: // If any fonts have been declared in this library, add an
0942: // ImportAssets(2?) to import assets. While this doesn't seem
0943: // to be of any use (importing our own assets) it does cause
0944: // the font assets to become visible to the main app for some
0945: // reason.
0946: //
0947: // Note: This trick doesn't seem to work for image resource,
0948: // unfortunately; we haven't figured out a way to make the
0949: // imported image assets be visible/attachable to the loading
0950: // movieclip.
0951:
0952: if (mLibFontsDefined) {
0953: ImportAssets2 ia = new ImportAssets2();
0954: //System.err.println("setting ImportAssets url="+this.liburl);
0955: //get property of base url for fonts to import
0956: String importFontUrlPath = LPS
0957: .getProperty("import.font.base.url");
0958: if (importFontUrlPath != null) {
0959: if (importFontUrlPath.endsWith("/") == false) {
0960: importFontUrlPath += "/";
0961: }
0962: if (liburl.startsWith("/")) {
0963: liburl = liburl.substring(1);
0964: }
0965: liburl = importFontUrlPath + liburl;
0966: }
0967: ia.setUrl(liburl);
0968:
0969: Enumeration enu = mFlashFile.definitions();
0970: while (enu.hasMoreElements()) {
0971: FlashDef def = (FlashDef) enu.nextElement();
0972: ia.addAsset(def.getName(), def);
0973: }
0974:
0975: Timeline timeline = mFlashFile.getMainScript()
0976: .getTimeline();
0977: Frame frame = timeline
0978: .getFrameAt(timeline.getFrameCount() - 1);
0979: frame.addFlashObject(ia);
0980: }
0981:
0982: ////////////////////////////////////////////////////////////////
0983:
0984: // Make sure we stop
0985: addScript("this._parent.loader.snippetLoaded(this._parent, null)");
0986: Program program = new Program();
0987: program.stop();
0988: program.none();
0989: addProgram(program);
0990:
0991: try {
0992: InputStream input;
0993: input = mFlashFile.generate(
0994: mEnv.getEmbedFonts() ? mFontsCollector
0995: : new FontsCollector(),
0996: mEnv.getEmbedFonts() ? mPreloaderFontsCollector
0997: : new FontsCollector(), mPreloaderAdded)
0998: .getInputStream();
0999: FileUtils.send(input, mStream);
1000: } catch (IVException e) {
1001: throw new ChainedException(e);
1002: }
1003:
1004: mCloseCalled = true;
1005: }
1006:
1007: /**
1008: * Generate a warning message
1009: */
1010: void warn(CompilationEnvironment env, String msg) {
1011: CompilationError cerr;
1012: env.warn(msg);
1013: }
1014:
1015: /**
1016: * Import a font of a given style into the SWF we are writing.
1017: *
1018: * @param fileName filename for font in LZX
1019: * @param face face name of font
1020: * @param style style of font
1021: */
1022: void importFontStyle(String fileName, String face, String style,
1023: CompilationEnvironment env) throws FileNotFoundException,
1024: CompilationError {
1025:
1026: int styleBits = FontInfo.styleBitsFromString(style);
1027:
1028: mLogger.debug(
1029: /* (non-Javadoc)
1030: * @i18n.test
1031: * @org-mes="importing " + p[0] + " of style " + p[1]
1032: */
1033: org.openlaszlo.i18n.LaszloMessages
1034: .getMessage(SWFWriter.class.getName(), "051018-1225",
1035: new Object[] { face, style }));
1036:
1037: FontInfo fontInfo = mEnv.getCanvas().getFontInfo();
1038: boolean isDefault = false;
1039:
1040: Font font = importFont(fileName, face, styleBits, false);
1041: mLibFontsDefined = true;
1042:
1043: if (fontInfo.getName().equals(face)) {
1044: if (styleBits == FontInfo.PLAIN) {
1045: isDefault = true;
1046: mDefaultFont = font;
1047: }
1048: }
1049:
1050: FontFamily family = mFontManager.getFontFamily(face, true);
1051:
1052: switch (styleBits) {
1053: case FontInfo.PLAIN:
1054: if (family.plain != null) {
1055: if (!isDefault || mDefaultFontUsedForMeasurement) {
1056: warn(env,
1057: /* (non-Javadoc)
1058: * @i18n.test
1059: * @org-mes="Redefined plain style of font: " + p[0]
1060: */
1061: org.openlaszlo.i18n.LaszloMessages.getMessage(
1062: SWFWriter.class.getName(), "051018-1252",
1063: new Object[] { face }));
1064: }
1065: }
1066: family.plain = font;
1067: break;
1068: case FontInfo.BOLD:
1069: if (family.bold != null) {
1070: warn(env,
1071: /* (non-Javadoc)
1072: * @i18n.test
1073: * @org-mes="Redefined bold style of font: " + p[0]
1074: */
1075: org.openlaszlo.i18n.LaszloMessages.getMessage(
1076: SWFWriter.class.getName(), "051018-1265",
1077: new Object[] { face }));
1078: }
1079: family.bold = font;
1080: break;
1081: case FontInfo.ITALIC:
1082: if (family.italic != null) {
1083: warn(env,
1084: /* (non-Javadoc)
1085: * @i18n.test
1086: * @org-mes="Redefined italic style of font: " + p[0]
1087: */
1088: org.openlaszlo.i18n.LaszloMessages.getMessage(
1089: SWFWriter.class.getName(), "051018-1277",
1090: new Object[] { face }));
1091: }
1092: family.italic = font;
1093: break;
1094: case FontInfo.BOLDITALIC:
1095: if (family.bitalic != null) {
1096: warn(env,
1097: /* (non-Javadoc)
1098: * @i18n.test
1099: * @org-mes="Redefined bold italic style of font: " + p[0]
1100: */
1101: org.openlaszlo.i18n.LaszloMessages.getMessage(
1102: SWFWriter.class.getName(), "051018-1289",
1103: new Object[] { face }));
1104: }
1105: family.bitalic = font;
1106: break;
1107: default:
1108: throw new ChainedException(
1109: /* (non-Javadoc)
1110: * @i18n.test
1111: * @org-mes="Unexpected style"
1112: */
1113: org.openlaszlo.i18n.LaszloMessages.getMessage(
1114: SWFWriter.class.getName(), "051018-1300"));
1115: }
1116:
1117: }
1118:
1119: /**
1120: * Import a font into the SWF we are writing
1121: *
1122: * @param fileName name of font file
1123: * @param face font name of font in LZX
1124: */
1125: private Font importFont(String fileName, String face,
1126: int styleBits, boolean replace)
1127: throws FileNotFoundException, CompilationError {
1128:
1129: if (isDeviceFont(face)) {
1130: return Font.createDummyFont(face);
1131: }
1132:
1133: mLogger.debug(
1134: /* (non-Javadoc)
1135: * @i18n.test
1136: * @org-mes="Importing font " + p[0] + " from " + p[1]
1137: */
1138: org.openlaszlo.i18n.LaszloMessages.getMessage(SWFWriter.class
1139: .getName(), "051018-1327", new Object[] { face,
1140: fileName }));
1141:
1142: String fromType = FontType.fromName(fileName);
1143: String location = null;
1144: try {
1145: File fontFile = mCache.transcode(new File(fileName),
1146: fromType, FontType.FFT);
1147: location = fontFile.getAbsolutePath();
1148: } catch (TranscoderException e) {
1149: throw new CompilationError(e);
1150: } catch (FileNotFoundException e) {
1151: throw e;
1152: } catch (IOException e) {
1153: throw new CompilationError(e);
1154: }
1155:
1156: Font font = Font.createDummyFont(face);
1157:
1158: long fileSize = FileUtils.getSize(new File(location));
1159:
1160: // FIXME: [2004-05-31 bloch] embed fonts shouldn't be global
1161: if (mFlashVersion == 5 || mEnv.getEmbedFonts()) {
1162: Element elt = new Element("font");
1163: elt.setAttribute("face", face);
1164: elt.setAttribute("style", FontInfo.styleBitsToString(
1165: styleBits, true));
1166: elt.setAttribute("location", location);
1167: elt.setAttribute("source", fileName);
1168: elt.setAttribute("filesize", "" + fileSize);
1169: mInfo.addContent(elt);
1170: }
1171:
1172: try {
1173:
1174: // Parse the font
1175: mLogger.debug(
1176: /* (non-Javadoc)
1177: * @i18n.test
1178: * @org-mes="Font file name " + p[0]
1179: */
1180: org.openlaszlo.i18n.LaszloMessages.getMessage(
1181: SWFWriter.class.getName(), "051018-1368",
1182: new Object[] { location }));
1183: FlashFile fontFile = FlashFile.parse(location);
1184: Enumeration defs = fontFile.definitions();
1185: FontDef fontDef = (FontDef) defs.nextElement();
1186:
1187: // Copy the font
1188: // For now, add the entire font (including "layout" info)
1189: fontDef.getFont().copyTo(font);
1190:
1191: if ((font.flags & Font.SHIFT_JIS) != 0) {
1192: throw new CompilationError(
1193: /* (non-Javadoc)
1194: * @i18n.test
1195: * @org-mes="Can't handle SHIFT_JIS font: " + p[0]
1196: */
1197: org.openlaszlo.i18n.LaszloMessages.getMessage(
1198: SWFWriter.class.getName(), "051018-1385",
1199: new Object[] { fileName }));
1200: }
1201: // Make sure font has LAYOUT info
1202: if ((font.flags & Font.HAS_LAYOUT) == 0) {
1203: throw new CompilationError(
1204: /* (non-Javadoc)
1205: * @i18n.test
1206: * @org-mes=p[0] + " has no layout information."
1207: */
1208: org.openlaszlo.i18n.LaszloMessages.getMessage(
1209: SWFWriter.class.getName(), "051018-1396",
1210: new Object[] { fileName }));
1211: }
1212:
1213: // Put in our face name
1214: font.fontName = face;
1215:
1216: // Clean out existing styles.
1217: font.flags &= ~(Font.BOLD | Font.ITALIC);
1218:
1219: // Write in ours.
1220: if ((styleBits & FontInfo.BOLD) != 0) {
1221: font.flags |= Font.BOLD;
1222: }
1223: if ((styleBits & FontInfo.ITALIC) != 0) {
1224: font.flags |= Font.ITALIC;
1225: }
1226:
1227: // FIXME: [OLD bloch] debugging shouldn't go to system.err; should log!
1228: // need adapter class for logger for that
1229: if (mProperties.getProperty("trace.fonts", "false").equals(
1230: "true")) {
1231: font.printContent(System.err, " ", 0);
1232: }
1233:
1234: // Add to the list of fonts
1235: FontDef fdef = mFontsCollector.addFont(font, null);
1236: // For now, write all characters.
1237: fdef.setWriteAllChars(true);
1238: fdef.setWriteLayout(true);
1239:
1240: } catch (IVException e) {
1241: throw new ChainedException(e);
1242: }
1243:
1244: return font;
1245: }
1246:
1247: /**
1248: * Import all action script blocks
1249: */
1250: void importActions(String fileName) throws FileNotFoundException,
1251: IVException {
1252:
1253: Timeline t = FlashFile.parse(fileName).getMainScript()
1254: .getTimeline();
1255:
1256: for (int i = 0; i < t.getFrameCount(); i++) {
1257: Frame frame = t.getFrameAt(i);
1258: for (int j = 0; j < frame.size(); j++) {
1259: FlashObject o = frame.getFlashObjectAt(j);
1260: if (o instanceof DoAction) {
1261: DoAction action = (DoAction) o;
1262: addProgram(action.getProgram());
1263: }
1264: }
1265: }
1266: }
1267:
1268: /**
1269: * @return first action block
1270: */
1271: DoAction getFirstDoAction(String fileName)
1272: throws FileNotFoundException, IVException {
1273:
1274: Timeline t = FlashFile.parse(fileName).getMainScript()
1275: .getTimeline();
1276:
1277: for (int i = 0; i < t.getFrameCount(); i++) {
1278: Frame frame = t.getFrameAt(i);
1279: for (int j = 0; j < frame.size(); j++) {
1280: FlashObject o = frame.getFlashObjectAt(j);
1281: if (o instanceof DoAction) {
1282: return (DoAction) o;
1283: }
1284: }
1285: }
1286: return null;
1287: }
1288:
1289: /**
1290: * Adds font information to the canvas
1291: */
1292: private void addFontTable() {
1293:
1294: if (mFontTableIndex == -1) {
1295: mLogger.error(
1296: /* (non-Javadoc)
1297: * @i18n.test
1298: * @org-mes="No canvas. Skipping font table"
1299: */
1300: org.openlaszlo.i18n.LaszloMessages.getMessage(
1301: SWFWriter.class.getName(), "051018-1485"));
1302: return;
1303: }
1304: Enumeration fonts = mFontManager.getNames();
1305: StringBuffer actions = new StringBuffer("");
1306: while (fonts.hasMoreElements()) {
1307: // TODO: [old bloch] Add assert that the canvas has been created
1308: // before this action script is added!
1309: String name = (String) fonts.nextElement();
1310: FontFamily family = mFontManager.getFontFamily(name);
1311: mLogger.debug(
1312: /* (non-Javadoc)
1313: * @i18n.test
1314: * @org-mes="Adding font family: " + p[0]
1315: */
1316: org.openlaszlo.i18n.LaszloMessages.getMessage(
1317: SWFWriter.class.getName(), "051018-1502",
1318: new Object[] { name }));
1319:
1320: actions.append("_root.LzFontManager.addFont('" + name
1321: + "', ");
1322:
1323: appendFont(actions, family.plain, family
1324: .getBounds(FontInfo.PLAIN));
1325: actions.append(",");
1326: appendFont(actions, family.bold, family
1327: .getBounds(FontInfo.BOLD));
1328: actions.append(",");
1329: appendFont(actions, family.italic, family
1330: .getBounds(FontInfo.ITALIC));
1331: actions.append(",");
1332: appendFont(actions, family.bitalic, family
1333: .getBounds(FontInfo.BOLDITALIC));
1334: actions.append("\n)\n");
1335: }
1336:
1337: if (mProperties.getProperty("trace.fonts", "false").equals(
1338: "true")) {
1339: mLogger.debug(actions.toString());
1340: }
1341:
1342: byte[] action = ScriptCompiler.compileToByteArray(actions
1343: .toString(), mProperties);
1344: Program program = new Program(action, 0, action.length);
1345:
1346: // Add the program right in the spot where it belongs
1347: Frame frame = mFlashFile.getMainScript().getFrameAt(mLastFrame);
1348: frame.setFlashObjectAt(new DoAction(program), mFontTableIndex);
1349: }
1350:
1351: /**
1352: * Adds resource information to the canvas
1353: */
1354: private void addResourceTable() {
1355:
1356: StringBuffer buf = new StringBuffer("");
1357: // Sort the keys, so that regression tests aren't sensitive to
1358: // the undefined order of iterating a (non-TreeSet) Set.
1359: SortedSet sset = new TreeSet(mMultiFrameResourceSet);
1360: Iterator resources = sset.iterator();
1361: while (resources.hasNext()) {
1362: Resource res = (Resource) resources.next();
1363: String str = "canvas.resourcetable[\"" + res.getName()
1364: + "\"]={ width : " + res.getWidth() + ", height :"
1365: + res.getHeight() + "};\n";
1366: buf.append(str);
1367: }
1368:
1369: byte[] action = ScriptCompiler.compileToByteArray(buf
1370: .toString(), mProperties);
1371: Program program = new Program(action, 0, action.length);
1372:
1373: // Add the program right in the spot where it belongs
1374: Frame frame = mFlashFile.getMainScript().getFrameAt(mLastFrame);
1375: frame.setFlashObjectAt(new DoAction(program),
1376: mResourceTableIndex);
1377: }
1378:
1379: /**
1380: * @return height of fontinfo in pixels
1381: * @param fontInfo
1382: */
1383: double getFontHeight(FontInfo fontInfo) {
1384: return fontHeight(getFontFromInfo(fontInfo));
1385: }
1386:
1387: /**
1388: * @return lineheight which lfc LzInputText expects for a given fontsize
1389: */
1390: double getLFCLineHeight(FontInfo fontInfo, int fontsize) {
1391: return lfcLineHeight(getFontFromInfo(fontInfo), fontsize);
1392: }
1393:
1394: /**
1395: * Convert em units to pixels, truncated to 2 decimal places.
1396: * Slow float math... but simple code to read.
1397: *
1398: * @param units number of 1024 em units
1399: * @return pixels
1400: */
1401: private static double emUnitsToPixels(int units) {
1402: int x = (100 * units * DEFAULT_SIZE) / 1024;
1403: return (double) (x / 100.0);
1404: }
1405:
1406: /**
1407: * Compute font bounding box
1408: *
1409: * @param font
1410: */
1411: static double fontHeight(Font font) {
1412: if (font == null) {
1413: return 0;
1414: }
1415: double ascent = emUnitsToPixels(font.ascent);
1416: double descent = emUnitsToPixels(font.descent);
1417: double leading = emUnitsToPixels(font.leading);
1418: double lineheight = ascent + descent + leading;
1419: return lineheight;
1420: }
1421:
1422: /**
1423: * Compute font bounding box
1424: *
1425: * @param font
1426: */
1427: double lfcLineHeight(Font font, int fontsize) {
1428: double ascent = emUnitsToPixels(font.ascent);
1429: double descent = emUnitsToPixels(font.descent);
1430: //double leading = emUnitsToPixels(font.leading);
1431: double lineheight = mTextLeading
1432: + ((ascent + descent) * ((double) fontsize) / DEFAULT_SIZE);
1433: return lineheight;
1434: }
1435:
1436: /**
1437: * Appends font to actionscript string buffer
1438: * @param actions string
1439: * @param font font
1440: */
1441: private static void appendFont(StringBuffer actions, Font font,
1442: Rectangle2D[] bounds) {
1443:
1444: final String newline = "\n ";
1445: actions.append(newline);
1446:
1447: if (font == null) {
1448: actions.append("null");
1449: return;
1450: }
1451:
1452: double ascent = emUnitsToPixels(font.ascent);
1453: double descent = emUnitsToPixels(font.descent);
1454: double leading = emUnitsToPixels(font.leading);
1455:
1456: final String comma = ", ";
1457:
1458: actions.append("{");
1459: actions.append("ascent:");
1460: actions.append(ascent);
1461: actions.append(comma);
1462: actions.append("descent:");
1463: actions.append(descent);
1464: actions.append(comma);
1465: actions.append("leading:");
1466: actions.append(leading);
1467: actions.append(comma);
1468: actions.append("advancetable:");
1469:
1470: int idx, adv;
1471:
1472: actions.append(newline);
1473: actions.append("[");
1474: // FIXME: [2003-03-19 bloch] We only support ANSI 8bit (up to
1475: // 255) char encodings. We lose the higher characters is
1476: // UNICODE and we don't support anything else.
1477:
1478: for (int i = 0; i < 256; i++) {
1479: idx = font.getIndex(i);
1480: adv = font.getAdvanceValue(idx);
1481:
1482: // Convert to pixels rounded to nearest 100th
1483: double advance = emUnitsToPixels(adv);
1484: actions.append(advance);
1485: if (i != 255) {
1486: actions.append(comma);
1487: }
1488:
1489: if (i % 10 == 9) {
1490: actions.append(newline);
1491: }
1492: }
1493: actions.append("],");
1494: actions.append(newline);
1495:
1496: actions.append("lsbtable:");
1497: actions.append(newline);
1498: actions.append("[");
1499:
1500: int m;
1501: int max;
1502: int adj;
1503: for (int i = 0; i < 256; i++) {
1504: idx = font.getIndex(i);
1505: try {
1506: m = (int) bounds[idx].getMinX();
1507: //max = (int)bounds[idx].getMaxX();
1508: } catch (Exception e) {
1509: m = 0;
1510: //max = 0;
1511: }
1512: adv = font.getAdvanceValue(idx);
1513: adj = m;
1514: if (adj < 0)
1515: adj = 0;
1516:
1517: /* The following makes the lsb bigger
1518: but is strictly wrong */
1519: /*max = max - adv;
1520: if (max < 0) max = 0;
1521:
1522: if (max > adj) {
1523: adj = max;
1524: }*/
1525:
1526: // Convert to pixels rounded to nearest 100th
1527: double lsb = emUnitsToPixels(adj);
1528: actions.append(lsb);
1529: if (i != 255) {
1530: actions.append(comma);
1531: }
1532:
1533: if (i % 10 == 9) {
1534: actions.append(newline);
1535: }
1536: }
1537:
1538: actions.append("],");
1539:
1540: actions.append(newline);
1541: actions.append("rsbtable:");
1542: actions.append(newline);
1543: actions.append("[");
1544:
1545: for (int i = 0; i < 256; i++) {
1546: idx = font.getIndex(i);
1547: try {
1548: m = (int) bounds[idx].getMaxX();
1549: } catch (Exception e) {
1550: m = 0;
1551: }
1552: adv = font.getAdvanceValue(idx);
1553: adj = m - adv;
1554: if (adj < 0)
1555: adj = 0;
1556:
1557: // Convert to pixels rounded to nearest 100th
1558: double rsb = emUnitsToPixels(adj);
1559: actions.append(rsb);
1560: if (i != 255) {
1561: actions.append(comma);
1562: }
1563:
1564: if (i % 10 == 9) {
1565: actions.append(newline);
1566: }
1567: }
1568:
1569: actions.append("]}");
1570: }
1571:
1572: /**
1573: * @return font given a font info
1574: */
1575: private Font getFontFromInfo(FontInfo fontInfo) {
1576: // This will bring in the default bold ofnt if it's not here yet
1577: checkFontExists(fontInfo);
1578: String fontName = fontInfo.getName();
1579: FontFamily family = mFontManager.getFontFamily(fontName);
1580: String style = fontInfo.getStyle();
1581:
1582: if (family == null) {
1583: return null;
1584: /*
1585: throw new CompilationError("Font '" + fontName +
1586: "' used but not defined");
1587: */
1588: }
1589: Font font = family.getStyle(fontInfo.styleBits);
1590: if (font == null) {
1591: throw new CompilationError(
1592: /* (non-Javadoc)
1593: * @i18n.test
1594: * @org-mes="Font '" + p[0] + "' style ('" + p[1] + "') used but not defined"
1595: */
1596: org.openlaszlo.i18n.LaszloMessages.getMessage(
1597: SWFWriter.class.getName(), "051018-2089",
1598: new Object[] { fontName, style }));
1599: }
1600: return font;
1601: }
1602:
1603: /**
1604: * @return true if the font exists
1605: *
1606: * If this is the default bold font and it hasn't been
1607: * declared, import it.
1608: */
1609: boolean checkFontExists(FontInfo fontInfo) {
1610:
1611: // Bulletproofing...
1612: if (fontInfo.getName() == null) {
1613: return false;
1614: }
1615:
1616: boolean a = mFontManager.checkFontExists(fontInfo);
1617: if (a) {
1618: return a;
1619: }
1620:
1621: if (fontInfo.getName().equals(mDefaultFontName)
1622: && fontInfo.styleBits == FontInfo.PLAIN) {
1623: try {
1624: File f = mEnv.resolve(mDefaultFontFileName, null);
1625: importFontStyle(f.getAbsolutePath(), mDefaultFontName,
1626: "plain", mEnv);
1627: } catch (FileNotFoundException fnfe) {
1628: throw new CompilationError(
1629: /* (non-Javadoc)
1630: * @i18n.test
1631: * @org-mes="default font " + p[0] + " missing " + p[1]
1632: */
1633: org.openlaszlo.i18n.LaszloMessages.getMessage(
1634: SWFWriter.class.getName(), "051018-2125",
1635: new Object[] { mDefaultFontFileName, fnfe }));
1636: }
1637: return true;
1638: }
1639:
1640: if (fontInfo.getName().equals(mDefaultFontName)
1641: && fontInfo.styleBits == FontInfo.BOLD) {
1642: try {
1643: File f = mEnv.resolve(mDefaultBoldFontFileName, null);
1644: importFontStyle(f.getAbsolutePath(), mDefaultFontName,
1645: "bold", mEnv);
1646: } catch (FileNotFoundException fnfe) {
1647: throw new CompilationError(
1648: /* (non-Javadoc)
1649: * @i18n.test
1650: * @org-mes="default bold font " + p[0] + " missing " + p[1]
1651: */
1652: org.openlaszlo.i18n.LaszloMessages
1653: .getMessage(
1654: SWFWriter.class.getName(),
1655: "051018-2143",
1656: new Object[] {
1657: mDefaultBoldFontFileName,
1658: fnfe }));
1659: }
1660: return true;
1661: }
1662:
1663: if (fontInfo.getName().equals(mDefaultFontName)
1664: && fontInfo.styleBits == FontInfo.ITALIC) {
1665: try {
1666: File f = mEnv.resolve(mDefaultItalicFontFileName, null);
1667: importFontStyle(f.getAbsolutePath(), mDefaultFontName,
1668: "italic", mEnv);
1669: } catch (FileNotFoundException fnfe) {
1670: throw new CompilationError(
1671: /* (non-Javadoc)
1672: * @i18n.test
1673: * @org-mes="default italic font " + p[0] + " missing " + p[1]
1674: */
1675: org.openlaszlo.i18n.LaszloMessages
1676: .getMessage(SWFWriter.class.getName(),
1677: "051018-2161", new Object[] {
1678: mDefaultItalicFontFileName,
1679: fnfe }));
1680: }
1681: return true;
1682: }
1683:
1684: if (fontInfo.getName().equals(mDefaultFontName)
1685: && fontInfo.styleBits == FontInfo.BOLDITALIC) {
1686: try {
1687: File f = mEnv.resolve(mDefaultBoldItalicFontFileName,
1688: null);
1689: importFontStyle(f.getAbsolutePath(), mDefaultFontName,
1690: "bold italic", mEnv);
1691: } catch (FileNotFoundException fnfe) {
1692: throw new CompilationError(
1693: /* (non-Javadoc)
1694: * @i18n.test
1695: * @org-mes="default bold italic font " + p[0] + " missing " + p[1]
1696: */
1697: org.openlaszlo.i18n.LaszloMessages.getMessage(
1698: SWFWriter.class.getName(), "051018-2179",
1699: new Object[] { mDefaultBoldItalicFontFileName,
1700: fnfe }));
1701: }
1702: return true;
1703: }
1704:
1705: return false;
1706: }
1707: }
|