licos-dev-sdk 0.3.1__tar.gz → 0.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {licos_dev_sdk-0.3.1 → licos_dev_sdk-0.3.2}/PKG-INFO +1 -1
- {licos_dev_sdk-0.3.1 → licos_dev_sdk-0.3.2}/pyproject.toml +1 -1
- {licos_dev_sdk-0.3.1 → licos_dev_sdk-0.3.2}/src/licos_dev_sdk/document_docx.py +13 -6
- {licos_dev_sdk-0.3.1 → licos_dev_sdk-0.3.2}/src/licos_dev_sdk/document_docx_layout.py +24 -0
- {licos_dev_sdk-0.3.1 → licos_dev_sdk-0.3.2}/tests/test_document_spreadsheet.py +12 -0
- {licos_dev_sdk-0.3.1 → licos_dev_sdk-0.3.2}/.gitignore +0 -0
- {licos_dev_sdk-0.3.1 → licos_dev_sdk-0.3.2}/src/licos_dev_sdk/__init__.py +0 -0
- {licos_dev_sdk-0.3.1 → licos_dev_sdk-0.3.2}/src/licos_dev_sdk/_utils.py +0 -0
- {licos_dev_sdk-0.3.1 → licos_dev_sdk-0.3.2}/src/licos_dev_sdk/archive.py +0 -0
- {licos_dev_sdk-0.3.1 → licos_dev_sdk-0.3.2}/src/licos_dev_sdk/chart.py +0 -0
- {licos_dev_sdk-0.3.1 → licos_dev_sdk-0.3.2}/src/licos_dev_sdk/data.py +0 -0
- {licos_dev_sdk-0.3.1 → licos_dev_sdk-0.3.2}/src/licos_dev_sdk/diagram.py +0 -0
- {licos_dev_sdk-0.3.1 → licos_dev_sdk-0.3.2}/src/licos_dev_sdk/document.py +0 -0
- {licos_dev_sdk-0.3.1 → licos_dev_sdk-0.3.2}/src/licos_dev_sdk/document_common.py +0 -0
- {licos_dev_sdk-0.3.1 → licos_dev_sdk-0.3.2}/src/licos_dev_sdk/image.py +0 -0
- {licos_dev_sdk-0.3.1 → licos_dev_sdk-0.3.2}/src/licos_dev_sdk/model.py +0 -0
- {licos_dev_sdk-0.3.1 → licos_dev_sdk-0.3.2}/src/licos_dev_sdk/observability.py +0 -0
- {licos_dev_sdk-0.3.1 → licos_dev_sdk-0.3.2}/src/licos_dev_sdk/pdf_inspect.py +0 -0
- {licos_dev_sdk-0.3.1 → licos_dev_sdk-0.3.2}/src/licos_dev_sdk/presentation.py +0 -0
- {licos_dev_sdk-0.3.1 → licos_dev_sdk-0.3.2}/src/licos_dev_sdk/spreadsheet.py +0 -0
- {licos_dev_sdk-0.3.1 → licos_dev_sdk-0.3.2}/src/licos_dev_sdk/spreadsheet_inspect.py +0 -0
- {licos_dev_sdk-0.3.1 → licos_dev_sdk-0.3.2}/src/licos_dev_sdk/web.py +0 -0
- {licos_dev_sdk-0.3.1 → licos_dev_sdk-0.3.2}/tests/test_model.py +0 -0
- {licos_dev_sdk-0.3.1 → licos_dev_sdk-0.3.2}/tests/test_observability.py +0 -0
- {licos_dev_sdk-0.3.1 → licos_dev_sdk-0.3.2}/tests/test_output_paths.py +0 -0
|
@@ -27,6 +27,7 @@ from .document_docx_layout import (
|
|
|
27
27
|
first_font as _first_font,
|
|
28
28
|
table_alignment as _table_alignment,
|
|
29
29
|
table_cell_placements as _table_cell_placements,
|
|
30
|
+
word_safe_fragments as _word_safe_fragments,
|
|
30
31
|
)
|
|
31
32
|
|
|
32
33
|
|
|
@@ -251,23 +252,21 @@ class _DocxRenderer:
|
|
|
251
252
|
elif level == 2:
|
|
252
253
|
_set_paragraph_border(p, self.theme["secondary"], side="left", size="20")
|
|
253
254
|
p.paragraph_format.left_indent = Cm(0.18)
|
|
254
|
-
|
|
255
|
+
self._append_text(p, text, tag)
|
|
255
256
|
if tag is not None:
|
|
256
257
|
self._apply_paragraph_style(p, tag)
|
|
257
|
-
self._apply_run_style(run, tag)
|
|
258
258
|
|
|
259
259
|
def render_inline(self, paragraph, node: Tag | NavigableString) -> None:
|
|
260
260
|
if isinstance(node, Comment):
|
|
261
261
|
return
|
|
262
262
|
if isinstance(node, NavigableString):
|
|
263
|
-
|
|
263
|
+
self._append_text(paragraph, str(node))
|
|
264
264
|
return
|
|
265
265
|
for child in node.children:
|
|
266
266
|
if isinstance(child, Comment):
|
|
267
267
|
continue
|
|
268
268
|
if isinstance(child, NavigableString):
|
|
269
|
-
|
|
270
|
-
self._apply_run_style(run, node)
|
|
269
|
+
self._append_text(paragraph, str(child), node)
|
|
271
270
|
elif not isinstance(child, Tag):
|
|
272
271
|
continue
|
|
273
272
|
elif child.name == "br":
|
|
@@ -296,7 +295,7 @@ class _DocxRenderer:
|
|
|
296
295
|
if isinstance(child, Tag) and child.name in {"ul", "ol"}:
|
|
297
296
|
continue
|
|
298
297
|
if isinstance(child, (Tag, NavigableString)):
|
|
299
|
-
self.render_inline(p, child) if isinstance(child, Tag) else
|
|
298
|
+
self.render_inline(p, child) if isinstance(child, Tag) else self._append_text(p, str(child), li)
|
|
300
299
|
for nested in li.find_all(["ul", "ol"], recursive=False):
|
|
301
300
|
self._render_list(nested, ordered=nested.name == "ol", level=level + 1)
|
|
302
301
|
|
|
@@ -444,6 +443,14 @@ class _DocxRenderer:
|
|
|
444
443
|
run.font.name = "Cascadia Mono"
|
|
445
444
|
_set_run_shading(run, "F2F2F2")
|
|
446
445
|
|
|
446
|
+
def _append_text(self, paragraph, text: str, tag: Tag | None = None) -> None:
|
|
447
|
+
for value, color in _word_safe_fragments(text):
|
|
448
|
+
run = paragraph.add_run(value)
|
|
449
|
+
if tag is not None:
|
|
450
|
+
self._apply_run_style(run, tag)
|
|
451
|
+
if color is not None:
|
|
452
|
+
run.font.color.rgb = RGBColor.from_string(color)
|
|
453
|
+
|
|
447
454
|
def _style(self, tag: Tag) -> dict[str, str]:
|
|
448
455
|
inherited: dict[str, str] = {}
|
|
449
456
|
if isinstance(tag.parent, Tag):
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from collections.abc import Iterator
|
|
5
6
|
import re
|
|
6
7
|
|
|
7
8
|
from bs4 import Tag
|
|
@@ -77,6 +78,29 @@ def first_font(value: str | None) -> str | None:
|
|
|
77
78
|
return value.split(",", 1)[0].strip().strip("\"'")
|
|
78
79
|
|
|
79
80
|
|
|
81
|
+
_WORD_SAFE_SYMBOLS: dict[str, tuple[str, str | None]] = {
|
|
82
|
+
"\U0001f4a1": ("提示:", None),
|
|
83
|
+
"\U0001f7e2": ("●", "70AD47"),
|
|
84
|
+
"\U0001f7e1": ("●", "FFC000"),
|
|
85
|
+
"\U0001f534": ("●", "C00000"),
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def word_safe_fragments(text: str) -> Iterator[tuple[str, str | None]]:
|
|
90
|
+
"""Replace color emoji that Word renders inconsistently across viewers."""
|
|
91
|
+
start = 0
|
|
92
|
+
for index, char in enumerate(text):
|
|
93
|
+
replacement = _WORD_SAFE_SYMBOLS.get(char)
|
|
94
|
+
if replacement is None:
|
|
95
|
+
continue
|
|
96
|
+
if index > start:
|
|
97
|
+
yield text[start:index], None
|
|
98
|
+
yield replacement
|
|
99
|
+
start = index + 1
|
|
100
|
+
if start < len(text):
|
|
101
|
+
yield text[start:], None
|
|
102
|
+
|
|
103
|
+
|
|
80
104
|
def table_cell_placements(rows: list[Tag]) -> tuple[list[list[tuple[Tag, int, int, int]]], int]:
|
|
81
105
|
placements: list[list[tuple[Tag, int, int, int]]] = [[] for _ in rows]
|
|
82
106
|
occupied_until: dict[int, int] = {}
|
|
@@ -55,6 +55,7 @@ class DocumentSpreadsheetTests(unittest.TestCase):
|
|
|
55
55
|
<tbody><tr><td rowspan="2">产线</td><td>OEE</td><td>85%</td></tr>
|
|
56
56
|
<tr><td>良率</td><td>99.2%</td></tr></tbody></table>
|
|
57
57
|
<p><mark>重点</mark> H<sub>2</sub>O 与 10<sup>2</sup></p>
|
|
58
|
+
<p>💡 跨平台提示:🟢 正常,🟡 关注,🔴 告警。</p>
|
|
58
59
|
</body></html>
|
|
59
60
|
"""
|
|
60
61
|
with tempfile.TemporaryDirectory() as tmp:
|
|
@@ -93,6 +94,17 @@ class DocumentSpreadsheetTests(unittest.TestCase):
|
|
|
93
94
|
self.assertTrue(sub.font.subscript)
|
|
94
95
|
self.assertTrue(sup.font.superscript)
|
|
95
96
|
|
|
97
|
+
status = next(p for p in doc.paragraphs if "跨平台提示" in p.text)
|
|
98
|
+
self.assertEqual(status.text, "提示: 跨平台提示:● 正常,● 关注,● 告警。")
|
|
99
|
+
self.assertNotIn("💡", status.text)
|
|
100
|
+
self.assertNotIn("🟢", status.text)
|
|
101
|
+
symbol_colors = [
|
|
102
|
+
str(run.font.color.rgb)
|
|
103
|
+
for run in status.runs
|
|
104
|
+
if run.text == "●"
|
|
105
|
+
]
|
|
106
|
+
self.assertEqual(symbol_colors, ["70AD47", "FFC000", "C00000"])
|
|
107
|
+
|
|
96
108
|
def test_create_docx_from_template_renders_placeholders(self) -> None:
|
|
97
109
|
with tempfile.TemporaryDirectory() as tmp:
|
|
98
110
|
tmp_path = Path(tmp)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|