Source code for knittingpattern.convert.KnittingPatternToSVG

"""This module provides functionality to convert knitting patterns to SVG."""

from collections import OrderedDict

#: Inside the svg, the instructions are put into definitions.
#: The svg tag is renamed to the tag given in :data:`DEFINITION_HOLDER`.
DEFINITION_HOLDER = "g"


[docs]class KnittingPatternToSVG(object): """Converts a KnittingPattern to SVG. This is inspired by the method object pattern, since building an SVG requires several steps. """
[docs] def __init__(self, knittingpattern, layout, instruction_to_svg, builder, zoom): """ :param knittingpattern.KnittingPattern.KnittingPattern knittingpattern: a knitting pattern :param knittingpattern.convert.Layout.GridLayout layout: :param instruction_to_svg: an :class:`~knittingpattern.convert.InstructionToSVG.InstructionToSVG` :class:` ~knittingpattern.convert.InstructionToSVGCache.InstructionSVGCache`, both with instructions already loaded. :param knittingpattern.convert.SVGBuilder.SVGBuilder builder: :param float zoom: the height and width of a knit instruction """ self._knittingpattern = knittingpattern self._layout = layout self._instruction_to_svg = instruction_to_svg self._builder = builder self._zoom = zoom self._instruction_type_color_to_symbol = OrderedDict() self._symbol_id_to_scale = {}
[docs] def build_SVG_dict(self): """Go through the layout and build the SVG. :return: an xml dict that can be exported using a :class:`~knittingpattern.Dumper.XMLDumper` :rtype: dict """ zoom = self._zoom layout = self._layout builder = self._builder bbox = list(map(lambda f: f * zoom, layout.bounding_box)) builder.bounding_box = bbox flip_x = bbox[2] + bbox[0] * 2 flip_y = bbox[3] + bbox[1] * 2 instructions = list(layout.walk_instructions( lambda i: (flip_x - (i.x + i.width) * zoom, flip_y - (i.y + i.height) * zoom, i.instruction))) instructions.sort(key=lambda x_y_i: x_y_i[2].render_z) for x, y, instruction in instructions: render_z = instruction.render_z z_id = ("" if not render_z else "-{}".format(render_z)) layer_id = "row-{}{}".format(instruction.row.id, z_id) def_id = self._register_instruction_in_defs(instruction) scale = self._symbol_id_to_scale[def_id] group = { "@class": "instruction", "@id": "instruction-{}".format(instruction.id), "@transform": "translate({},{}),scale({})".format( x, y, scale) } builder.place_svg_use(def_id, layer_id, group) builder.insert_defs(self._instruction_type_color_to_symbol.values()) return builder.get_svg_dict()
def _register_instruction_in_defs(self, instruction): """Create a definition for the instruction. :return: the id of a symbol in the defs for the specified :paramref:`instruction` :rtype: str If no symbol yet exists in the defs for the :paramref:`instruction` a symbol is created and saved using :meth:`_make_symbol`. """ type_ = instruction.type color_ = instruction.color instruction_to_svg_dict = \ self._instruction_to_svg.instruction_to_svg_dict instruction_id = "{}:{}".format(type_, color_) defs_id = instruction_id + ":defs" if instruction_id not in self._instruction_type_color_to_symbol: svg_dict = instruction_to_svg_dict(instruction) self._compute_scale(instruction_id, svg_dict) symbol = self._make_definition(svg_dict, instruction_id) self._instruction_type_color_to_symbol[defs_id] = \ symbol[DEFINITION_HOLDER].pop("defs", {}) self._instruction_type_color_to_symbol[instruction_id] = symbol return instruction_id def _make_definition(self, svg_dict, instruction_id): """Create a symbol out of the supplied :paramref:`svg_dict`. :param dict svg_dict: dictionary containing the SVG for the instruction currently processed :param str instruction_id: id that will be assigned to the symbol """ instruction_def = svg_dict["svg"] blacklisted_elements = ["sodipodi:namedview", "metadata"] whitelisted_attributes = ["@sodipodi:docname"] symbol = OrderedDict({"@id": instruction_id}) for content, value in instruction_def.items(): if content.startswith('@'): if content in whitelisted_attributes: symbol[content] = value elif content not in blacklisted_elements: symbol[content] = value return {DEFINITION_HOLDER: symbol} def _compute_scale(self, instruction_id, svg_dict): """Compute the scale of an instruction svg. Compute the scale using the bounding box stored in the :paramref:`svg_dict`. The scale is saved in a dictionary using :paramref:`instruction_id` as key. :param str instruction_id: id identifying a symbol in the defs :param dict svg_dict: dictionary containing the SVG for the instruction currently processed """ bbox = list(map(float, svg_dict["svg"]["@viewBox"].split())) scale = self._zoom / (bbox[3] - bbox[1]) self._symbol_id_to_scale[instruction_id] = scale
__all__ = ["KnittingPatternToSVG", "DEFINITION_HOLDER"]