"""
This module defines exporters for the SWF fileformat.
"""
from .consts import *
from .geom import *
from .utils import *
from .data import *
from .tag import *
from .filters import *
from lxml import objectify
from lxml import etree
import base64
try:
import Image
except ImportError:
from PIL import Image
try:
from cBytesIO import BytesIO
except ImportError:
from io import BytesIO
import math
import re
import copy
import cgi
SVG_VERSION = "1.1"
SVG_NS = "http://www.w3.org/2000/svg"
XLINK_NS = "http://www.w3.org/1999/xlink"
XLINK_HREF = "{%s}href" % XLINK_NS
NS = {"svg" : SVG_NS, "xlink" : XLINK_NS}
PIXELS_PER_TWIP = 20
EM_SQUARE_LENGTH = 1024
MINIMUM_STROKE_WIDTH = 0.5
CAPS_STYLE = {
0 : 'round',
1 : 'butt',
2 : 'square'
}
JOIN_STYLE = {
0 : 'round',
1 : 'bevel',
2 : 'miter'
}
class DefaultShapeExporter(object):
"""
The default (abstract) Shape exporter class.
All shape exporters should extend this class.
"""
def __init__(self, swf=None, debug=False, force_stroke=False):
self.swf = None
self.debug = debug
self.force_stroke = force_stroke
def begin_bitmap_fill(self, bitmap_id, matrix=None, repeat=False, smooth=False):
pass
def begin_fill(self, color, alpha=1.0):
pass
def begin_gradient_fill(self, type, colors, alphas, ratios,
matrix=None,
spreadMethod=SpreadMethod.PAD,
interpolationMethod=InterpolationMethod.RGB,
focalPointRatio=0.0):
pass
def line_style(self,
thickness=float('nan'), color=0, alpha=1.0,
pixelHinting=False,
scaleMode=LineScaleMode.NORMAL,
startCaps=None, endCaps=None,
joints=None, miterLimit=3.0):
pass
def line_gradient_style(self,
thickness=float('nan'), color=0, alpha=1.0,
pixelHinting=False,
scaleMode=LineScaleMode.NORMAL,
startCaps=None, endCaps=None,
joints=None, miterLimit=3.0,
type = 1, colors = [], alphas = [], ratios = [],
matrix=None,
spreadMethod=SpreadMethod.PAD,
interpolationMethod=InterpolationMethod.RGB,
focalPointRatio=0.0):
pass
def line_bitmap_style(self,
thickness=float('nan'),
pixelHinting=False,
scaleMode=LineScaleMode.NORMAL,
startCaps=None, endCaps=None,
joints=None, miterLimit = 3.0,
bitmap_id=None, matrix=None, repeat=False, smooth=False):
pass
def end_fill(self):
pass
def begin_fills(self):
pass
def end_fills(self):
pass
def begin_lines(self):
pass
def end_lines(self):
pass
def begin_shape(self):
pass
def end_shape(self):
pass
def move_to(self, x, y):
#print "move_to", x, y
pass
def line_to(self, x, y):
#print "line_to", x, y
pass
def curve_to(self, cx, cy, ax, ay):
#print "curve_to", cx, cy, ax, ay
pass
class DefaultSVGShapeExporter(DefaultShapeExporter):
def __init__(self, defs=None):
self.defs = defs
self.current_draw_command = ""
self.path_data = ""
self._e = objectify.ElementMaker(annotate=False,
namespace=SVG_NS, nsmap={None : SVG_NS, "xlink" : XLINK_NS})
super(DefaultSVGShapeExporter, self).__init__()
def move_to(self, x, y):
self.current_draw_command = ""
self.path_data += "M" + \
str(NumberUtils.round_pixels_20(x)) + " " + \
str(NumberUtils.round_pixels_20(y)) + " "
def line_to(self, x, y):
if self.current_draw_command != "L":
self.current_draw_command = "L"
self.path_data += "L"
self.path_data += "" + \
str(NumberUtils.round_pixels_20(x)) + " " + \
str(NumberUtils.round_pixels_20(y)) + " "
def curve_to(self, cx, cy, ax, ay):
if self.current_draw_command != "Q":
self.current_draw_command = "Q"
self.path_data += "Q"
self.path_data += "" + \
str(NumberUtils.round_pixels_20(cx)) + " " + \
str(NumberUtils.round_pixels_20(cy)) + " " + \
str(NumberUtils.round_pixels_20(ax)) + " " + \
str(NumberUtils.round_pixels_20(ay)) + " "
def begin_bitmap_fill(self, bitmap_id, matrix=None, repeat=False, smooth=False):
self.finalize_path()
def begin_fill(self, color, alpha=1.0):
self.finalize_path()
def end_fill(self):
pass
#self.finalize_path()
def begin_fills(self):
pass
def end_fills(self):
self.finalize_path()
def begin_gradient_fill(self, type, colors, alphas, ratios,
matrix=None,
spreadMethod=SpreadMethod.PAD,
interpolationMethod=InterpolationMethod.RGB,
focalPointRatio=0.0):
self.finalize_path()
def line_style(self,
thickness=float('nan'), color=0, alpha=1.0,
pixelHinting=False,
scaleMode=LineScaleMode.NORMAL,
startCaps=None, endCaps=None,
joints=None, miterLimit=3.0):
self.finalize_path()
def end_lines(self):
self.finalize_path()
def end_shape(self):
self.finalize_path()
def finalize_path(self):
self.current_draw_command = ""
self.path_data = ""
class SVGShapeExporter(DefaultSVGShapeExporter):
def __init__(self):
self.path = None
self.num_patterns = 0
self.num_gradients = 0
self._gradients = {}
self._gradient_ids = {}
self.paths = {}
self.fills_ended = False
super(SVGShapeExporter, self).__init__()
def begin_shape(self):
self.g = self._e.g()
def begin_fill(self, color, alpha=1.0):
self.finalize_path()
self.path.set("fill", ColorUtils.to_rgb_string(color))
if alpha < 1.0:
self.path.set("fill-opacity", str(alpha))
elif self.force_stroke:
self.path.set("stroke", ColorUtils.to_rgb_string(color))
self.path.set("stroke-width", "1")
else:
self.path.set("stroke", "none")
def begin_gradient_fill(self, type, colors, alphas, ratios,
matrix=None,
spreadMethod=SpreadMethod.PAD,
interpolationMethod=InterpolationMethod.RGB,
focalPointRatio=0.0):
self.finalize_path()
gradient_id = self.export_gradient(type, colors, alphas, ratios, matrix, spreadMethod, interpolationMethod, focalPointRatio)
self.path.set("stroke", "none")
self.path.set("fill", "url(#%s)" % gradient_id)
def export_gradient(self, type, colors, alphas, ratios,
matrix=None,
spreadMethod=SpreadMethod.PAD,
interpolationMethod=InterpolationMethod.RGB,
focalPointRatio=0.0):
self.num_gradients += 1
gradient_id = "gradient%d" % self.num_gradients
gradient = self._e.linearGradient() if type == GradientType.LINEAR \
else self._e.radialGradient()
gradient.set("gradientUnits", "userSpaceOnUse")
if type == GradientType.LINEAR:
gradient.set("x1", "-819.2")
gradient.set("x2", "819.2")
else:
gradient.set("r", "819.2")
gradient.set("cx", "0")
gradient.set("cy", "0")
if focalPointRatio < 0.0 or focalPointRatio > 0.0:
gradient.set("fx", str(819.2 * focalPointRatio))
gradient.set("fy", "0")
if spreadMethod == SpreadMethod.PAD:
gradient.set("spreadMethod", "pad")
elif spreadMethod == SpreadMethod.REFLECT:
gradient.set("spreadMethod", "reflect")
elif spreadMethod == SpreadMethod.REPEAT:
gradient.set("spreadMethod", "repeat")
if interpolationMethod == InterpolationMethod.LINEAR_RGB:
gradient.set("color-interpolation", "linearRGB")
if matrix is not None:
sm = _swf_matrix_to_svg_matrix(matrix)
gradient.set("gradientTransform", sm);
for i in range(0, len(colors)):
entry = self._e.stop()
offset = ratios[i] / 255.0
entry.set("offset", str(offset))
if colors[i] != 0.0:
entry.set("stop-color", ColorUtils.to_rgb_string(colors[i]))
if alphas[i] != 1.0:
entry.set("stop-opacity", str(alphas[i]))
gradient.append(entry)
# prevent same gradient in
key = etree.tostring(gradient)
if key in self._gradients:
gradient_id = self._gradient_ids[key]
else:
self._gradients[key] = copy.copy(gradient)
self._gradient_ids[key] = gradient_id
gradient.set("id", gradient_id)
self.defs.append(gradient)
return gradient_id
def export_pattern(self, bitmap_id, matrix, repeat=False, smooth=False):
self.num_patterns += 1
bitmap_id = "c%d" % bitmap_id
e = self.defs.xpath("./svg:image[@id='%s']" % bitmap_id, namespaces=NS)
if len(e) < 1:
raise Exception("SVGShapeExporter::begin_bitmap_fill Could not find bitmap!")
image = e[0]
pattern_id = "pat%d" % (self.num_patterns)
pattern = self._e.pattern()
pattern.set("id", pattern_id)
pattern.set("width", image.get("width"))
pattern.set("height", image.get("height"))
pattern.set("patternUnits", "userSpaceOnUse")
#pattern.set("patternContentUnits", "objectBoundingBox")
if matrix is not None:
pattern.set("patternTransform", _swf_matrix_to_svg_matrix(matrix, True, True, True))
pass
use = self._e.use()
use.set(XLINK_HREF, "#%s" % bitmap_id)
pattern.append(use)
self.defs.append(pattern)
return pattern_id
def begin_bitmap_fill(self, bitmap_id, matrix=None, repeat=False, smooth=False):
self.finalize_path()
pattern_id = self.export_pattern(bitmap_id, matrix, repeat, smooth)
self.path.set("stroke", "none")
self.path.set("fill", "url(#%s)" % pattern_id)
def line_style(self,
thickness=float('nan'), color=0, alpha=1.0,
pixelHinting=False,
scaleMode=LineScaleMode.NORMAL,
startCaps=None, endCaps=None,
joints=None, miterLimit=3.0):
self.finalize_path()
self.path.set("fill", "none")
self.path.set("stroke", ColorUtils.to_rgb_string(color))
thickness = 1 if math.isnan(thickness) else thickness
thickness = MINIMUM_STROKE_WIDTH if thickness < MINIMUM_STROKE_WIDTH else thickness
self.path.set("stroke-width", str(thickness))
if alpha < 1.0:
self.path.set("stroke-opacity", str(alpha))
def line_gradient_style(self,
thickness=float('nan'),
pixelHinting = False,
scaleMode=LineScaleMode.NORMAL,
startCaps=0, endCaps=0,
joints=0, miterLimit=3.0,
type = 1,
colors = [],
alphas = [],
ratios = [],
matrix=None,
spreadMethod=SpreadMethod.PAD,
interpolationMethod=InterpolationMethod.RGB,
focalPointRatio=0.0):
self.finalize_path()
gradient_id = self.export_gradient(type, colors, alphas, ratios, matrix, spreadMethod, interpolationMethod, focalPointRatio)
self.path.set("fill", "none")
self.path.set("stroke-linejoin", JOIN_STYLE[joints])
self.path.set("stroke-linecap", CAPS_STYLE[startCaps])
self.path.set("stroke", "url(#%s)" % gradient_id)
thickness = 1 if math.isnan(thickness) else thickness
thickness = MINIMUM_STROKE_WIDTH if thickness < MINIMUM_STROKE_WIDTH else thickness
self.path.set("stroke-width", str(thickness))
def line_bitmap_style(self,
thickness=float('nan'),
pixelHinting=False,
scaleMode=LineScaleMode.NORMAL,
startCaps=None, endCaps=None,
joints=None, miterLimit = 3.0,
bitmap_id=None, matrix=None, repeat=False, smooth=False):
self.finalize_path()
pattern_id = self.export_pattern(bitmap_id, matrix, repeat, smooth)
self.path.set("fill", "none")
self.path.set("stroke", "url(#%s)" % pattern_id)
self.path.set("stroke-linejoin", JOIN_STYLE[joints])
self.path.set("stroke-linecap", CAPS_STYLE[startCaps])
thickness = 1 if math.isnan(thickness) else thickness
thickness = MINIMUM_STROKE_WIDTH if thickness < MINIMUM_STROKE_WIDTH else thickness
self.path.set("stroke-width", str(thickness))
def begin_fills(self):
self.fills_ended = False
def end_fills(self):
self.finalize_path()
self.fills_ended = True
def finalize_path(self):
if self.path is not None and len(self.path_data) > 0:
self.path_data = self.path_data.rstrip()
self.path.set("d", self.path_data)
self.g.append(self.path)
self.path = self._e.path()
super(SVGShapeExporter, self).finalize_path()
class BaseExporter(object):
def __init__(self, swf=None, shape_exporter=None, force_stroke=False):
self.shape_exporter = SVGShapeExporter() if shape_exporter is None else shape_exporter
self.clip_depth = 0
self.mask_id = None
self.jpegTables = None
self.force_stroke = force_stroke
if swf is not None:
self.export(swf)
def export(self, swf, force_stroke=False):
self.force_stroke = force_stroke
self.export_define_shapes(swf.tags)
self.export_display_list(self.get_display_tags(swf.tags))
def export_define_bits(self, tag):
png_buffer = BytesIO()
image = None
if isinstance(tag, TagDefineBitsJPEG3):
tag.bitmapData.seek(0)
tag.bitmapAlphaData.seek(0, 2)
num_alpha = tag.bitmapAlphaData.tell()
tag.bitmapAlphaData.seek(0)
image = Image.open(tag.bitmapData)
if num_alpha > 0:
image_width = image.size[0]
image_height = image.size[1]
image_data = image.getdata()
image_data_len = len(image_data)
if num_alpha == image_data_len:
buff = b""
for i in range(0, num_alpha):
alpha = ord(tag.bitmapAlphaData.read(1))
rgb = list(image_data[i])
buff += struct.pack("BBBB", rgb[0], rgb[1], rgb[2], alpha)
image = Image.frombytes("RGBA", (image_width, image_height), buff)
elif isinstance(tag, TagDefineBitsJPEG2):
tag.bitmapData.seek(0)
image = Image.open(tag.bitmapData)
else:
tag.bitmapData.seek(0)
if self.jpegTables is not None:
buff = BytesIO()
self.jpegTables.seek(0)
buff.write(self.jpegTables.read())
buff.write(tag.bitmapData.read())
buff.seek(0)
image = Image.open(buff)
else:
image = Image.open(tag.bitmapData)
self.export_image(tag, image)
def export_define_bits_lossless(self, tag):
tag.bitmapData.seek(0)
image = Image.open(tag.bitmapData)
self.export_image(tag, image)
def export_define_sprite(self, tag, parent=None):
display_tags = self.get_display_tags(tag.tags)
self.export_display_list(display_tags, parent)
def export_define_shape(self, tag):
self.shape_exporter.debug = isinstance(tag, TagDefineShape4)
tag.shapes.export(self.shape_exporter)
def export_define_shapes(self, tags):
for tag in tags:
if isinstance(tag, SWFTimelineContainer):
self.export_define_sprite(tag)
self.export_define_shapes(tag.tags)
elif isinstance(tag, TagDefineShape):
self.export_define_shape(tag)
elif isinstance(tag, TagJPEGTables):
if tag.length > 0:
self.jpegTables = tag.jpegTables
elif isinstance(tag, TagDefineBits):
self.export_define_bits(tag)
elif isinstance(tag, TagDefineBitsLossless):
self.export_define_bits_lossless(tag)
elif isinstance(tag, TagDefineFont):
self.export_define_font(tag)
elif isinstance(tag, TagDefineText):
self.export_define_text(tag)
def export_display_list(self, tags, parent=None):
self.clip_depth = 0
for tag in tags:
self.export_display_list_item(tag, parent)
def export_display_list_item(self, tag, parent=None):
pass
def export_image(self, tag, image=None):
pass
def get_display_tags(self, tags, z_sorted=True):
dp_tuples = []
for tag in tags:
if isinstance(tag, TagPlaceObject):
dp_tuples.append((tag, tag.depth))
elif isinstance(tag, TagShowFrame):
break
if z_sorted:
dp_tuples = sorted(dp_tuples, key=lambda tag_info: tag_info[1])
display_tags = []
for item in dp_tuples:
display_tags.append(item[0])
return display_tags
def serialize(self):
return None
class SVGExporter(BaseExporter):
def __init__(self, swf=None, margin=0):
self._e = objectify.ElementMaker(annotate=False,
namespace=SVG_NS, nsmap={None : SVG_NS, "xlink" : XLINK_NS})
self._margin = margin
super(SVGExporter, self).__init__(swf)
def export(self, swf, force_stroke=False):
""" Exports the specified SWF to SVG.
@param swf The SWF.
@param force_stroke Whether to force strokes on non-stroked fills.
"""
self.svg = self._e.svg(version=SVG_VERSION)
self.force_stroke = force_stroke
self.defs = self._e.defs()
self.root = self._e.g()
self.svg.append(self.defs)
self.svg.append(self.root)
self.shape_exporter.defs = self.defs
self._num_filters = 0
self.fonts = dict([(x.characterId,x) for x in swf.all_tags_of_type(TagDefineFont)])
self.fontInfos = dict([(x.characterId,x) for x in swf.all_tags_of_type(TagDefineFontInfo)])
# GO!
super(SVGExporter, self).export(swf, force_stroke)
# Setup svg @width, @height and @viewBox
# and add the optional margin
self.bounds = SVGBounds(self.svg)
self.svg.set("width", "%dpx" % round(self.bounds.width))
self.svg.set("height", "%dpx" % round(self.bounds.height))
if self._margin > 0:
self.bounds.grow(self._margin)
vb = [self.bounds.minx, self.bounds.miny,
self.bounds.width, self.bounds.height]
self.svg.set("viewBox", "%s" % " ".join(map(str,vb)))
# Return the SVG as BytesIO
return self._serialize()
def _serialize(self):
return BytesIO(etree.tostring(self.svg,
encoding="UTF-8", xml_declaration=True))
def export_define_sprite(self, tag, parent=None):
id = "c%d"%tag.characterId
g = self._e.g(id=id)
self.defs.append(g)
self.clip_depth = 0
super(SVGExporter, self).export_define_sprite(tag, g)
def export_define_font(self, tag):
if not tag.characterId in self.fontInfos:
return
fontInfo = self.fontInfos[tag.characterId]
if not fontInfo.useGlyphText:
return
defs = self._e.defs(id="font_{0}".format(tag.characterId))
for index, glyph in enumerate(tag.glyphShapeTable):
# Export the glyph as a shape and add the path to the "defs"
# element to be referenced later when exporting text.
code_point = fontInfo.codeTable[index]
pathGroup = glyph.export().g.getchildren()
if len(pathGroup):
path = pathGroup[0]
path.set("id", "font_{0}_{1}".format(tag.characterId, code_point))
# SWF glyphs are always defined on an EM square of 1024 by 1024 units.
path.set("transform", "scale({0})".format(float(1)/EM_SQUARE_LENGTH))
# We'll be setting the color on the USE element that
# references this element.
del path.attrib["stroke"]
del path.attrib["fill"]
defs.append(path)
self.defs.append(defs)
def export_define_text(self, tag):
g = self._e.g(id="c{0}".format(int(tag.characterId)))
g.set("class", "text_content")
x = 0
y = 0
for rec in tag.records:
if rec.hasXOffset:
x = rec.xOffset/PIXELS_PER_TWIP
if rec.hasYOffset:
y = rec.yOffset/PIXELS_PER_TWIP
size = rec.textHeight/PIXELS_PER_TWIP
if rec.fontId not in self.fontInfos:
continue
fontInfo = self.fontInfos[rec.fontId]
if not fontInfo.useGlyphText:
inner_text = ""
xValues = []
for glyph in rec.glyphEntries:
code_point = fontInfo.codeTable[glyph.index]
# Ignore control characters
if code_point in range(32):
continue
if fontInfo.useGlyphText:
use = self._e.use()
use.set(XLINK_HREF, "#font_{0}_{1}".format(rec.fontId, code_point))
use.set(
'transform',
"scale({0}) translate({1} {2})".format(
size, float(x)/size, float(y)/size
)
)
color = ColorUtils.to_rgb_string(ColorUtils.rgb(rec.textColor))
use.set("style", "fill: {0}; stroke: {0}".format(color))
g.append(use)
else:
inner_text += chr(code_point)
xValues.append(str(x))
x = x + float(glyph.advance)/PIXELS_PER_TWIP
if not fontInfo.useGlyphText:
text = self._e.text(inner_text)
text.set("font-family", fontInfo.fontName)
text.set("font-size", str(size))
text.set("fill", ColorUtils.to_rgb_string(ColorUtils.rgb(rec.textColor)))
text.set("y", str(y))
text.set("x", " ".join(xValues))
if fontInfo.bold:
text.set("font-weight", "bold")
if fontInfo.italic:
text.set("font-style", "italic")
g.append(text)
self.defs.append(g)
def export_define_shape(self, tag):
self.shape_exporter.force_stroke = self.force_stroke
super(SVGExporter, self).export_define_shape(tag)
shape = self.shape_exporter.g
shape.set("id", "c%d" % tag.characterId)
self.defs.append(shape)
def export_display_list_item(self, tag, parent=None):
g = self._e.g()
use = self._e.use()
is_mask = False
if tag.hasMatrix:
use.set("transform", _swf_matrix_to_svg_matrix(tag.matrix))
if tag.hasClipDepth:
self.mask_id = "mask%d" % tag.characterId
self.clip_depth = tag.clipDepth
g = self._e.mask(id=self.mask_id)
# make sure the mask is completely filled white
paths = self.defs.xpath("./svg:g[@id='c%d']/svg:path" % tag.characterId, namespaces=NS)
for path in paths:
path.set("fill", "#ffffff")
elif tag.depth <= self.clip_depth and self.mask_id is not None:
g.set("mask", "url(#%s)" % self.mask_id)
filters = []
filter_cxform = None
self._num_filters += 1
filter_id = "filter%d" % self._num_filters
svg_filter = self._e.filter(id=filter_id)
if tag.hasColorTransform:
filter_cxform = self.export_color_transform(tag.colorTransform, svg_filter)
filters.append(filter_cxform)
if tag.hasFilterList and len(tag.filters) > 0:
cxform = "color-xform" if tag.hasColorTransform else None
f = self.export_filters(tag, svg_filter, cxform)
if len(f) > 0:
filters.extend(f)
if tag.hasColorTransform or (tag.hasFilterList and len(filters) > 0):
self.defs.append(svg_filter)
use.set("filter", "url(#%s)" % filter_id)
use.set(XLINK_HREF, "#c%s" % tag.characterId)
g.append(use)
if is_mask:
self.defs.append(g)
else:
if parent is not None:
parent.append(g)
else:
self.root.append(g)
return use
def export_color_transform(self, cxform, svg_filter, result='color-xform'):
fe_cxform = self._e.feColorMatrix()
fe_cxform.set("in", "SourceGraphic")
fe_cxform.set("type", "matrix")
fe_cxform.set("values", " ".join(map(str, cxform.matrix)))
fe_cxform.set("result", "cxform")
fe_composite = self._e.feComposite(operator="in")
fe_composite.set("in2", "SourceGraphic")
fe_composite.set("result", result)
svg_filter.append(fe_cxform)
svg_filter.append(fe_composite)
return result
def export_filters(self, tag, svg_filter, cxform=None):
num_filters = len(tag.filters)
elements = []
attr_in = None
for i in range(0, num_filters):
swf_filter = tag.filters[i]
#print swf_filter
if isinstance(swf_filter, FilterDropShadow):
elements.append(self.export_filter_dropshadow(swf_filter, svg_filter, cxform))
#print swf_filter.strength
pass
elif isinstance(swf_filter, FilterBlur):
pass
elif isinstance(swf_filter, FilterGlow):
#attr_in = SVGFilterFactory.export_glow_filter(self._e, svg_filter, attr_in=attr_in)
#elements.append(attr_in)
pass
elif isinstance(swf_filter, FilterBevel):
pass
elif isinstance(swf_filter, FilterGradientGlow):
pass
elif isinstance(swf_filter, FilterConvolution):
pass
elif isinstance(swf_filter, FilterColorMatrix):
attr_in = SVGFilterFactory.export_color_matrix_filter(self._e, svg_filter, swf_filter.colorMatrix, svg_filter, attr_in=attr_in)
elements.append(attr_in)
pass
elif isinstance(swf_filter, FilterGradientBevel):
pass
else:
raise Exception("unknown filter: ", swf_filter)
return elements
#
#
#
#
#
#
# ;
def export_filter_dropshadow(self, swf_filter, svg_filter, blend_in=None, result="offsetBlur"):
gauss = self._e.feGaussianBlur()
gauss.set("in", "SourceAlpha")
gauss.set("stdDeviation", "6")
gauss.set("result", "blur")
if swf_filter.knockout:
composite0 = self._e.feComposite(
in2="SourceAlpha", operator="arithmetic",
k2="-1", k3="1", result="shadowDiff")
flood = self._e.feFlood()
flood.set("flood-color", "black")
flood.set("flood-opacity", "1")
composite1 = self._e.feComposite(
in2="shadowDiff", operator="in", result=result)
svg_filter.append(gauss)
svg_filter.append(composite0)
svg_filter.append(flood)
svg_filter.append(composite1)
else:
SVGFilterFactory.create_drop_shadow_filter(self._e, svg_filter,
None,
swf_filter.blurX/20.0,
swf_filter.blurY/20.0,
blend_in,
result)
#print etree.tostring(svg_filter, pretty_print=True)
return result
def export_image(self, tag, image=None):
if image is not None:
buff = BytesIO()
image.save(buff, "PNG")
buff.seek(0)
with open("C:\\Users\\Administrator\\Desktop\\a.png","wb") as f:
f.write(buff.getvalue())
data_url = _encode_png(buff.read())
img = self._e.image()
img.set("id", "c%s" % tag.characterId)
img.set("x", "0")
img.set("y", "0 ")
img.set("width", "%s" % str(image.size[0]))
img.set("height", "%s" % str(image.size[1]))
img.set(XLINK_HREF, "%s" % data_url)
self.defs.append(img)
class SingleShapeSVGExporter(SVGExporter):
"""
An SVG exporter which knows how to export a single shape.
"""
def __init__(self, margin=0):
super(SingleShapeSVGExporter, self).__init__(margin = margin)
def export_single_shape(self, shape_tag, swf):
from swf.movie import SWF
# find a typical use of this shape
example_place_objects = [x for x in swf.all_tags_of_type(TagPlaceObject) if x.hasCharacter and x.characterId == shape_tag.characterId]
if len(example_place_objects):
place_object = example_place_objects[0]
characters = swf.build_dictionary()
ids_to_export = place_object.get_dependencies()
ids_exported = set()
tags_to_export = []
# this had better form a dag!
while len(ids_to_export):
id = ids_to_export.pop()
if id in ids_exported or id not in characters:
continue
tag = characters[id]
ids_to_export.update(tag.get_dependencies())
tags_to_export.append(tag)
ids_exported.add(id)
tags_to_export.reverse()
tags_to_export.append(place_object)
else:
place_object = TagPlaceObject()
place_object.hasCharacter = True
place_object.characterId = shape_tag.characterId
tags_to_export = [ shape_tag, place_object ]
stunt_swf = SWF()
stunt_swf.tags = tags_to_export
return super(SingleShapeSVGExporter, self).export(stunt_swf)
class SVGFilterFactory(object):
# http://commons.oreilly.com/wiki/index.php/SVG_Essentials/Filters
# http://dev.opera.com/articles/view/svg-evolution-3-applying-polish/
@classmethod
def create_drop_shadow_filter(cls, e, filter, attr_in=None, blurX=0, blurY=0, blend_in=None, result=None):
gaussianBlur = SVGFilterFactory.create_gaussian_blur(e, attr_deviaton="1", result="blur-out")
offset = SVGFilterFactory.create_offset(e, "blur-out", blurX, blurY, "the-shadow")
blend = SVGFilterFactory.create_blend(e, blend_in, attr_in2="the-shadow", result=result)
filter.append(gaussianBlur)
filter.append(offset)
filter.append(blend)
return result
@classmethod
def export_color_matrix_filter(cls, e, filter, matrix, svg_filter, attr_in=None, result='color-matrix'):
attr_in = "SourceGraphic" if attr_in is None else attr_in
fe_cxform = e.feColorMatrix()
fe_cxform.set("in", attr_in)
fe_cxform.set("type", "matrix")
fe_cxform.set("values", " ".join(map(str, matrix)))
fe_cxform.set("result", result)
filter.append(fe_cxform)
#print etree.tostring(filter, pretty_print=True)
return result
@classmethod
def export_glow_filter(cls, e, filter, attr_in=None, result="glow-out"):
attr_in = "SourceGraphic" if attr_in is None else attr_in
gaussianBlur = SVGFilterFactory.create_gaussian_blur(e, attr_in=attr_in, attr_deviaton="1", result=result)
filter.append(gaussianBlur)
return result
@classmethod
def create_blend(cls, e, attr_in=None, attr_in2="BackgroundImage", mode="normal", result=None):
blend = e.feBlend()
attr_in = "SourceGraphic" if attr_in is None else attr_in
blend.set("in", attr_in)
blend.set("in2", attr_in2)
blend.set("mode", mode)
if result is not None:
blend.set("result", result)
return blend
@classmethod
def create_gaussian_blur(cls, e, attr_in="SourceAlpha", attr_deviaton="3", result=None):
gaussianBlur = e.feGaussianBlur()
gaussianBlur.set("in", attr_in)
gaussianBlur.set("stdDeviation", attr_deviaton)
if result is not None:
gaussianBlur.set("result", result)
return gaussianBlur
@classmethod
def create_offset(cls, e, attr_in=None, dx=0, dy=0, result=None):
offset = e.feOffset()
if attr_in is not None:
offset.set("in", attr_in)
offset.set("dx", "%d" % round(dx))
offset.set("dy", "%d" % round(dy))
if result is not None:
offset.set("result", result)
return offset
class SVGBounds(object):
def __init__(self, svg=None):
self.minx = 1000000.0
self.miny = 1000000.0
self.maxx = -self.minx
self.maxy = -self.miny
self._stack = []
self._matrix = self._calc_combined_matrix()
if svg is not None:
self._svg = svg;
self._parse(svg)
def add_point(self, x, y):
self.minx = x if x < self.minx else self.minx
self.miny = y if y < self.miny else self.miny
self.maxx = x if x > self.maxx else self.maxx
self.maxy = y if y > self.maxy else self.maxy
def set(self, minx, miny, maxx, maxy):
self.minx = minx
self.miny = miny
self.maxx = maxx
self.maxy = maxy
def grow(self, margin):
self.minx -= margin
self.miny -= margin
self.maxx += margin
self.maxy += margin
@property
def height(self):
return self.maxy - self.miny
def merge(self, other):
self.minx = other.minx if other.minx < self.minx else self.minx
self.miny = other.miny if other.miny < self.miny else self.miny
self.maxx = other.maxx if other.maxx > self.maxx else self.maxx
self.maxy = other.maxy if other.maxy > self.maxy else self.maxy
def shrink(self, margin):
self.minx += margin
self.miny += margin
self.maxx -= margin
self.maxy -= margin
@property
def width(self):
return self.maxx - self.minx
def _parse(self, element):
if element.get("transform") and element.get("transform").find("matrix") < 0:
pass
if element.get("transform") and element.get("transform").find("matrix") >= 0:
self._push_transform(element.get("transform"))
if element.tag == "{%s}path" % SVG_NS:
self._handle_path_data(str(element.get("d")))
elif element.tag == "{%s}use" % SVG_NS:
href = element.get(XLINK_HREF)
if href:
href = href.replace("#", "")
els = self._svg.xpath("./svg:defs//svg:g[@id='%s']" % href,
namespaces=NS)
if len(els) > 0:
self._parse(els[0])
for child in element.getchildren():
if child.tag == "{%s}defs" % SVG_NS: continue
self._parse(child)
if element.get("transform") and element.get("transform").find("matrix") >= 0:
self._pop_transform()
def _build_matrix(self, transform):
if transform.find("matrix") >= 0:
raw = str(transform).replace("matrix(", "").replace(")", "")
f = map(float, re.split("\s+|,", raw))
return Matrix2(f[0], f[1], f[2], f[3], f[4], f[5])
def _calc_combined_matrix(self):
m = Matrix2()
for mat in self._stack:
m.append_matrix(mat)
return m
def _handle_path_data(self, d):
parts = re.split("[\s]+", d)
for i in range(0, len(parts), 2):
try:
p0 = parts[i]
p1 = parts[i+1]
p0 = p0.replace("M", "").replace("L", "").replace("Q", "")
p1 = p1.replace("M", "").replace("L", "").replace("Q", "")
v = [float(p0), float(p1)]
w = self._matrix.multiply_point(v)
self.minx = w[0] if w[0] < self.minx else self.minx
self.miny = w[1] if w[1] < self.miny else self.miny
self.maxx = w[0] if w[0] > self.maxx else self.maxx
self.maxy = w[1] if w[1] > self.maxy else self.maxy
except:
continue
def _pop_transform(self):
m = self._stack.pop()
self._matrix = self._calc_combined_matrix()
return m
def _push_transform(self, transform):
self._stack.append(self._build_matrix(transform))
self._matrix = self._calc_combined_matrix()
def _encode_jpeg(data):
return "data:image/jpeg;base64," + str(base64.encodestring(data)[:-1])
def _encode_png(data):
return "data:image/png;base64," + str(base64.encodestring(data)[:-1])
def _swf_matrix_to_matrix(swf_matrix=None, need_scale=False, need_translate=True, need_rotation=False, unit_div=20.0):
if swf_matrix is None:
values = [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]
else:
values = swf_matrix.to_array()
if need_rotation:
values[1] /= unit_div
values[2] /= unit_div
if need_scale:
values[0] /= unit_div
values[3] /= unit_div
if need_translate:
values[4] /= unit_div
values[5] /= unit_div
return values
def _swf_matrix_to_svg_matrix(swf_matrix=None, need_scale=False, need_translate=True, need_rotation=False, unit_div=20.0):
values = _swf_matrix_to_matrix(swf_matrix, need_scale, need_translate, need_rotation, unit_div)
str_values = ",".join(map(str, values))
return "matrix(%s)" % str_values