Skip to content

Commit 84c3df2

Browse files
committed
Merged with origin master
2 parents d3ab52c + 7672ae4 commit 84c3df2

27 files changed

+210
-151
lines changed

.coveragerc

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
[report]
2-
exclude_lines =
3-
except ImportError
1+
[run]
2+
omit =
3+
test/cairosvg_reference/*

.gitignore

-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@ __pycache__
33
/test/output
44
/dist
55
/MANIFEST
6-
/.tox
76
.coverage
8-
.noseids
97
.cache
108
/.idea
119
build

.travis.yml

+3-6
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,14 @@ python:
55
- 3.5
66

77
install:
8-
- pip install cairocffi
9-
- pip install tinycss
10-
- pip install cssselect
11-
- pip install lxml
12-
- pip install pillow
8+
- pip install --upgrade -e.[test]
139

1410
before_script:
1511
- mkdir ~/.fonts
1612
- cp ./test/resources/*.*tf ~/.fonts
1713
- fc-cache -f -v
1814

19-
script: nosetests
15+
script:
16+
- python setup.py test
2017

2118
sudo: false

cairosvg/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ def main():
7272
help='height of the parent container in pixels')
7373
parser.add_argument(
7474
'-u', '--unsafe', action='store_true',
75-
help='resolve XML entities and allow very large files (WARNING: vulnerable to XXE attacks)')
75+
help='resolve XML entities and allow very large files '
76+
'(WARNING: vulnerable to XXE attacks and various DoS)')
7677
parser.add_argument('-o', '--output', default='-', help='output filename')
7778

7879
options = parser.parse_args()

cairosvg/bounding_box.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,12 @@
2121
2222
"""
2323

24-
from math import isinf, fmod, pi, radians, sin, cos, tan, acos, atan, sqrt
24+
from math import acos, atan, cos, fmod, isinf, pi, radians, sin, sqrt, tan
2525

26+
from .features import match_features
2627
from .helpers import PATH_LETTERS, normalize, point, size
27-
from .defs import parse_url
2828
from .parser import Tree
29-
from .features import match_features
30-
29+
from .url import parse_url
3130

3231
EMPTY_BOUNDING_BOX = float('inf'), float('inf'), 0, 0
3332

@@ -136,9 +135,10 @@ def bounding_box_path(surface, node):
136135
# Extend bounding box with start and end coordinates
137136
arc_bounding_box = bounding_box_elliptical_arc(
138137
previous_x, previous_y, rx, ry, rotation, large, sweep, x, y)
139-
points = (arc_bounding_box[0:2],
140-
tuple(sum(value) for value in
141-
zip(arc_bounding_box[0:2], arc_bounding_box[2:])))
138+
x1, y1, width, height = arc_bounding_box
139+
x2 = x1 + width
140+
y2 = y1 + height
141+
points = (x1, y1), (x2, y2)
142142
bounding_box = extend_bounding_box(bounding_box, points)
143143
previous_x = x
144144
previous_y = y

cairosvg/defs.py

+3-13
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,14 @@
2121
2222
"""
2323

24+
from .bounding_box import calculate_bounding_box, is_non_empty_bounding_box
2425
from .colors import color
2526
from .features import match_features
2627
from .helpers import paint, size, transform
2728
from .parser import Tree
2829
from .shapes import rect
2930
from .surface import cairo
3031
from .url import parse_url
31-
from .bounding_box import calculate_bounding_box, is_non_empty_bounding_box
32-
3332

3433
BLEND_OPERATORS = {
3534
'darken': cairo.OPERATOR_DARKEN,
@@ -210,17 +209,8 @@ def draw_gradient(surface, node, name):
210209
1 / width, 0, 0, 1 / height, - x / width, - y / height))
211210

212211
# Apply transform of gradient
213-
transform(surface,
214-
gradient_node.get('gradientTransform'),
215-
gradient_pattern)
216-
217-
# Set spread method for gradient outside target bounds
218-
"""
219-
# This does not seem to do anything.
220-
# Is there a test showing its functionality?
221-
gradient_pattern.set_extend(EXTEND_OPERATORS.get(
222-
node.get('spreadMethod', 'pad'), EXTEND_OPERATORS['pad']))
223-
"""
212+
transform(
213+
surface, gradient_node.get('gradientTransform'), gradient_pattern)
224214

225215
# Apply gradient (<stop> by <stop>)
226216
offset = 0

cairosvg/helpers.py

+40-43
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,12 @@
1919
2020
"""
2121

22-
from math import cos, sin, tan, atan2, radians
2322
import re
23+
from math import atan2, cos, radians, sin, tan
2424

2525
from .surface import cairo
2626
from .url import parse_url
2727

28-
2928
UNITS = {
3029
'mm': 1 / 25.4,
3130
'cm': 1 / 2.54,
@@ -125,78 +124,77 @@ def preserve_ratio(surface, node):
125124
width = size(surface, node.get('markerWidth', '3'), 'x')
126125
height = size(surface, node.get('markerHeight', '3'), 'y')
127126
_, _, viewbox = node_format(surface, node)
128-
viewbox_width = viewbox[2]
129-
viewbox_height = viewbox[3]
127+
viewbox_width, viewbox_height = viewbox[2:]
130128
elif node.tag in ('svg', 'image'):
131129
width, height, _ = node_format(surface, node)
132130
viewbox_width, viewbox_height = node.image_width, node.image_height
133131
else:
134132
# Safety measure
135-
return 1, 1, 0, 0, None
133+
return 1, 1, 0, 0
136134

137135
translate_x = 0
138136
translate_y = 0
139137
scale_x = width / viewbox_width if viewbox_width > 0 else 1
140138
scale_y = height / viewbox_height if viewbox_height > 0 else 1
141-
clip_box = None
142-
align = node.get('preserveAspectRatio', 'xMidYMid').split(' ')[0]
139+
140+
aspect_ratio = node.get('preserveAspectRatio', 'xMidYMid').split()
141+
align = aspect_ratio[0]
143142
if align == 'none':
144143
x_position = 'min'
145144
y_position = 'min'
146145
else:
147-
mos_properties = node.get('preserveAspectRatio', '').split()
148-
meet_or_slice = mos_properties[1] if len(mos_properties) > 1 else None
146+
meet_or_slice = aspect_ratio[1] if len(aspect_ratio) > 1 else None
149147
if meet_or_slice == 'slice':
150148
scale_value = max(scale_x, scale_y)
151149
else:
152150
scale_value = min(scale_x, scale_y)
153151
scale_x = scale_y = scale_value
154-
155152
x_position = align[1:4].lower()
156153
y_position = align[5:].lower()
157154

158155
if node.tag == 'marker':
159156
translate_x = -size(surface, node.get('refX', '0'), 'x')
160157
translate_y = -size(surface, node.get('refY', '0'), 'y')
161-
if x_position == 'min':
162-
clip_x = viewbox[0]
163-
164-
if y_position == 'min':
165-
clip_y = viewbox[1]
166-
158+
else:
159+
translate_x = 0
167160
if x_position == 'mid':
168-
clip_x = viewbox[0] + (viewbox_width - width / scale_x) / 2.
161+
translate_x = (width / scale_x - viewbox_width) / 2
162+
elif x_position == 'max':
163+
translate_x = width / scale_x - viewbox_width
169164

165+
translate_y = 0
170166
if y_position == 'mid':
171-
clip_y = viewbox[1] + (viewbox_height - height / scale_y) / 2.
167+
translate_y += (height / scale_y - viewbox_height) / 2
168+
elif y_position == 'max':
169+
translate_y += height / scale_y - viewbox_height
172170

173-
if x_position == 'max':
174-
clip_x = viewbox[0] + (viewbox_width - width / scale_x)
171+
return scale_x, scale_y, translate_x, translate_y
175172

176-
if y_position == 'max':
177-
clip_y = viewbox[1] + (viewbox_height - height / scale_y)
178173

179-
clip_box = (clip_x, clip_y, width / scale_x, height / scale_y)
180-
else:
181-
if x_position == 'min':
182-
translate_x = 0
183-
184-
if y_position == 'min':
185-
translate_y = 0
186-
187-
if x_position == 'mid':
188-
translate_x = (width / scale_x - viewbox_width) / 2.
174+
def clip_marker_box(surface, node, scale_x, scale_y):
175+
"""Get the clip ``(x, y, width, height)`` of the marker box."""
176+
width = size(surface, node.get('markerWidth', '3'), 'x')
177+
height = size(surface, node.get('markerHeight', '3'), 'y')
178+
_, _, viewbox = node_format(surface, node)
179+
viewbox_width, viewbox_height = viewbox[2:]
189180

190-
if y_position == 'mid':
191-
translate_y = (height / scale_y - viewbox_height) / 2.
181+
align = node.get('preserveAspectRatio', 'xMidYMid').split(' ')[0]
182+
x_position = 'min' if align == 'none' else align[1:4].lower()
183+
y_position = 'min' if align == 'none' else align[5:].lower()
192184

193-
if x_position == 'max':
194-
translate_x = width / scale_x - viewbox_width
185+
clip_x = viewbox[0]
186+
if x_position == 'mid':
187+
clip_x += (viewbox_width - width / scale_x) / 2.
188+
elif x_position == 'max':
189+
clip_x += viewbox_width - width / scale_x
195190

196-
if y_position == 'max':
197-
translate_y = height / scale_y - viewbox_height
191+
clip_y = viewbox[1]
192+
if y_position == 'mid':
193+
clip_y += (viewbox_height - height / scale_y) / 2.
194+
elif y_position == 'max':
195+
clip_y += viewbox_height - height / scale_y
198196

199-
return scale_x, scale_y, translate_x, translate_y, clip_box
197+
return clip_x, clip_y, width / scale_x, height / scale_y
200198

201199

202200
def quadratic_points(x1, y1, x2, y2, x3, y3):
@@ -213,9 +211,8 @@ def rotate(x, y, angle):
213211
return x * cos(angle) - y * sin(angle), y * cos(angle) + x * sin(angle)
214212

215213

216-
def transform(surface, string, gradient = None):
217-
"""Update ``surface`` or ``gradient`` (if supplied)
218-
according to transformation ``string``.
214+
def transform(surface, string, gradient=None):
215+
"""Transform ``surface`` or ``gradient`` if supplied using ``string``.
219216
220217
See http://www.w3.org/TR/SVG/coords.html#TransformAttribute
221218
@@ -252,7 +249,7 @@ def transform(surface, string, gradient = None):
252249
apply_matrix_transform(surface, matrix, gradient)
253250

254251

255-
def apply_matrix_transform(surface, matrix, gradient = None):
252+
def apply_matrix_transform(surface, matrix, gradient=None):
256253
"""Apply a ``matrix`` to ``surface`` or ``gradient`` if supplied.
257254
258255
When the matrix is not invertible, this function clips the context to an

cairosvg/image.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
from PIL import Image
2626

27-
from .helpers import node_format, size, preserve_ratio, preserved_ratio
27+
from .helpers import node_format, preserve_ratio, preserved_ratio, size
2828
from .parser import Tree
2929
from .surface import cairo
3030
from .url import parse_url, read_url
@@ -67,7 +67,7 @@ def image(surface, node):
6767
tree_height = tree['height'] = height
6868
node.image_width = tree_width or width
6969
node.image_height = tree_height or height
70-
scale_x, scale_y, translate_x, translate_y, _ = preserve_ratio(
70+
scale_x, scale_y, translate_x, translate_y = preserve_ratio(
7171
surface, node)
7272
surface.set_context_size(
7373
*node_format(surface, tree, reference=False),
@@ -87,7 +87,7 @@ def image(surface, node):
8787

8888
node.image_width = image_surface.get_width()
8989
node.image_height = image_surface.get_height()
90-
scale_x, scale_y, translate_x, translate_y, _ = preserve_ratio(
90+
scale_x, scale_y, translate_x, translate_y = preserve_ratio(
9191
surface, node)
9292

9393
surface.context.rectangle(x, y, width, height)

0 commit comments

Comments
 (0)