Skip to content

Commit

Permalink
added ability to enable/disable the SVG attributes substitutions
Browse files Browse the repository at this point in the history
  • Loading branch information
kamichal committed Oct 4, 2017
1 parent 80fc2bf commit c951b7f
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 109 deletions.
22 changes: 19 additions & 3 deletions test/tests_simpledoc.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import unittest
from yattag import SimpleDoc
import xml.etree.ElementTree as ET

from yattag import SimpleDoc, add_svg_attributes, reset_attr_substitutions


class TestSimpledoc(unittest.TestCase):

def test_tag(self):
Expand Down Expand Up @@ -127,7 +129,21 @@ def test_stag(self):
doc.getvalue(),
'<img src="/salmon-plays-piano.jpg">'
)



def test_attributes_substitution(self):
doc = SimpleDoc()

doc.stag('rect', stroke_width=1)
add_svg_attributes()
doc.stag('circle', stroke_width=2)
reset_attr_substitutions()
doc.stag('line', stroke_width=3)

self.assertEqual(
doc.getvalue(),
'<rect stroke_width="1" /><circle stroke-width="2" /><line stroke_width="3" />'
)


if __name__ == '__main__':
unittest.main()
1 change: 1 addition & 0 deletions yattag/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,4 @@
from yattag.simpledoc import SimpleDoc
from yattag.doc import Doc
from yattag.indentation import indent
from yattag.attr_substitution import add_svg_attributes, reset_attr_substitutions
141 changes: 141 additions & 0 deletions yattag/attr_substitution.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@

__all__ = ['add_svg_attributes', 'reset_attr_substitutions']

DEFAULT_ATTR_SUBSTITUTION_MAP = {
"klass": "class",
}

SVG_ATTR_SUBSTITUTIONS = {
"font_face": "font-face",
"font_face_format": "font-face-format",
"font_face_name": "font-face-name",
"font_face_src": "font-face-src",
"font_face_uri": "font-face-uri",
"missing_glyph": "missing-glyph",
"glyph_name": "glyph-name",
"cap_height": "cap-height",
"horiz_adv_x": "horiz-adv-x",
"horiz_adv_y": "horiz-adv-y",
"horiz_origin_x": "horiz-origin-x",
"horiz_origin_y": "horiz-origin-y",
"overline_position": "overline-position",
"overline_thickness": "overline-thickness",
"panose_1": "panose-1",
"rendering_intent": "rendering-intent",
"strikethrough_position": "strikethrough-position",
"strikethrough_thickness": "strikethrough-thickness",
"underline_position": "underline-position",
"underline_thickness": "underline-thickness",
"unicode_range": "unicode-range",
"units_per_em": "units-per-em",
"v_alphabetic": "v-alphabetic",
"v_hanging": "v-hanging",
"v_ideographic": "v-ideographic",
"v_mathematical": "v-mathematical",
"vert_adv_y": "vert-adv-y",
"vert_adv_y": "vert-adv-y",
"vert_origin_x": "vert-origin-x",
"vert_origin_x": "vert-origin-x",
"vert_origin_y": "vert-origin-y",
"vert_origin_y": "vert-origin-y",
"x_heght": "x-heght",
"xlink_actuate": "xlink:actuate",
"xlink_actuate": "xlink:actuate",
"xlink_arcrole": "xlink:arcrole",
"xlink_href": "xlink:href",
"xlink_role": "xlink:role",
"xlink_show": "xlink:show",
"xlink_show": "xlink:show",
"xlink_title": "xlink:title",
"xlink_type": "xlink:type",
"xml_base": "xml:base",
"xml_lang": "xml:lang",
"alignment_baseline": "alignment-baseline",
"baseline_shift": "baseline-shift",
"clip_path": "clip-path",
"clip_rule": "clip-rule",
"color_interpolation_filters": "color-interpolation-filters",
"color_interpolation": "color-interpolation",
"color_profile": "color-profile",
"color_rendering": "color-rendering",
"dominant_baseline": "dominant-baseline",
"enable_background": "enable-background",
"fill_opacity": "fill-opacity",
"fill_rule": "fill-rule",
"flood_color": "flood-color",
"flood_opacity": "flood-opacity",
"font_family": "font-family",
"font_size_adjust": "font-size-adjust",
"font_size": "font-size",
"font_stretch": "font-stretch",
"font_style": "font-style",
"font_variant": "font-variant",
"font_weight": "font-weight",
"glyph_orientation_horizontal": "glyph-orientation-horizontal",
"glyph_orientation_vertical": "glyph-orientation-vertical",
"image_rendering": "image-rendering",
"letter_spacing": "letter-spacing",
"lighting_color": "lighting-color",
"marker_end": "marker-end",
"marker_mid": "marker-mid",
"marker_start": "marker-start",
"pointer_events": "pointer-events",
"shape_rendering": "shape-rendering",
"stop_color": "stop-color",
"stop_opacity": "stop-opacity",
"stroke_dasharray": "stroke-dasharray",
"stroke_dashoffset": "stroke-dashoffset",
"stroke_linecap": "stroke-linecap",
"stroke_linejoin": "stroke-linejoin",
"stroke_miterlimit": "stroke-miterlimit",
"stroke_opacity": "stroke-opacity",
"stroke_width": "stroke-width",
"text_anchor": "text-anchor",
"text_decoration": "text-decoration",
"text_rendering": "text-rendering",
"unicode_bidi": "unicode-bidi",
"word_spacing": "word-spacing",
"writing_mode": "writing-mode",
}


