swiss-pdf은(는) 무엇을 하나요?
md / docx / txt / html 파일을 스위스 국제 타이포그래피 스타일의 A4 PDF로 한 번에 변환합니다.
흰색 배경, 검은색 글자, 매우 가는 구분선, 큰 장 번호, 고정폭 숫자, 유일한 컬러 빨강 #E5484D.
디자인 토큰은 스위스 국제주의 스타일의 양장본(큰 장 번호, 매우 가는 구분선, 단일 강조색)에서 가져왔으며, 단일 흐름 자동 페이지 나누기로 변경되어 모든 길이의 문서를 바로 처리할 수 있습니다.
사용 방법
방법 1: 드래그 앤 드롭 (권장)
파일을 swiss-pdf.bat 위로 드래그 → 같은 디렉터리에 동일한 이름의 .pdf 생성, xxx.pdf-src.html(확인 가능한 HTML 소스)도 함께 보관.
방법 2: 명령줄
python make.py 입력파일 # PDF 출력
python make.py 입력파일 --html # HTML만 출력 (xxx.html로 저장, 브라우저에서 열어 수동 인쇄 가능)
python make.py 입력파일 -o 출력.pdf
python make.py a.md b.docx c.txt # 일괄 처리
python make.py 입력파일 --no-cover --no-toc
python make.py 입력파일 --title "제목" --kick "각주" --date 2026.11.09
지원하는 입력
| 형식 | 변환 방식 | 설명 |
|---|---|---|
.md | pandoc | YAML front-matter, GFM 표, 목록, 코드 블록, 이미지 지원 |
.docx | pandoc | 제목 스타일 → 장 제목; 표 → 스타일 표; 이미지 자동 추출 |
.txt | 내장 파서 | 빈 줄로 단락 구분; 짧은 줄에 번호(一、/ 1. / 第X章) → 장 제목 |
.html | 내장 파서 | <body> 가져오기, script/style 제거; <title>을 문서 제목으로 사용 |
표지: YAML front-matter
md 파일 맨 위에 작성 (다른 형식은 --title 등의 매개변수 사용):
---
title: 도시 자전거 안전 백서
subtitle: 세 도시 표본 조사에 기반한 관찰 및 제안
kick: 교통治理 연구
en: Urban Cycling Safety White Paper
slug: 도시자전거안전
nature: 공개판
project: 도시 자전거 교통 시스템 연구 계획
party_a: 의뢰처 이름
party_b: 수탁처 이름
stage: 조사 → 작성 → 확정
source: 01_관찰기록 02_인터뷰요약 03_계획
date: 2026.11.10
version: v2.1
badges: 공개; !중요; 데이터기반
---
badges는;로 구분, 접두사!는 검은색 배경에 흰색 글자.date미입력 시 자동으로 당일 날짜 사용.- front-matter 없이도 실행 가능, 표지는 제목 + 날짜 + 버전 빈칸만 표시.
본문: 6가지 스타일 블록
markdown에서 :::로 감싸기 (블록 내 각 줄 = 하나의 항목):
::: note
부연 설명, 범위 알림. 회색 배경.
:::
::: quote
핵심 결론 인용문, 왼쪽 3pt 세로선.
:::
::: inverse
검은색 배경 흰색 글자 블록, 가장 중요한 한 문장 배치.
:::
::: warn
빨간색 테두리 경고, 유일하게 컬러를 사용할 수 있는 위치.
:::
::: stat
4,280 | 관측 표본 총계 || 24 | 관측 지점 || 3 | 대상 도시
:::
::: check
출근 시간 통학로 (7:30 이전 도착 필요)
야간 조명 구간 (보조 조명 필요)
:::
::: kv
페이지 수 | A안 20쪽 / C안 32쪽
추천 | C안, 단 제2도시 데이터 보완 필요
:::
stat은 숫자 | 설명으로 한 칸 구성, ||로 칸 구분. kv는 키 | 값.
자동 동작
| 동작 | 규칙 |
|---|---|
| 장 번호 | # 1단계 제목 → 큰 숫자 00/01/02…; 제목에 이미 '01 ', '一、' 번호 포함 시 그대로 사용 |
| 목차 | 장 + ## 절 합계 ≥ 5개일 때만 생성, 아니면 한 페이지 생략; 장 제목에 앵커 포함, PDF 내 클릭 가능 |
| 표 | 열 개수 ≥ 6 자동 글꼴 크기 축소(table.wide); 순수 숫자 셀 오른쪽 정렬 + 고정폭 글꼴 |
| 머리글/바닥글 | 표지 페이지 미적용; 본문 머리글 왼쪽에 실행 제목, 바닥글 오른쪽에 'i쪽 / 총 N쪽' |
| 인쇄 | @page A4, 여백 14/15/13mm; 제목이 본문과 분리되지 않음, 표 행이 페이지 넘어 끊기지 않음 |
HTML 소스 추가 처리 (실제 측정 보충)
소스가 '자체 디자인된 웹 페이지'인 경우, 이 스킬이 자동으로 정규화하여 재배치 후 고립된 번호나 중복 제목을 방지합니다:
| 소스 구조 | 처리 |
|---|---|
<div class="sec-head"><span class="sec-num">3</span><h2>제목</h2></div> | 템플릿의 .sec 장 헤더로 변환 (번호는 span, 제목은 h2) |
<h1>03 제목</h1> / <h1>제목</h1> | 동일; 번호 없으면 나타나는 순서대로 자동 증가 |
단독 단락의 순수 숫자 <p>0</p> | 폐기 (장 번호를 장식 숫자로 사용하는 일반적인 작성 방식) |
<header class="masthead"> / <nav> | 폐기 (표지는 별도로 처리되며, 중복된 kicker/큰 제목/하단 목차는 본문을 오염시킴) |
<section> 분할 | 분할 컨테이너로 유지, 제목은 여전히 sec-head 또는 h1이어야 목차에 포함 |
<div id="xxx" class="chart"> + echarts | 헤드리스 브라우저로 PNG 스크린샷 후 삽입; 스크린샷 전 networkidle + 2.5초 대기 |
차트 스크린샷 전제 조건: echarts가 CDN을 사용할 경우 네트워크 연결 필요; 오프라인 환경에서는 소스에 echarts.min.js를 인라인으로 포함.
글꼴 및 문장 부호
글꼴: 저장소 내장 Source Han Sans, @font-face 인라인
fonts/ 디렉터리에 저장소와 함께 제공되는 두 파일, make.py가 HTML 생성 시 해당 파일들의 base64를 <style>에 인라인하여
글꼴 패밀리 Swiss Sans SC로 등록:
| 파일 | 글꼴 두께 |
|---|---|
SourceHanSansCN-Regular.subset.ttf | 400 |
SourceHanSansCN-Bold.subset.ttf | 700 |
서브셋 범위 약 7100자(ASCII + 중영 문장 부호 + GB2312 전체 6763 한자), 문자 집합은 fonts/charset.txt 참조.
장점: Source Han Sans가 설치되지 않은 기계에서도 출력이 완전히 동일, 사용자가 글꼴을 설치할 필요 없음.
사용자는 직접 글꼴을 교체하거나 다운로드 가능, 세 가지 방법(fonts/README.md 참조):
- 파일 교체 (권장): 자신의 TTF 파일을 위 두 파일 이름으로 변경하여 덮어쓰기. 먼저 서브셋화하지 않으면 HTML에 전체 10MB가 포함:
pyftsubset your-font-Regular.ttf --text-file=fonts/charset.txt --output-file=fonts/SourceHanSansCN-Regular.subset.ttf fonts/삭제:make.py가 파일을 감지하지 못하면@font-face를 건너뛰고 CSS의 시스템 글꼴 스택으로 폴백 (Source Han Sans SC/CN → Noto Sans SC → Noto Sans CJK SC → 梦源黑体 CN → PingFang SC → Segoe UI Symbol).- CSS만 변경:
templates/swiss.css상단의--sans첫 번째 글꼴 이름 변경.
⚠️ 반드시 .ttf(TrueType 윤곽선) 사용, .otf(CFF) 사용 불가. Chromium이 PDF를 인쇄할 때 CFF의
웹 글꼴을 Type3 글리프 명령어(각 글리프가 그리기 경로 저장)로 다운그레이드하여 볼륨이 몇 배로 증가합니다. 동일한 4페이지 문서:
OTF → 328 KB / 205개의 Type3; TTF → 79 KB / 1개의 Type3.
Microsoft YaHei는 Chromium에서 ·(U+00B7), → ↓ ≤ ≥ 등의 기호 글리프가 누락되어 사각형으로 표시되므로 주 글꼴 스택에서 제거되었습니다.
인용 부호: 전체 문서 강제 국가 표준 굽은 따옴표
convert.normalize_quotes()가 변환의 마지막 단계에서 통합 처리, 제목, 표지, 머리글, 목차, 표, 스타일 블록 모두 포함:
| 입력 | 출력 |
|---|---|
「」 | “” |
『』 | ‘’ |
코드 내 직선 따옴표 " ' | markdown은 pandoc +smart로 자동 굽은 따옴표 변환; 코드 블록 <code> / <pre> 내부는 변경하지 않음 |
구현은 4개의 코드 포인트만 대체(U+300C/D/E/F → U+201C/D/8/9), HTML 태그 속성과 CSS/JS는 변경하지 않음.
검증된 실제 실행 기록
| 입력 | 규모 | 페이지 | 소요 시간 |
|---|---|---|---|
| md | 29 KB | 15 | ~6초 |
| html | 105 KB + 2개의 echarts 차트 | 21 | ~15초 |
| docx | 제목 스타일 + 표 | 2 | ~6초 |
txt (samples/) | 1.2 KB | 2 | ~5초 |
| md 긴 문서 회귀 | 71 KB | 21 | ~24초 |
환경 종속성
| 종속성 | 로컬 상태 | 누락 시 |
|---|---|---|
| pandoc(≥2.19) | pandoc -v 실행하여 확인 | md/docx 변환 불가 |
| playwright + chromium | 또는 시스템 내장 Chrome/Edge | PDF 렌더링 불가 |
| python-docx (선택 사항) | 보조 파싱 전용 | 주 흐름에 영향 없음 |
브라우저 지정: set SWISS_PDF_CHROME=<chrome.exe 경로>
파일 구조
swiss-pdf/
├── SKILL.md 이 파일
├── swiss-pdf.bat 드래그 앤 드롭 진입점
├── make.py 주 흐름: 변환 → 조립 → 렌더링
├── convert.py 각 형식 → HTML 조각 + 블록 수준 구문
├── templates/
│ └── swiss.css 전체 스타일 (디자인 토큰 + 인쇄 규칙)
└── samples/ 세 가지 형식의 비교 샘플
한계 (할 수 없는 것)
- 양장본 같은 수동 페이지 나누기(각 페이지 내용을 세심하게 배치, 펼침면 대면 디자인) 템플릿으로는 불가능, 여전히 수동으로 HTML 작성 필요. 이 스킬의 포지셔닝은 '일반 문서 빠른 초안 작성'입니다.
- 삽화 레이아웃은 원본 문서의 이미지 크기에 의존하며, 자르기나 도련 처리 없음.
- 목차 오른쪽 인쇄 페이지 번호 미구현(페이지 내 요소가 페이지 번호를 얻을 수 없음); 장 제목은 클릭 가능하며, 바닥글에 'i쪽 / 총 N쪽' 표시.
- 너무 짧은 표 블록은 전체가 다음 페이지로 밀려날 수 있음(키-값 표가 분리되는 것을 방지하기 위해
.kv블록은 페이지 넘김 불가로 설정). - 복잡한 Word 스타일(텍스트 상자, 워드아트, 다단계 번호 목록)은 pandoc 변환 시 다운그레이드됩니다.