123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065 |
- """
- 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 <defs />
- 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
- # <filter id="test-filter" x="-50%" y="-50%" width="200%" height="200%">
- # <feGaussianBlur in="SourceAlpha" stdDeviation="6" result="blur"/>
- # <feOffset dy="0" dx="0"/>
- # <feComposite in2="SourceAlpha" operator="arithmetic"
- # k2="-1" k3="1" result="shadowDiff"/>
- # <feFlood flood-color="black" flood-opacity="1"/>
- # <feComposite in2="shadowDiff" operator="in"/>
- # </filter>;
- 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
|