class AttrSubstitution(object):
current_map = DEFAULT_ATTR_SUBSTITUTION_MAP.copy()

@classmethod
def translate(cls, attr_name):
""" get fixed atribute from map or return unchanged if not found """
return cls.current_map.get(attr_name, attr_name)


def add_svg_attributes():
""" Call it anywhere in your script to enable a helper that allows for
adding attributes natively containing minus caracter in name, as a keyword
argument of tag and stag yattag methds. Underscores in known attributes will
be replaced with minus'es.
Regulary to produce e.g.::
<font-face font-family="Sans" units-per-em="1000 />
without the helper you need to type:
doc.stag("font-face", ("font-family", "Sans"), ("units-per-em", 1000)
after calling add_svg_attributes() it can also be::
doc.stag("font-face", font_family = "Sans", units_per_em = 1000)
(Note the underscores, that avoids python syntax errors)
You can disable the helper anytime by calliing reset_attr_substitutions()
"""

AttrSubstitution.current_map.update(SVG_ATTR_SUBSTITUTIONS)


def reset_attr_substitutions():
""" Reset all modifications made to attribute substitution helper,
e.g. if add_svg_attributes() has been invoked.
"""
AttrSubstitution.current_map = DEFAULT_ATTR_SUBSTITUTION_MAP.copy()
113 changes: 7 additions & 106 deletions yattag/simpledoc.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
__all__ = ['SimpleDoc']

from attr_substitution import AttrSubstitution

class SimpleDoc(object):

