PDFTranslator / pdf2zh /render /compiler.py
hoang.nguyen6
deploy
f66643d unverified
Raw
History Blame Contribute Delete
2.49 kB
from __future__ import annotations
import logging
import re
import subprocess
from pathlib import Path
logger = logging.getLogger(__name__)
_TYPST_LOCATION = re.compile(r"\.typ:(\d+):(\d+)")
class TypstCompileError(RuntimeError):
"""Typst compile failure carrying the full compiler stderr for diagnosis."""
def __init__(self, message: str, stderr: str = ""):
super().__init__(message)
self.stderr = stderr
def _source_context(source: str, stderr: str, radius: int = 2) -> str:
match = _TYPST_LOCATION.search(stderr)
if not match:
return ""
line_number = int(match.group(1))
lines = source.splitlines()
start = max(0, line_number - radius - 1)
end = min(len(lines), line_number + radius)
return "\n".join(f"{index + 1:>5} | {lines[index]}" for index in range(start, end))
def compile_typst(
source: str,
font_paths: list[str],
output_pdf: Path,
typst_bin: str = "typst",
work_dir: Path | None = None,
) -> Path:
"""Compile a Typst source string to PDF.
Args:
source: Complete Typst source code.
font_paths: Directories or file paths passed to --font-path.
output_pdf: Destination PDF path.
typst_bin: Path/name of the typst binary.
work_dir: Directory to write the intermediate .typ file (defaults to
output_pdf.parent).
Returns:
output_pdf path.
Raises:
TypstCompileError: If the typst process exits non-zero.
"""
work_dir = work_dir or output_pdf.parent
work_dir.mkdir(parents=True, exist_ok=True)
typ_path = work_dir / (output_pdf.stem + ".typ")
typ_path.write_text(source, encoding="utf-8")
cmd = [typst_bin, "compile"]
for fp in font_paths:
cmd += ["--font-path", fp]
cmd += [str(typ_path), str(output_pdf)]
logger.debug("typst: %s", " ".join(cmd))
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
stderr = result.stderr or ""
tail = "\n".join(stderr.splitlines()[-50:])
context = _source_context(source, stderr)
if context:
logger.error("Typst source near the failing markup:\n%s", context)
tail = f"{tail}\n\nTypst source context:\n{context}"
raise TypstCompileError(
f"typst compile failed (exit {result.returncode}):\n{tail}", stderr=stderr
)
logger.debug("typst compiled → %s", output_pdf)
return output_pdf