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
|
from pathlib import Path
from lxml.etree import Element, XMLParser, fromstring, tostring
PARSER = XMLParser(remove_blank_text=False)
BASE_DIR = Path("charts")
def read_svg(path: Path) -> Element:
raw = path.read_bytes()
return fromstring(raw, PARSER)
def write_svg(path: Path, element: Element) -> None:
raw = tostring(element, encoding="utf-8", pretty_print=False, xml_declaration=True)
path.write_bytes(raw)
def split_plotly_svg(input_path: Path, outline_path: Path, data_path: Path) -> None:
outline_tree, data_tree = read_svg(input_path), read_svg(input_path)
data_xpath = "//*[local-name()='g' and contains(@class, 'overplot')]"
for element in outline_tree.xpath(data_xpath):
element.attrib["display"] = "none"
write_svg(outline_path, outline_tree)
frame_layers = [
"bglayer",
"gridlayer",
"zerolinelayer",
"axis",
"infolayer",
"shapelayer",
"overaxes-above",
]
frame_conditions = " or ".join(
[f"contains(@class, '{cls}')" for cls in frame_layers]
)
frame_xpath = f"//*[local-name()='g' and ({frame_conditions})]"
for element in data_tree.xpath(frame_xpath):
element.attrib["display"] = "none"
bg_rect_xpath = "//*[local-name()='rect' and (contains(@class, 'bg') or contains(@class, 'plot-bg'))]"
for rect in data_tree.xpath(bg_rect_xpath):
rect.attrib["display"] = "none"
write_svg(data_path, data_tree)
def split_all_svgs() -> None:
paths = list(BASE_DIR.glob("*.svg"))
for path in paths:
if path.stem.endswith(("_outline", "_data")):
continue
split_plotly_svg(
path,
path.with_stem(f"{path.stem}_outline"),
path.with_stem(f"{path.stem}_data"),
)
|