dsh-office-kit 0.3.0 → 0.3.1
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.
- package/assets/skills/office-documents/render_docx.py +7 -0
- package/assets/skills/office-documents/scripts/a11y_audit.py +7 -0
- package/assets/skills/office-documents/scripts/accept_tracked_changes.py +7 -0
- package/assets/skills/office-documents/scripts/add_tracked_replacements.py +7 -0
- package/assets/skills/office-documents/scripts/append_blocks.py +7 -0
- package/assets/skills/office-documents/scripts/apply_template_styles.py +7 -0
- package/assets/skills/office-documents/scripts/captions_and_crossrefs.py +7 -0
- package/assets/skills/office-documents/scripts/comments_add.py +7 -0
- package/assets/skills/office-documents/scripts/comments_apply_patch.py +7 -0
- package/assets/skills/office-documents/scripts/comments_extract.py +7 -0
- package/assets/skills/office-documents/scripts/comments_strip.py +7 -0
- package/assets/skills/office-documents/scripts/content_controls.py +7 -0
- package/assets/skills/office-documents/scripts/core_properties.py +7 -0
- package/assets/skills/office-documents/scripts/docx_kit.py +7 -0
- package/assets/skills/office-documents/scripts/docx_ooxml_patch.py +7 -0
- package/assets/skills/office-documents/scripts/docx_table_to_csv.py +7 -0
- package/assets/skills/office-documents/scripts/fields_materialize.py +7 -0
- package/assets/skills/office-documents/scripts/fields_report.py +7 -0
- package/assets/skills/office-documents/scripts/flatten_ref_fields.py +7 -0
- package/assets/skills/office-documents/scripts/footnotes_report.py +7 -0
- package/assets/skills/office-documents/scripts/google_docs_title_sanitize.py +7 -0
- package/assets/skills/office-documents/scripts/heading_audit.py +7 -0
- package/assets/skills/office-documents/scripts/images_audit.py +7 -0
- package/assets/skills/office-documents/scripts/insert_note.py +7 -0
- package/assets/skills/office-documents/scripts/insert_ref_fields.py +7 -0
- package/assets/skills/office-documents/scripts/insert_toc.py +7 -0
- package/assets/skills/office-documents/scripts/internal_nav.py +7 -0
- package/assets/skills/office-documents/scripts/make_fixtures.py +7 -0
- package/assets/skills/office-documents/scripts/merge_docx_append.py +7 -0
- package/assets/skills/office-documents/scripts/privacy_scrub.py +7 -0
- package/assets/skills/office-documents/scripts/redact_docx.py +7 -0
- package/assets/skills/office-documents/scripts/render_and_diff.py +7 -0
- package/assets/skills/office-documents/scripts/replace_text.py +7 -0
- package/assets/skills/office-documents/scripts/section_audit.py +7 -0
- package/assets/skills/office-documents/scripts/set_protection.py +7 -0
- package/assets/skills/office-documents/scripts/style_lint.py +7 -0
- package/assets/skills/office-documents/scripts/style_normalize.py +7 -0
- package/assets/skills/office-documents/scripts/table_geometry.py +7 -0
- package/assets/skills/office-documents/scripts/watermark_add.py +7 -0
- package/assets/skills/office-documents/scripts/watermark_audit_remove.py +7 -0
- package/assets/skills/office-documents/scripts/xlsx_to_docx_table.py +7 -0
- package/assets/skills/office-presentations/container_tools/add_slides.py +7 -0
- package/assets/skills/office-presentations/container_tools/core_properties.py +7 -0
- package/assets/skills/office-presentations/container_tools/create_montage.py +7 -0
- package/assets/skills/office-presentations/container_tools/inspect_presentation_layout_geometry.py +7 -0
- package/assets/skills/office-presentations/container_tools/inspect_presentation_package_integrity.py +7 -0
- package/assets/skills/office-presentations/container_tools/native_chart_title_gate.py +7 -0
- package/assets/skills/office-presentations/container_tools/native_quantitative_chart_gate.py +7 -0
- package/assets/skills/office-presentations/container_tools/native_table_arithmetic_gate.py +7 -0
- package/assets/skills/office-presentations/container_tools/ooxml_helpers.py +7 -0
- package/assets/skills/office-presentations/container_tools/preview_render.py +7 -0
- package/assets/skills/office-presentations/container_tools/render_slides.py +7 -0
- package/assets/skills/office-presentations/container_tools/replace_text.py +7 -0
- package/assets/skills/office-presentations/container_tools/template_source_fidelity_gate.py +7 -0
- package/assets/skills/office-presentations/template_following_scripts/check_template_fidelity.py +7 -0
- package/assets/skills/office-presentations/template_following_scripts/inspect_template_deck.py +7 -0
- package/assets/skills/office-presentations/template_following_scripts/make_contact_sheet.py +7 -0
- package/assets/skills/office-presentations/template_following_scripts/prepare_template_starter_deck.py +7 -0
- package/assets/skills/office-presentations/template_following_scripts/validate_template_plan.py +7 -0
- package/assets/skills/office-spreadsheets/container_tools/add_chart.py +7 -0
- package/assets/skills/office-spreadsheets/container_tools/add_image.py +7 -0
- package/assets/skills/office-spreadsheets/container_tools/append_rows.py +7 -0
- package/assets/skills/office-spreadsheets/container_tools/diff_workbooks.py +7 -0
- package/assets/skills/office-spreadsheets/container_tools/inspect_workbook.py +7 -0
- package/assets/skills/office-spreadsheets/container_tools/recalc_gate.py +19 -7
- package/assets/skills/office-spreadsheets/container_tools/set_cells.py +7 -0
- package/assets/skills/office-spreadsheets/container_tools/sheet_ops.py +7 -0
- package/assets/skills/office-spreadsheets/container_tools/style_cells.py +7 -0
- package/assets/skills/shared/check_runtime.py +7 -0
- package/dist/index.js +14 -7
- package/package.json +1 -1
- package/python/office_cli.py +42 -8
|
@@ -13,6 +13,13 @@ import json
|
|
|
13
13
|
import sys
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
|
|
16
|
+
# Windows consoles default to a legacy code page; every payload is UTF-8 JSON.
|
|
17
|
+
for _stream in (sys.stdout, sys.stderr):
|
|
18
|
+
try:
|
|
19
|
+
_stream.reconfigure(encoding="utf-8", errors="replace")
|
|
20
|
+
except (AttributeError, ValueError):
|
|
21
|
+
pass
|
|
22
|
+
|
|
16
23
|
try:
|
|
17
24
|
from PIL import Image, ImageDraw, ImageFont
|
|
18
25
|
except ImportError: # pragma: no cover - whitelist dependency, but fail clearly
|
package/assets/skills/office-presentations/container_tools/inspect_presentation_layout_geometry.py
CHANGED
|
@@ -42,6 +42,13 @@ from ooxml_helpers import ( # noqa: E402
|
|
|
42
42
|
text_of,
|
|
43
43
|
)
|
|
44
44
|
|
|
45
|
+
# Windows consoles default to a legacy code page; every payload is UTF-8 JSON.
|
|
46
|
+
for _stream in (sys.stdout, sys.stderr):
|
|
47
|
+
try:
|
|
48
|
+
_stream.reconfigure(encoding="utf-8", errors="replace")
|
|
49
|
+
except (AttributeError, ValueError):
|
|
50
|
+
pass
|
|
51
|
+
|
|
45
52
|
TITLE_OUTSIDE_MARGIN_IN = 0.0
|
|
46
53
|
DEFAULT_TITLE_FLOOR_PT = 28.0
|
|
47
54
|
DEFAULT_BODY_FLOOR_PT = 14.0
|
package/assets/skills/office-presentations/container_tools/inspect_presentation_package_integrity.py
CHANGED
|
@@ -16,6 +16,13 @@ import zipfile
|
|
|
16
16
|
from pathlib import Path
|
|
17
17
|
from xml.etree import ElementTree
|
|
18
18
|
|
|
19
|
+
# Windows consoles default to a legacy code page; every payload is UTF-8 JSON.
|
|
20
|
+
for _stream in (sys.stdout, sys.stderr):
|
|
21
|
+
try:
|
|
22
|
+
_stream.reconfigure(encoding="utf-8", errors="replace")
|
|
23
|
+
except (AttributeError, ValueError):
|
|
24
|
+
pass
|
|
25
|
+
|
|
19
26
|
REL_NS = "{http://schemas.openxmlformats.org/package/2006/relationships}"
|
|
20
27
|
CT_NS = "{http://schemas.openxmlformats.org/package/2006/content-types}"
|
|
21
28
|
P_NS = "{http://schemas.openxmlformats.org/presentationml/2006/main}"
|
|
@@ -20,6 +20,13 @@ import zipfile
|
|
|
20
20
|
from pathlib import Path
|
|
21
21
|
from xml.etree import ElementTree
|
|
22
22
|
|
|
23
|
+
# Windows consoles default to a legacy code page; every payload is UTF-8 JSON.
|
|
24
|
+
for _stream in (sys.stdout, sys.stderr):
|
|
25
|
+
try:
|
|
26
|
+
_stream.reconfigure(encoding="utf-8", errors="replace")
|
|
27
|
+
except (AttributeError, ValueError):
|
|
28
|
+
pass
|
|
29
|
+
|
|
23
30
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
24
31
|
|
|
25
32
|
C_NS = "{http://schemas.openxmlformats.org/drawingml/2006/chart}"
|
package/assets/skills/office-presentations/container_tools/native_quantitative_chart_gate.py
CHANGED
|
@@ -22,6 +22,13 @@ from decimal import Decimal, InvalidOperation
|
|
|
22
22
|
from pathlib import Path
|
|
23
23
|
from xml.etree import ElementTree
|
|
24
24
|
|
|
25
|
+
# Windows consoles default to a legacy code page; every payload is UTF-8 JSON.
|
|
26
|
+
for _stream in (sys.stdout, sys.stderr):
|
|
27
|
+
try:
|
|
28
|
+
_stream.reconfigure(encoding="utf-8", errors="replace")
|
|
29
|
+
except (AttributeError, ValueError):
|
|
30
|
+
pass
|
|
31
|
+
|
|
25
32
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
26
33
|
|
|
27
34
|
C_NS = "{http://schemas.openxmlformats.org/drawingml/2006/chart}"
|
|
@@ -24,6 +24,13 @@ import sys
|
|
|
24
24
|
from decimal import Decimal, InvalidOperation
|
|
25
25
|
from pathlib import Path
|
|
26
26
|
|
|
27
|
+
# Windows consoles default to a legacy code page; every payload is UTF-8 JSON.
|
|
28
|
+
for _stream in (sys.stdout, sys.stderr):
|
|
29
|
+
try:
|
|
30
|
+
_stream.reconfigure(encoding="utf-8", errors="replace")
|
|
31
|
+
except (AttributeError, ValueError):
|
|
32
|
+
pass
|
|
33
|
+
|
|
27
34
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
28
35
|
|
|
29
36
|
TOTAL_PATTERN = re.compile(r"\btotal\b", re.IGNORECASE)
|
|
@@ -23,6 +23,13 @@ import sys
|
|
|
23
23
|
from pathlib import Path
|
|
24
24
|
from typing import Any, Iterable, Iterator
|
|
25
25
|
|
|
26
|
+
# Windows consoles default to a legacy code page; every payload is UTF-8 JSON.
|
|
27
|
+
for _stream in (sys.stdout, sys.stderr):
|
|
28
|
+
try:
|
|
29
|
+
_stream.reconfigure(encoding="utf-8", errors="replace")
|
|
30
|
+
except (AttributeError, ValueError):
|
|
31
|
+
pass
|
|
32
|
+
|
|
26
33
|
EMU_PER_INCH = 914400
|
|
27
34
|
|
|
28
35
|
# Anything that renders about a full em wide and wraps mid-token.
|
|
@@ -48,6 +48,13 @@ from ooxml_helpers import ( # noqa: E402
|
|
|
48
48
|
WIDE_CHAR,
|
|
49
49
|
)
|
|
50
50
|
|
|
51
|
+
# Windows consoles default to a legacy code page; every payload is UTF-8 JSON.
|
|
52
|
+
for _stream in (sys.stdout, sys.stderr):
|
|
53
|
+
try:
|
|
54
|
+
_stream.reconfigure(encoding="utf-8", errors="replace")
|
|
55
|
+
except (AttributeError, ValueError):
|
|
56
|
+
pass
|
|
57
|
+
|
|
51
58
|
INK = (0.13, 0.15, 0.18)
|
|
52
59
|
MUTED = (0.42, 0.45, 0.50)
|
|
53
60
|
RULE = (0.80, 0.82, 0.85)
|
|
@@ -23,6 +23,13 @@ import sys
|
|
|
23
23
|
import tempfile
|
|
24
24
|
from pathlib import Path
|
|
25
25
|
|
|
26
|
+
# Windows consoles default to a legacy code page; every payload is UTF-8 JSON.
|
|
27
|
+
for _stream in (sys.stdout, sys.stderr):
|
|
28
|
+
try:
|
|
29
|
+
_stream.reconfigure(encoding="utf-8", errors="replace")
|
|
30
|
+
except (AttributeError, ValueError):
|
|
31
|
+
pass
|
|
32
|
+
|
|
26
33
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
27
34
|
|
|
28
35
|
|
|
@@ -26,6 +26,13 @@ import json
|
|
|
26
26
|
import sys
|
|
27
27
|
from pathlib import Path
|
|
28
28
|
|
|
29
|
+
# Windows consoles default to a legacy code page; every payload is UTF-8 JSON.
|
|
30
|
+
for _stream in (sys.stdout, sys.stderr):
|
|
31
|
+
try:
|
|
32
|
+
_stream.reconfigure(encoding="utf-8", errors="replace")
|
|
33
|
+
except (AttributeError, ValueError):
|
|
34
|
+
pass
|
|
35
|
+
|
|
29
36
|
|
|
30
37
|
def _emit(payload: dict, code: int = 0) -> int:
|
|
31
38
|
print(json.dumps(payload, ensure_ascii=False, indent=2))
|
|
@@ -30,6 +30,13 @@ sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
|
30
30
|
|
|
31
31
|
from ooxml_helpers import box, is_picture, slide_size_in # noqa: E402
|
|
32
32
|
|
|
33
|
+
# Windows consoles default to a legacy code page; every payload is UTF-8 JSON.
|
|
34
|
+
for _stream in (sys.stdout, sys.stderr):
|
|
35
|
+
try:
|
|
36
|
+
_stream.reconfigure(encoding="utf-8", errors="replace")
|
|
37
|
+
except (AttributeError, ValueError):
|
|
38
|
+
pass
|
|
39
|
+
|
|
33
40
|
EMU_PER_INCH = 914400
|
|
34
41
|
DEFAULT_TOLERANCE_EMU = 9144 # 0.01 in
|
|
35
42
|
|
package/assets/skills/office-presentations/template_following_scripts/check_template_fidelity.py
CHANGED
|
@@ -20,6 +20,13 @@ sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "container_tools
|
|
|
20
20
|
|
|
21
21
|
from ooxml_helpers import box, has_visible_text, is_picture, overlap_ratio, text_of # noqa: E402
|
|
22
22
|
|
|
23
|
+
# Windows consoles default to a legacy code page; every payload is UTF-8 JSON.
|
|
24
|
+
for _stream in (sys.stdout, sys.stderr):
|
|
25
|
+
try:
|
|
26
|
+
_stream.reconfigure(encoding="utf-8", errors="replace")
|
|
27
|
+
except (AttributeError, ValueError):
|
|
28
|
+
pass
|
|
29
|
+
|
|
23
30
|
PLACEHOLDER_PATTERNS = [
|
|
24
31
|
re.compile(pattern, re.IGNORECASE)
|
|
25
32
|
for pattern in (
|
package/assets/skills/office-presentations/template_following_scripts/inspect_template_deck.py
CHANGED
|
@@ -23,6 +23,13 @@ sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "container_tools
|
|
|
23
23
|
|
|
24
24
|
from ooxml_helpers import box, has_visible_text, is_chart, is_picture, is_table, placeholder_kind, slide_size_in, text_of # noqa: E402
|
|
25
25
|
|
|
26
|
+
# Windows consoles default to a legacy code page; every payload is UTF-8 JSON.
|
|
27
|
+
for _stream in (sys.stdout, sys.stderr):
|
|
28
|
+
try:
|
|
29
|
+
_stream.reconfigure(encoding="utf-8", errors="replace")
|
|
30
|
+
except (AttributeError, ValueError):
|
|
31
|
+
pass
|
|
32
|
+
|
|
26
33
|
|
|
27
34
|
def _theme(presentation) -> dict:
|
|
28
35
|
fonts: set[str] = set()
|
|
@@ -15,6 +15,13 @@ import math
|
|
|
15
15
|
import sys
|
|
16
16
|
from pathlib import Path
|
|
17
17
|
|
|
18
|
+
# Windows consoles default to a legacy code page; every payload is UTF-8 JSON.
|
|
19
|
+
for _stream in (sys.stdout, sys.stderr):
|
|
20
|
+
try:
|
|
21
|
+
_stream.reconfigure(encoding="utf-8", errors="replace")
|
|
22
|
+
except (AttributeError, ValueError):
|
|
23
|
+
pass
|
|
24
|
+
|
|
18
25
|
sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "container_tools"))
|
|
19
26
|
|
|
20
27
|
|
|
@@ -23,6 +23,13 @@ sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "container_tools
|
|
|
23
23
|
|
|
24
24
|
from ooxml_helpers import box, placeholder_kind, slide_size_in # noqa: E402
|
|
25
25
|
|
|
26
|
+
# Windows consoles default to a legacy code page; every payload is UTF-8 JSON.
|
|
27
|
+
for _stream in (sys.stdout, sys.stderr):
|
|
28
|
+
try:
|
|
29
|
+
_stream.reconfigure(encoding="utf-8", errors="replace")
|
|
30
|
+
except (AttributeError, ValueError):
|
|
31
|
+
pass
|
|
32
|
+
|
|
26
33
|
PLACEHOLDER_ALIASES = {
|
|
27
34
|
"title": ("TITLE", "CENTER_TITLE"),
|
|
28
35
|
"subtitle": ("SUBTITLE",),
|
package/assets/skills/office-presentations/template_following_scripts/validate_template_plan.py
CHANGED
|
@@ -26,6 +26,13 @@ import json
|
|
|
26
26
|
import sys
|
|
27
27
|
from pathlib import Path
|
|
28
28
|
|
|
29
|
+
# Windows consoles default to a legacy code page; every payload is UTF-8 JSON.
|
|
30
|
+
for _stream in (sys.stdout, sys.stderr):
|
|
31
|
+
try:
|
|
32
|
+
_stream.reconfigure(encoding="utf-8", errors="replace")
|
|
33
|
+
except (AttributeError, ValueError):
|
|
34
|
+
pass
|
|
35
|
+
|
|
29
36
|
sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "container_tools"))
|
|
30
37
|
|
|
31
38
|
ALLOWED_ACTIONS = {"rewrite", "rewrite-and-reposition", "replace", "delete", "keep", "fill-placeholder"}
|
|
@@ -35,6 +35,13 @@ import json
|
|
|
35
35
|
import sys
|
|
36
36
|
from pathlib import Path
|
|
37
37
|
|
|
38
|
+
# Windows consoles default to a legacy code page; every payload is UTF-8 JSON.
|
|
39
|
+
for _stream in (sys.stdout, sys.stderr):
|
|
40
|
+
try:
|
|
41
|
+
_stream.reconfigure(encoding="utf-8", errors="replace")
|
|
42
|
+
except (AttributeError, ValueError):
|
|
43
|
+
pass
|
|
44
|
+
|
|
38
45
|
if hasattr(sys.stdout, "reconfigure"):
|
|
39
46
|
sys.stdout.reconfigure(errors="replace")
|
|
40
47
|
|
|
@@ -23,6 +23,13 @@ import json
|
|
|
23
23
|
import sys
|
|
24
24
|
from pathlib import Path
|
|
25
25
|
|
|
26
|
+
# Windows consoles default to a legacy code page; every payload is UTF-8 JSON.
|
|
27
|
+
for _stream in (sys.stdout, sys.stderr):
|
|
28
|
+
try:
|
|
29
|
+
_stream.reconfigure(encoding="utf-8", errors="replace")
|
|
30
|
+
except (AttributeError, ValueError):
|
|
31
|
+
pass
|
|
32
|
+
|
|
26
33
|
if hasattr(sys.stdout, "reconfigure"):
|
|
27
34
|
sys.stdout.reconfigure(errors="replace")
|
|
28
35
|
|
|
@@ -29,6 +29,13 @@ import json
|
|
|
29
29
|
import sys
|
|
30
30
|
from pathlib import Path
|
|
31
31
|
|
|
32
|
+
# Windows consoles default to a legacy code page; every payload is UTF-8 JSON.
|
|
33
|
+
for _stream in (sys.stdout, sys.stderr):
|
|
34
|
+
try:
|
|
35
|
+
_stream.reconfigure(encoding="utf-8", errors="replace")
|
|
36
|
+
except (AttributeError, ValueError):
|
|
37
|
+
pass
|
|
38
|
+
|
|
32
39
|
if hasattr(sys.stdout, "reconfigure"):
|
|
33
40
|
sys.stdout.reconfigure(errors="replace")
|
|
34
41
|
|
|
@@ -21,6 +21,13 @@ import json
|
|
|
21
21
|
import sys
|
|
22
22
|
from pathlib import Path
|
|
23
23
|
|
|
24
|
+
# Windows consoles default to a legacy code page; every payload is UTF-8 JSON.
|
|
25
|
+
for _stream in (sys.stdout, sys.stderr):
|
|
26
|
+
try:
|
|
27
|
+
_stream.reconfigure(encoding="utf-8", errors="replace")
|
|
28
|
+
except (AttributeError, ValueError):
|
|
29
|
+
pass
|
|
30
|
+
|
|
24
31
|
if hasattr(sys.stdout, "reconfigure"):
|
|
25
32
|
# Legacy console codepages (cp936, cp1252) must not abort a report.
|
|
26
33
|
sys.stdout.reconfigure(errors="replace")
|
|
@@ -22,6 +22,13 @@ import sys
|
|
|
22
22
|
import zipfile
|
|
23
23
|
from pathlib import Path
|
|
24
24
|
|
|
25
|
+
# Windows consoles default to a legacy code page; every payload is UTF-8 JSON.
|
|
26
|
+
for _stream in (sys.stdout, sys.stderr):
|
|
27
|
+
try:
|
|
28
|
+
_stream.reconfigure(encoding="utf-8", errors="replace")
|
|
29
|
+
except (AttributeError, ValueError):
|
|
30
|
+
pass
|
|
31
|
+
|
|
25
32
|
if hasattr(sys.stdout, "reconfigure"):
|
|
26
33
|
# Legacy console codepages (cp936, cp1252) must not abort a report.
|
|
27
34
|
sys.stdout.reconfigure(errors="replace")
|
|
@@ -28,6 +28,13 @@ import subprocess
|
|
|
28
28
|
import sys
|
|
29
29
|
from pathlib import Path
|
|
30
30
|
|
|
31
|
+
# Windows consoles default to a legacy code page; every payload is UTF-8 JSON.
|
|
32
|
+
for _stream in (sys.stdout, sys.stderr):
|
|
33
|
+
try:
|
|
34
|
+
_stream.reconfigure(encoding="utf-8", errors="replace")
|
|
35
|
+
except (AttributeError, ValueError):
|
|
36
|
+
pass
|
|
37
|
+
|
|
31
38
|
if hasattr(sys.stdout, "reconfigure"):
|
|
32
39
|
# Legacy console codepages (cp936, cp1252) must not abort a report.
|
|
33
40
|
sys.stdout.reconfigure(errors="replace")
|
|
@@ -116,23 +123,28 @@ def _referenced_sheets(formula: str) -> list[str]:
|
|
|
116
123
|
return found
|
|
117
124
|
|
|
118
125
|
|
|
119
|
-
def _recalculate(python: str, cli: Path, source: Path, out: Path) -> tuple[bool, str | None]:
|
|
126
|
+
def _recalculate(python: str, cli: Path, source: Path, out: Path) -> tuple[bool, str | None, str | None]:
|
|
120
127
|
command = [python, str(cli), "xlsx", "recalc", "--input", str(source), "--out", str(out)]
|
|
121
128
|
try:
|
|
122
129
|
completed = subprocess.run(command, capture_output=True, text=True, timeout=300)
|
|
123
130
|
except FileNotFoundError as error:
|
|
124
|
-
return False, f"cannot start the helper CLI: {error}"
|
|
131
|
+
return False, f"cannot start the helper CLI: {error}", None
|
|
125
132
|
except subprocess.TimeoutExpired:
|
|
126
|
-
return False, "recalculation timed out after 300 seconds"
|
|
133
|
+
return False, "recalculation timed out after 300 seconds", None
|
|
127
134
|
if completed.returncode == 0 and out.is_file():
|
|
128
|
-
|
|
135
|
+
renderer = None
|
|
136
|
+
try:
|
|
137
|
+
renderer = json.loads(completed.stdout).get("renderer")
|
|
138
|
+
except (json.JSONDecodeError, AttributeError):
|
|
139
|
+
renderer = None
|
|
140
|
+
return True, None, renderer
|
|
129
141
|
message = (completed.stderr or completed.stdout or "").strip()
|
|
130
142
|
try:
|
|
131
143
|
parsed = json.loads(message)
|
|
132
144
|
message = str(parsed.get("error", message))
|
|
133
145
|
except json.JSONDecodeError:
|
|
134
146
|
pass
|
|
135
|
-
return False, message or f"helper CLI exited with code {completed.returncode}"
|
|
147
|
+
return False, message or f"helper CLI exited with code {completed.returncode}", None
|
|
136
148
|
|
|
137
149
|
|
|
138
150
|
def _ignored(entries: str | None) -> dict[str, set[str]]:
|
|
@@ -176,10 +188,10 @@ def main() -> int:
|
|
|
176
188
|
if not cli.is_file():
|
|
177
189
|
recalc_error = f"helper CLI not found: {cli}; pass --cli or use --no-recalc"
|
|
178
190
|
else:
|
|
179
|
-
recalculated, recalc_error = _recalculate(args.python, cli, source, out)
|
|
191
|
+
recalculated, recalc_error, renderer = _recalculate(args.python, cli, source, out)
|
|
180
192
|
if recalculated:
|
|
181
193
|
checked = out
|
|
182
|
-
engine = "
|
|
194
|
+
engine = renderer or "recalculation engine"
|
|
183
195
|
|
|
184
196
|
try:
|
|
185
197
|
cached = _cached_values(checked, cells) if cells else {}
|
|
@@ -26,6 +26,13 @@ import json
|
|
|
26
26
|
import sys
|
|
27
27
|
from pathlib import Path
|
|
28
28
|
|
|
29
|
+
# Windows consoles default to a legacy code page; every payload is UTF-8 JSON.
|
|
30
|
+
for _stream in (sys.stdout, sys.stderr):
|
|
31
|
+
try:
|
|
32
|
+
_stream.reconfigure(encoding="utf-8", errors="replace")
|
|
33
|
+
except (AttributeError, ValueError):
|
|
34
|
+
pass
|
|
35
|
+
|
|
29
36
|
if hasattr(sys.stdout, "reconfigure"):
|
|
30
37
|
sys.stdout.reconfigure(errors="replace")
|
|
31
38
|
|
|
@@ -24,6 +24,13 @@ import json
|
|
|
24
24
|
import sys
|
|
25
25
|
from pathlib import Path
|
|
26
26
|
|
|
27
|
+
# Windows consoles default to a legacy code page; every payload is UTF-8 JSON.
|
|
28
|
+
for _stream in (sys.stdout, sys.stderr):
|
|
29
|
+
try:
|
|
30
|
+
_stream.reconfigure(encoding="utf-8", errors="replace")
|
|
31
|
+
except (AttributeError, ValueError):
|
|
32
|
+
pass
|
|
33
|
+
|
|
27
34
|
if hasattr(sys.stdout, "reconfigure"):
|
|
28
35
|
sys.stdout.reconfigure(errors="replace")
|
|
29
36
|
|
|
@@ -30,6 +30,13 @@ import json
|
|
|
30
30
|
import sys
|
|
31
31
|
from pathlib import Path
|
|
32
32
|
|
|
33
|
+
# Windows consoles default to a legacy code page; every payload is UTF-8 JSON.
|
|
34
|
+
for _stream in (sys.stdout, sys.stderr):
|
|
35
|
+
try:
|
|
36
|
+
_stream.reconfigure(encoding="utf-8", errors="replace")
|
|
37
|
+
except (AttributeError, ValueError):
|
|
38
|
+
pass
|
|
39
|
+
|
|
33
40
|
if hasattr(sys.stdout, "reconfigure"):
|
|
34
41
|
sys.stdout.reconfigure(errors="replace")
|
|
35
42
|
|
|
@@ -24,6 +24,13 @@ import subprocess
|
|
|
24
24
|
import sys
|
|
25
25
|
from pathlib import Path
|
|
26
26
|
|
|
27
|
+
# Windows consoles default to a legacy code page; every payload is UTF-8 JSON.
|
|
28
|
+
for _stream in (sys.stdout, sys.stderr):
|
|
29
|
+
try:
|
|
30
|
+
_stream.reconfigure(encoding="utf-8", errors="replace")
|
|
31
|
+
except (AttributeError, ValueError):
|
|
32
|
+
pass
|
|
33
|
+
|
|
27
34
|
DEFAULT_ROOT = Path(__file__).resolve().parents[3]
|
|
28
35
|
RENDERER_HINT = (
|
|
29
36
|
"docx/pptx/xlsx rendering needs LibreOffice, Microsoft Office, or WPS Office; "
|
package/dist/index.js
CHANGED
|
@@ -324,20 +324,27 @@ function runPostinstall() {
|
|
|
324
324
|
}
|
|
325
325
|
let cache;
|
|
326
326
|
let healing;
|
|
327
|
+
/** A cached `ok` is only worth reusing while its interpreter still exists on disk. */
|
|
328
|
+
function runtimeAlive(status) {
|
|
329
|
+
return status.state !== "ok" || status.python != null && isFile(status.python.path);
|
|
330
|
+
}
|
|
327
331
|
/**
|
|
328
332
|
* Resolve a runtime that is proven to work, self-healing via postinstall.mjs when it
|
|
329
333
|
* is missing or incomplete. Never returns a bare interpreter without dependencies.
|
|
334
|
+
* Pass `force` for an explicit status check that must not trust the process cache.
|
|
330
335
|
*/
|
|
331
|
-
function ensureRuntime(configFile = officePaths().configFile) {
|
|
332
|
-
if (
|
|
336
|
+
function ensureRuntime(configFile = officePaths().configFile, force = false) {
|
|
337
|
+
if (!force && cache != null) {
|
|
338
|
+
if (cache.status.state === "ok" ? runtimeAlive(cache.status) : Date.now() - cache.at < FAILURE_RETRY_DELAY) return Promise.resolve(cache.status);
|
|
339
|
+
}
|
|
333
340
|
healing ??= (async () => {
|
|
334
|
-
const
|
|
335
|
-
if (
|
|
341
|
+
const probed = await inspectRuntime(configFile);
|
|
342
|
+
if (probed.state === "ok") {
|
|
336
343
|
cache = {
|
|
337
|
-
status:
|
|
344
|
+
status: probed,
|
|
338
345
|
at: Date.now()
|
|
339
346
|
};
|
|
340
|
-
return
|
|
347
|
+
return probed;
|
|
341
348
|
}
|
|
342
349
|
const healError = await runPostinstall();
|
|
343
350
|
const status = {
|
|
@@ -519,7 +526,7 @@ function apply(ctx) {
|
|
|
519
526
|
},
|
|
520
527
|
timeoutMs: 6e5,
|
|
521
528
|
async execute() {
|
|
522
|
-
const runtime = await ensureRuntime(paths.configFile);
|
|
529
|
+
const runtime = await ensureRuntime(paths.configFile, true);
|
|
523
530
|
const report = {
|
|
524
531
|
plugin: {
|
|
525
532
|
name,
|
package/package.json
CHANGED
package/python/office_cli.py
CHANGED
|
@@ -21,7 +21,18 @@ import tempfile
|
|
|
21
21
|
from pathlib import Path
|
|
22
22
|
from typing import Any, Iterable
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
PACKAGE_ROOT = Path(__file__).resolve().parents[1]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _package_version() -> str:
|
|
28
|
+
try:
|
|
29
|
+
data = json.loads((PACKAGE_ROOT / "package.json").read_text(encoding="utf-8"))
|
|
30
|
+
return str(data["version"])
|
|
31
|
+
except (OSError, ValueError, KeyError, TypeError):
|
|
32
|
+
return "0.0.0"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
VERSION = _package_version()
|
|
25
36
|
|
|
26
37
|
# Windows consoles default to a legacy code page; every payload is UTF-8 JSON.
|
|
27
38
|
for _stream in (sys.stdout, sys.stderr):
|
|
@@ -339,6 +350,16 @@ def _docx_add_equation(paragraph, latex: str) -> bool:
|
|
|
339
350
|
return False
|
|
340
351
|
|
|
341
352
|
|
|
353
|
+
def _docx_mark_header_row(row) -> None:
|
|
354
|
+
"""Repeat this row across pages and mark it as a header for assistive tech."""
|
|
355
|
+
from docx.oxml.ns import qn
|
|
356
|
+
|
|
357
|
+
properties = row._tr.get_or_add_trPr()
|
|
358
|
+
if properties.find(qn("w:tblHeader")) is None:
|
|
359
|
+
header = properties.makeelement(qn("w:tblHeader"), {})
|
|
360
|
+
properties.append(header)
|
|
361
|
+
|
|
362
|
+
|
|
342
363
|
def _docx_add_block(document, block: dict[str, Any], counters: dict[str, int]) -> None:
|
|
343
364
|
from docx.shared import Inches, Pt
|
|
344
365
|
|
|
@@ -391,6 +412,7 @@ def _docx_add_block(document, block: dict[str, Any], counters: dict[str, int]) -
|
|
|
391
412
|
for paragraph in cells[index].paragraphs:
|
|
392
413
|
for run in paragraph.runs:
|
|
393
414
|
run.bold = True
|
|
415
|
+
_docx_mark_header_row(table.rows[0])
|
|
394
416
|
for row in rows:
|
|
395
417
|
cells = table.add_row().cells
|
|
396
418
|
for index, value in enumerate(row):
|
|
@@ -459,6 +481,10 @@ def docx_create(spec: dict[str, Any], out: Path) -> dict[str, Any]:
|
|
|
459
481
|
if spec.get("author") or spec.get("title"):
|
|
460
482
|
document.core_properties.title = str(spec.get("title", ""))
|
|
461
483
|
document.core_properties.author = str(spec.get("author", ""))
|
|
484
|
+
# A fresh python-docx document carries the library name in comments; a
|
|
485
|
+
# delivered file should not advertise the tool that wrote it.
|
|
486
|
+
document.core_properties.comments = str(spec.get("comments", ""))
|
|
487
|
+
document.core_properties.last_modified_by = str(spec.get("author", ""))
|
|
462
488
|
if spec.get("header"):
|
|
463
489
|
document.sections[0].header.paragraphs[0].text = str(spec["header"])
|
|
464
490
|
if spec.get("footer"):
|
|
@@ -659,17 +685,21 @@ def pdf_create(spec: dict[str, Any], out: Path) -> dict[str, Any]:
|
|
|
659
685
|
author=str(spec.get("author", "")),
|
|
660
686
|
)
|
|
661
687
|
counters = {key: 0 for key in ("headings", "paragraphs", "list_items", "tables", "images")}
|
|
688
|
+
following: list[dict[str, Any]] = []
|
|
689
|
+
for page in spec.get("pages", []):
|
|
690
|
+
if isinstance(page, dict):
|
|
691
|
+
following.extend(page.get("blocks", []))
|
|
692
|
+
if page.get("break", True):
|
|
693
|
+
following.append({"type": "page_break"})
|
|
694
|
+
following.extend(spec.get("blocks", []))
|
|
695
|
+
kinds = {str(block.get("type", "")).lower() for block in following if isinstance(block, dict)}
|
|
662
696
|
blocks: list[dict[str, Any]] = []
|
|
663
|
-
|
|
697
|
+
# `title` is metadata; only draw it when the blocks do not already carry one.
|
|
698
|
+
if spec.get("title") and "title" not in kinds:
|
|
664
699
|
blocks.append({"type": "title", "text": spec["title"]})
|
|
665
700
|
if spec.get("subtitle"):
|
|
666
701
|
blocks.append({"type": "paragraph", "text": spec["subtitle"]})
|
|
667
|
-
|
|
668
|
-
if isinstance(page, dict):
|
|
669
|
-
blocks.extend(page.get("blocks", []))
|
|
670
|
-
if page.get("break", True):
|
|
671
|
-
blocks.append({"type": "page_break"})
|
|
672
|
-
blocks.extend(spec.get("blocks", []))
|
|
702
|
+
blocks.extend(following)
|
|
673
703
|
document.build(_pdf_flowables(blocks, font, counters))
|
|
674
704
|
return {"ok": True, "kind": "pdf", "command": "create", "output": str(out), "font": font, "counts": counters}
|
|
675
705
|
|
|
@@ -849,6 +879,10 @@ def pptx_create(spec: dict[str, Any], out: Path) -> dict[str, Any]:
|
|
|
849
879
|
prs.slide_height = int(7.5 * 914400)
|
|
850
880
|
prs.core_properties.title = str(spec.get("title", ""))
|
|
851
881
|
prs.core_properties.author = str(spec.get("author", "DSH Office Suite"))
|
|
882
|
+
# python-pptx leaves its own name in the properties; a delivered deck should
|
|
883
|
+
# carry the author's, not the library's.
|
|
884
|
+
prs.core_properties.comments = str(spec.get("comments", ""))
|
|
885
|
+
prs.core_properties.last_modified_by = str(spec.get("author", "DSH Office Suite"))
|
|
852
886
|
counters = {key: 0 for key in ("slides", "bullets", "tables", "charts", "images")}
|
|
853
887
|
for slide_spec in spec.get("slides", []):
|
|
854
888
|
if isinstance(slide_spec, dict):
|