返回 ppt-master
tspan_flattener.py
根目录 / skills / ppt-master / scripts / svg_to_pptx / tspan_flattener.py
1 """In-memory flattening of positional ``<tspan>`` elements.
2
3 DrawingML's text-run model has no way to express "jump to a new x/y inside
4 the same paragraph". Every ``<tspan>`` carrying ``x``, ``y`` or non-zero
5 ``dy`` is therefore a layout instruction this converter cannot honour
6 inline — without flattening, a 4-line dy-stacked block collapses onto a
7 single baseline and an x-anchored tspan jumps to the wrong column.
8
9 The on-disk ``finalize_svg`` pipeline solves this by promoting each
10 positional tspan to an independent ``<text>`` element. This module
11 performs the same transformation in memory so ``svg_to_pptx`` can consume
12 ``svg_output/`` directly without that disk step.
13
14 Public API:
15 flatten_positional_tspans(tree) -> bool
16 Walk the SVG element tree, replace every positional ``<tspan>``
17 with an independent ``<text>``, and return whether anything
18 changed.
19
20 Heavy lifting is delegated to ``svg_finalize.flatten_tspan`` so the two
21 pipelines stay behaviourally aligned.
22 """
23
24 from __future__ import annotations
25
26 import sys
27 from pathlib import Path
28 from xml.etree import ElementTree as ET
29
30
31 def _flatten_module():
32 """Load the shared on-disk flattener after exposing the scripts root."""
33 scripts_dir = Path(__file__).resolve().parent.parent
34 if str(scripts_dir) not in sys.path:
35 sys.path.insert(0, str(scripts_dir))
36 from svg_finalize import flatten_tspan # type: ignore
37 return flatten_tspan
38
39
40 def flatten_positional_tspans(
41 tree: ET.ElementTree,
42 merge_paragraphs: bool = False,
43 preserve_line_breaks: bool = False,
44 ) -> bool:
45 """Flatten positional ``<tspan>`` elements into independent ``<text>``.
46
47 Delegates to ``svg_finalize.flatten_tspan.flatten_text_with_tspans`` so
48 the in-memory transform exactly matches the on-disk one. When
49 ``merge_paragraphs`` is True, mergeable paragraph blocks are preserved
50 as a single <text>. ``preserve_line_breaks`` marks visual rows for hard
51 DrawingML line breaks instead of reflow.
52
53 Returns True if any tspan was rewritten.
54 """
55 return _flatten_module().flatten_text_with_tspans(
56 tree,
57 merge_paragraphs=merge_paragraphs,
58 preserve_line_breaks=preserve_line_breaks,
59 )
60
61
62 def nested_positional_tspan_errors(root: ET.Element) -> list[str]:
63 """Return shared diagnostics for unsupported nested baseline jumps."""
64 return _flatten_module().nested_positional_tspan_errors(root)
65
65 lines PYTHON