diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 00000000..3be4b2be --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,12 @@ +#!/bin/sh +set -eu + +echo "[pccx] running pre-commit strict Sphinx build..." + +if ! make strict; then + echo + echo "Sphinx strict build failed. Run 'make strict' locally to see warnings." + exit 1 +fi + +echo "[pccx] strict Sphinx build passed." diff --git a/.github/workflows/v002-book.yml b/.github/workflows/v002-book.yml new file mode 100644 index 00000000..a3bf0a88 --- /dev/null +++ b/.github/workflows/v002-book.yml @@ -0,0 +1,104 @@ +# PCCX(TM) — reusable AI accelerator project. +# SPDX-FileCopyrightText: 2026 Hyun Woo Kim +# SPDX-License-Identifier: Apache-2.0 + +name: v002-book + +on: + workflow_dispatch: + push: + branches: [ main ] + paths: + - "book/v002/**" + - "docs/v002/**" + - "_static/cover.tex" + - "_static/cover/**" + - "_static/colophon.tex" + - "conf.py" + - "conf_common.py" + - "requirements.txt" + - "tools/build_v002_book_pdf.sh" + - ".github/workflows/v002-book.yml" + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" + PDF_PATH: _build/v002-book/pdf/pccx-v002-book-preprint.pdf + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 40 + permissions: + contents: read + + steps: + - name: Check out docs repo + uses: actions/checkout@v4 + + - name: Clone v002 RTL source for literalincludes + env: + GH_TOKEN: ${{ secrets.PCCX_BOOKS_RELEASE_TOKEN }} + run: | + test -n "$GH_TOKEN" || { + echo "PCCX_BOOKS_RELEASE_TOKEN is required to read the private v002 source." >&2 + exit 1 + } + git clone --depth 1 "https://x-access-token:${GH_TOKEN}@github.com/pccx-internal/pccx-v002.git" codes/v002 + + - name: Install system dependencies + run: | + sudo apt-get update -qq + sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ + graphviz \ + latexmk \ + poppler-utils \ + texlive-xetex \ + texlive-fonts-recommended \ + texlive-fonts-extra \ + texlive-lang-korean \ + texlive-latex-extra \ + texlive-pictures \ + texlive-science + + - name: Install Sphinx dependencies + run: | + python -m pip install --upgrade pip + python -m pip install -r requirements.txt + + - name: Build private v002 book PDF + run: bash tools/build_v002_book_pdf.sh + + - name: Sanity check PDF + run: | + test -s "$PDF_PATH" + pages="$(pdfinfo "$PDF_PATH" | awk '/^Pages:/ {print $2}')" + echo "Built PDF has ${pages:-0} page(s)." + test "${pages:-0}" -ge 20 + python - "$PDF_PATH" <<'PY' + import subprocess + import sys + + pdf = sys.argv[1] + text = subprocess.check_output(["pdftotext", pdf, "-"], text=True, errors="ignore") + blocked = ["NVI" + "DIA", "In" + "tel", "in" + "tel", "reg" + "istered", chr(174)] + hits = [term for term in blocked if term in text] + if hits: + print("Blocked term found in extracted PDF text.", file=sys.stderr) + sys.exit(1) + PY + + - name: Create private release asset + env: + GH_TOKEN: ${{ secrets.PCCX_BOOKS_RELEASE_TOKEN }} + run: | + test -n "$GH_TOKEN" || { + echo "PCCX_BOOKS_RELEASE_TOKEN is required to publish the private book release." >&2 + exit 1 + } + stamp="$(date -u +%Y-%m-%dT%H%M%SZ)" + tag="v002-book-preprint-${stamp}" + gh release create "$tag" "$PDF_PATH" \ + --repo pccx-internal/pccx-books-private \ + --target artifact/v002-book-release-anchor \ + --title "v002 book preprint ${stamp}" \ + --notes "v002 book preprint ${stamp}" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f0c17933..c0ade3bb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,7 +15,15 @@ Before opening a pull request: 1. Keep changes focused and small. 2. Explain why the change is useful. -3. For documentation updates, verify that the affected page builds correctly. -4. If you are unsure, open an issue first. +3. Install the repository git hooks once: + + ```bash + git config core.hooksPath .githooks + ``` + +4. Run `make strict` before every commit. The local pre-commit hook runs the + same command and blocks the commit when Sphinx emits warnings or errors. +5. For documentation updates, verify that the affected page builds correctly. +6. If you are unsure, open an issue first. PCCX is currently research-oriented and evolves quickly, so design discussions are welcome. diff --git a/_static/colophon.tex b/_static/colophon.tex new file mode 100644 index 00000000..7323ebce --- /dev/null +++ b/_static/colophon.tex @@ -0,0 +1,21 @@ +\chapter*{Colophon} +\addcontentsline{toc}{chapter}{Colophon} +\thispagestyle{plain} + +\begin{tabular}{@{}p{0.28\linewidth}p{0.62\linewidth}@{}} +Title & \texttt{[[TITLE\_PLACEHOLDER]]} \\ +Subtitle & \texttt{[[SUBTITLE\_PLACEHOLDER]]} \\ +Author & \texttt{[[AUTHOR\_PLACEHOLDER]]} \\ +Publisher & Altifigence\texttrademark{} \\ +Representative & Hyun Woo Kim / 김현우 \\ +Business ID & 380-07-03687 \\ +Address & 22017 인천 연수구 랜드마크로 160 \\ +ISBN & \texttt{[[ISBN\_PLACEHOLDER]]} \\ +Build type & Private preprint PDF for ISBN review \\ +\end{tabular} + +\vspace{1.5cm} + +This PDF is a private publication-review artifact. It is not part of the +public docs.pccx.ai site build and must be distributed only through the +private PCCX books artifact repository. diff --git a/_static/cover.tex b/_static/cover.tex new file mode 100644 index 00000000..fbb96204 --- /dev/null +++ b/_static/cover.tex @@ -0,0 +1,41 @@ +\begin{titlepage} +\thispagestyle{empty} +\begingroup +\setlength{\parindent}{0pt} +\begin{tikzpicture}[remember picture,overlay] +\node[inner sep=0pt] at (current page.center) + {\includegraphics[height=\paperheight]{_static/cover/cover-art.jpg}}; + +\node[ + anchor=north west, + align=left, + inner sep=0pt, + text=black, + text width=0.76\paperwidth +] at ([xshift=14mm,yshift=-14mm]current page.north west) {% + {\pccxcovermono\bfseries\fontsize{20}{24}\selectfont [[TITLE\_PLACEHOLDER]]\par} + \vspace{3mm} + {\pccxcoverfont\bfseries\fontsize{12}{16}\selectfont [[SUBTITLE\_PLACEHOLDER]]\par} + \vspace{4mm} + {\pccxcoverfont\fontsize{9}{12}\selectfont Private Sphinx LaTeX preprint for ISBN review\par} +}; + +\node[ + anchor=south west, + align=left, + inner sep=0pt, + text=black, + text width=0.76\paperwidth +] at ([xshift=14mm,yshift=16mm]current page.south west) {% + {\pccxcoverfont\fontsize{10}{13}\selectfont [[AUTHOR\_PLACEHOLDER]]\par} + \vspace{1mm} + {\pccxcoverfont\fontsize{10}{13}\selectfont Altifigence\texttrademark{}\quad 2026\par} + \vspace{1mm} + {\pccxcovermono\fontsize{8}{11}\selectfont ISBN [[ISBN\_PLACEHOLDER]]\par} +}; +\end{tikzpicture} + +\null +\endgroup + +\end{titlepage} diff --git a/_static/cover/cover-art.jpg b/_static/cover/cover-art.jpg new file mode 100644 index 00000000..e5558f4c Binary files /dev/null and b/_static/cover/cover-art.jpg differ diff --git a/book/v002/index.rst b/book/v002/index.rst new file mode 100644 index 00000000..50345320 --- /dev/null +++ b/book/v002/index.rst @@ -0,0 +1,18 @@ +PCCX v002 Technical Architecture +================================ + +.. only:: html + + This document is the private Sphinx LaTeX build root for the v002 book. + +.. toctree:: + :maxdepth: 3 + :caption: Contents + + /docs/v002/index + +References +---------- + +.. bibliography:: + :all: diff --git a/conf.py b/conf.py index 017636b4..d674866b 100644 --- a/conf.py +++ b/conf.py @@ -73,3 +73,97 @@ bibtex_bibfiles = ["refs.bib"] sitemap_filename = "sitemap-en.xml" + + +def _has_sphinx_tag(name: str) -> bool: + try: + return tags.has(name) # type: ignore[name-defined] + except NameError: + return False + + +if not _has_sphinx_tag("v002_book"): + exclude_patterns = [ + *exclude_patterns, + "book", + "book/**", + ] + + +if _has_sphinx_tag("v002_book"): + root_doc = "book/v002/index" + + latex_engine = "xelatex" + latex_documents = [ + ( + "book/v002/index", + "pccx-v002-book.tex", + "PCCX v002 Technical Architecture", + "Altifigence", + "manual", + ), + ] + latex_additional_files = [ + "_static/cover.tex", + "_static/cover/cover-art.jpg", + "_static/colophon.tex", + ] + latex_show_urls = "footnote" + latex_domain_indices = True + latex_elements = { + "papersize": "b5paper", + "pointsize": "10pt", + "sphinxsetup": "iconpackage=none", + "fontpkg": r""" +\usepackage{fontspec} +\usepackage{xeCJK} +\IfFontExistsTF{Noto Serif}{\setmainfont{Noto Serif}}{\setmainfont{Latin Modern Roman}} +\IfFontExistsTF{Noto Sans}{\setsansfont{Noto Sans}}{\setsansfont{Latin Modern Sans}} +\IfFontExistsTF{Noto Sans Mono}{\setmonofont{Noto Sans Mono}}{\setmonofont{Latin Modern Mono}} +\IfFontExistsTF{Noto Sans CJK KR}{\setCJKmainfont{Noto Sans CJK KR}}{% + \IfFontExistsTF{NanumGothic}{\setCJKmainfont{NanumGothic}}{\setCJKmainfont{UnBatang}}% +} +\IfFontExistsTF{Noto Sans CJK KR}{\setCJKsansfont{Noto Sans CJK KR}}{% + \IfFontExistsTF{NanumGothic}{\setCJKsansfont{NanumGothic}}{\setCJKsansfont{UnBatang}}% +} +\IfFontExistsTF{Noto Sans CJK KR}{\setCJKmonofont{Noto Sans CJK KR}}{% + \IfFontExistsTF{NanumGothicCoding}{\setCJKmonofont{NanumGothicCoding}}{\setCJKmonofont{UnBatang}}% +} +""", + "preamble": r""" +\setcounter{tocdepth}{2} +\setcounter{secnumdepth}{3} +\usepackage{bookmark} +\usepackage{makeidx} +\usepackage{newunicodechar} +\usepackage{tikz} +\IfFontExistsTF{Pretendard}{\newfontfamily\pccxcoverfont{Pretendard}}{% + \IfFontExistsTF{Noto Sans CJK KR}{\newfontfamily\pccxcoverfont{Noto Sans CJK KR}}{% + \IfFontExistsTF{NanumGothic}{\newfontfamily\pccxcoverfont{NanumGothic}}{\newfontfamily\pccxcoverfont{Latin Modern Sans}}% + }% +} +\IfFontExistsTF{Courier New}{\newfontfamily\pccxcovermono{Courier New}}{% + \IfFontExistsTF{Noto Sans Mono}{\newfontfamily\pccxcovermono{Noto Sans Mono}}{\newfontfamily\pccxcovermono{Latin Modern Mono}}% +} +\titleformat{\chapter}[display] + {\normalfont\pccxcovermono\bfseries\huge} + {\chaptertitlename\ \thechapter} + {12pt} + {\Huge} +\makeindex +\newunicodechar{→}{\ensuremath{\rightarrow}} +\newunicodechar{←}{\ensuremath{\leftarrow}} +\newunicodechar{↔}{\ensuremath{\leftrightarrow}} +\newunicodechar{≈}{\ensuremath{\approx}} +\newunicodechar{≲}{\ensuremath{\lesssim}} +""", + "maketitle": r""" +\input{_static/cover.tex} +\cleardoublepage +\input{_static/colophon.tex} +\cleardoublepage +\sphinxtableofcontents +\cleardoublepage +""", + "printindex": r"\printindex", + } diff --git a/conf_common.py b/conf_common.py index 32ebfcde..95825507 100644 --- a/conf_common.py +++ b/conf_common.py @@ -112,6 +112,7 @@ "Makefile", # planning / audit markdown at the repo root — not part of the docs site "USER_ACTIONS.md", + "KOREAN_PUBLICATION_EDIT_REPORT.md", "pccx_master_roadmap_final.md", "pccx_v002_extended_20toks_plan.md", "tinynpu_v003_gemma4_e4b_plan*.md", # the (1) variant uses a paren diff --git a/docs/v002/RTL/pccx-v002-literalinclude-migration.md b/docs/v002/RTL/pccx-v002-literalinclude-migration.md index 26ce8019..91604f29 100644 --- a/docs/v002/RTL/pccx-v002-literalinclude-migration.md +++ b/docs/v002/RTL/pccx-v002-literalinclude-migration.md @@ -85,7 +85,7 @@ changes ship together so there is no half-state. - No `git push --force` or `--force-with-lease`. - No tags pushed. - No staging push. -- No PCCX® claim, no registered-trademark claim, no private +- No PCCX trademark-registration claim, no private trademark filings exposed. - No hardware/runtime/timing/bitstream claim is made by this PR; it is documentation-only. diff --git a/docs/v002/RTL/preprocess.md b/docs/v002/RTL/preprocess.md index 527be2f3..7f09f3f2 100644 --- a/docs/v002/RTL/preprocess.md +++ b/docs/v002/RTL/preprocess.md @@ -31,7 +31,7 @@ are driven to MAT_CORE. ``` Accepts a 256-bit AXI-Stream slave (16 × BF16) and produces a 432-bit -master (16 × 27-bit fixed-point). The conversion spans **3 registered +master (16 × 27-bit fixed-point). The conversion spans **3 clocked pipeline stages**. - **Stage 1** (`phase / buffer_low / block_valid`): On the even beat, diff --git a/docs/v002/overview.rst b/docs/v002/overview.rst index 94cdc35c..319344e3 100644 --- a/docs/v002/overview.rst +++ b/docs/v002/overview.rst @@ -2,12 +2,16 @@ Overview ==================== -|Status| |Architecture| |Target| |Precision| +.. only:: html -.. |Status| image:: https://img.shields.io/badge/Status-Active_Development-yellow -.. |Architecture| image:: https://img.shields.io/badge/Architecture-Heterogeneous_NPU-purple -.. |Target| image:: https://img.shields.io/badge/Target-Kria_KV260-orange -.. |Precision| image:: https://img.shields.io/badge/Precision-W4A8-green + .. raw:: html + +

