summaryrefslogtreecommitdiff
path: root/lib/svg/units.py
blob: 126027bce7292dd739fd01b653979f50081dcad9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import simpletransform

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:
        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):
    return svg.get('viewBox').strip().replace(',', ' ').split()


@cache
def get_doc_size(svg):
    width = svg.get('width')
    height = svg.get('height')

    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