Files
context-continuity/venv.old-py38/lib/python3.9/site-packages/wavedrom/tspan.py
T

140 lines
4.6 KiB
Python
Raw Normal View History

# Copyright wavedrompy contributors.
# SPDX-License-Identifier: MIT
import sys
import svgwrite
from six import string_types
from svgwrite.base import BaseElement
from svgwrite.etree import etree
from .attrdict import AttrDict
if sys.version_info < (3, 0):
from HTMLParser import HTMLParser
else:
from html.parser import HTMLParser
class TspanParser(HTMLParser, object):
tags = {
'o': {'text_decoration': 'overline'},
'ins': {'text_decoration': 'underline'},
'sub': {'baseline_shift': 'sub'},
'sup': {'baseline_shift': 'super'},
'b': {'font_weight': 'bold'},
'i': {'font_style': 'italic'},
's': {'text_decoration': 'line-through'},
'tt': {'font_family': 'monospace'},
}
def __init__(self):
super(TspanParser, self).__init__()
self.text = []
self.state = []
def handle_starttag(self, tag, attrs):
self.state.append(tag)
def handle_endtag(self, tag):
if self.state.pop() != tag:
raise RuntimeError("Unexpected closing tag: {}".format(tag))
def get_style(self):
return {k: v for d in [self.tags[t] for t in self.state] for k, v in d.items()}
def handle_data(self, data):
if len(self.state) == 0:
self.text.append(svgwrite.text.TSpan(data))
else:
self.text.append(svgwrite.text.TSpan(data, **self.get_style()))
def get_text(self):
return self.text
class JsonMLElement(BaseElement):
"""Class that generates xml elements from jsonml."""
def __init__(self, source, **extra):
"""Constructs from jsonml source."""
self._jsonml = self.extract_element(source)
self.elementname = self._jsonml.tagname
self._jsonml.attributes.update(extra)
super(JsonMLElement, self).__init__(**extra)
def extract_element(self, e):
"""Extract AttrDict from jsonml
This function non-recursively extracts an AttrDict from jsonml.
This AttrDict has the three elements tagname, attributes and
element_list according to the jsonml specification.
:param e: element as jsonml list/tuple
:return: AttrDict
"""
if not isinstance(e, (list, tuple)):
raise ValueError("JsonML must be a list")
if len(e) == 0:
raise ValueError("JsonML cannot be an empty list")
if not isinstance(e[0], string_types):
raise ValueError("JsonML tagname must be string")
ret = AttrDict({"tagname": e[0], "attributes": {}, "element-list": []})
if len(e) > 1:
if isinstance(e[1], dict):
ret.attributes = e[1]
if len(e) > 2:
ret.element_list = e[2:]
else:
ret.element_list = e[1:]
return ret
def get_xml_element(self, e):
"""Generate xml element from jsonml AttrDict
Recursively generates xml element from jsonml AttrDict.
:param e: jsonml AttrDict
:return: xml element
"""
# create element
element = etree.Element(e.tagname)
# set element attributes
for attribute, value in sorted(e.attributes.items()):
# filter 'None' values
if value is not None:
value = self.value_to_string(value)
if value: # just add not empty attributes
element.set(attribute, value)
# store the last xml sibling, because we may need to add
# text to it's tail. This is to support the tagged text
# style ("<tspan>a<tspan>b</tspan>c</tspan>")
last = None
for c in e.element_list:
if isinstance(c, string_types):
# Strings need special treatment for insertion
# as those are not elements
if last is None:
# No non-text element seen so far
if element.text is None:
# No other element seen so far
element.text = c
else:
# Append to other texts
element.text += c
else:
# There was an element already
if last.tail is None:
# No text after that so far
last.tail = c
else:
# Append to other text
last.tail += c
else:
# Recurse
last = self.get_xml_element(self.extract_element(c))
element.append(last)
return element
def get_xml(self):
return self.get_xml_element(self._jsonml)