| 1 4 5 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 ; 34 import java.lang.Character ; 35 36 import org.jdom.Element; 37 38 import java.awt.geom.Rectangle2D ; 40 41 import org.apache.log4j.*; 42 43 55 class SWFWriter { 56 57 private OutputStream mStream = null; 58 59 private SWFFile mFlashFile; 60 61 private FontsCollector mFontsCollector = new FontsCollector(); 62 private FontsCollector mPreloaderFontsCollector = new FontsCollector(); 63 private FontManager mFontManager = new FontManager(); 64 65 private boolean mCloseCalled = false; 66 67 private final CompilationEnvironment mEnv; 68 69 70 private final HashSet mMultiFrameResourceSet = new HashSet(); 71 72 73 private final int TWIP = 20; 74 75 76 private int mLastFrame = 0; 77 78 79 protected SymbolGenerator mNameSupply = new SymbolGenerator("$LZ"); 80 81 82 private Properties mProperties; 83 84 85 private final TreeMap mInputTextSet = new TreeMap(); 86 87 88 private int mFontTableIndex = -1; 89 90 91 private int mResourceTableIndex = -1; 92 93 94 private Font mDefaultFont = null; 95 private String mDefaultFontName = null; 96 private String mDefaultFontFileName = null; 97 private String mDefaultBoldFontFileName = null; 98 private String mDefaultItalicFontFileName = null; 99 private String mDefaultBoldItalicFontFileName = null; 100 private boolean mDefaultFontUsedForMeasurement = false; 102 103 104 private int mFlashVersion = 6; 105 106 107 private int mFrameRate = 30; 108 109 110 private int mTextLeading = 2; 111 112 113 private CompilerMediaCache mCache = null; 114 115 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 124 private static Logger mLogger = org.apache.log4j.Logger.getLogger(SWFWriter.class); 125 126 127 public static final int DEFAULT_SIZE = 8; 128 129 132 static { 133 org.openlaszlo.iv.flash.util.Util.init(LPS.getConfigDirectory()); 134 } 135 136 139 private static Program mExpiryProgram = null; 140 141 142 private int mHeight = 0; 143 144 145 private int mWidth = 0; 146 147 148 private int mMaxTextWidth = 0; 149 150 151 152 private int mMaxTextHeight = 0; 153 154 155 private boolean mPreloaderAdded = false; 156 157 158 private Element mInfo = new Element("swf"); 159 160 161 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 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 library, CompilationEnvironment env) { 203 204 try { 205 File f = env.resolve(library, ""); 206 mFlashFile = new SWFFile(f.getAbsolutePath(), mProperties); 207 mFlashFile.setVersion(mFlashVersion); 208 mFlashFile.setFrameRate(mFrameRate << 8); 210 211 213 if (LPS.isInternalBuild()) { 214 long lfcModTime = f.lastModified(); 215 List newerFiles = new Vector(); List sourceDirs = new Vector(); 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 face) { 244 return (mDeviceFontTable.get(face) != null); 245 } 246 247 public void setDeviceFont(String face) { 248 mDeviceFontTable.put(face,"true"); 249 } 250 251 public void setFontManager(FontManager fm) { 252 this.mFontManager = fm; 253 } 254 255 void addPreloaderScript(String script) { 256 if (mPreloaderAdded != true) 257 throw new RuntimeException ("Preloader not added yet."); 258 259 addScript(script, 0); 260 } 261 262 void addPreloader(CompilationEnvironment env) { 263 if (mPreloaderAdded == true) 264 throw new RuntimeException ("Preloader already added."); 265 266 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 286 void setCanvas(Canvas canvas, String canvasConstructor) { 287 Rectangle2D r = new Rectangle2D.Double ( 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 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 mWidth = canvas.getWidth(); 313 mHeight = canvas.getHeight(); 314 315 mMaxTextWidth = canvas.getMaxTextWidth(); 316 if (mMaxTextWidth == 0) { 318 mMaxTextWidth = mWidth; 319 } 320 321 mMaxTextHeight = canvas.getMaxTextHeight(); 322 if (mMaxTextHeight == 0) { 324 mMaxTextHeight = mHeight; 325 } 326 327 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 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 350 void setCanvasDefaults(Canvas canvas, CompilerMediaCache mc) { 351 Rectangle2D r = new Rectangle2D.Double ( 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 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 371 Program program(String script) { 372 byte[] action = ScriptCompiler.compileToByteArray(script, mProperties); 373 return new Program(action, 0, action.length); 374 } 375 376 382 int addScript(String 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 396 private void addScript(String 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 408 void addProgram(Program program) { 409 Frame frame = mFlashFile.getMainScript().getFrameAt(mLastFrame); 410 frame.addFlashObject(new DoAction(program)); 411 } 412 413 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 fileName, CompilationEnvironment env) { 428 super("Can't import " + stripBaseName(fileName, env)); 429 } 430 ImportResourceError(String fileName, String type, CompilationEnvironment env) { 431 super("Can't import " + type + " " + stripBaseName(fileName, env)); 432 } 433 ImportResourceError(String fileName, Exception e, CompilationEnvironment env) { 434 super("Can't import " + stripBaseName(fileName, env) + ": " + e.getMessage()); 435 } 436 ImportResourceError(String fileName, Exception e, String type, CompilationEnvironment env) { 437 super("Can't import " + type + " " + stripBaseName(fileName, env) + ": " + e.getMessage()); 438 } 439 440 } 441 442 public static String stripBaseName (String fileName, CompilationEnvironment env) { 443 try { 444 fileName = (new File(fileName)).getCanonicalFile().toString(); 445 } catch (java.io.IOException e) { 446 } 447 String 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 while (i < base.length() && fileName.startsWith(base.substring(0, i))) { i++; } 456 return fileName.substring(i-1); 458 } else { 459 return fileName; 460 } 461 } 462 463 469 private Resource getMultiFrameResource(String fileName, String 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 489 private Resource getResource(String fileName, String name) 490 throws ImportResourceError 491 { 492 return getResource(fileName, name, true); 493 } 494 495 501 private Resource getResource(String fileName, String name, boolean stop) 502 throws ImportResourceError 503 { 504 try { 505 String 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 ("bad mime type"), mEnv); 511 } 512 } 513 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 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 e) { 552 mLogger.error("Can't get resource " + fileName); 553 throw new ImportResourceError(fileName, e, mEnv); 554 } 555 556 } 557 558 566 public void importPreloadResource(String fileName, String name) 567 throws ImportResourceError 568 { 569 if (name.equals("")) 570 name = createName(); 571 importResource(fileName, name, 0, mPreloaderFontsCollector); 572 } 573 574 577 public void importPreloadResource(List sources, String name, File parent) 578 throws ImportResourceError 579 { 580 if (name.equals("")) 581 name = createName(); 582 importResource(sources, name, parent, 0, mPreloaderFontsCollector); 583 } 584 585 593 public void importResource(String fileName, String name) 594 throws ImportResourceError 595 { 596 importResource(fileName, name, -1); 597 } 598 599 608 public void importResource(String fileName, String name, int frameNum) 609 throws CompilationError 610 { 611 importResource(fileName, name, frameNum, null); 612 } 613 614 615 625 public void importResource(String fileName, String 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 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 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 Element elt = new Element("resource"); 655 elt.setAttribute("name", name); 656 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 676 public String 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 e) { 684 throw new CompilationError(e); 685 } 686 if (res == null) { 687 String clipName = createName(); 688 importResource(file.getPath(), clipName); 689 return clipName; 690 } else { 691 return res.getName(); 692 } 693 } 694 695 699 public String importClickResource(File file) throws ImportResourceError 700 { 701 String fileName; 702 try { 703 fileName = file.getCanonicalPath(); 704 } catch (java.io.IOException e) { 705 throw new CompilationError(e); 706 } 707 708 String name = (String ) mClickResourceMap.get(fileName); 709 710 if (name == null) { 711 712 Button2 but = new Button2(); 713 714 FlashDef def; 715 Rectangle2D bounds; 716 717 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 matrix = inst.matrix; 734 if (matrix == null) { 735 matrix = new java.awt.geom.AffineTransform (); 736 } 737 738 but.addButtonRecord(new ButtonRecord(ButtonRecord.HitTest, 739 inst.def, 1, matrix, cxform)); 740 } 741 } 742 } catch (Exception e) { 743 throw new ImportResourceError(fileName, e, mEnv); 744 } 745 746 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 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 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 798 public void importResource(List sources, String name, File parent) 799 { 800 importResource(sources, name, parent, -1); 801 } 802 803 811 public void importResource(List sources, String name, File parent, int frameNum) 812 { 813 importResource(sources, name, parent, frameNum, null); 814 } 815 816 817 826 public void importResource(List sources, String name, File parent, int frameNum, 827 FontsCollector fontsCollector) 828 { 829 Script out = new Script(1); 830 String 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 )e.next(); 837 |