"""
Expand Down Expand Up @@ -376,104 +378,6 @@ def attr_escape(s):

ATTR_NO_VALUE = object()

ATTRIBUTE_SUBSTITUTIONS = {
"klass": "class",
"font_face": "font-face",
"font_face_format": "font-face-format",
"font_face_name": "font-face-name",
"font_face_src": "font-face-src",
"font_face_uri": "font-face-uri",
"missing_glyph": "missing-glyph",
"glyph_name": "glyph-name",
"cap_height": "cap-height",
"horiz_adv_x": "horiz-adv-x",
"horiz_adv_y": "horiz-adv-y",
"horiz_origin_x": "horiz-origin-x",
"horiz_origin_y": "horiz-origin-y",
"overline_position": "overline-position",
"overline_thickness": "overline-thickness",
"panose_1": "panose-1",
"rendering_intent": "rendering-intent",
"strikethrough_position": "strikethrough-position",
"strikethrough_thickness": "strikethrough-thickness",
"underline_position": "underline-position",
"underline_thickness": "underline-thickness",
"unicode_range": "unicode-range",
"units_per_em": "units-per-em",
"v_alphabetic": "v-alphabetic",
"v_hanging": "v-hanging",
"v_ideographic": "v-ideographic",
"v_mathematical": "v-mathematical",
"vert_adv_y": "vert-adv-y",
"vert_adv_y": "vert-adv-y",
"vert_origin_x": "vert-origin-x",
"vert_origin_x": "vert-origin-x",
"vert_origin_y": "vert-origin-y",
"vert_origin_y": "vert-origin-y",
"x_heght": "x-heght",
"xlink_actuate": "xlink:actuate",
"xlink_actuate": "xlink:actuate",
"xlink_arcrole": "xlink:arcrole",
"xlink_href": "xlink:href",
"xlink_role": "xlink:role",
"xlink_show": "xlink:show",
"xlink_show": "xlink:show",
"xlink_title": "xlink:title",
"xlink_type": "xlink:type",
"xml_base": "xml:base",
"xml_lang": "xml:lang",
"alignment_baseline": "alignment-baseline",
"baseline_shift": "baseline-shift",
"clip_path": "clip-path",
"clip_rule": "clip-rule",
"color_interpolation_filters": "color-interpolation-filters",
"color_interpolation": "color-interpolation",
"color_profile": "color-profile",
"color_rendering": "color-rendering",
"dominant_baseline": "dominant-baseline",
"enable_background": "enable-background",
"fill_opacity": "fill-opacity",
"fill_rule": "fill-rule",
"flood_color": "flood-color",
"flood_opacity": "flood-opacity",
"font_family": "font-family",
"font_size_adjust": "font-size-adjust",
"font_size": "font-size",
"font_stretch": "font-stretch",
"font_style": "font-style",
"font_variant": "font-variant",
"font_weight": "font-weight",
"glyph_orientation_horizontal": "glyph-orientation-horizontal",
"glyph_orientation_vertical": "glyph-orientation-vertical",
"image_rendering": "image-rendering",
"letter_spacing": "letter-spacing",
"lighting_color": "lighting-color",
"marker_end": "marker-end",
"marker_mid": "marker-mid",
"marker_start": "marker-start",
"pointer_events": "pointer-events",
"shape_rendering": "shape-rendering",
"stop_color": "stop-color",
"stop_opacity": "stop-opacity",
"stroke_dasharray": "stroke-dasharray",
"stroke_dashoffset": "stroke-dashoffset",
"stroke_linecap": "stroke-linecap",
"stroke_linejoin": "stroke-linejoin",
"stroke_miterlimit": "stroke-miterlimit",
"stroke_opacity": "stroke-opacity",
"stroke_width": "stroke-width",
"text_anchor": "text-anchor",
"text_decoration": "text-decoration",
"text_rendering": "text-rendering",
"unicode_bidi": "unicode-bidi",
"word_spacing": "word-spacing",
"writing_mode": "writing-mode",
}

def _fix_attribute(attr_name):
""" get fixed atribute from dict or return unchanged if not found """
return ATTRIBUTE_SUBSTITUTIONS.get(attr_name, attr_name)

def dict_to_attrs(dct):
return ' '.join(
(key if value is ATTR_NO_VALUE
Expand All @@ -482,20 +386,17 @@ def dict_to_attrs(dct):
)

def _attributes(args, kwargs):
lst = []
for arg in args:
def tr(arg):
if isinstance(arg, tuple):
lst.append(arg)
return arg
elif isinstance(arg, str):
lst.append((arg, ATTR_NO_VALUE))
return (arg, ATTR_NO_VALUE)
else:
raise ValueError(
"Couldn't make a XML or HTML attribute/value pair out of %s."
% repr(arg)
)
result = dict(lst)
result.update((_fix_attribute(key), value) for key, value in kwargs.iteritems())

result = dict(map(tr, args))
result.update((AttrSubstitution.translate(key), value) for key, value in kwargs.iteritems())
return result


0 comments on commit c951b7f

Please sign in to comment.