| /* |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| package org.apache.royale.swf.io; |
| |
| import java.io.BufferedWriter; |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.io.OutputStreamWriter; |
| import java.io.Writer; |
| |
| import org.apache.commons.io.output.CountingOutputStream; |
| |
| import org.apache.royale.swf.Header.Compression; |
| import org.apache.royale.swf.ISWF; |
| import org.apache.royale.swf.tags.CharacterTag; |
| import org.apache.royale.swf.tags.DefineBinaryDataTag; |
| import org.apache.royale.swf.tags.DefineFont2Tag; |
| import org.apache.royale.swf.tags.DefineFont4Tag; |
| import org.apache.royale.swf.tags.DefineFontNameTag; |
| import org.apache.royale.swf.tags.DefineFontTag; |
| import org.apache.royale.swf.tags.DefineShapeTag; |
| import org.apache.royale.swf.tags.DefineSoundTag; |
| import org.apache.royale.swf.tags.DefineSpriteTag; |
| import org.apache.royale.swf.tags.DefineVideoStreamTag; |
| import org.apache.royale.swf.tags.DoABCTag; |
| import org.apache.royale.swf.tags.FileAttributesTag; |
| import org.apache.royale.swf.tags.FrameLabelTag; |
| import org.apache.royale.swf.tags.ITag; |
| import org.apache.royale.swf.tags.SymbolClassTag; |
| |
| /** |
| * A class that writes a SWF and a size report. The path of the size report is |
| * passed to the constructor. When the SWF is written to disk, the size report |
| * is also written. |
| */ |
| public class SizeReportWritingSWFWriter extends SWFWriter |
| { |
| /** |
| * SWF writer factory for SWF writers that also create size reports. |
| */ |
| private static class SWFWriterFactory implements ISWFWriterFactory |
| { |
| |
| SWFWriterFactory(File sizeReport) |
| { |
| this.sizeReport = sizeReport; |
| } |
| |
| private final File sizeReport; |
| |
| @Override |
| public ISWFWriter createSWFWriter(ISWF swf, Compression useCompression, |
| boolean enableDebug, boolean enableTelemetry) |
| { |
| return new SizeReportWritingSWFWriter(swf, useCompression, enableDebug, |
| enableTelemetry, sizeReport); |
| } |
| |
| } |
| |
| /** |
| * Get a SWF Writer factory that can create an instance of a class that can |
| * write a SWF with the optional capability of creating a size report. |
| * |
| * @param sizeReport if non-null, return a factory for an object that can |
| * create a size report in addition to a writing a SWF. Otherwise the |
| * factory is for an object that just writes a SWF. |
| * @return a SWF writer factory. |
| */ |
| public static ISWFWriterFactory getSWFWriterFactory(File sizeReport) |
| { |
| if (sizeReport != null) |
| return new SWFWriterFactory(sizeReport); |
| |
| return SWFWriter.DEFAULT_SWF_WRITER_FACTORY; |
| } |
| |
| /** |
| * Create a SWF writer with a size reporter. |
| * |
| * @param swf the SWF model to be encoded |
| * @param useCompression use ZLIB compression if true |
| * @param enableDebug enable debugging of the SWF if true |
| * @param sizeReport the file the size report is written to. |
| */ |
| public SizeReportWritingSWFWriter(ISWF swf, Compression useCompression, |
| boolean enableDebug, boolean enableTelemetry, File sizeReport) |
| { |
| super(swf, useCompression, enableDebug, enableTelemetry); |
| |
| assert sizeReport != null; |
| |
| this.sizeReportFile = sizeReport; |
| this.report = new SizeReport(); |
| } |
| |
| private final File sizeReportFile; |
| private final SizeReport report; |
| private boolean definingSprite = false; |
| |
| @Override |
| public int writeTo(File outputFile) throws FileNotFoundException, IOException |
| { |
| final int bytes = super.writeTo(outputFile); |
| report.setCompressedSize(bytes); |
| |
| return bytes; |
| } |
| |
| @Override |
| public void writeTo(OutputStream output) |
| { |
| CountingOutputStream countingOutput = new CountingOutputStream(output); |
| super.writeTo(countingOutput); |
| |
| report.setCompressedSize(countingOutput.getCount()); |
| |
| writeSizeReport(); |
| } |
| |
| @SuppressWarnings("incomplete-switch") |
| @Override |
| protected void finishTag(ITag tag, IOutputBitStream tagData, IOutputBitStream out) |
| { |
| int startPos = out.size(); |
| super.finishTag(tag, tagData, out); |
| |
| int recordLength = out.size() - startPos; |
| |
| switch (tag.getTagType()) |
| { |
| case DoABC: |
| reportDoABC((DoABCTag)tag, recordLength); |
| break; |
| case FileAttributes: |
| reportFileAttributes((FileAttributesTag)tag, recordLength); |
| break; |
| case SymbolClass: |
| reportSymbolClass((SymbolClassTag)tag, recordLength); |
| break; |
| case ShowFrame: |
| reportShowFrame(recordLength); |
| break; |
| case SetBackgroundColor: |
| reportSetBackgroundColor(recordLength); |
| break; |
| case EnableDebugger2: |
| reportEnableDebugger2(recordLength); |
| break; |
| case ScriptLimits: |
| reportScriptLimits(recordLength); |
| break; |
| // case ProductInfo: |
| // reportProductInfo((ProductInfoTag)tag, recordLength); |
| // break; |
| case Metadata: |
| reportMetadata(recordLength); |
| break; |
| case DefineBits: |
| case DefineBitsJPEG2: |
| case DefineBitsJPEG3: |
| case DefineBitsLossless2: |
| reportBitmap((CharacterTag)tag, recordLength); |
| break; |
| case DefineBinaryData: |
| reportDefineBinaryData((DefineBinaryDataTag)tag, recordLength); |
| break; |
| case DefineShape: |
| case DefineShape2: |
| case DefineShape3: |
| case DefineShape4: |
| reportDefineShape((DefineShapeTag)tag, recordLength); |
| break; |
| case DefineSprite: |
| reportDefineSprite((DefineSpriteTag)tag, recordLength); |
| break; |
| case ExportAssets: |
| reportExportAssets(recordLength); |
| break; |
| // case DefineScalingGrid: |
| // reportDefineScalingGrid((DefineScalingGridTag)tag, recordLength); |
| // break; |
| case DefineFont: |
| reportDefineFont((DefineFontTag)tag, recordLength); |
| break; |
| case DefineFont2: |
| case DefineFont3: |
| reportDefineFont2or3((DefineFont2Tag)tag, recordLength); |
| break; |
| case DefineFont4: |
| reportDefineFont4((DefineFont4Tag)tag, recordLength); |
| break; |
| // case DefineFontInfo: |
| // reportDefineFontInfo((IFontInfo)tag, recordLength); |
| // break; |
| // case DefineFontInfo2: |
| // reportDefineFontInfo2((DefineFontInfo2Tag)tag, recordLength); |
| // break; |
| // case DefineFontAlignZones: |
| // reportDefineFontAlignZones((DefineFontAlignZonesTag)tag); |
| // break; |
| // case DefineFontName: |
| // reportDefineFontName((DefineFontNameTag)tag); |
| // break; |
| // case DefineText: |
| // reportDefineText((DefineTextTag)tag, extraTags); |
| // break; |
| // case DefineText2: |
| // reportDefineText2((DefineText2Tag)tag, extraTags); |
| // break; |
| // case DefineEditText: |
| // reportDefineEditText((DefineEditTextTag)tag, extraTags); |
| // break; |
| case DefineSound: |
| reportDefineSound((DefineSoundTag)tag, recordLength); |
| break; |
| case DefineVideoStream: |
| reportDefineVideoStream((DefineVideoStreamTag)tag, recordLength); |
| break; |
| // case VideoFrame: |
| // reportVideoFrame((VideoFrameTag)tag); |
| // break; |
| // case StartSound: |
| // reportStartSound((StartSoundTag)tag); |
| // break; |
| // case StartSound2: |
| // reportStartSound2((StartSound2Tag)tag); |
| // break; |
| // case SoundStreamHead: |
| // reportSoundStreamHead((SoundStreamHeadTag)tag); |
| // break; |
| // case SoundStreamHead2: |
| // reportSoundStreamHead((SoundStreamHead2Tag)tag); |
| // break; |
| // case SoundStreamBlock: |
| // reportSoundStreamBlock((SoundStreamBlockTag)tag); |
| // break; |
| // case DefineButton: |
| // reportDefineButton((DefineButtonTag)tag); |
| // break; |
| // case DefineButton2: |
| // reportDefineButton2((DefineButton2Tag)tag); |
| // break; |
| // case DefineButtonSound: |
| // reportDefineButtonSound((DefineButtonSoundTag)tag); |
| // break; |
| // case CSMTextSettings: |
| // reportCSMTextSettings((CSMTextSettingsTag)tag); |
| // break; |
| case End: |
| reportEnd(); |
| break; |
| // case JPEGTables: |
| // reportJPEGTables(((JPEGTablesTag)tag)); |
| // break; |
| // case DefineMorphShape: |
| // reportDefineMorphShape((DefineMorphShapeTag)tag); |
| // break; |
| // case DefineMorphShape2: |
| // reportDefineMorphShape2((DefineMorphShape2Tag)tag); |
| // break; |
| // case PlaceObject: |
| // reportPlaceObject((PlaceObjectTag)tag); |
| // break; |
| // case PlaceObject2: |
| // reportPlaceObject2((PlaceObject2Tag)tag); |
| // break; |
| // case PlaceObject3: |
| // reportPlaceObject3((PlaceObject3Tag)tag); |
| // break; |
| // case RemoveObject: |
| // reportRemoveObject((RemoveObjectTag)tag); |
| // break; |
| // case RemoveObject2: |
| // reportRemoveObject2((RemoveObject2Tag)tag); |
| // break; |
| // case SetTabIndex: |
| // reportSetTabIndex((SetTabIndexTag)tag); |
| // break; |
| case FrameLabel: |
| reportFrameLabel((FrameLabelTag)tag, recordLength); |
| break; |
| } |
| |
| } |
| |
| @Override |
| protected void writeCompressibleHeader() |
| { |
| super.writeCompressibleHeader(); |
| |
| tagBuffer.flush(); |
| report.startEntry(SizeReport.HEADER_DATA, 0, -1, "swfHeader"); |
| |
| // Here we are recording the size of the full header, not just |
| // the compressible part. The full header is 12 bytes plus the |
| // size needed to encode a RECT record. The 'tagBuffer' contains |
| // the size of the RECT so we just add 12 to it to get the full size. |
| report.endEntry(SizeReport.HEADER_DATA, tagBuffer.size() + 12); |
| } |
| |
| @Override |
| protected void writeDefineSprite(DefineSpriteTag tag) |
| { |
| definingSprite = true; |
| super.writeDefineSprite(tag); |
| definingSprite = false; |
| } |
| |
| private void reportShowFrame(int recordLength) |
| { |
| if (!definingSprite) |
| { |
| report.addEntry(SizeReport.FRAME_DATA, -1, 2, "showFrame"); |
| report.endEntry(SizeReport.FRAME, 0); |
| } |
| } |
| |
| private void reportDefineBinaryData(DefineBinaryDataTag tag, int recordLength) |
| { |
| report.addEntry(SizeReport.BINARY, tag.getCharacterID(), recordLength); |
| } |
| |
| private void reportDefineShape(DefineShapeTag tag, int recordLength) |
| { |
| report.addEntry(SizeReport.SHAPE, tag.getCharacterID(), recordLength); |
| } |
| |
| public void reportDoABC(DoABCTag tag, int recordLength) |
| { |
| report.addEntry(SizeReport.SCRIPT, -1, recordLength, tag.getName()); |
| } |
| |
| private void reportEnableDebugger2(int recordLength) |
| { |
| report.addEntry(SizeReport.HEADER_DATA, -1, recordLength, "enableDebugger"); |
| } |
| |
| public void reportFileAttributes(FileAttributesTag tag, int recordLength) |
| { |
| report.addEntry(SizeReport.HEADER_DATA, -1, recordLength, "fileAttributes"); |
| } |
| |
| private void reportMetadata(int recordLength) |
| { |
| report.addEntry(SizeReport.HEADER_DATA, -1, recordLength, "metaData"); |
| } |
| |
| private void reportScriptLimits(int recordLength) |
| { |
| report.addEntry(SizeReport.HEADER_DATA, -1, recordLength, "scriptLimits"); |
| } |
| |
| private void reportSetBackgroundColor(int recordLength) |
| { |
| report.addEntry(SizeReport.HEADER_DATA, -1, recordLength, "backgroundColor"); |
| } |
| |
| private void reportEnd() |
| { |
| report.addEntry(SizeReport.HEADER_DATA, -1, 2, "endMarker"); |
| report.setSize(outputBuffer.size()); |
| } |
| |
| private void reportSymbolClass(SymbolClassTag tag, int recordLength) |
| { |
| for (String symbol : tag.getSymbolNames()) |
| report.addSymbol(symbol, tag.getSymbol(symbol).getCharacterID()); |
| |
| report.addEntry(SizeReport.FRAME_DATA, -1, recordLength, "symbolClass"); |
| } |
| |
| private void reportDefineVideoStream(DefineVideoStreamTag tag, int recordLength) |
| { |
| report.addEntry(SizeReport.VIDEO, tag.getCharacterID(), recordLength); |
| } |
| |
| private void reportDefineSound(DefineSoundTag tag, int recordLength) |
| { |
| report.addEntry(SizeReport.SOUND, tag.getCharacterID(), recordLength); |
| } |
| |
| private void reportDefineFont(DefineFontTag tag, int recordLength) |
| { |
| String fontName = null; |
| DefineFontNameTag license = tag.getLicense(); |
| if (license != null) |
| fontName = license.getFontName(); |
| |
| report.addEntry(SizeReport.FONT, tag.getCharacterID(), recordLength, |
| fontName); |
| } |
| |
| private void reportDefineFont2or3(DefineFont2Tag tag, int recordLength) |
| { |
| report.addEntry(SizeReport.FONT, tag.getCharacterID(), recordLength, |
| tag.getFontName()); |
| } |
| |
| private void reportDefineFont4(DefineFont4Tag tag, int recordLength) |
| { |
| report.addEntry(SizeReport.FONT, tag.getCharacterID(), recordLength, |
| tag.getFontName()); |
| } |
| |
| private void reportBitmap(CharacterTag tag, int recordLength) |
| { |
| report.addEntry(SizeReport.BITMAP, tag.getCharacterID(), recordLength); |
| } |
| |
| private void reportExportAssets(int recordLength) |
| { |
| report.addEntry(SizeReport.FRAME_DATA, -1, recordLength, "exportAssets"); |
| } |
| |
| private void reportDefineSprite(DefineSpriteTag tag, int recordLength) |
| { |
| report.addEntry(SizeReport.SPRITE, tag.getCharacterID(), recordLength); |
| } |
| |
| private void reportFrameLabel(FrameLabelTag tag, int recordLength) |
| { |
| report.startEntry(SizeReport.FRAME, 0, -1, tag.getName()); |
| report.addEntry(SizeReport.FRAME_DATA, -1, recordLength, "frameLabel"); |
| } |
| |
| /** |
| * Write the size report to the specified file. |
| */ |
| private void writeSizeReport() |
| { |
| Writer reportOut = null; |
| try |
| { |
| reportOut = new BufferedWriter(new OutputStreamWriter( |
| new FileOutputStream(sizeReportFile), "UTF8")); |
| reportOut.write(report.generate()); |
| reportOut.flush(); |
| } |
| catch (Exception e) |
| { |
| // TODO: report a problem |
| throw new RuntimeException(e); |
| } |
| finally |
| { |
| if (reportOut != null) |
| try |
| { |
| reportOut.close(); |
| } |
| catch (IOException e) |
| { |
| // ignore |
| } |
| } |
| } |
| } |