Files
wehub-resource-sync 8ef9db2513
Deploy to GitHub Pages / deploy (push) Has been cancelled
Deploy to GitHub Pages / build (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:56:49 +08:00

184 lines
7.1 KiB
Python

#!/usr/bin/env python3
"""Unified PPTX preparation entry point for the /create-template workflow.
Reads OOXML directly via `pptx_to_svg` and writes a reusable reference workspace:
- `manifest.json` — single source of truth for slide size, theme colors, fonts,
asset inventory, and per-slide / per-layout / per-master metadata
- `native_structure.json` + `source_template.pptx` — source-structure facts and
a byte-identical analysis copy used to rebuild explicit SVG structure
- `summary.md` — short human-readable digest derived from manifest.json
- `assets/` — extracted reusable image assets
- `svg/` — primary view: by default the layered template view (every master
and layout in the deck rendered once each as `master_*.svg` /
`layout_*.svg`, slides contain only their own shapes, and an
`inheritance.json` describes the reuse graph)
- `svg-flat/` — companion view (default mode "both"): each `slide_NN.svg`
is self-contained — master/layout decoration is inlined — so opening any
one slide shows the full page like PowerPoint would
"""
from __future__ import annotations
import argparse
import json
from pathlib import Path
from console_encoding import configure_utf8_stdio
from template_import.manifest import build_manifest
from template_import.native_structure import write_native_structure_bundle
configure_utf8_stdio()
def parse_args() -> argparse.Namespace:
"""Build the CLI argument parser for the import entry point."""
parser = argparse.ArgumentParser(
description="Prepare a PPTX reference workspace for /create-template."
)
parser.add_argument("pptx_file", help="Path to the source .pptx file")
parser.add_argument(
"-o",
"--output",
help="Output directory (default: <pptx_stem>_template_import beside the source file)",
)
parser.add_argument(
"--skip-manifest",
action="store_true",
help=(
"Skip metadata, asset inventory, native structure contract, and "
"preserved source-package generation"
),
)
parser.add_argument(
"--manifest-only",
action="store_true",
help=(
"Only extract manifest.json + summary.md + reusable assets + the "
"native structure/source pair, without exporting slides to SVG"
),
)
parser.add_argument(
"--embed-images",
action="store_true",
help="Inline images as data: URIs instead of writing files to assets/",
)
parser.add_argument(
"--inheritance-mode",
choices=("both", "layered", "flat"),
default="both",
help=(
"How to render master/layout shapes for slide SVGs. "
"'both' (default): emit both views — svg/ holds the layered "
"renderings (template designers see master/layout/slide as "
"separate files plus svg/inheritance.json) and svg-flat/ holds "
"self-contained per-slide SVGs (each one renders correctly when "
"opened on its own). 'layered': only the svg/ tree, useful when "
"you don't need the flat view. 'flat': only self-contained slide "
"SVGs in svg/, the round-trip view used by svg_to_pptx."
),
)
return parser.parse_args()
def main() -> int:
"""CLI entry point: write the PPTX reference workspace to disk."""
args = parse_args()
pptx_path = Path(args.pptx_file).expanduser().resolve()
if not pptx_path.exists():
print(f"Error: file does not exist: {pptx_path}")
return 1
if pptx_path.suffix.lower() != ".pptx":
print(f"Error: expected a .pptx file, got: {pptx_path.name}")
return 1
output_dir = (
Path(args.output).expanduser().resolve()
if args.output
else pptx_path.with_name(f"{pptx_path.stem}_template_import")
)
output_dir.mkdir(parents=True, exist_ok=True)
if args.skip_manifest and args.manifest_only:
print("Error: --skip-manifest and --manifest-only cannot be used together")
return 1
manifest = None
native_structure = None
manifest_path = output_dir / "manifest.json"
if not args.skip_manifest:
try:
manifest = build_manifest(pptx_path, output_dir)
except (RuntimeError, OSError, ValueError) as exc:
print(f"Error: failed to extract PPTX metadata: {exc}")
return 1
manifest_path.write_text(
json.dumps(manifest, ensure_ascii=False, indent=2) + "\n",
encoding="utf-8",
)
try:
native_structure = write_native_structure_bundle(
pptx_path,
output_dir,
manifest,
)
except (OSError, ValueError) as exc:
print(f"Error: failed to write native structure bundle: {exc}")
return 1
if args.manifest_only:
print(f"Imported PPTX template source: {pptx_path.name}")
print(f"Output directory: {output_dir}")
if manifest is not None:
print(f"Manifest: {manifest_path.name}")
print("Native structure: native_structure.json")
print("Source package analysis copy: source_template.pptx")
print(
"Source structure assessment: "
f"{native_structure['strategy']['recommendedMode']}"
)
print("Template output mode: explicit SVG structure")
print("Summary: summary.md")
print(f"Assets exported: {len(manifest['assets']['allAssets'])}")
print(f"Common assets: {len(manifest['assets']['commonAssets'])}")
print(f"Slides analyzed: {len(manifest['slides'])}")
print(f"Layouts (unique): {len(manifest.get('layouts', []))}")
print(f"Masters (unique): {len(manifest.get('masters', []))}")
return 0
from pptx_to_svg import convert_pptx_to_svg
from pptx_to_svg.converter import ConvertOptions
options = ConvertOptions(
media_subdir="assets",
embed_images=args.embed_images,
keep_hidden=False,
inheritance_mode=args.inheritance_mode,
asset_name_map=manifest.get("assets", {}).get("assetMap", {}) if manifest else {},
)
result = convert_pptx_to_svg(pptx_path, output_dir, options)
total_bytes = sum(len(art.svg.encode("utf-8")) for art in result.slides)
print(f"Inheritance mode: {args.inheritance_mode}")
print(f"Exported SVG slides: {len(result.slides)}")
if args.inheritance_mode in {"layered", "both"}:
print(f"Exported masters: {len(result.masters)}")
print(f"Exported layouts: {len(result.layouts)}")
print("Inheritance graph: svg/inheritance.json")
if result.flat_slides:
print(f"Flat companion slides: {len(result.flat_slides)} (svg-flat/)")
print(f"SVG bytes (primary): {total_bytes}")
print(f"Output directory: {output_dir}")
if native_structure is not None:
print(
"Source structure assessment: "
f"{native_structure['strategy']['recommendedMode']}; "
"create-template rebuilds explicit SVG structure"
)
return 0
if __name__ == "__main__":
raise SystemExit(main())