ckb/.agents/skills/clouddrift-docx/scripts/convert.py
2026-07-15 09:54:12 +02:00

112 lines
4.1 KiB
Python

#!/usr/bin/env python3
"""Convert a Markdown file into a Cloud Drift branded .docx and/or .pdf.
Usage:
python3 convert.py INPUT.md [--format docx|pdf|both] [--output PATH]
[--reference-doc PATH]
docx is produced by pandoc using the bundled Cloud Drift reference.docx
(fonts, colors, logo header, footer pagination). pdf is produced by asking
macOS Pages to open that docx and export it to PDF, so the PDF is pixel-for-
pixel the same branded layout, not a second independent template.
"""
import argparse
import shutil
import subprocess
import sys
from pathlib import Path
SKILL_DIR = Path(__file__).resolve().parent.parent
REFERENCE_DOC = SKILL_DIR / "assets" / "clouddrift-reference.docx"
def convert_to_docx(input_md: Path, output_docx: Path, reference_doc: Path):
if shutil.which("pandoc") is None:
sys.exit("pandoc is not installed or not on PATH.")
cmd = [
"pandoc",
str(input_md),
"-o",
str(output_docx),
f"--reference-doc={reference_doc}",
"--standalone",
]
subprocess.run(cmd, check=True)
print(f"Wrote {output_docx}")
def convert_docx_to_pdf(input_docx: Path, output_pdf: Path):
"""Use macOS Pages (via AppleScript) to export the docx to PDF, preserving
the exact branded layout produced by the reference.docx styles."""
if sys.platform != "darwin":
sys.exit("PDF export currently requires macOS (uses Pages via AppleScript).")
script = f'''
try
tell application "Pages"
set theDoc to open POSIX file "{input_docx.resolve()}"
delay 2
export theDoc to POSIX file "{output_pdf.resolve()}" as PDF
close theDoc saving no
end tell
return "SUCCESS"
on error errMsg number errNum
return "ERROR: " & errMsg & " (" & errNum & ")"
end try
'''
result = subprocess.run(["osascript", "-e", script], capture_output=True, text=True)
out = result.stdout.strip()
if out != "SUCCESS":
sys.exit(
"Pages PDF export failed: "
f"{out or result.stderr.strip()}\n"
"If this is the first run, macOS may need you to grant automation "
"permission for controlling Pages (System Settings > Privacy & "
"Security > Automation), or Pages may need a moment after "
"launching — try again."
)
print(f"Wrote {output_pdf}")
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("input", type=Path, help="Input Markdown file")
parser.add_argument(
"--format", choices=["docx", "pdf", "both"], default="docx",
help="Output format (default: docx)",
)
parser.add_argument(
"--output", type=Path, default=None,
help="Output path (without extension needed for --format both). "
"Defaults to the input filename next to the input file.",
)
parser.add_argument(
"--reference-doc", type=Path, default=REFERENCE_DOC,
help="Override the Cloud Drift reference.docx template",
)
args = parser.parse_args()
if not args.input.exists():
sys.exit(f"Input file not found: {args.input}")
if not args.reference_doc.exists():
sys.exit(f"Reference doc not found: {args.reference_doc}")
stem = args.output if args.output else args.input.with_suffix("")
docx_path = stem.with_suffix(".docx")
pdf_path = stem.with_suffix(".pdf")
if args.format in ("docx", "both"):
convert_to_docx(args.input, docx_path, args.reference_doc)
if args.format in ("pdf", "both"):
if not docx_path.exists():
convert_to_docx(args.input, docx_path, args.reference_doc)
convert_docx_to_pdf(docx_path, pdf_path)
if args.format == "pdf" and docx_path.exists() and args.output is None:
# pdf-only was requested and we only made the docx as an
# intermediate step; clean it up unless the caller named an
# explicit --output (in which case leave both, they may want it).
docx_path.unlink()
if __name__ == "__main__":
main()