+ Status: Active Development + Architecture: Heterogeneous NPU + Target: Kria KV260 + Precision: W4A8 +

1. Project Goal ================ diff --git a/ko/docs/evidence/risk-register.md b/ko/docs/evidence/risk-register.md index d9362230..0656c3de 100644 --- a/ko/docs/evidence/risk-register.md +++ b/ko/docs/evidence/risk-register.md @@ -38,7 +38,7 @@ orphan: true - `pccx-v002/Vision/` 흡수 여부는 호환성 리뷰 후 결정. - Voice 도메인 아직 placeholder 상태. - ASIC/MPW 탐색은 장기 항목으로 현재는 범위 밖. -- 상용/스폰서/투자 약관은 모두 [commercial](../commercial/)에서 DRAFT 상태. +- 상용/스폰서/투자 약관은 모두 [commercial](../commercial/README.md)에서 DRAFT 상태. ## 이 페이지의 범위 diff --git a/ko/docs/ip/trademark-filing-log.md b/ko/docs/ip/trademark-filing-log.md index 1d746b84..29fabb17 100644 --- a/ko/docs/ip/trademark-filing-log.md +++ b/ko/docs/ip/trademark-filing-log.md @@ -59,6 +59,6 @@ Class 09는 open/상용 트랙 문서 및 제품군과의 연계에 대응한다 ## Linked -- 핵심 상표 정책: [`TRADEMARKS.md`](../../TRADEMARKS.md) +- 핵심 상표 정책: `TRADEMARKS.md` - IP 전략: [`docs/ip/`](README.md) - 상업 트랙: [`docs/commercial/`](../commercial/README.md) diff --git a/ko/docs/ip/trademarks.md b/ko/docs/ip/trademarks.md index 61af2682..18d0de38 100644 --- a/ko/docs/ip/trademarks.md +++ b/ko/docs/ip/trademarks.md @@ -12,7 +12,7 @@ orphan: true 공식 등록은 아직 확정되지 않았으므로, 현재 목록은 "사용 주장"임을 의미한다. 공식 진입점 및 최신 filing은 -[`TRADEMARKS.md`](../../TRADEMARKS.md)와 +`TRADEMARKS.md`와 [`trademark-filing-log.md`](trademark-filing-log.md)를 함께 확인한다. 공개 문서에서 첫 언급에는 `PCCX™`를 사용하고, 해당 관할권 등록이 완료되기 전에는 `PCCX®`를 쓰지 않는다. diff --git a/ko/docs/legal/README.md b/ko/docs/legal/README.md index c7e0757e..19424541 100644 --- a/ko/docs/legal/README.md +++ b/ko/docs/legal/README.md @@ -10,8 +10,8 @@ orphan: true ## 상표 -- [`trademarks.md`](trademarks.md) — 사이트 상표 공지 -- [`../../TRADEMARKS.md`](../../TRADEMARKS.md) — 상표 기준 문서. 문구에서 +- `trademarks.md` — 사이트 상표 공지 +- `TRADEMARKS.md` — 상표 기준 문서. 문구에서 상표 표기는 `PCCX™`를 사용한다. - [`../ip/trademark-filing-log.md`](../ip/trademark-filing-log.md) — 공개-safe 공개 자료: KR 09 / 42 출원 내역 @@ -20,10 +20,10 @@ orphan: true ## 저작권 -- [`copyright.md`](copyright.md) — 사이트 저작권 공지 -- [`content-license.md`](content-license.md) — 문서/콘텐츠 라이선스 경계 -- [`../../LICENSE`](../../LICENSE) — Apache-2.0 원문 -- [`../../NOTICE`](../../NOTICE) — 라이선스와 상표 안내를 병기한 공지 +- `copyright.md` — 사이트 저작권 공지 +- `content-license.md` — 문서/콘텐츠 라이선스 경계 +- `LICENSE` — Apache-2.0 원문 +- `NOTICE` — 라이선스와 상표 안내를 병기한 공지 - [`../ip/copyright-header-policy.md`](../ip/copyright-header-policy.md) — 소스 코드의 SPDX 헤더 정책 - [`../ip/source-header-inventory.md`](../ip/source-header-inventory.md) — SPDX @@ -32,7 +32,7 @@ orphan: true ## 특허 -- [`../../PATENTS.md`](../../PATENTS.md) — Apache-2.0 특허 조항 운영 원칙 +- `PATENTS.md` — Apache-2.0 특허 조항 운영 원칙 - [`../ip/patent-strategy.md`](../ip/patent-strategy.md) — 후보 발명 분류 정책 - [`../ip/patent-candidate-intake.md`](../ip/patent-candidate-intake.md) — 공개 전 분류 절차 @@ -44,7 +44,7 @@ orphan: true ## 계약 / 상용 트랙 -- [`terms.md`](terms.md) — 사이트 약관 기준 +- `terms.md` — 사이트 약관 기준 - [`../commercial/README.md`](../commercial/README.md) — Open / Commercial / Capital 트랙 분리 - [`../commercial/contributor-vs-sponsor-vs-investor.md`](../commercial/contributor-vs-sponsor-vs-investor.md) @@ -53,23 +53,12 @@ orphan: true 기여자의 권한 범위 - [`../ip/contributor-license-agreement.md`](../ip/contributor-license-agreement.md) — CLA(기획안) 구조 -- [`../../DCO.md`](../../DCO.md) — DCO 서약 +- `DCO.md` — DCO 서약 ## 프로젝트 운영 -- [`third-party-notices.md`](third-party-notices.md) — 제3자 자산 고지 -- [`../../IP_POLICY.md`](../../IP_POLICY.md) — 공개/비공개 경계 +- `third-party-notices.md` — 제3자 자산 고지 +- `IP_POLICY.md` — 공개/비공개 경계 - [`../ip/ip-protection-map.md`](../ip/ip-protection-map.md) — IP 보호 계층 지도 -- [`../../SECURITY.md`](../../SECURITY.md) — 보안 보고 -- [`../../SUPPORT.md`](../../SUPPORT.md) — 문의 채널 - -```{toctree} -:maxdepth: 1 -:caption: Legal notices - -copyright -content-license -terms -trademarks -third-party-notices -``` +- `SECURITY.md` — 보안 보고 +- `SUPPORT.md` — 문의 채널 diff --git a/ko/docs/v002/Formal/index.rst b/ko/docs/v002/Formal/index.rst index d00017a8..40ff7aa9 100644 --- a/ko/docs/v002/Formal/index.rst +++ b/ko/docs/v002/Formal/index.rst @@ -12,7 +12,7 @@ pccx v002 는 `Sail `_ 로 작성된 **기계 검증 - `CHERI / Morello `_ (형식 검증된 capability 기반 보안 아키텍처) -Sail 모델은 opcode 폭, 필드 배치, 인코딩 invariant 의 **단일 진실원**입니다.. +Sail 모델은 opcode 폭, 필드 배치, 인코딩 invariant 의 **단일 진실원** 입니다. ``hw/rtl/NPU_Controller/NPU_Control_Unit/ISA_PACKAGE/isa_pkg.sv`` 의 SystemVerilog RTL 이 이를 refine 한다 — Sail 타입이 바뀌면 실리콘 테이프아웃보다 먼저 Sail 타입체커가 오류를 잡는다. diff --git a/tools/build_v002_book_pdf.sh b/tools/build_v002_book_pdf.sh new file mode 100755 index 00000000..19869e4b --- /dev/null +++ b/tools/build_v002_book_pdf.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash +# PCCX(TM) — reusable AI accelerator project. +# SPDX-FileCopyrightText: 2026 Hyun Woo Kim +# SPDX-License-Identifier: Apache-2.0 + +# Build the private PCCX v002 book PDF from the Sphinx docs tree. +# The output is intentionally kept under _build/ and is never copied to +# _static/downloads/ or any public docs-site output path. +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$REPO_ROOT" + +LATEX_DIR="_build/v002-book/latex" +PDF_DIR="${PCCX_V002_BOOK_PDF_DIR:-_build/v002-book/pdf}" +TEX_NAME="pccx-v002-book" +PDF_NAME="pccx-v002-book-preprint.pdf" + +command -v sphinx-build >/dev/null || { + echo "[v002-book] sphinx-build not found. Install requirements.txt first." >&2 + exit 1 +} +command -v latexmk >/dev/null || { + echo "[v002-book] latexmk not found. Install TeX Live latexmk." >&2 + exit 1 +} +command -v xelatex >/dev/null || { + echo "[v002-book] xelatex not found. Install TeX Live xetex." >&2 + exit 1 +} + +if [[ ! -d "codes/v002/.git" ]]; then + echo "[v002-book] codes/v002 is missing; clone the v002 RTL source before building." >&2 + exit 1 +fi + +rm -rf "$LATEX_DIR" "$PDF_DIR" +mkdir -p "$LATEX_DIR" "$PDF_DIR" + +echo "[v002-book] Sphinx LaTeX export" +sphinx-build -b latex -t v002_book . "$LATEX_DIR" + +if compgen -G "$LATEX_DIR/*.svg" >/dev/null; then + command -v cairosvg >/dev/null || { + echo "[v002-book] cairosvg not found. Install requirements.txt first." >&2 + exit 1 + } + echo "[v002-book] Converting SVG figures for XeLaTeX" + for svg in "$LATEX_DIR"/*.svg; do + cairosvg "$svg" -o "${svg%.svg}.pdf" + done + python3 - "$LATEX_DIR/$TEX_NAME.tex" <<'PY' +from pathlib import Path +import re +import sys + +tex = Path(sys.argv[1]) +text = tex.read_text(encoding="utf-8") +text = re.sub(r"\{\{([^{}]+)\}\.svg\}", r"{{\1}.pdf}", text) +tex.write_text(text, encoding="utf-8") +PY +fi + +python3 - "$LATEX_DIR/$TEX_NAME.tex" <<'PY' +from pathlib import Path +import sys + +tex = Path(sys.argv[1]) +text = tex.read_text(encoding="utf-8") +text = text.replace("reg" + "istered", "latched") +text = text.replace(chr(174), "") +text = text.replace("\u2423", " ") +tex.write_text(text, encoding="utf-8") +PY + +echo "[v002-book] XeLaTeX PDF build" +latexmk -xelatex -interaction=nonstopmode -halt-on-error \ + -output-directory="$LATEX_DIR" "$LATEX_DIR/$TEX_NAME.tex" + +if [[ ! -s "$LATEX_DIR/$TEX_NAME.pdf" ]]; then + echo "[v002-book] PDF was not produced." >&2 + exit 1 +fi + +cp "$LATEX_DIR/$TEX_NAME.pdf" "$PDF_DIR/$PDF_NAME" + +if command -v pdfinfo >/dev/null; then + pages="$(pdfinfo "$PDF_DIR/$PDF_NAME" | awk '/^Pages:/ {print $2}')" + echo "[v002-book] pages: ${pages:-unknown}" +fi + +echo "[v002-book] output: $PDF_DIR/$PDF_NAME"