inkstitch/lib/svg/units.py

136 wiersze
3.3 KiB
Python

import simpletransform
from ..i18n import _
from ..utils import cache
# modern versions of Inkscape use 96 pixels per inch as per the CSS standard
PIXELS_PER_MM = 96 / 25.4
# cribbed from inkscape-silhouette
def parse_length_with_units(str):
'''
Parse an SVG value which may or may not have units attached
This version is greatly simplified in that it only allows: no units,
units of px, mm, and %. Everything else, it returns None for.
There is a more general routine to consider in scour.py if more
generality is ever needed.
'''
u = 'px'
s = str.strip()
if s[-2:] == 'px':
s = s[:-2]
elif s[-2:] == 'mm':
u = 'mm'
s = s[:-2]
elif s[-2:] == 'pt':
u = 'pt'
s = s[:-2]
elif s[-2:] == 'pc':
u = 'pc'
s = s[:-2]
elif s[-2:] == 'cm':
u = 'cm'
s = s[:-2]
elif s[-2:] == 'in':
u = 'in'
s = s[:-2]
elif s[-1:] == '%':
u = '%'
s = s[:-1]
try:
v = float(s)
except BaseException:
raise ValueError(_("parseLengthWithUnits: unknown unit %s") % s)
return v, u
def convert_length(length):
value, units = parse_length_with_units(length)
if not units or units == "px":
return value
if units == 'pt':
value /= 72
units = 'in'
if units == 'pc':
value /= 6
units = 'in'
if units == 'cm':
value *= 10
units = 'mm'
if units == 'mm':
value = value / 25.4
units = 'in'
if units == 'in':
# modern versions of Inkscape use CSS's 96 pixels per inch. When you
# open an old document, inkscape will add a viewbox for you.
return value * 96
raise ValueError(_("Unknown unit: %s") % units)
@cache
def get_viewbox(svg):
viewbox = svg.get('viewBox')
if viewbox is None:
viewbox = "0 0 0 0"
return viewbox.strip().replace(',', ' ').split()
@cache
def get_doc_size(svg):
width = svg.get('width')
height = svg.get('height')
if width == "100%" and height == "100%":
# Some SVG editors set width and height to "100%". I can't find any
# solid documentation on how one is supposed to interpret that, so
# just ignore it and use the viewBox. That seems to have the intended
# result anyway.
width = None
height = None
if width is None or height is None:
# fall back to the dimensions from the viewBox
viewbox = get_viewbox(svg)
width = viewbox[2]
height = viewbox[3]
doc_width = convert_length(width)
doc_height = convert_length(height)
return doc_width, doc_height
@cache
def get_viewbox_transform(node):
# somewhat cribbed from inkscape-silhouette
doc_width, doc_height = get_doc_size(node)
viewbox = get_viewbox(node)
dx = -float(viewbox[0])
dy = -float(viewbox[1])
transform = simpletransform.parseTransform("translate(%f, %f)" % (dx, dy))
try:
sx = doc_width / float(viewbox[2])
sy = doc_height / float(viewbox[3])
scale_transform = simpletransform.parseTransform("scale(%f, %f)" % (sx, sy))
transform = simpletransform.composeTransform(transform, scale_transform)
except ZeroDivisionError:
pass
return transform