236 lines
8.3 KiB
Python
236 lines
8.3 KiB
Python
# coding=utf-8
|
|
"""
|
|
Test Inkex style parsing functionality.
|
|
"""
|
|
|
|
import pytest
|
|
import warnings
|
|
|
|
from inkex.styles import Style
|
|
from inkex.colors import Color
|
|
from inkex.tester import TestCase
|
|
from inkex.tester.svg import svg_file
|
|
|
|
|
|
class StyleTest(TestCase):
|
|
"""Test path API and calculations"""
|
|
|
|
def test_new_style(self):
|
|
"""Create a style from a path string"""
|
|
stl = Style("border-color: blue; border-width: 4px;")
|
|
self.assertEqual(str(stl), "border-color:blue;border-width:4px")
|
|
|
|
def test_composite(self):
|
|
"""Test chaining styles together"""
|
|
stl = Style("border-color: blue;")
|
|
stl += "border-color: red; border-issues: true;"
|
|
self.assertEqual(str(stl), "border-color:red;border-issues:true")
|
|
st2 = stl + "border-issues: false;"
|
|
self.assertEqual(str(st2), "border-color:red;border-issues:false")
|
|
|
|
def test_inbuilts(self):
|
|
"""Test inbuild style functions"""
|
|
stadd = Style("a: 1") + Style("b: 2")
|
|
self.assertTrue(stadd == Style("b: 2; a: 1"))
|
|
self.assertFalse(stadd == Style("b: 2"))
|
|
self.assertFalse(stadd != Style("b: 2; a: 1"))
|
|
self.assertEqual(stadd - "a: 4", "b: 2")
|
|
stadd -= "b: 3; c: 4"
|
|
self.assertEqual(stadd, Style("a: 1"))
|
|
|
|
def test_set_property(self):
|
|
"""Set the style attribute directly"""
|
|
stl = Style()
|
|
stl["border-pain"] = "green"
|
|
self.assertEqual(str(stl), "border-pain:green")
|
|
|
|
def test_color_property(self):
|
|
"""Color special handling"""
|
|
stl = Style("fill-opacity:0.7;fill:red;")
|
|
self.assertEqual(stl.get_color("fill").alpha, 0.7)
|
|
self.assertEqual(str(stl.get_color("fill")), "rgba(255, 0, 0, 0.7)")
|
|
stl.set_color("rgba(0, 127, 0, 0.5)", "stroke")
|
|
self.assertEqual(
|
|
str(stl), "fill-opacity:0.7;fill:red;stroke-opacity:0.5;stroke:#007f00"
|
|
)
|
|
|
|
def test_interpolate(self):
|
|
"""Test interpolation method."""
|
|
stl1 = Style(
|
|
{"stroke-width": "0px", "fill-opacity": 1.0, "fill": Color((200, 0, 0))}
|
|
)
|
|
stl2 = Style(
|
|
{"stroke-width": "1pc", "fill-opacity": 0.0, "fill": Color((100, 0, 100))}
|
|
)
|
|
stl3 = stl1.interpolate(stl2, 0.5)
|
|
print(stl3)
|
|
self.assertAlmostEqual(stl3("fill-opacity"), 0.5, 1e-3)
|
|
assert stl3("fill") == [150, 0, 50]
|
|
assert stl3["stroke-width"] == "8px"
|
|
|
|
def test_callback(self):
|
|
"""Test callback."""
|
|
calls = 0
|
|
|
|
def cb(style):
|
|
nonlocal calls
|
|
self.assertNotIn("fill-opacity", style)
|
|
calls += 1
|
|
|
|
st = Style(
|
|
{"stroke-width": "0px", "fill-opacity": 1.0, "fill": Color((200, 0, 0))},
|
|
callback=cb,
|
|
)
|
|
self.assertEqual(calls, 0)
|
|
st.pop("fill-opacity")
|
|
self.assertEqual(calls, 1)
|
|
|
|
def cb2(style):
|
|
nonlocal calls
|
|
self.assertEqual(style["fill-opacity"], ".75")
|
|
calls += 1
|
|
|
|
st.callback = cb2
|
|
st["fill-opacity"] = ".75"
|
|
self.assertEqual(calls, 2)
|
|
|
|
# del and pop should both work, and call the callback only once.
|
|
st.callback = cb
|
|
del st["fill-opacity"]
|
|
self.assertEqual(calls, 3)
|
|
|
|
|
|
class StyleSheetTest(TestCase):
|
|
"""Test parsing style sheets"""
|
|
|
|
def setUp(self):
|
|
super(StyleSheetTest, self).setUp()
|
|
self.svg = svg_file(self.data_file("svg", "css.svg"))
|
|
self.css = self.svg.stylesheet
|
|
|
|
def test_classes(self):
|
|
"""Test element class manipulation"""
|
|
rect = self.svg.getElementById("rect2")
|
|
self.assertEqual(rect.get("class"), "two")
|
|
self.assertEqual(rect.classes, ["two"])
|
|
rect.classes[0] = "twa"
|
|
self.assertEqual(rect.get("class"), "twa")
|
|
rect.classes.append("tri")
|
|
rect.classes.append("four")
|
|
self.assertEqual(rect.get("class"), "twa tri four")
|
|
rect.classes.remove("twa")
|
|
self.assertEqual(rect.get("class"), "tri four")
|
|
rect.classes.toggle("toggle")
|
|
self.assertEqual(rect.get("class"), "tri four toggle")
|
|
rect.classes.toggle("toggle")
|
|
self.assertEqual(rect.get("class"), "tri four")
|
|
|
|
def test_creation(self):
|
|
"""Stylesheet is created when needed"""
|
|
self.svg = svg_file(self.data_file("svg", "empty.svg"))
|
|
self.assertEqual(len(self.svg.stylesheets), 0)
|
|
self.assertEqual(len(self.svg.stylesheet), 0)
|
|
self.assertEqual(len(self.svg.stylesheets), 1)
|
|
self.svg.stylesheet.append(".cls1 { fill: blue; }")
|
|
self.assertIn(
|
|
b"style><![CDATA[\n.cls1 {\n fill:blue;\n}\n]]><", self.svg.tostring()
|
|
)
|
|
|
|
def test_parsing(self):
|
|
"""SVG parsing provides access to stylesheets"""
|
|
sheets = self.svg.stylesheets
|
|
self.assertEqual(len(sheets), 3)
|
|
self.assertEqual(len(sheets[0]), 7)
|
|
self.assertEqual(len(sheets[1]), 0)
|
|
self.assertEqual(len(sheets[2]), 2)
|
|
|
|
def test_string(self):
|
|
"""Rendered to a string"""
|
|
sheets = self.svg.stylesheets
|
|
self.assertEqual(str(sheets[0][0]), "#layer1 {\n stroke:yellow;\n}")
|
|
self.assertEqual(str(sheets[2][1]), ".rule {}")
|
|
|
|
def test_lookup_by_id(self):
|
|
"""ID CSS lookup"""
|
|
matches = list(self.css[0].all_matches(self.svg))
|
|
assert len(matches) == 1
|
|
elem = matches[0]
|
|
self.assertEqual(elem.get("id"), "layer1")
|
|
|
|
def test_lookup_by_element(self):
|
|
"""Element name CSS lookup"""
|
|
elems = list(self.css[1].all_matches(self.svg))
|
|
self.assertEqual(len(elems), 2)
|
|
self.assertEqual(elems[0].get("id"), "circle1")
|
|
self.assertEqual(elems[1].get("id"), "circle2")
|
|
|
|
def test_lookup_by_class(self):
|
|
"""Class name CSS lookup"""
|
|
matches = list(self.css[2].all_matches(self.svg))
|
|
assert len(matches) == 1
|
|
elem = matches[0]
|
|
self.assertEqual(elem.get("id"), "rect2")
|
|
|
|
def test_lookup_and(self):
|
|
"""Multiple CSS lookups"""
|
|
with self.assertWarns(DeprecationWarning):
|
|
xpath = self.css[3].to_xpath()
|
|
self.assertIn(
|
|
xpath,
|
|
[
|
|
"//*[@id='rect3']"
|
|
"[contains(concat(' ', normalize-space(@class), ' '), ' three ')]",
|
|
# cssselect 1.1.0
|
|
"descendant-or-self::*[@id = 'rect3' and "
|
|
"(@class and contains(concat(' ', normalize-space(@class), ' '), ' three '))]",
|
|
# cssselect 1.2.0
|
|
"descendant-or-self::*[(@id = 'rect3') and "
|
|
"(@class and contains(concat(' ', normalize-space(@class), ' '), ' three '))]",
|
|
],
|
|
)
|
|
elem_old = self.svg.getElement(xpath)
|
|
|
|
matches = list(self.css[3].all_matches(self.svg))
|
|
assert len(matches) == 1
|
|
elem = matches[0]
|
|
assert elem == elem_old
|
|
self.assertEqual(elem.get("id"), "rect3")
|
|
|
|
def test_lookup_or(self):
|
|
"""SVG rules can look up the right elements"""
|
|
elems = list(self.css[6].all_matches(self.svg))
|
|
self.assertEqual(len(elems), 3)
|
|
self.assertEqual(elems[0].get("id"), "rect2")
|
|
self.assertEqual(elems[1].get("id"), "circle1")
|
|
self.assertEqual(elems[2].get("id"), "circle2")
|
|
|
|
def test_applied_styles(self):
|
|
"""Are styles applied to the svg elements correctly"""
|
|
font_default = (
|
|
"font-style:normal;font-variant:normal;"
|
|
"font-weight:normal;font-stretch:normal;font-size:Homie;"
|
|
"line-height:normal;font-family:sans-serif"
|
|
)
|
|
self.assertEqual(
|
|
str(self.svg.getElementById("rect1").cascaded_style()), "fill:blue"
|
|
)
|
|
self.assertEqual(
|
|
str(self.svg.getElementById("rect2").cascaded_style()),
|
|
"fill:green;" + font_default,
|
|
)
|
|
self.assertEqual(
|
|
str(self.svg.getElementById("rect3").cascaded_style()), "fill:cyan"
|
|
)
|
|
self.assertEqual(
|
|
str(self.svg.getElementById("rect4").cascaded_style()),
|
|
"fill:grey;stroke:red",
|
|
)
|
|
|
|
self.assertEqual(
|
|
str(self.svg.getElementById("circle1").cascaded_style()),
|
|
"fill:red;" + font_default,
|
|
)
|
|
self.assertEqual(
|
|
str(self.svg.getElementById("circle2").cascaded_style()),
|
|
"fill:red;" + font_default,
|
|
)
|