| name | pdf |
| description | Use this skill whenever the user wants to do anything with PDF files. This includes reading or extracting text/tables from PDFs, combining or merging multiple PDFs into one, splitting PDFs apart, rotating pages, adding watermarks, creating new PDFs, filling PDF forms, encrypting/decrypting PDFs, extracting images, and OCR on scanned PDFs to make them searchable. If the user mentions a .pdf file or asks to produce one, use this skill. |
PDF Processing Guide
Overview
This guide covers essential PDF processing operations using Python libraries.
Use the execute_code tool to run the Python code shown below.
All generated files should be saved to the artifacts/ directory.
ํ๊ตญ์ด ํฐํธ ์ค์ (ํ์ โ ๋ชจ๋ PDF ์์ฑ ์ฝ๋์ ํฌํจํ ๊ฒ)
ํ๊ตญ์ด๊ฐ ํฌํจ๋ PDF๋ฅผ ๋ง๋ค ๋๋ ๋ฐ๋์ ํ๊ตญ์ด ํฐํธ๋ฅผ ๋ฑ๋กํด์ผ ํฉ๋๋ค.
execute_code ์คํ ์ ์์
๋๋ ํฐ๋ฆฌ๋ WORKING_DIR(application ํด๋)์ด๋ฉฐ, ๋ฒ๋ค TTF๋ WORKING_DIR/assets/NanumGothic-Regular.ttf ์
๋๋ค. ๊ฒฝ๋ก๊ฐ ๋ถ์์ ํ๋ฉด os.path.join(WORKING_DIR, "assets", "NanumGothic-Regular.ttf")๋ฅผ ์ฌ์ฉํ์ธ์.
๊ถ์ฅ: ๋ฐํ์์ ์ด๋ฏธ ์ฃผ์
๋ register_korean_font() ๋ฅผ ํธ์ถํ์ธ์. ์ด ํจ์๋ ์ Nanum TTF๋ฅผ ์ต์ฐ์ ์ผ๋ก ๋ฑ๋กํ๊ณ , ์์ผ๋ฉด CID HYGothic-Medium ๋ฑ์ผ๋ก ํด๋ฐฑํฉ๋๋ค.
font_name = register_korean_font()
์ค์: macOS์ AppleSDGothicNeo.ttc๋ PostScript outlines์ด๋ผ reportlab์์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
ParagraphStyle์์ fontName ์ค๋ณต ์ค๋ฅ ๋ฐฉ์ง
TypeError: ParagraphStyle() got multiple values for keyword argument 'fontName' ๋ ๊ฐ์ ํธ์ถ์์ fontName์ด ๋ ๋ฒ ๋์ด๊ฐ ๋ ๋ฐ์ํฉ๋๋ค. ์: ParagraphStyle(..., fontName=font_name, **extra) ์ธ๋ฐ extra ์์๋ fontName์ด ์๋ ๊ฒฝ์ฐ. ํฌํผ์ ์คํ์ผ ์ต์
์ ๋๊ธธ ๋๋ extra.pop("fontName", None) ํ๊ฑฐ๋, fontName์ ๋ช
์ ์ธ์๋ง ์ฐ๊ณ **kwargs์๋ ๋ฃ์ง ๋ง์ธ์.
๊ธ๋จธ๋ฆฌ ๊ธฐํธ(๋ถ๋ฆฟ) ํ๊ธ์ ๋ณธ๋ฌธ๊ณผ ๊ฐ์ด bulletFontName=font_name ์ ์ง์ ํ๋ ๊ฒ์ด ์์ ํฉ๋๋ค.
ํฐํธ ์ฐ์ ์์ (register_korean_font์ ๋์ผ):
WORKING_DIR/assets/NanumGothic-Regular.ttf (TTF โ ์ ์ฅ์์ ํฌํจ ๊ถ์ฅ)
- Linux/macOS ์ผ๋ฐ ๊ฒฝ๋ก์ Nanum TTF
- CID ํฐํธ
HYGothic-Medium (reportlab ๋ด์ฅ)
- ์ตํ ํด๋ฐฑ
Helvetica (ํ๊ตญ์ด ๋ฏธ์ง์)
Creating New PDFs (reportlab)
Basic PDF Creation (์๋ฌธ ์ ์ฉ)
from reportlab.lib.pagesizes import A4
from reportlab.pdfgen import canvas
import os
os.makedirs("artifacts", exist_ok=True)
c = canvas.Canvas("artifacts/hello.pdf", pagesize=A4)
width, height = A4
c.drawString(100, height - 100, "Hello World!")
c.save()
ํ๊ตญ์ด PDF ์์ฑ (๊ถ์ฅ ํจํด)
import os
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import inch
from reportlab.lib import colors
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, PageBreak
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
font_name = register_korean_font()
styles = getSampleStyleSheet()
styles.add(ParagraphStyle("Title_KO", parent=styles["Title"], fontName=font_name, fontSize=20, spaceAfter=20, textColor=colors.HexColor("#1a1a2e")))
styles.add(ParagraphStyle("H1_KO", parent=styles["Heading1"], fontName=font_name, fontSize=16, spaceAfter=14, textColor=colors.HexColor("#16213e")))
styles.add(ParagraphStyle("H2_KO", parent=styles["Heading2"], fontName=font_name, fontSize=14, spaceAfter=12, textColor=colors.HexColor("#0f3460")))
styles.add(ParagraphStyle("Normal_KO", parent=styles["Normal"], fontName=font_name, fontSize=10, leading=14, spaceAfter=8))
styles.add(ParagraphStyle("Bullet_KO", parent=styles["Normal"], fontName=font_name, bulletFontName=font_name, fontSize=10, leading=14, leftIndent=20, bulletIndent=10))
os.makedirs("artifacts", exist_ok=True)
doc = SimpleDocTemplate("artifacts/report.pdf", pagesize=A4,
topMargin=0.8*inch, bottomMargin=0.8*inch, leftMargin=0.75*inch, rightMargin=0.75*inch)
story = []
story.append(Paragraph("๋ณด๊ณ ์ ์ ๋ชฉ", styles["Title_KO"]))
story.append(Spacer(1, 16))
story.append(Paragraph("1. ๊ฐ์", styles["H1_KO"]))
story.append(Paragraph("์ด ๋ณด๊ณ ์๋ ์์ ๋ด์ฉ์ ๋ด๊ณ ์์ต๋๋ค.", styles["Normal_KO"]))
story.append(Spacer(1, 8))
story.append(Paragraph("2. ์์ธ ๋ด์ฉ", styles["H1_KO"]))
story.append(Paragraph("โข ์ฒซ ๋ฒ์งธ ํญ๋ชฉ", styles["Bullet_KO"]))
story.append(Paragraph("โข ๋ ๋ฒ์งธ ํญ๋ชฉ", styles["Bullet_KO"]))
story.append(PageBreak())
story.append(Paragraph("3. ๋ถ๋ก", styles["H1_KO"]))
story.append(Paragraph("์ถ๊ฐ ๋ด์ฉ์
๋๋ค.", styles["Normal_KO"]))
doc.build(story)
print("PDF ์์ฑ ์๋ฃ: artifacts/report.pdf")
ํ๊ตญ์ด ํ
์ด๋ธ์ด ํฌํจ๋ PDF
import os
from reportlab.lib.pagesizes import A4
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib import colors
font_name = register_korean_font()
styles = getSampleStyleSheet()
styles.add(ParagraphStyle("Title_KO", parent=styles["Title"], fontName=font_name, fontSize=18, textColor=colors.HexColor("#1a1a2e")))
styles.add(ParagraphStyle("Normal_KO", parent=styles["Normal"], fontName=font_name, fontSize=10))
os.makedirs("artifacts", exist_ok=True)
doc = SimpleDocTemplate("artifacts/table_report.pdf", pagesize=A4)
data = [
["ํญ๋ชฉ", "์ค๋ช
", "๋น์ฉ"],
["์๋ฒ", "EC2 ์ธ์คํด์ค", "$120"],
["์คํ ๋ฆฌ์ง", "S3 ๋ฒํท", "$45"],
["๋ฐ์ดํฐ๋ฒ ์ด์ค", "RDS PostgreSQL", "$89"],
]
table = Table(data, colWidths=[120, 200, 80])
table.setStyle(TableStyle([
("BACKGROUND", (0, 0), (-1, 0), colors.HexColor("#e8eaf6")),
("TEXTCOLOR", (0, 0), (-1, 0), colors.HexColor("#1a1a2e")),
("FONTNAME", (0, 0), (-1, -1), font_name),
("FONTSIZE", (0, 0), (-1, -1), 10),
("GRID", (0, 0), (-1, -1), 0.5, colors.HexColor("#bdbdbd")),
("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
("TOPPADDING", (0, 0), (-1, -1), 6),
("BOTTOMPADDING", (0, 0), (-1, -1), 6),
]))
doc.build([
Paragraph("๋น์ฉ ๋ถ์ ๋ณด๊ณ ์", styles["Title_KO"]),
Spacer(1, 16),
table
])
print("PDF ์์ฑ ์๋ฃ: artifacts/table_report.pdf")
Important Notes for reportlab
- ํ๊ตญ์ด ํ
์คํธ๋ฅผ ์ฌ์ฉํ ๋๋
register_korean_font()๋ฅผ ํธ์ถํ์ธ์ (execute_code ํ๊ฒฝ์์ ์ ์ญ ์ ๊ณต).
- macOS์
AppleSDGothicNeo.ttc๋ PostScript outlines์ด๋ผ reportlab์์ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์ ๋ ์ฌ์ฉํ์ง ๋ง์ธ์.
- ๋ฒ๋ค TTF๋
WORKING_DIR/assets/NanumGothic-Regular.ttf์ด๋ฉฐ, ์์ผ๋ฉด CID HYGothic-Medium ๋ฑ์ผ๋ก ํด๋ฐฑํฉ๋๋ค.
- ํฐํธ๋ฅผ ๋ฑ๋กํ์ง ์์ผ๋ฉด ํ๊ตญ์ด๊ฐ ๊นจ์ง๊ฑฐ๋ ๋น ์ฌ๊ฐํ(โก)์ผ๋ก ํ์๋ฉ๋๋ค.
- NEVER use Unicode subscript/superscript characters (โโโโโโ
โโโโ) in ReportLab PDFs โ use
<sub> and <super> tags instead.
Reading PDFs (pypdf)
Extract Text
from pypdf import PdfReader
reader = PdfReader("input.pdf")
text = ""
for page in reader.pages:
text += page.extract_text()
print(text)
Merge PDFs
from pypdf import PdfWriter, PdfReader
writer = PdfWriter()
for pdf_file in ["doc1.pdf", "doc2.pdf"]:
reader = PdfReader(pdf_file)
for page in reader.pages:
writer.add_page(page)
with open("artifacts/merged.pdf", "wb") as output:
writer.write(output)
Split PDF
from pypdf import PdfReader, PdfWriter
reader = PdfReader("input.pdf")
for i, page in enumerate(reader.pages):
writer = PdfWriter()
writer.add_page(page)
with open(f"artifacts/page_{i+1}.pdf", "wb") as output:
writer.write(output)
Rotate Pages
from pypdf import PdfReader, PdfWriter
reader = PdfReader("input.pdf")
writer = PdfWriter()
page = reader.pages[0]
page.rotate(90)
writer.add_page(page)
with open("artifacts/rotated.pdf", "wb") as output:
writer.write(output)
Password Protection
from pypdf import PdfReader, PdfWriter
reader = PdfReader("input.pdf")
writer = PdfWriter()
for page in reader.pages:
writer.add_page(page)
writer.encrypt("userpassword", "ownerpassword")
with open("artifacts/encrypted.pdf", "wb") as output:
writer.write(output)
Table Extraction (pdfplumber)
import pdfplumber
with pdfplumber.open("input.pdf") as pdf:
for page in pdf.pages:
text = page.extract_text()
tables = page.extract_tables()
for table in tables:
for row in table:
print(row)
Quick Reference
| Task | Library | Key Function |
|---|
| Create PDF | reportlab | SimpleDocTemplate / canvas |
| Read/Merge/Split PDF | pypdf | PdfReader / PdfWriter |
| Extract tables | pdfplumber | page.extract_tables() |
| Password protect | pypdf | writer.encrypt() |
| Rotate pages | pypdf | page.rotate() |