Source code for madam.vector
import io
from xml.etree import ElementTree as ET
from madam.core import Asset, MetadataProcessor, Processor, UnsupportedFormatError
_INCH_TO_MM = 1 / 25.4
_PX_PER_INCH = 90
_PT_PER_INCH = 1 / 72
_FONT_SIZE_PT = 12
_X_HEIGHT = 0.7
def svg_length_to_px(length):
if length is None:
raise ValueError()
unit_len = 2
if length.endswith('%'):
unit_len = 1
try:
value = float(length)
unit = 'px'
except ValueError:
value = float(length[:-unit_len])
unit = length[-unit_len:]
if unit == 'em':
return value * _PX_PER_INCH * _FONT_SIZE_PT * _PT_PER_INCH
elif unit == 'ex':
return value * _PX_PER_INCH * _X_HEIGHT * _FONT_SIZE_PT * _PT_PER_INCH
elif unit == 'px':
return value
elif unit == 'in':
return value * _PX_PER_INCH
elif unit == 'cm':
return value * _PX_PER_INCH * _INCH_TO_MM * 10
elif unit == 'mm':
return value * _PX_PER_INCH * _INCH_TO_MM
elif unit == 'pt':
return value * _PX_PER_INCH * _PT_PER_INCH
elif unit == 'pc':
return value * _PX_PER_INCH * _PT_PER_INCH * 12
elif unit == '%':
return value
XML_NS = dict(
svg='http://www.w3.org/2000/svg',
rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#',
dc='http://purl.org/dc/elements/1.1/',
)
[docs]class SVGProcessor(Processor):
"""
Represents a processor that handles Scalable Vector Graphics (SVG) data.
"""
def can_read(self, file):
try:
ET.parse(file)
return True
except ET.ParseError:
return False
def read(self, file):
try:
tree = ET.parse(file)
except ET.ParseError as e:
raise UnsupportedFormatError('Error while parsing XML in line %d, column %d' % e.position)
root = tree.getroot()
metadata = dict(mime_type='image/svg+xml')
if 'width' in root.keys():
metadata['width'] = svg_length_to_px(root.get('width'))
if 'height' in root.keys():
metadata['height'] = svg_length_to_px(root.get('height'))
file.seek(0)
return Asset(essence=file, **metadata)
[docs]class SVGMetadataProcessor(MetadataProcessor):
"""
Represents a metadata processor that handles Scalable Vector Graphics (SVG)
data.
It is assumed that the SVG XML uses UTF-8 encoding.
"""
@property
def formats(self):
return {'rdf'}
@staticmethod
def __parse(file):
try:
tree = ET.parse(file)
except ET.ParseError as e:
raise UnsupportedFormatError('Error while parsing XML: %s' % e)
root = tree.getroot()
return tree, root, root.find('./svg:metadata', XML_NS)
@staticmethod
def __register_xml_namespaces():
for prefix, uri in XML_NS.items():
if prefix == 'svg':
prefix = ''
ET.register_namespace(prefix, uri)
def read(self, file):
_, _, metadata_elem = SVGMetadataProcessor.__parse(file)
if metadata_elem is None or len(metadata_elem) == 0:
return {'rdf': {}}
return {'rdf': {'xml': ET.tostring(metadata_elem[0], encoding='unicode')}}
def strip(self, file):
tree, root, metadata_elem = SVGMetadataProcessor.__parse(file)
if metadata_elem is not None:
root.remove(metadata_elem)
result = io.BytesIO()
SVGMetadataProcessor.__register_xml_namespaces()
tree.write(result, xml_declaration=True, encoding='utf-8')
result.seek(0)
return result
def combine(self, file, metadata):
if not metadata:
raise ValueError('No metadata provided.')
if 'rdf' not in metadata:
raise UnsupportedFormatError('No RDF metadata found.')
rdf = metadata['rdf']
if 'xml' not in rdf:
raise ValueError('XML string missing from RDF metadata.')
tree, root, metadata_elem = SVGMetadataProcessor.__parse(file)
if metadata_elem is None:
metadata_elem = ET.SubElement(root, '{%(svg)s}metadata' % XML_NS)
metadata_elem.append(ET.fromstring(rdf['xml']))
result = io.BytesIO()
SVGMetadataProcessor.__register_xml_namespaces()
tree.write(result, xml_declaration=True, encoding='utf-8')
result.seek(0)
return result