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 | import json
import os
import re
from typing import Union
from ....utils.sxml import Xml
from ....version import VERSION
from ...inkscape import InkscapeShell
from ...render import SvgRenderUnit
from ..backend import DEFAULT_CACHE_DIR, Backend
from .draw import draw_text
from .query import compute_query
from .rcontext import SvgRenderingContext
VERSION_REGEX = re.compile(r"Inkscape\s+(\d+\..*)")
class InkscapeBackend(Backend):
"""Backend that maps Elsie primitives to SVG and renders them to PDF using Inkscape."""
def __init__(
self,
inkscape: Union[str, InkscapeShell] = None,
cache_dir: str = DEFAULT_CACHE_DIR,
):
"""
Parameters
----------
inkscape: Union[str, InkscapeShell]
Either a path to the Inkscape binary or an instance of the InkscapeShell class.
cache_dir: str
Cache directory for caching SVG files.
"""
super().__init__(cache_dir)
if isinstance(inkscape, InkscapeShell):
self.inkscape = inkscape
else:
inkscape_bin = (
inkscape or os.environ.get("ELSIE_INKSCAPE") or "/usr/bin/inkscape"
)
self.inkscape = InkscapeShell(inkscape_bin)
self.inkscape_version = self.inkscape.get_version()
match = VERSION_REGEX.search(self.inkscape_version)
if not match:
print(f"WARNING: Unknown Inkscape version ({self.inkscape_version})")
else:
version = match.group(1)
major_version = version.split(".")[0]
if int(major_version) < 1:
print(
f"WARNING: You are using Inkscape {version}, which might be incompatible "
f"with Elsie. Please consider upgrading to Inkscape 1.0+."
)
self.query_cache = self._load_query_cache()
self.used_query_cache = {}
def get_version(self, elsie_version: str) -> str:
return f"{elsie_version}/{self.inkscape_version}"
def create_render_unit(self, slide, step, export_type):
ctx = SvgRenderingContext(slide, step, slide.debug_boxes)
painters = slide._box.get_painters(ctx, 0)
painters.sort(key=lambda painter: painter.z_level)
for p in painters:
p.render(ctx)
return SvgRenderUnit(slide, step, ctx.render(), self.inkscape)
def prune_cache(self):
self.query_cache = self.used_query_cache
self.used_query_cache = {}
def save_cache(self):
self._save_query_cache(self.query_cache)
def compute_text_width(self, parsed_text, style, styles, id_index=None):
return self._text_query("inkscape-w", parsed_text, style, styles, id_index)
def compute_text_height(self, parsed_text, style, styles, id_index=None):
return self._text_query("inkscape-h", parsed_text, style, styles, id_index)
def compute_text_x(self, parsed_text, style, styles, id_index=None):
return self._text_query("inkscape-x", parsed_text, style, styles, id_index)
def _text_query(self, query, parsed_text, style, styles, id_index):
xml = Xml()
draw_text(xml, 0, 0, parsed_text, style, styles, id="target", id_index=id_index)
key = xml.to_string()
return self.process_query(query, key)
def process_query(self, method: str, data: str):
key = (method, data)
value = self.query_cache.get(key)
if value is None:
value = compute_query(self.inkscape, method, data)
self.query_cache[key] = value
self.used_query_cache[key] = value
return value
def _query_cache_file(self):
return os.path.join(self.cache_dir, "queries3.cache")
def _load_query_cache(self):
cache_file = self._query_cache_file()
if os.path.isfile(cache_file):
with open(cache_file) as f:
cache_config = json.load(f)
if cache_config.get("version") != VERSION:
print("Elsie version changed; cache dropped")
return {}
if cache_config.get("inkscape") != self.inkscape_version:
print("Inkscape version changed; cache dropped")
return {}
return dict(
(tuple(key), value) for key, value in cache_config.get("queries", ())
)
else:
return {}
def _save_query_cache(self, cache):
cache_file = self._query_cache_file()
cache_config = {
"version": VERSION,
"inkscape": self.inkscape_version,
"queries": list(cache.items()),
}
with open(cache_file, "w") as f:
json.dump(cache_config, f)
|