한국어
기업용

제로에서 PDF 손으로 작성하기 (01): Hello, World——최소한의 사용 가능한 PDF 구축하기

Doclingo Team2026년 1월 30일

제로에서 PDF 손으로 작성하기 (01): Hello, World——최소한의 사용 가능한 PDF 구축하기

시리즈 목표: PDF를 읽을 수 있는 파일 형식으로 이해하기 - 먼저 "작동하는" 최소 예제부터 시작하여 점차 그래픽, 다중 페이지, 압축 및 자원 재사용으로 확장합니다.

시리즈 목차

  • 제 01 편 (본 문서): 최소 PDF(1 페이지 + 1 행 텍스트)를 손으로 작성하고 도구를 사용하여 열 수 있는 표준 PDF로 보완합니다.
  • 제 02 편: 콘텐츠 스트림에서 선/사각형 그리기(경로, 테두리, 채우기 이해하기)
  • 제 03 편: 다중 페이지 PDF(페이지 트리가 어떻게 생성되는지)
  • 제 04 편: 실제 세계에 더 가까워지기(압축 스트림, 자원 재사용, 선택적 구조 등)

왜 PDF의 하위 구조를 이해해야 할까요?

PDF(Portable Document Format, 휴대용 문서 형식)는 오늘날 가장 인기 있는 페이지 설명 언어 중 하나입니다. HTML/CSS와 같은 "내용과 표현 분리, 재흐름 가능" 접근 방식과는 달리, PDF는 **레이아웃 고정, WYSIWYG(What You See Is What You Get)**를 강조합니다 - 어떤 장치에서 열더라도 레이아웃이 일관됩니다.

PDF 하위 구조를 이해하는 데는 몇 가지 실제적인 이점이 있습니다:

  • PDF 생성 문제 디버깅: 코드 라이브러리를 사용하여 PDF를 생성할 때 오류가 발생하면, 하위 구조를 이해해야 문제를 빠르게 찾을 수 있습니다.
  • 자동화 처리: 대량 텍스트 추출, 문서 병합, 워터마크 추가 등의 작업은 구조를 이해해야 정확하게 수행할 수 있습니다.
  • 보안 감사: PDF에 어떤 내용을 삽입할 수 있는지(자바스크립트, 첨부 파일, 양식 등)를 이해하면 보안 분석에 도움이 됩니다.
  • 파일 형식 설계 학습: PDF의 "객체 그래프 + 임의 접근" 설계는 고전적인 예로, 배울 가치가 있습니다.

준비 작업

본 문서에서는 먼저 "구조는 불완전하지만 논리는 올바른" hello-broken.pdf를 작성한 후, pdftk를 사용하여 주요 구조를 자동으로 보완하고 hello.pdf로 출력합니다.

  • 필요한 도구: pdftk (무료 명령줄 도구, Windows/macOS/Linux 지원)
  • 출력 파일: hello-broken.pdf (손으로 작성), hello.pdf (수정 후 열 수 있음)

핵심 개념: PDF의 세 가지 구조

PDF를 이해하는 데 가장 중요한 것은 세 가지 정신 모델을 구축하는 것입니다:

PDF 세 가지 정신 모델

1. 객체 계층 (Document Content)

PDF 문서는 많은 객체로 구성되어 있으며, 객체 간에는 간접 참조(예: 2 0 R)로 연결되어 있습니다. 일반적인 객체 유형:

유형예시설명
Name/Page/로 시작하는 이름
정수/실수50, 36.0숫자
문자열(Hello, World!)괄호로 묶인 문자열
배열[0 0 612 792]순서가 있는 집합
사전<< /Type /Page >>키-값 쌍의 집합
간접 참조2 0 R객체 2를 참조 (생성 번호 0)
스트림stream...endstream이진 데이터 (예: 그리기 명령, 이미지)

2. 콘텐츠 계층 (Page Content)

실제로 "텍스트/그래픽을 페이지에 그리는" 명령어 시퀀스는 일반적으로 stream ... endstream 안에 작성됩니다. 형식은: 작업 수가 앞에, 작업자가 뒤에 있습니다.

/F0 36 Tf          ← 작업 수: /F0, 36  작업자: Tf (글꼴 설정)
(Hello, World!) Tj ← 작업 수: 문자열   작업자: Tj (텍스트 그리기)

3. 파일 구조 계층 (File Structure)

