Skip to content

Commit

Permalink
Merge pull request #151 from meerk40t/1.6.4
Browse files Browse the repository at this point in the history
1.6.4 Maintainence
  • Loading branch information
tatarize authored Nov 16, 2021
2 parents 972923c + 713b8cf commit 32d6c94
Show file tree
Hide file tree
Showing 10 changed files with 172 additions and 92 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ The `context` permits giving a context of already set values that are come from

The second function within parsing that matters is the `.elements()` this is a function that exists on any `SVG` object and will flatten the elements yielding them in order.

Here's an example parser with elements().
Here's an example parser with elements().

```python
for element in svg.elements():
try:
Expand All @@ -168,7 +169,7 @@ Here's an example parser with elements().
pass
```

Here a few things are checked. The element.values for ['visibility'] is checked if it's hidden it is not added to our flat object list. Texts are specific added. Paths are only added if they have `PathSegments` and are not completely blank. Any Shape object is converted to a Path() object and reified. Any SVGImage objects are loaded. This is a soft dependency on PIL/Pillow to load images stored within SVG. The SVG `.elements()` function can also take a conditional function that well be used to test each element before yielding it. In most cases we don't want every single type of thing an svg can produce. We might just want all the Path objects so we check for any Path and include that but also for any non-Path Shape and convert that to a path.
Here a few things are checked. The element.values for ['visibility'] is checked if it's hidden it is not added to our flat object list. Texts are specific added. Paths are only added if they have `PathSegments` and are not completely blank. Any Shape object is converted to a Path() object and reified. Any SVGImage objects are loaded. This is a soft dependency on PIL/Pillow to load images stored within SVG. The SVG `.elements()` function can also take a conditional function that well be used to test each element before yielding it. In most cases we don't want every single type of thing an svg can produce. We might just want all the Path objects so we check for any Path and include that but also for any non-Path Shape and convert that to a path. `pathname` is an attempt to get the local directory for loading relative path images.


