from pathlib import Path
from hwpapi import App
src_dir = Path("contracts")
dst_dir = Path("contracts_pdf")
dst_dir.mkdir(exist_ok=True)
with App() as app:
for hwp in sorted(src_dir.glob("*.hwp")):
app.open(str(hwp.absolute()))
out = dst_dir / hwp.with_suffix(".pdf").name
app.save(str(out.absolute()))
app.close()
print(f"β {hwp.name} β {out.name}")Batch-process a folder of .hwp files
Open β transform β save, for every file in a directory
μλν μμ² 1μμ β ν΄λμ μλ λͺ¨λ .hwp λ₯Ό μ°¨λ‘λ‘ μ΄κ³ , μ΄λ€ λ³νμ μ μ©ν λ€, λ€λ₯Έ ν΄λμ μ μ₯. ν κ°μ App μΈμ€ν΄μ€λ₯Ό μ¬μ¬μ©νκΈ° λλ¬Έμ HWP μμ§μ Nλ² λμ°μ§ μμλ λμ΄ λΉ λ¦
λλ€.
1. κ°μ₯ λ¨μν νν β λͺ¨λ .hwp β .pdf
2. μΌκ΄ find/replace ν μ μ₯
from pathlib import Path
from hwpapi import App
substitutions = {
"[[YEAR]]": "2026",
"[[QUARTER]]": "Q1",
"[[CONTACT]]": "ops@example.com",
}
with App() as app:
for hwp in Path("templates").glob("*.hwp"):
app.open(str(hwp.absolute()))
for needle, replacement in substitutions.items():
app.doc.find_replace(needle, replacement, all=True)
out = Path("rendered") / hwp.name
out.parent.mkdir(exist_ok=True)
app.save(str(out.absolute()))
app.close()3. λ°μ΄ν° ν ν β λ¬Έμ ν κ° (λ©μΌλ¨Έμ§ ν μν¬νλ‘)
CSV/JSON μ ν λ¨μλ‘ ν
νλ¦Ώμ μ±μ Nκ°μ κ²°κ³Όλ¬Όμ λ§λλλ€. fill-fields λ μνΌμ κ°μ ν¨ν΄μ΄μ§λ§, μ¬κΈ°μλ μΆλ ₯μ PDF λ‘ λ°λ‘ λ¨κ΅½λλ€.
import csv
from pathlib import Path
from hwpapi import App
template = "templates/invoice.hwp"
out_dir = Path("out_invoices")
out_dir.mkdir(exist_ok=True)
with App() as app, open("clients.csv", encoding="utf-8-sig") as f:
reader = csv.DictReader(f)
for row in reader:
app.open(template)
for field, value in row.items():
if field in app.doc.fields:
app.doc.fields[field] = value
pdf = out_dir / f"invoice-{row['client_id']}.pdf"
app.save(str(pdf.absolute()))
app.close()
print(f" β {pdf.name}")4. κ²¬κ³ ν μλ¬ μ²λ¦¬ β ν νμΌ μ€ν¨ν΄λ μ§ν
λ°°μΉ μμ
μμλ ν νμΌμ΄ κΉ¨μ‘λ€κ³ λ©μΆλ©΄ μ λ©λλ€. hwpapi.errors.HwpApiError κ³μ΄μ μ‘μ λ‘κ·Ένκ³ λ€μ νμΌλ‘ λμ΄κ°μΈμ.
import logging
from pathlib import Path
from hwpapi import App
from hwpapi.errors import HwpApiError
logging.basicConfig(level=logging.INFO)
log = logging.getLogger("batch")
results = {"ok": 0, "fail": []}
with App() as app:
for hwp in Path("input").glob("*.hwp"):
try:
app.open(str(hwp.absolute()))
app.save(f"output/{hwp.stem}.pdf")
app.close()
results["ok"] += 1
except HwpApiError as exc:
log.warning(f"skip {hwp.name}: {exc}")
results["fail"].append(hwp.name)
try:
app.close(save=False)
except Exception:
pass
log.info(f"converted {results['ok']}, failed {len(results['fail'])}")5. μ§νλ₯ νμ β tqdm ν΅ν©
from pathlib import Path
from hwpapi import App
from tqdm import tqdm
files = sorted(Path("input").glob("*.hwp"))
with App() as app:
for hwp in tqdm(files, desc="HWPβPDF"):
app.open(str(hwp.absolute()))
app.save(f"output/{hwp.stem}.pdf")
app.close()λ³λ ¬ μ²λ¦¬ μ£Όμ β HWP COM μλ²λ λ¨μΌ μ€λ λλ₯Ό κ°μ ν©λλ€. multiprocessing μΌλ‘ νλ‘μΈμ€λ₯Ό λΆλ¦¬νμ§ μλ ν threading / asyncio λ‘ λ³λ ¬νν΄λ λΉ¨λΌμ§μ§ μμ΅λλ€ (μ€νλ € dead-lock μν). νλ‘μΈμ€ λ¨μλ‘ λλ λλ κ° νλ‘μΈμ€κ° μ체 App() μ λ€μ΄μΌ νλ©°, HWP μΈμ€ν΄μ€ μλ§νΌ λ©λͺ¨λ¦¬λ₯Ό λ¨Ήμ΅λλ€.
See also
- Recipe: export-formats β λ€μν μΆλ ₯ ν¬λ§·
- Recipe: fill-fields β νλ μΌκ΄ μ±μ°κΈ°
- Recipe: find-replace
- Reference: errors