리더가 임의 객체에 빠르게 접근할 수 있도록 하여 처음부터 끝까지 읽지 않아도 됩니다:

요소역할
%PDF-1.x파일 헤더, PDF 버전 식별
xref교차 참조 테이블: 객체 번호 → 바이트 오프셋
trailer꼬리 사전: 루트 객체 /Root를 가리킴
startxrefxref 테이블의 시작 위치를 나타냄
%%EOF파일 종료 마크

최소 PDF에 필요한 객체는 무엇인가요?

"최소하지만 텍스트를 표시할 수 있는" PDF의 객체 간 참조 관계는 다음과 같습니다:

PDF 최소 객체 관계도

최소 객체 목록:

객체역할주요 필드
Catalog루트 객체, 문서 진입점/Type /Catalog, /Pages
Pages페이지 트리/Type /Pages, /Kids, /Count
Page단일 페이지/Type /Page, /MediaBox, /Resources, /Contents, /Parent
Resources자원 컨테이너/Font (글꼴 사전)
Font글꼴 정의/Type /Font, /BaseFont, /Subtype
Contents콘텐츠 스트림그리기 명령의 스트림

실전: hello-broken.pdf 손으로 작성하기

새 파일 hello-broken.pdf를 만들고 아래 내용을 완전히 붙여넣습니다:

%PDF-1.0
1 0 obj
<< /Type /Pages
   /Count 1
   /Kids [2 0 R]
>>
endobj

2 0 obj
<< /Type /Page
   /MediaBox [0 0 612 792]
   /Resources 3 0 R
   /Parent 1 0 R
   /Contents [4 0 R]
>>
endobj

3 0 obj
<< /Font
     << /F0
          << /Type /Font
             /BaseFont /Times-Italic
             /Subtype /Type1 >>
     >>
>>
endobj

4 0 obj
<< >>
stream
1. 0. 0. 1. 50. 700. cm
BT
 /F0 36. Tf
 (Hello, World!) Tj
ET
endstream
endobj

5 0 obj
<< /Type /Catalog
   /Pages 1 0 R
>>
endobj

xref
0 6
trailer
<< /Size 6
   /Root 5 0 R
>>
startxref
0
%%EOF

왜 이 파일이 "잘못된" 것인가요?

우리는 일부 내용을 의도적으로 생략하거나 잘못 기입했습니다:

누락/오류 항목설명
xref 오프셋각 객체의 실제 바이트 오프셋을 기입하지 않았습니다.
startxref0으로 기입했으며, 이는 xref의 실제 위치가 아닙니다.
/Length콘텐츠 스트림의 길이를 선언하지 않았습니다.
이진 마크헤더의 이진 식별 행이 누락되었습니다.

이들은 리더가 필요로 하는 중요한 정보로, 누락되면 열 수 없거나 오류로 열 수 있습니다.


주요 콘텐츠 스트림 명령어 상세 설명

콘텐츠 스트림은 객체 4 0 objstream ... endstream 사이에 있으며, 각 줄을 설명합니다:

1. 0. 0. 1. 50. 700. cm   ← 변환 행렬 설정 (주의: 1.은 부동 소수점 수 1.0을 나타냄)
BT                         ← 텍스트 객체 시작
 /F0 36. Tf                ← 글꼴 F0 선택, 글꼴 크기 36pt
 (Hello, World!) Tj        ← 문자열 그리기
ET                         ← 텍스트 객체 종료

변환 행렬 cm 작업자

1 0 0 1 50 700 cm는 6 요소의 변환 행렬 [a b c d e f]에 해당하며, 다음과 같습니다:

| a  b  0 |     | 1  0  0 |
| c  d  0 |  =  | 0  1  0 |
| e  f  1 |     | 50 700 1 |

a=1, b=0, c=0, d=1일 때, 이는 순수 이동 행렬로, 좌표계 원점(즉, 이후 그리기 작업의 (0,0) 점)을 (50, 700)으로 이동시킵니다. 이동하지 않으면 기본 원점은 페이지의 왼쪽 하단에 있습니다.

텍스트 작업자

작업자의미예시
BTBegin Text, 텍스트 객체 시작BT
ETEnd Text, 텍스트 객체 종료ET
Tf글꼴 및 글꼴 크기 설정/F0 36 Tf
Tj문자열 그리기(Hello!) Tj

pdftk를 사용하여 열 수 있는 PDF로 수정하기

hello-broken.pdf가 있는 디렉토리에서 다음을 실행합니다:

