report-to-pdf — 마크다운 리포트를 정교한 PDF로
결정론적이고 스크립트 기반의 마크다운-투-PDF 렌더러입니다.
제한된 'report' 마크다운 하위 집합을 작은 핸드메이드 블록 파서(외부 마크다운 라이브러리 없음)로 파싱하고, ColeteOnline Magento 모듈의 build-user-guide.py 생성기와 동일한 시각적 규칙(표지 페이지, 헤더/푸터, 목차, 악센트 바 섹션 제목, 음영 처리된 코드 블록, 테두리/얼룩말 줄무늬 테이블)을 사용하여 fpdf2를 통해 레이아웃합니다. 레이아웃 로직은 동일한 입력에 대해 실행 간에 절대 변경되지 않습니다. 동일한 리포트 입력, 동일한 PDF 출력, 매번 동일합니다. 렌더링 루프에 LLM이 개입하지 않습니다.
사용 시기
- "이 마크다운 리뷰를 PDF로 만들어 주세요."
- "내 리포트를 정교한 PDF로 내보내 주세요."
- "매번 동일하게 보이길 원합니다. LLM이 매번 다시 포맷하지 않았으면 좋겠어요."
- 제목, 목록, 코드 블록 또는 테이블이 있는 모든
.md리포트/문서를 클라이언트, 멘토, 또는 브랜드 품질의 PDF 버전으로 만들어야 할 때.
실행 방법
python3 <skill-dir>/scripts/build_report_pdf.py <input.md> [옵션]
(<skill-dir>는 이 스킬의 기본 디렉터리, 즉 이 SKILL.md가 포함된 폴더입니다.)
옵션:
-o / --output <경로>— 출력 PDF 경로 (기본값: 소스 파일과 같은 위치에<input>.pdf).--logo <경로>— 표지 페이지에 표시할 선택적 로고 이미지 (PNG/WebP/JPEG, Pillow가 열 수 있는 모든 형식). 여러 로고를 한 줄에 나란히 그리려면 플래그를 반복하세요 (프론트매터logo: a.png, b.png로도 설정 가능, 경로는.md파일 기준).--title,--subtitle,--version,--vendor,--author— 해당 프론트매터 값을 재정의합니다 (아래 참조). CLI 플래그가 우선합니다.--cover center|left— 표지 페이지 레이아웃. 전체 제목 블록(로고, 제목, 부제목, 작성자, 버전)은 두 모드 모두 수직 중앙에 정렬됩니다.center(기본값)는 수평 중앙에도 정렬하고,left는 왼쪽 정렬합니다. 프론트매터cover:로도 설정 가능합니다.--no-header— 실행 헤더와 그 예약 공간을 생략하여 모든 페이지의 콘텐츠가 동일한 상단 여백에서 시작하도록 합니다 (프론트매터header: false로도 설정 가능).--accent "R,G,B"— 표지 페이지 상단 바와 섹션 제목 악센트 바에 사용되는 악센트 색상을 재정의합니다 (기본값220,50,50, ColeteOnline 빨간색).--dark— 기본 밝은 테마 대신 어두운 색 테마로 렌더링합니다 (프론트매터theme: dark로도 설정 가능. 둘 다 있으면--dark가 우선). 아래 "다크 테마"를 참조하세요.
스크립트는 가져오기 실패 시 pip install --user를 통해 fpdf2, Pillow, Pygments를 자동 설치합니다 (참조 스크립트의 Pillow 자동 설치와 동일). 세 라이브러리 모두 가져오기가 확인되었으며, 개발 중에 스크립트가 처음부터 끝까지 실행되었습니다 (아래 테스트 참조).
예상되는 마크다운 규칙
파일 맨 위에 선택적 프론트매터 블록:
---
title: 내 리포트 제목
subtitle: 한 줄짜리 부제목
version: 1.0
vendor: ColeteOnline SRL
accent: 220,50,50
---
모든 키는 선택 사항이며 적절하게 기본값으로 대체됩니다:
title— 본문에서 찾은 첫 번째# H1제목으로 대체되며, 그 다음에는 입력 파일 이름으로 대체됩니다.subtitle— 기본적으로 비어 있음 (표지 페이지에서 생략).version— 기본적으로 오늘 날짜 (YYYY-MM-DD).version: none으로 설정하면 버전 줄이 숨겨집니다.vendor— 기본적으로 비어 있음 (설정되지 않으면 표지 페이지 바닥글의 저작권 줄이 생략).author— 작성자 이름. 부제목 아래 표지에 표시됩니다 (예:author: 홍길동, 김철수). 설정되지 않으면 생략.cover—center(기본값) 또는left. 위의--cover참조.header—false는 실행 헤더를 생략합니다.--no-header참조.justify—true는 본문 텍스트, 제목 및 테이블 셀을 양쪽 정렬합니다. 기본값은 왼쪽 정렬입니다 (양쪽 정렬된 텍스트는 짧은 줄바꿈 행에서 단어 간격을 늘립니다. 예: 두 줄 제목).accent— 기본값은 ColeteOnline 브랜드 빨간색220,50,50.
본문 마크다운 및 각 요소가 렌더링되는 방식:
| 마크다운 | 렌더링 결과 |
|---|---|
# 제목 | 파일의 첫 번째 #는 표지 페이지 제목으로만 사용됩니다 (소비되며 본문에 반복되지 않음). 이후의 #는 실제 주요 제목입니다 (아래 참조). |
# 주요 섹션 (첫 번째 # 이후) | 주요 제목. 항상 새로운 페이지에서 시작합니다 (커서가 이미 빈 페이지 상단에 있지 않은 한). 큰 색상 왼쪽 막대, 볼드 16pt, 검은색 텍스트 — 그리고 목차에 항목이 추가됩니다. 진정한 최상위 구분에만 사용하세요. 각 항목마다 한 페이지가 소비됩니다. |
## 하위 섹션 | 부제목. 볼드 13pt, 악센트 색상 텍스트와 짧은 악센트 색상 밑줄 (위 및 아래 계층과 시각적으로 구별됨) — 페이지 나누기를 강제하지 않으며 흐름을 계속합니다. 여전히 목차 항목이 추가됩니다. "페이지 맨 아래에 제목이 외로이 남지 않도록" 하는 자체 검사가 있습니다. |
### 하위-하위 섹션 (및 ####+) | 3차 제목. 볼드 12pt, 중립 회색, 전체 너비 가는 규칙, 페이지 나누기 없음, 목차 항목 없음 (목차를 읽기 쉽게 유지). |
| 문단 텍스트 | 양쪽 정렬 본문 문단, 10pt. |
- 항목 / * 항목 | 글머리 기호 목록 (•). |
1. 항목 | 번호 목록. |
```lang ... ``` | 음영 처리되고 테두리가 있는 고정폭 코드 블록. 언어 태그(있는 경우)는 블록 위에 작은 이탤릭 레이블로 표시되며 실제 Pygments 구문 강조를 구동합니다. 아래 참조. |
| a | b | (--- 구분 행 포함) | 테두리 테이블, 볼드 헤더 행, 얼룩말 줄무늬 본문 행, 자동 줄바꿈 셀, 동일한 열 너비. |
--- / *** / ___ (단독 줄) | 세로 공백 (페이지 나누기 아님). |
<br> (단독 줄) | 명시적 빈 본문 줄 하나 (5.5mm). 여러 개를 쌓으면 더 많은 공간을 확보할 수 있습니다. 렌더러는 페이지 상단에 자체 간격을 추가하지 않으므로 추가 공간이 필요할 때 사용하세요. |
<!-- pagebreak --> (단독 줄) | 새 페이지 시작 (이미 상단에 있으면 무효). |
 (단독 줄) | 중앙 정렬, 테두리 있는 이미지, 맞게 크기 조절. 상대 경로는 .md 파일의 폴더를 기준으로 확인됩니다. PNG/JPEG/WebP 및 SVG (아래 "그림, 상자 및 수식" 참조). 너비가 900px 미만인 일반 래스터 이미지는 약 130x105mm로 제한됩니다. 그림(이미지 뒤에 캡션이 있는 경우), 모든 SVG 및 너비가 900px 이상인 이미지는 전체 텍스트 너비를 사용할 수 있습니다 (최대 높이 120mm). 페이지에 남은 공간에 맞지 않는 그림은 다음 페이지로 넘어가서 여백을 남기는 대신 공간에 맞게 축소됩니다 (최대 높이의 70%까지). |
