-
Notifications
You must be signed in to change notification settings - Fork 19
/
_cairosvg.py
136 lines (118 loc) · 4.64 KB
/
_cairosvg.py
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
# This file is part of CairoSVG
# Copyright © 2010-2015 Kozea
#
# This library is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
# This library 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 Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with CairoSVG. If not, see <http://www.gnu.org/licenses/>.
"""
Images manager.
"""
import os.path
from io import BytesIO
import base64
from PIL import Image
from cairosvg.helpers import node_format, preserve_ratio, preserved_ratio, size
from cairosvg.parser import Tree
from cairosvg.surface import cairo
from cairosvg.url import parse_url
def uri2image_bytes(image_str):
"""Convert a 'data:...' URI to image_bytes"""
header, image_data = image_str.split(",")
if header.endswith("base64"):
image_binary = base64.b64decode(image_data)
### elif (): ## FIXME: handle other bases here
return image_binary
def image(surface, node):
"""Draw an image ``node``."""
base_url = node.get('{http://www.w3.org/XML/1998/namespace}base')
if not base_url and node.url:
base_url = os.path.dirname(node.url) + '/'
url = parse_url(node.get('{http://www.w3.org/1999/xlink}href'), base_url)
if "href" in node and node["href"].startswith("data:image/"): # gif, etc
image_bytes = uri2image_bytes(node["href"])
else:
image_bytes = node.fetch_url(url, 'image/*')
if len(image_bytes) < 5:
return
x, y = size(surface, node.get('x'), 'x'), size(surface, node.get('y'), 'y')
width = size(surface, node.get('width'), 'x')
height = size(surface, node.get('height'), 'y')
if image_bytes[:4] == b'\x89PNG':
png_file = BytesIO(image_bytes)
elif (image_bytes[:5] in (b'<svg ', b'<?xml', b'<!DOC') or
image_bytes[:2] == b'\x1f\x8b'):
if 'x' in node:
del node['x']
if 'y' in node:
del node['y']
tree = Tree(
url=url.geturl(), url_fetcher=node.url_fetcher,
bytestring=image_bytes, tree_cache=surface.tree_cache,
unsafe=node.unsafe)
tree_width, tree_height, viewbox = node_format(
surface, tree, reference=False)
if not viewbox:
tree_width = tree['width'] = width
tree_height = tree['height'] = height
node.image_width = tree_width or width
node.image_height = tree_height or height
scale_x, scale_y, translate_x, translate_y = preserve_ratio(
surface, node)
# Clip image region
surface.context.rectangle(x, y, width, height)
surface.context.clip()
# Draw image
surface.context.save()
surface.context.translate(x, y)
surface.set_context_size(
*node_format(surface, tree, reference=False), scale=1,
preserved_ratio=preserved_ratio(tree))
surface.context.translate(*surface.context.get_current_point())
surface.context.scale(scale_x, scale_y)
surface.context.translate(translate_x, translate_y)
surface.draw(tree)
surface.context.restore()
return
else:
png_file = BytesIO()
Image.open(BytesIO(image_bytes)).save(png_file, 'PNG')
png_file.seek(0)
image_surface = cairo.ImageSurface.create_from_png(png_file)
### DSB
## Added:
image_surface.pattern = cairo.SurfacePattern(image_surface)
image_surface.pattern.set_filter(cairo.FILTER_NEAREST)
### DSB
node.image_width = image_surface.get_width()
node.image_height = image_surface.get_height()
scale_x, scale_y, translate_x, translate_y = preserve_ratio(
surface, node)
# Clip image region (if necessary)
if not (translate_x == 0 and
translate_y == 0 and
width == scale_x * node.image_width and
height == scale_y * node.image_height):
surface.context.rectangle(x, y, width, height)
surface.context.clip()
# Paint raster image
surface.context.save()
surface.context.translate(x, y)
surface.context.scale(scale_x, scale_y)
surface.context.translate(translate_x, translate_y)
### DSB
## Removed:
#surface.context.set_source_surface(image_surface)
## Added:
surface.context.set_source(image_surface.pattern)
### DSB
surface.context.paint()
surface.context.restore()