blob: 344035bc611728ec8f461c77d1aee36e8ea3dccd [file] [log] [blame]
/*
*
* 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 flash.swf;
import flash.swf.tags.*;
import flash.swf.types.ActionList;
import flash.swf.types.ImportRecord;
import java.util.HashSet;
import java.util.Iterator;
/**
* Encode movies by traversing them and calling the taghandler with
* each tag of interest. This class encapsulates knowlege about how
* the flash player executes. In particular, the order of execution
* of initActions and frame actions.
*
* @author Edwin Smith
*/
public class MovieEncoder
{
private final TagHandler handler;
private HashSet<Tag> done;
private boolean compress;
public MovieEncoder(TagHandler handler)
{
this.handler = handler;
done = new HashSet<Tag>();
}
/**
* Export SWF model to bytes.
* @param m SWF object
*/
public void export(Movie m)
{
export(m, Header.useCompression(m.version));
}
/**
* Export SWF model to bytes.
* @param m SWF object
* @param compress use compression if true
*/
public void export(Movie m, boolean compress)
{
// define the header
Header h = new Header();
h.version = m.version;
h.compressed = compress;
h.size = m.size;
h.rate = m.framerate;
handler.header(h);
// movie-wide tags
if (m.fileAttributes != null)
{
if (m.metadata != null)
m.fileAttributes.hasMetadata = true;
m.fileAttributes.visit(handler); // FileAttributes MUST be first tag after header!
}
if (m.metadata != null)
{
m.metadata.visit(handler);
}
if (m.enableDebugger != null)
{
m.enableDebugger.visit(handler);
}
if (m.uuid != null)
{
new DebugID(m.uuid).visit(handler);
}
if (m.protect != null)
{
m.protect.visit(handler);
}
if (m.scriptLimits != null)
{
m.scriptLimits.visit(handler);
}
if (m.bgcolor != null)
{
m.bgcolor.visit(handler);
}
if (m.productInfo != null)
{
m.productInfo.visit(handler);
}
if (m.sceneAndFrameLabelData != null)
{
m.sceneAndFrameLabelData.visit(handler);
}
// finally, output the frames
boolean associateRootClass = (m.topLevelClass != null);
for (Iterator i = m.frames.iterator(); i.hasNext();)
{
Frame frame = (Frame) i.next();
if (frame.label != null)
{
frame.label.visit(handler);
}
if (!frame.imports.isEmpty())
{
for (Iterator j = frame.imports.iterator(); j.hasNext();)
{
ImportAssets importAssets = (ImportAssets) j.next();
importAssets.visit(handler);
}
}
// definitions needed in this frame
for (Iterator j = frame.getReferences(); j.hasNext();)
{
DefineTag ref = (DefineTag) j.next();
define(ref);
}
// exports
if (frame.hasExports())
{
ExportAssets exportAssets = new ExportAssets();
for (Iterator j = frame.exportIterator(); j.hasNext();)
{
DefineTag tag = (DefineTag) j.next();
exportAssets.exports.add(tag);
}
exportAssets.visit(handler);
}
// TODO: Review this... temporarily special casing fonts here as they should not be
// included in ExportAssets as they are not required to be exported by name!
// fonts
if (frame.hasFonts())
{
for (Iterator k = frame.fontsIterator(); k.hasNext();)
{
DefineFont tag = (DefineFont) k.next();
// We may have already visited this font because of symbolClasses.
if (!done.contains( tag ))
{
tag.visit(handler);
done.add( tag );
}
}
}
// abc tags
for (Iterator j = frame.doABCs.iterator(); j.hasNext();)
{
Tag tag = (Tag) j.next();
tag.visit(handler);
}
SymbolClass classes = new SymbolClass();
if (frame.hasSymbolClasses())
{
classes.class2tag.putAll( frame.symbolClass.class2tag );
}
if (associateRootClass)
{
// only works on frame 1
classes.topLevelClass = m.topLevelClass; // Why do we do this on every frame's symclass?
}
if (associateRootClass || frame.hasSymbolClasses())
{
classes.visit(handler);
}
associateRootClass = false;
// control tags
for (Iterator j = frame.controlTags.iterator(); j.hasNext();)
{
Tag tag = (Tag) j.next();
tag.visit(handler);
}
// then frame actions
for (Iterator<ActionList> j = frame.doActions.iterator(); j.hasNext();)
{
ActionList list = j.next();
new DoAction(list).visit(handler);
}
// oh yeah, then showFrame!
new ShowFrame().visit(handler);
}
handler.finish();
}
// changed from private to public to support Flash Authoring - jkamerer 2007.07.30
public void define(Tag tag)
{
if (!done.contains(tag))
{
for (Iterator i = tag.getReferences(); i.hasNext();)
{
Tag ref = (Tag) i.next();
define(ref);
}
// ImportRecords are pre-visited via their parent ImportAssets tag.
if (!(tag instanceof ImportRecord))
{
tag.visit(handler);
// FIXME: we really need a general handler for references that should be handled after the
// parent tag is visited. Or maybe all references can be changed so that they are handled
// after the main tag is visited?
Tag visitAfter = null;
if (tag instanceof DefineSprite)
{
visitAfter = ((DefineSprite) tag).scalingGrid;
}
else if (tag instanceof DefineButton)
{
visitAfter = ((DefineButton) tag).scalingGrid;
}
else if (tag instanceof DefineShape)
{
visitAfter = ((DefineShape) tag).scalingGrid;
}
else if (tag instanceof DefineFont3)
{
visitAfter = ((DefineFont3) tag).zones;
}
else if (tag instanceof DefineEditText)
{
visitAfter = ((DefineEditText) tag).csmTextSettings;
}
else if (tag instanceof DefineText)
{
visitAfter = ((DefineText) tag).csmTextSettings;
}
visitAfter(visitAfter);
visitAfter = null;
if (tag instanceof DefineFont)
{
visitAfter = ((DefineFont)tag).license;
}
visitAfter(visitAfter);
}
done.add(tag);
}
}
private void visitAfter(Tag visitAfter)
{
if (visitAfter != null)
{
assert !done.contains(visitAfter);
visitAfter.visit(handler);
done.add(visitAfter);
}
}
}