*그림: ...* (이미지 바로 다음에 오는 전체 이탤릭 줄) | 이미지의 캡션: 작게, 중앙 정렬, 이탤릭체, 이미지와 같은 페이지에 유지. 대체 텍스트는 절대 인쇄되지 않습니다. |
> 텍스트 줄 | 정의 상자: 블록 인용문당 하나의 테두리 있고 약간 음영 처리된 상자 (굵은 용어 및 인라인 수식 사용 가능. > 다음에 아무것도 없는 줄은 같은 상자에서 새 문단을 시작합니다). |
$...$ | 인라인 수식. 유니코드 텍스트로 변환 (그리스 문자, ≤ ≥ × ·, 위/아래 첨자, \frac{a}{b} -> (a)/b, \sqrt, \text{}, 악센트). $5와 같은 가격은 수식으로 처리되지 않습니다. |
$$...$$ (한 줄 또는 여러 줄) | 디스플레이 수식. 첫 번째로 작동하는 수학 백엔드에서 생성된 중앙 정렬 이미지 (아래 "그림, 상자 및 수식" 참조). |
| 빈 줄 뒤의 4-공백 들여쓰기 블록 | 언어 태그가 없는 코드 블록 (노트북 내보내기는 셀 출력을 이렇게 인쇄합니다). 목록 바로 뒤에는 적용되지 않습니다. |
**굵게** | fpdf2의 내장 markdown=True 렌더러를 통한 실제 볼드 텍스트. |
[텍스트](url) | 실제 클릭 가능한 PDF 링크 주석 — 원시 URL이 아닌 표시 텍스트만 표시되며 별개의 링크 색상으로 스타일링됩니다. |
*이탤릭* / _이탤릭_ | 렌더링된 이탤릭체 (내부적으로 fpdf2의 네이티브 __italic__ 형식으로 접힘). |
`코드` | 인라인 코드 백틱은 제거됩니다 — 고정폭 인라인 지원 없음, 일반 텍스트로 렌더링됩니다. |
제목, 문단, 글머리 기호, 번호 항목 및 테이블 셀은 모두 fpdf2의 markdown=True 경로를 통해 렌더링되므로 굵게 및 [텍스트](url) 링크는 제목 및 테이블 셀 내부를 포함하여 텍스트가 나타나는 모든 곳에서 작동합니다. 펜스 코드 블록이 유일한 예외입니다. 해당 콘텐츠는 완전히 문자 그대로 렌더링됩니다 (마크다운 파싱 없음). 따라서 **, __ 또는 []()가 포함된 코드 샘플이 잘못 해석되지 않습니다.
그림, 상자 및 수식
노트북의 마크다운 컴패니언(nbconvert 출력)을 직접 렌더링하도록 작성되었습니다. report-to-pdf notebook.md는 별도의 Chrome 기반 PDF 단계를 대체합니다.
SVG 이미지. 가 작동합니다. fpdf2 자체 SVG 드로잉은 <marker> 화살촉, 필터 및 마스크를 건너뛰고, 핵심 글꼴(그리스어 없음)로 텍스트를 그리고 fill이 없는 텍스트 색상을 잘못 지정하므로 모든 SVG는 2.5배로 래스터화되어 PNG로 포함됩니다. 이때 헤드리스 Chrome/Edge(자동으로 찾음. REPORT_PDF_BROWSER를 브라우저 경로로 설정하여 재정의 가능)를 먼저 사용하고, 그 다음 cairosvg(가져오기 가능한 경우)를 사용합니다. 둘 다 없으면 SVG가 fpdf2에 의해 기본적으로 그려지며 불완전할 수 있다는 경고가 표시됩니다. 래스터화는 실행당 캐시됩니다.
디스플레이 수학 백엔드 (--math auto|latex|mathtext|unicode 또는 프론트매터 math:; 기본값 auto). 스크립트는 사용된 백엔드를 출력합니다.
latex:pdflatex,xelatex또는tectonic이 PATH에 있거나 (그리고pdftocairo, PyMuPDF 또는dvisvgm가 이미지를 만들기 위해), 또는latex+dvipng가 있을 때 실제 LaTeX. 각 수식은standalone,amsmath,amssymb및xcolor로 12pt에서 컴파일된 다음 텍스트 색상의 투명한 300dpi PNG로 잘립니다. 설치: MiKTeX (Windows), TeX Live (apt install texlive-latex-base texlive-latex-extra poppler-utils, macOSbrew install --cask mactex-no-gui), 또는tectonic(단일 바이너리, 요청 시 패키지 다운로드.conda install -c conda-forge tectonic또는scoop install tectonic. PyMuPDF를 이미지 변환기로 사용하여 Windows에서 종단 간 테스트 완료).mathtext: matplotlib의 LaTeX 하위 집합 (분수, 합계, 그리스어,\text{}는\mathrm으로 매핑됨). TeX가 필요 없으며 matplotlib만 있으면 됩니다.aligned와 같은 환경은 지원되지 않으며 통과되지 않습니다.unicode: 인라인 수식과 동일한 텍스트 변환, 중앙 정렬.
백엔드가 없거나 실패하면 (예: TeX가 거부하는 수식) 다음 백엔드로 자동 전환됩니다. --math latex도 동일한 방식으로 다운그레이드됩니다.
테스트: python tests/run_checks.py (수식 변환, 파서, SVG 라우팅, 조롱된 PATH를 사용한 백엔드 대체, tests/fixture.md의 종단 간 빌드). 컴패니언 렌더링: python scripts/build_report_pdf.py notebook.md -o notebook.pdf.
구문 강조
펜스 코드 블록은 실제 Pygments 렉서(핸드메이드 정규식 하이라이터가 아님 — 코드는 작은 임시 파서가 미묘하게 잘못 이해하는 종류의 것: 문자열 이스케이프, 중첩 주석, 템플릿 리터럴)로 토큰화됩니다. 펜스의 언어 태그(```typescript, ```sql, ```python 등)는 렉서를 선택합니다. 몇 가지 일반적인 별칭은 정규화됩니다 (ts/tsx → typescript, js/jsx → javascript, sh/shell/zsh → bash, yml → yaml, prisma → graphql — 가장 가까운 내장 렉서이며 Prisma의 @attribute(...) 구문과 GraphQL의 @directive(...) 구문이 거의 동일하게 토큰화되므로 실제로 잘 맞습니다).
강조는 보너스이며 코드 블록을 렌더링하기 위한 엄격한 요구 사항이 아닙니다. 언어 태그가 없거나, Pygments가 인식하지 못하는 언어(별칭 테이블 이후에도)이거나, Pygments 자체를 가져올 수 없는 경우 모두 강조 전의 정확한 동작으로 대체됩니다. 블록 전체가 하나의 평평한 색상으로 표시되며 충돌이나 콘텐츠 누락이 없습니다.
색상 팔레트 (코드 블록의 밝은 회색 배경에 대해 가독성을 위해 선택되었으며 특정 Pygments 테마에 연결되지 않음):
| 토큰 범주 | 색상 |
|---|---|
키워드 (interface, async, const, NOT NULL, string/number 같은 타입 키워드) | 마젠타 |
| 문자열 | 초록 |
| 숫자 | 갈색/주황 |
| 주석 | 회색 |
함수/클래스 이름, 데코레이터/속성 (@map, @relation) | 파랑 / 갈색 |
내장 (Promise 등) | 청록 |
| 기타 모든 것 (식별자, 구두점, 연산자) | 기본 코드 색상 |
팔레트에 정확한 항목이 없는 토큰 유형은 Pygments의 토큰 유형 계층 구조를 따라 올라가며 (예: Keyword.Declaration에는 자체 항목이 없지만 부모 Keyword에는 있음) 일치하는 항목을 찾을 때까지 해결됩니다. 기본 Token 루트는 항상 일치하므로 어떤 색상으로든 해결되지 않는 경우는 없습니다.
고정폭 축소-맞춤 로직 (페이지보다 너무 넓은 코드 줄은 오버플로 대신 전체 블록의 글꼴을 균일하게 축소)은 강조의 영향을 받지 않습니다. 일반 문자 너비를 측정하며 토큰이 그려지는 색상에 따라 변경되지 않습니다.
다크 테마
--dark를 전달하거나 프론트매터에 theme: dark를 설정하면 전체 문서가 기본 밝은 테마 대신 다크 테마로 렌더링됩니다. 문서의 모든 색상이 코드 블록뿐만 아니라 어두운 배경에 적합한 대응 색상으로 전환됩니다.
- 페이지 배경 자체가 어둡게 채워집니다 (fpdf2에는 네이티브 "페이지 배경색" 설정이 없으므로 페이지 상단에 다른 항목이 그려지기 전에
header()후크에서 전체 페이지를 채워서 수행됩니다. 표지 페이지 포함). - 본문 텍스트, 제목(세 수준 모두), 희미한 텍스트/푸터 텍스트, 규칙 선, 테이블 얼룩말 줄무늬 및 실행 헤더/푸터는 모두 밝은 색상을 어둡게 한 것이 아닌 별도의 다크 모드 색상 세트를 사용합니다.
- 구문 강조는 자체 다크 모드 팔레트(어두운 코드 배경과 대비되도록 선택된 더 밝은 분홍/초록/파랑)를 사용하며 밝은 모드 색상을 어둡게 한 것이 아닙니다.
- 링크 색상(
[텍스트](url)마크다운 링크 및 목차의 점프-투-페이지 링크 모두)은 어두운 배경에서도 읽을 수 있는 더 밝은 파란색으로 전환됩니다.
--accent 색상(상단 바, 주요 제목 왼쪽 바 및 부제목 텍스트/밑줄)은 두 테마에서 그대로 사용됩니다. 두 테마에서 동일한 리포트를 생성하려는 경우 흰색 배경과 거의 검은색 배경 모두에서 잘 보이는 색상을 선택하거나, 그렇지 않은 경우 테마당 --accent를 재정의하세요.
목차
백필 목차 (위의 "렌더링 방식" 참조)는 fpdf2의 Bookmark 기능을 사용하여 생성되며, 각 항목은 클릭 가능한 PDF 링크입니다.
속성
이 스킬은 ColeteOnline의 Magento 사용자 가이드 PDF 생성기 build-user-guide.py를 기반으로 합니다. 원본 스크립트는 별도의 markdown 라이브러리 없이 fpdf2를 통해 직접 마크다운을 PDF로 변환하기 위해 작성되었으며, 이 스킬은 동일한 접근 방식을 재현하고 노트북 AI 어시스턴트를 위한 report-to-pdf로 확장합니다.