Skip to content

Commit

Permalink
Merge pull request #39 from tuomassalo/names
Browse files Browse the repository at this point in the history
Add --shape, --frame and --names options; use mixins instead of SVGExporter subclasses
  • Loading branch information
timknip authored Jun 23, 2017
2 parents 0b04b2e + d4ba101 commit 1848e35
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 3 deletions.
31 changes: 29 additions & 2 deletions bin/swf2svg.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,52 @@
from __future__ import absolute_import
import argparse
from swf.movie import SWF
from swf.export import SVGExporter
from swf.export import SVGExporter, SingleShapeSVGExporterMixin, FrameSVGExporterMixin, NamesSVGExporterMixin

parser = argparse.ArgumentParser(description="Convert an SWF file into an SVG")
parser.add_argument("--swf", type=argparse.FileType('rb'),
help="Location of SWG file to convert", required=True)
parser.add_argument("--svg", type=argparse.FileType('wb'),
help="Location of converted SVG file", required=True)
parser.add_argument("--shape", type=int,
help="Only export shape SHAPE (integer)", required=False)
parser.add_argument("--frame", type=int,
help="Export frame FRAME (0-based index) instead of frame 0", required=False)
parser.add_argument("--names", action='store_true',
help='For each element, extract SWF instanceName to class="n-<name>"', required=False)

options = parser.parse_args()
argparse.swf_file = options.swf

# load and parse the SWF
swf = SWF(options.swf)

export_opts = {}
export_mixins = []


# process the optional arguments

if options.shape is not None:
export_mixins.append(SingleShapeSVGExporterMixin)
export_opts['shape'] = options.shape

if options.frame is not None:
export_mixins.append(FrameSVGExporterMixin)
export_opts['frame'] = options.frame

if options.names:
export_mixins.append(NamesSVGExporterMixin)


# create the SVG exporter
svg_exporter = SVGExporter()

# NB: Construct the class dynamically, since the chosen options dictate which mixins to use.
svg_exporter.__class__ = type('ThisExporter', tuple(export_mixins + [SVGExporter]), {})

# export!
svg = swf.export(svg_exporter)
svg = svg_exporter.export(swf, **export_opts)

# save the SVG
options.svg.write(svg.read())
72 changes: 71 additions & 1 deletion swf/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -811,11 +811,36 @@ def export_image(self, tag, image=None):
class SingleShapeSVGExporter(SVGExporter):
"""
An SVG exporter which knows how to export a single shape.
NB: This class is here just for backward compatibility.
Use SingleShapeSVGExporterMixin instead to mix with other functionality.
"""
def __init__(self, margin=0):
super(SingleShapeSVGExporter, self).__init__(margin = margin)

def export_single_shape(self, shape_tag, swf):
class MySingleShapeSVGExporter(SingleShapeSVGExporterMixin, SVGExporter):
pass
exporter = MySingleShapeSVGExporter()
return exporter.export(swf, shape=shape_tag)

class SingleShapeSVGExporterMixin(object):
def export(self, swf, shape, **export_opts):
""" Exports the specified shape of the SWF to SVG.
@param swf The SWF.
@param shape Which shape to export, either by characterId(int) or as a Tag object.
"""

# If `shape` is given as int, find corresponding shape tag.
if isinstance(shape, Tag):
shape_tag = shape
else:
shapes = [x for x in swf.all_tags_of_type((TagDefineShape, TagDefineSprite)) if x.characterId == shape]
if len(shapes):
shape_tag = shapes[0]
else:
raise Exception("Shape %s not found" % shape)

from swf.movie import SWF

# find a typical use of this shape
Expand Down Expand Up @@ -848,7 +873,52 @@ def export_single_shape(self, shape_tag, swf):
stunt_swf = SWF()
stunt_swf.tags = tags_to_export

return super(SingleShapeSVGExporter, self).export(stunt_swf)
return super(SingleShapeSVGExporterMixin, self).export(stunt_swf, **export_opts)

class FrameSVGExporterMixin(object):
def export(self, swf, frame, **export_opts):
""" Exports a frame of the specified SWF to SVG.
@param swf The SWF.
@param frame Which frame to export, by 0-based index (int)
"""
self.wanted_frame = frame
return super(FrameSVGExporterMixin, self).export(swf, *export_opts)

def get_display_tags(self, tags, z_sorted=True):

current_frame = 0
frame_tags = dict() # keys are depths, values are placeobject tags
for tag in tags:
if isinstance(tag, TagShowFrame):
if current_frame == self.wanted_frame:
break
current_frame += 1
elif isinstance(tag, TagPlaceObject):
if tag.hasMove:
orig_tag = frame_tags.pop(tag.depth)

if not tag.hasCharacter:
tag.characterId = orig_tag.characterId
# this is for NamesSVGExporterMixin
if not tag.hasName:
tag.instanceName = orig_tag.instanceName
frame_tags[tag.depth] = tag
elif isinstance(tag, TagRemoveObject):
del frame_tags[tag.depth]

return super(FrameSVGExporterMixin, self).get_display_tags(frame_tags.values(), z_sorted)

class NamesSVGExporterMixin(object):
'''
Add class="n-<name>" to SVG elements for tags that have an instanceName.
'''
def export_display_list_item(self, tag, parent=None):
use = super(NamesSVGExporterMixin, self).export_display_list_item(tag, parent)
if hasattr(tag, 'instanceName') and tag.instanceName is not None:
use.set('class', 'n-%s' % tag.instanceName)
return use


class SVGFilterFactory(object):
# http://commons.oreilly.com/wiki/index.php/SVG_Essentials/Filters
Expand Down

0 comments on commit 1848e35

Please sign in to comment.