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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
import re
units = {"": 1.0,
"px": 1.0,
"in": 90.0,
"mm": 90.0 / 25.4,
"cm": 90.0 / 2.54,
"pt": 1.25,
"pc": 15.0,
"em": 1.0,
"ex": 1.0,
"INVALID": 1.0, # some DocBook files contain this
}
def srgb_to_linearrgb(c):
if c < 0.04045:
return 0.0 if c < 0.0 else c * (1.0 / 12.92)
else:
return pow((c + 0.055) * (1.0 / 1.055), 2.4)
def check_points_equal(point_a, point_b):
return (abs(point_a[0] - point_b[0]) < 1e-6 and
abs(point_a[1] - point_b[1]) < 1e-6)
match_number = r"-?\d+(\.\d+)?([eE][-+]?\d+)?"
match_first_comma = r"^\s*(?=,)"
match_comma_pair = r",\s*(?=,)"
match_last_comma = r",\s*$"
array_of_floats_pattern = f"({match_number})|{match_first_comma}|{match_comma_pair}|{match_last_comma}"
re_array_of_floats_pattern = re.compile(array_of_floats_pattern)
def parse_array_of_floats(text):
"""
Accepts comma or space separated list of floats (without units) and returns an array
of floating point values.
"""
elements = re_array_of_floats_pattern.findall(text)
return [value_to_float(v[0]) for v in elements]
def read_float(s: str, i: int = 0):
"""
Reads floating point value from a string. Parsing starts at the given index.
Returns the value itself (as a string) and index of first character after the value.
"""
start = i
n = len(s)
token = ''
# Skip leading whitespace characters
while i < n and (s[i].isspace() or s[i] == ','):
i += 1
if i == n:
return "0", i
# Read sign
if s[i] == '-':
token += '-'
i += 1
elif s[i] == '+':
i += 1
# Read integer part
if s[i].isdigit():
while i < n and s[i].isdigit():
token += s[i]
i += 1
# Fractional part
if i < n and s[i] == '.':
token += '.'
i += 1
if i < n and s[i].isdigit():
while i < n and s[i].isdigit():
token += s[i]
i += 1
elif i == n or s[i].isspace() or s[i] == ',':
# Inkscape sometimes uses weird float format with missed
# fractional part after dot. Suppose zero fractional part
# for this case
pass
else:
raise Exception('Invalid float value near ' + s[start:start + 10])
# Degree
if i < n and (s[i] == 'e' or s[i] == 'E'):
token += s[i]
i += 1
if s[i] == '+' or s[i] == '-':
token += s[i]
i += 1
if s[i].isdigit():
while i < n and s[i].isdigit():
token += s[i]
i += 1
else:
raise Exception('Invalid float value near ' + s[start:start + 10])
return token, i
def parse_coord(coord, size):
"""
Parse coordinate component to common basis
Needed to handle coordinates set in cm, mm, inches.
"""
token, last_char = read_float(coord)
val = float(token)
unit = coord[last_char:].strip() # strip() in case there is a space
if unit == '%':
return float(size) / 100.0 * val
else:
return val * units[unit]
return val
def value_to_float(value_encoded: str):
"""
A simple wrapper around float() which supports empty strings (which are converted to 0).
"""
if len(value_encoded) == 0:
return 0
return float(value_encoded)
|