pdftk hello-broken.pdf output hello.pdf

任意 PDF 리더로 hello.pdf를 열면 페이지에 "Hello, World!" (Times-Italic 글꼴, 36pt, 페이지 왼쪽 상단에 위치)가 나타나는 것을 볼 수 있어야 합니다.

pdftk가 보완한 내용은 무엇인가요?

보완 항목설명
이진 마크 행%PDF-1.0 뒤에 인쇄할 수 없는 문자를 추가하여 이진 파일로 인식되도록 합니다.
/Length콘텐츠 스트림의 바이트 길이를 계산하고 추가합니다.
xref 테이블각 객체의 바이트 오프셋을 계산하여 기입합니다.
startxrefxref 테이블의 실제 시작 위치를 기입합니다.

왜 xref / trailer / startxref가 필요할까요?

핵심 목적: 임의 접근

500 페이지의 PDF를 상상해 보세요. xref가 없다면 리더는 450 페이지를 표시하기 위해 처음부터 449 페이지까지 해석해야 합니다 - 이는 너무 느립니다.

xref가 있으면 리더는 다음과 같이 할 수 있습니다:

  1. 먼저 startxref를 읽고 → xref 위치 찾기
  2. trailer를 읽고 → 루트 객체 /Root 찾기
  3. 루트 객체를 따라가며 → 450 페이지 객체로 직접 점프
  4. xref를 통해 해당 객체의 바이트 오프셋을 확인하고 → 직접 seek하여 읽기

시간 복잡도가 O(n)에서 O(1)로 감소합니다.


본문 연습

hello-broken.pdf를 실제로 수정한 후, pdftk로 다시 수정하여 효과를 관찰해 보세요:

연습수정 내용관찰 포인트
A(Hello, World!)를 다른 영어 문구로 변경텍스트 변화
B3612 또는 72로 변경글꼴 크기 변화
C50 70050 100으로 변경위치 하강 (PDF 좌표계 원점은 왼쪽 하단에 있음)
D/Times-Italic/Helvetica 또는 /Courier로 변경글꼴 변화
E/MediaBox [0 0 612 792][0 0 595 842]로 변경용지가 US Letter에서 A4로 변경

: PDF 좌표계 원점은 페이지의 왼쪽 하단에 있으며, Y축은 위로 향합니다. (50, 700)은 왼쪽에서 50pt, 아래에서 700pt 떨어진 지점을 나타냅니다.


자주 묻는 질문

Q: 왜 Type1 내장 글꼴을 사용하고 TrueType을 사용하지 않나요?

A: Type1의 14가지 표준 글꼴(타임스, 헬베티카, 쿠리어 등)은 PDF 리더가 반드시 내장해야 하며, 글꼴 파일을 삽입할 필요가 없어 가장 간단합니다. 실제 상황에서는 일반적으로 글꼴을 삽입하여 플랫폼 간 일관성을 보장해야 합니다.

Q: /MediaBox [0 0 612 792]의 숫자는 무엇인가요?

A: 단위는 포인트(1 포인트 = 1/72 인치)입니다. 612 × 792 포인트 = 8.5 × 11 인치 = US Letter 용지. A4는 595 × 842 포인트입니다.

Q: 생성 번호(예: 2 0 R0)는 무엇인가요?

A: 증분 업데이트에 사용됩니다. 객체가 수정되면 생성 번호가 1 증가합니다. 새로 생성된 PDF의 모든 객체 생성 번호는 일반적으로 0입니다.


다음 편 예고

제 02 편에서는 "손으로 콘텐츠 스트림 작성" 방식을 계속 사용하여 가장 기본적인 그래픽 경로 작업을 추가합니다:

  • m (moveto), l (lineto): 경로 정의
  • S (stroke): 테두리 그리기
  • re (rectangle), f (fill): 사각형 그리기 및 채우기

같은 페이지에서 제목 텍스트 + 수평 구분선 + 사각형을 동시에 그려 "글씨를 쓸 수 있는" 단계에서 "그래픽을 그릴 수 있는" 단계로 나아갑니다.

Copyright © 2026 Doclingo. All Rights Reserved.
제품
문서 번역
더 많은 도구
API
기업용
리소스
요금제
정보
고객센터
서비스 약관
개인정보 보호 정책
버전 업데이트
블로그
연락처 정보
이메일: support@doclingo.ai
한국어
Copyright © 2026 Doclingo. All Rights Reserved.