# Overview
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = svgelements
version = 1.6.3
version = 1.6.4
description = Svg Elements Parsing
long_description_content_type=text/markdown
long_description = file: README.md
Expand Down
85 changes: 49 additions & 36 deletions svgelements/svgelements.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
and the Arc can do exact arc calculations if scipy is installed.
"""

SVGELEMENTS_VERSION = "1.6.3"
SVGELEMENTS_VERSION = "1.6.4"

MIN_DEPTH = 5
ERROR = 1e-12
Expand Down Expand Up @@ -195,6 +195,7 @@
)

REGEX_IRI = re.compile(r"url\(#?(.*)\)")
REGEX_DATA_URL = re.compile(r"^data:([^,]*),(.*)")
REGEX_FLOAT = re.compile(PATTERN_FLOAT)
REGEX_COORD_PAIR = re.compile(
"(%s)%s(%s)" % (PATTERN_FLOAT, PATTERN_COMMA, PATTERN_FLOAT)
Expand Down Expand Up @@ -1518,7 +1519,7 @@ def parse_color_hex(hex_string):

@staticmethod
def parse_color_rgb(values):
"""Parse SVG Color, RGB value declarations """
"""Parse SVG Color, RGB value declarations"""
r = int(values[0])
g = int(values[1])
b = int(values[2])
Expand Down Expand Up @@ -4160,7 +4161,7 @@ def length(self, error=None, min_depth=None):
return 0

def closest_segment_point(self, p, respect_bounds=True):
""" Gives the point on the line closest to the given point. """
"""Gives the point on the line closest to the given point."""
a = self.start
b = self.end
v_ap_x = p[0] - a.x
Expand Down Expand Up @@ -4285,8 +4286,8 @@ def npoint(self, positions):
def _compute_point(position):
# compute factors
n_pos = 1 - position
pos_2 = position ** 2
n_pos_2 = n_pos ** 2
pos_2 = position * position
n_pos_2 = n_pos * n_pos
n_pos_pos = n_pos * position

return (
Expand Down Expand Up @@ -4336,9 +4337,9 @@ def length(self, error=None, min_depth=None):
try:
# For an explanation of this case, see
# http://www.malczak.info/blog/quadratic-bezier-curve-length/
A = 4 * (a.real ** 2 + a.imag ** 2)
A = 4 * (a.real * a.real + a.imag * a.imag)
B = 4 * (a.real * b.real + a.imag * b.imag)
C = b.real ** 2 + b.imag ** 2
C = b.real * b.real + b.imag * b.imag

Sabc = 2 * sqrt(A + B + C)
A2 = sqrt(A)
Expand All @@ -4349,7 +4350,7 @@ def length(self, error=None, min_depth=None):
s = (
A32 * Sabc
+ A2 * B * (Sabc - C2)
+ (4 * C * A - B ** 2) * log((2 * A2 + BA + Sabc) / (BA + C2))
+ (4 * C * A - B * B) * log((2 * A2 + BA + Sabc) / (BA + C2))
) / (4 * A32)
except (ZeroDivisionError, ValueError):
# a_dot_b = a.real * b.real + a.imag * b.imag
Expand All @@ -4360,7 +4361,7 @@ def length(self, error=None, min_depth=None):
if k >= 2:
s = abs(b) - abs(a)
else:
s = abs(a) * (k ** 2 / 2 - k + 1)
s = abs(a) * (k * k / 2 - k + 1)
return s

def is_smooth_from(self, previous):
Expand Down Expand Up @@ -4482,9 +4483,9 @@ def npoint(self, positions):

def _compute_point(position):
# compute factors
pos_3 = position ** 3
pos_3 = position * position * position
n_pos = 1 - position
n_pos_3 = n_pos ** 3
n_pos_3 = n_pos * n_pos * n_pos
pos_2_n_pos = position * position * n_pos
n_pos_2_pos = n_pos * n_pos * position
return (
Expand Down Expand Up @@ -4519,7 +4520,9 @@ def _real_minmax(self, v):
a = [c[v] for c in self]
denom = a[0] - 3 * a[1] + 3 * a[2] - a[3]
if abs(denom) >= 1e-12:
delta = a[1] ** 2 - (a[0] + a[1]) * a[2] + a[2] ** 2 + (a[0] - a[1]) * a[3]
delta = (
a[1] * a[1] - (a[0] + a[1]) * a[2] + a[2] * a[2] + (a[0] - a[1]) * a[3]
)
if delta >= 0: # otherwise no local extrema
sqdelta = sqrt(delta)
tau = a[0] - 2 * a[1] + a[2]
Expand Down Expand Up @@ -4768,17 +4771,15 @@ def __init__(self, *args, **kwargs):
if r < hq:
kwargs["r"] = r = hq # Correct potential math domain error.
self.center = Point(
mid.x + sqrt(r ** 2 - hq ** 2) * (self.start.y - self.end.y) / q,
mid.y + sqrt(r ** 2 - hq ** 2) * (self.end.x - self.start.x) / q,
mid.x + sqrt(r * r - hq * hq) * (self.start.y - self.end.y) / q,
mid.y + sqrt(r * r - hq * hq) * (self.end.x - self.start.x) / q,
)
cw = bool(Point.orientation(self.start, self.center, self.end) == 1)
if "ccw" in kwargs and kwargs["ccw"] and cw or not cw:
# ccw arg exists, is true, and we found the cw center, or we didn't find the cw center.
self.center = Point(
mid.x
- sqrt(r ** 2 - hq ** 2) * (self.start.y - self.end.y) / q,
mid.y
- sqrt(r ** 2 - hq ** 2) * (self.end.x - self.start.x) / q,
mid.x - sqrt(r * r - hq * hq) * (self.start.y - self.end.y) / q,
mid.y - sqrt(r * r - hq * hq) * (self.end.x - self.start.x) / q,
)
elif "rx" in kwargs and "ry" in kwargs:
# This formulation will assume p1 and p2 are both axis aligned.
Expand Down Expand Up @@ -4991,7 +4992,8 @@ def _integral_length(self):
def ellipse_part_integral(t1, t2, a, b, n=100000):
# function to integrate
def f(t):
return sqrt(1 - (1 - a ** 2 / b ** 2) * sin(t) ** 2)
sint = sin(t)
return sqrt(1 - (1 - (a * a) / (b * b)) * sint * sint)

start = min(t1, t2)
seg_len = abs(t1 - t2) / n
Expand All @@ -5009,11 +5011,11 @@ def _exact_length(self):

a = self.rx
b = self.ry
adb = a / b
m = 1 - adb * adb
phi = self.get_start_t()
m = 1 - (a / b) ** 2
d1 = ellipeinc(phi, m)
phi = phi + self.sweep
m = 1 - (a / b) ** 2
d2 = ellipeinc(phi, m)
return b * abs(d2 - d1)

Expand Down Expand Up @@ -6841,7 +6843,7 @@ def _ramanujan_length(self):
b = self.implicit_ry
if b > a:
a, b = b, a
h = (a - b) ** 2 / (a + b) ** 2
h = ((a - b) * (a - b)) / ((a + b) * (a + b))
return pi * (a + b) * (1 + (3 * h / (10 + sqrt(4 - 3 * h))))


Expand Down Expand Up @@ -7940,6 +7942,7 @@ class Image(SVGElement, GraphicObject, Transformable):
def __init__(self, *args, **kwargs):
self.url = None
self.data = None
self.media_type = None
self.viewbox = None
self.preserve_aspect_ratio = None
self.x = None
Expand All @@ -7957,18 +7960,19 @@ def __init__(self, *args, **kwargs):
) # Dataurl requires this be processed first.

if self.url is not None:
if self.url.startswith("data:image/"):
match = REGEX_DATA_URL.match(self.url)
if match:
# Data URL
from base64 import b64decode
self.media_type = match.group(1).split(";")
self.data = match.group(2)
if "base64" in self.media_type:
from base64 import b64decode

self.data = b64decode(self.data)
else:
from urllib.parse import unquote_to_bytes

if self.url.startswith("data:image/png;base64,"):
self.data = b64decode(self.url[22:])
elif self.url.startswith("data:image/jpg;base64,"):
self.data = b64decode(self.url[22:])
elif self.url.startswith("data:image/jpeg;base64,"):
self.data = b64decode(self.url[23:])
elif self.url.startswith("data:image/svg+xml;base64,"):
self.data = b64decode(self.url[26:])
self.data = unquote_to_bytes(self.data)

def __repr__(self):
values = []
Expand Down Expand Up @@ -8460,8 +8464,8 @@ def parse(
source,
reify=True,
ppi=DEFAULT_PPI,
width=1000,
height=1000,
width=None,
height=None,
color="black",
transform=None,
context=None,
Expand Down Expand Up @@ -8502,7 +8506,7 @@ def parse(
stack.append((context, values))
if (
SVG_ATTR_DISPLAY in values
and values[SVG_ATTR_DISPLAY] == SVG_VALUE_NONE
and values[SVG_ATTR_DISPLAY].lower() == SVG_VALUE_NONE
):
continue # Values has a display=none. Do not render anything. No Shadow Dom.
current_values = values
Expand Down Expand Up @@ -8596,13 +8600,22 @@ def parse(
values[SVG_STRUCT_ATTRIB] = attributes
if (
SVG_ATTR_DISPLAY in values
and values[SVG_ATTR_DISPLAY] == SVG_VALUE_NONE
and values[SVG_ATTR_DISPLAY].lower() == SVG_VALUE_NONE
):
continue # If the attributes flags our values to display=none, stop rendering.
if SVG_NAME_TAG == tag:
# The ordering for transformations on the SVG object are:
# explicit transform, parent transforms, attribute transforms, viewport transforms
s = SVG(values)

if width is None:
# If a dim was not provided but a viewbox was, use the viewbox dim as physical size, else 1000
width = (
s.viewbox.width if s.viewbox is not None else 1000
) # 1000 default no information.
if height is None:
height = s.viewbox.height if s.viewbox is not None else 1000

s.render(ppi=ppi, width=width, height=height)
height, width = s.width, s.height
if s.viewbox is not None:
Expand Down
29 changes: 15 additions & 14 deletions test/test_arc_length.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,20 +386,21 @@ def test_arc_point_start_stop(self):
self.assertTrue(np.all(np.array([list(arc.start), list(arc.end)])
== arc.npoint([0, 1])))

# def test_arc_point_implementations_match(self):
# import numpy as np
# for _ in range(1000):
# arc = get_random_arc()
#
# pos = np.linspace(0, 1, 100)
#
# v1 = arc.npoint(pos)
# # with disable_numpy():
# v2 = arc.npoint(pos) # test is rendered pointless.
#
# for p, p1, p2 in zip(pos, v1, v2):
# self.assertEqual(arc.point(p), Point(p1))
# self.assertEqual(Point(p1), Point(p2))
def test_arc_point_implementations_match(self):
import numpy as np
for _ in range(1000):
arc = get_random_arc()

pos = np.linspace(0, 1, 100)

v1 = arc.npoint(pos)
v2 = []
for i in range(len(pos)):
v2.append(arc.point(pos[i]))

for p, p1, p2 in zip(pos, v1, v2):
self.assertEqual(arc.point(p), Point(p1))
self.assertEqual(Point(p1), Point(p2))


class TestElementArcApproximation(unittest.TestCase):
Expand Down
29 changes: 15 additions & 14 deletions test/test_cubic_bezier.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,18 @@ def test_cubic_bezier_point_start_stop(self):
self.assertTrue(np.all(np.array([list(b.start), list(b.end)])
== b.npoint([0, 1])))

# def test_cubic_bezier_point_implementations_match(self):
# import numpy as np
# for _ in range(1000):
# b = get_random_cubic_bezier()
#
# pos = np.linspace(0, 1, 100)
#
# v1 = b.npoint(pos)
# # with disable_numpy():
# v2 = b.npoint(pos) # Test rendered pointless.
#
# for p, p1, p2 in zip(pos, v1, v2):
# self.assertEqual(b.point(p), Point(p1))
# self.assertEqual(Point(p1), Point(p2))
def test_cubic_bezier_point_implementations_match(self):
import numpy as np
for _ in range(1000):
b = get_random_cubic_bezier()

pos = np.linspace(0, 1, 100)

v1 = b.npoint(pos)
v2 = []
for i in range(len(pos)):
v2.append(b.point(pos[i]))

for p, p1, p2 in zip(pos, v1, v2):
self.assertEqual(b.point(p), Point(p1))
self.assertEqual(Point(p1), Point(p2))
19 changes: 19 additions & 0 deletions test/test_image.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import unittest

from svgelements import *


class TestElementImage(unittest.TestCase):

def test_image_datauri(self):
e = Image(href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAEFklEQVR4nO3dsW7bQBRFwSjw//8yU6UJYDgil9xdnZnalp6LewhBhV/HcRy/gKTfsw8A5hEACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACPuafUDd6/X68WeO43jgEooE4GH/M/iffkcQGEUAHnJm+D+9lhBwlQDcbOTwv3ttIeAsAbjJncP/7r2EgHf5FuAGT45/hfdlXwIw2OwRzn5/9iIAA60yvlXuYH0CMMhqo1vtHtYkAAOsOrZV72IdAnDR6iNb/T7mEoALdhnXLnfyPAGAMAE4aben6m738gwBgDABOGHXp+mud3MfAYAwAXjT7k/R3e9nLAGAMAGAMAGAMAF4w6d8fv6Uv4PrBADCBADCBADCBADCBADCBADCBADCBADCBOANn/Kfdz7l7+A6AYAwAYAwAYAwAXjT7p+fd7+fsQQAwgTghF2forvezX0EAMIE4KTdnqa73cszBADCBOCCXZ6qu9zJ8wTgotXHtfp9zCUAA6w6slXvYh0CMMhqY1vtHtYkAAOtMrpV7mB9AjDY7PHNfn/2IgA3mDVC4+ddX7MP+FR/x/jEf+ExfM4SgJvdGQLD5yoBeMjIEBg+owjAw/4d7/8EweC5iwBMZtzM5FsACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACBMACPsDhkRyDUVGTB8AAAAASUVORK5CYII=")
self.assertEqual(e.data[:6], b"\x89PNG\r\n")
e1 = Image(href="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAEAAQADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD3+iiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiq99f2emWcl5f3cFpax43zTyCNFyQBljwMkgfjXj/iH9ozQtPvBBoemT6vGPvzvIbZDwCNoKlj1IOQuMcZBzQB7RRXzB/w0d4w/6Buh/wDfib/47XV+Hv2kLG5uILfxBoslmhRVku7WXzVD5ALGMgFU+8eCxGAMN1oA90orP0bXNL8Q6cl/pF/Be2rYG+F87SQDtYdVbBGVOCM8itCgAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAqvf31vpmnXN/eSeXa2sTzTPtJ2ooJY4HJwAelWK8X/aM8Q3Gn+F9O0OBcR6pKzzvkcpEUITBHdmU5BGNmOQTQB5J8TPiZfeP9UCIJLbRbdybW0J5J6eZJjguR26KDgdSW4OiigAooooA6jwL461TwHrgv7A+bbyYW6tHbCXCDsfRhk4btnuCQfsPw5r9j4p8P2etaa0htLpCyeYu1lIJVlI9QwI4yOOCRzXwpXvH7N3iG4XUdW8NMu61eL7fGcgeW4KRt2ydwZO/GzpyaAPoeiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACvmD9o7/koen/8AYKj/APRstfT9eF/tIeHprnS9I8QW9tGyWjvb3UioS4V8GMsQPuBgw5PBkGPvGgD50ooooAKKKKACvQPgl/yV7Qv+3j/0nkrz+vbP2cvD01z4l1DxBLbRtaWlubeKSRCSJnIOUOMZCBg2DkCQdmoA+k6KKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKz9c0az8Q6He6RfpvtbuJonwASuejLkEBgcEHHBANaFFAHxR468C6p4D1w2F+PNt5Mta3aLhLhB3HowyMr2z3BBPL1916/4c0jxTpbabrVjHd2hcPsYlSrDoVZSCp6jII4JHQmvDPEP7N14t4G8NazA9q3WPUiVePgfxIpD5O7+FccDnrQB4PRXoH/AApL4h/9C9/5O2//AMcrrPD37OWtXNxBL4g1O0s7QoryRWpMs4ORmM5ARTjcNwLAEDhhQB5X4Y8Map4u1yHSNIg824k5Zm4SJB1dz2UZH5gAEkA/Y/gvwnZ+CvC9rotm/m+Vl5pygRppGOWYgfgBnJChRk4zR4T8F6F4K05rPRbTyvN2meZ2LyTMowCzH8TgYUEnAGTXQUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAH//Z")
self.assertEqual(e1.data[:3], b"\xff\xd8\xff")
e2 = Image(href="data:text/plain;base64,c3ZnZWxlbWVudHMgcmVhZHMgc3ZnIGZpbGVz")
self.assertEqual(e2.data, b"svgelements reads svg files")
e3 = Image(href="data:text/vnd-example+xyz;foo=bar;base64,R0lGODdh")
self.assertEqual(e3.data, b"GIF87a")
e4 = Image(href="data:text/plain;charset=UTF-8;page=21,the%20data:1234,5678")
self.assertEqual(e4.data, b"the data:1234,5678")

Loading

0 comments on commit 32d6c94

Please sign in to comment.