GitPedia
airmang

airmang/python-hwpx

Pure Python HWPX automation: read, edit, generate, and validate documents without Hancom Office.

16 Releases
Latest: yesterday
v2.15.0Latest
github-actions[bot]github-actions[bot]ยทyesterdayยทJune 27, 2026
GitHub

๐Ÿ“ฆ ์ถ”๊ฐ€

  • `HwpxDocument.set_paragraph_format(keep_with_next=, keep_lines=, page_break_before=)` โ€” ๋ฌธ๋‹จ keep-together ํ”Œ๋ž˜๊ทธ๋ฅผ ์—”์ง„ `ensure_paragraph_format(break_setting=)`๋กœ ์ „๋‹ฌํ•œ๋‹ค(์ƒˆ paraPr ๋ฐœํ–‰, ๊ธฐ์กด paraPr ๋ฏธ์ˆ˜์ • = ๋ฌด์†์‹ค). ์‹œํ—˜์ง€ ์กฐํŒ ๋“ฑ์—์„œ ํ•œ ๋ฌธํ•ญ์ด ๋‹จ/์ชฝ ๊ฒฝ๊ณ„์—์„œ ์ž˜๋ฆฌ์ง€ ์•Š๊ฒŒ ๋ฌถ์„ ๋•Œ ์“ด๋‹ค.
  • `hwpx.exam`: re-typeset an authored exam (Markdown) into a school form `.hwpx`
  • โ€” Exam IR + strict md parser, form profiler (roleโ†’existing form style),
  • keep-together body composition (insert into the form's body region, never
  • append; ๊ด€๋ฆฌ๋ฐ•์Šค + footer preserved byte-identical), and an oracle convergence
  • driver `compose_exam_into_form`. The driver renders via Hancom and, when the
  • composed ๋ฌธํ•ญ are in the extractable text layer, verifies ๋ฌธํ•ญ-split / overflow
  • / placeholder integrity (inserting column/page breaks to converge); when they
  • + 4 more

๐Ÿ“ฆ ์ˆ˜์ •

  • `paragraph.add_picture` โ€” `treat_as_char=True`(inline)์ธ๋ฐ `pos_overrides`(PAPER relTo/offset)๋ฅผ ์ฃผ๋ฉด ๋ชจ์ˆœ๋œ inline/floating `<hp:pos>`๋ฅผ ๋ฐฉ์ถœํ•˜๋˜ ๊ฒƒ์„ `ValueError`๋กœ fail-fast. floating ๋ฐฐ์น˜๋Š” `treat_as_char=False`์—์„œ๋งŒ.
v2.13.0
github-actions[bot]github-actions[bot]ยท4d agoยทJune 24, 2026
GitHub

๐Ÿ“ฆ ์ถ”๊ฐ€

  • `hwpx.conformance` โ€” VisualComplete ์ ํ•ฉ์„ฑ ์ฝ”ํผ์Šค + ๋ฐฐ์ง€ ๋“ฑ๊ธ‰(plan ยง2 Phase G). `hwpx-conformance run`์ด ์ฝ”ํผ์Šค๋ฅผ 4๊ฐœ ๋ฐฐ์ง€ ๋“ฑ๊ธ‰(Open-Safe/Semantic-Safe/Form-Safe/VisualComplete)์œผ๋กœ ์ฑ„์ ํ•˜๊ณ  ๋“ฑ๊ธ‰๋ณ„ ํ†ต๊ณผ์œจ์„ ์‚ฐ์ถœํ•ฉ๋‹ˆ๋‹ค. ์ž„๊ณ„๊ฐ’์€ ์—„๊ฒฉ ๊ธฐ๋ณธ๊ฐ’(๊ตฌ์กฐ ๋“ฑ๊ธ‰ 100%, ํผ์…‹ overflow 0%, VisualComplete โ‰ฅ95%). golden ๋ฒ ์ด์Šค๋ผ์ธ(`tests/conformance/golden/structural.json`) ๋Œ€๋น„ ํšŒ๊ท€๋ฅผ ์ˆซ์ž๋กœ ๊ฐ์ง€ํ•˜๋ฉฐ(`--check`), CI๊ฐ€ ๊ตฌ์กฐ ๋“ฑ๊ธ‰์„ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค. ์–ด์Šˆ์–ด๋Ÿฐ์Šค ๋“ฑ๊ธ‰์€ ์ ˆ๋Œ€ ์„ž์ง€ ์•Š์Šต๋‹ˆ๋‹ค(ยง0.0): ํ•œ์ปด์ด ์—†๋Š” ๊ตฌ์กฐ ์‹คํ–‰์€ VisualComplete๋ฅผ `unverified`๋กœ ๋ณด๊ณ ํ•˜๊ณ , ์˜ค๋ผํด ์‹คํ–‰(๋„๋‹ฌ ๊ฐ€๋Šฅํ•œ ํ•œ์ปด ๋ฐฑ์—”๋“œ)๋งŒ VisualComplete๋ฅผ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค. ์ผ€์ด์Šค์— `before`(+์„ ํƒ์  `editMask`)๋ฅผ ์„ ์–ธํ•˜๋ฉด VisualComplete๊ฐ€ ์˜ค๋ผํด์˜ before/after diff ๊ฒฝ๋กœ๋กœ ๊ฒŒ์ดํŠธ๋ฉ๋‹ˆ๋‹ค(๋งˆ์Šคํฌ ๋ฐ– ๋ณ€๊ฒฝยท๊ธ€์ž ๊ฒน์นจ์„ ์žก์Œ). `expectVisualDefect`๋Š” ์ผ๋ถ€๋Ÿฌ ๊นจ๋œจ๋ฆฐ ์Œ์„ positive control๋กœ ์‚ผ์•„ ๊ฒŒ์ดํŠธ๊ฐ€ ๊ฒฐํ•จ์„ ์‹ค์ œ๋กœ ์žก๋Š”์ง€ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค. (์‹ค์ธก: ์‹ค์ œ ํ•œ์ปด-์ €์žฅ ์ฝ”ํผ์Šค์—์„œ clean ์Œ์€ ํ†ต๊ณผ, out-of-slot ๋ณ€๊ฒฝ์€ catch.)
v2.11.1
github-actions[bot]github-actions[bot]ยท2w agoยทJune 12, 2026
GitHub

๐Ÿ“ฆ ์ˆ˜์ •

  • `create_document_from_plan()`์˜ `heading` block๊ณผ builder `Heading`์ด ๊ธฐ๋ณธ ํ…œํ”Œ๋ฆฟ์˜ `๊ฐœ์š” N`/`Outline N` ๋ฌธ๋‹จ ์Šคํƒ€์ผ์„ ์‹ค์ œ๋กœ ์ ์šฉํ•˜๋„๋ก ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ƒ์„ฑ ๋ฌธ์„œ๊ฐ€ ํ•œ์ปด ๊ฐœ์š”/๋ฌธ์„œ ํƒ์ƒ‰๊ณผ MCP outline readback์—์„œ ๊ตฌ์กฐํ™”๋œ ์ œ๋ชฉ์œผ๋กœ ์ธ์‹๋ฉ๋‹ˆ๋‹ค.
  • document-plan ๊ธฐ๋ณธ ์Šคํƒ€์ผ preset์— ์ œ๋ชฉ 18pt, ๋ถ€์ œ 12pt, ์žฅ ์ œ๋ชฉ 14pt ๊ธ€์ž ํฌ๊ธฐ์™€ ํ•จ์ดˆ๋กฌ๋ฐ”ํƒ• ํฐํŠธ๋ฅผ ์ ์šฉํ•ด ๋ณด๊ณ ์„œ ์ƒ์„ฑ ์‹œ ์ œ๋ชฉ/๋ณธ๋ฌธ ์‹œ๊ฐ ์œ„๊ณ„๊ฐ€ ๋ช…ํ™•ํ•˜๊ฒŒ ๋ณด์ด๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.
v2.11.0
github-actions[bot]github-actions[bot]ยท2w agoยทJune 11, 2026
GitHub

๐Ÿ“ฆ ์ถ”๊ฐ€

  • ์‹œ๋“œ ๊ฒฐ์ •์  ํผ์ง• ์ˆ˜๋ ด ๋ฃจํ”„ `hwpx.tools.fuzz`(์‹œ๋‚˜๋ฆฌ์˜ค ์นดํƒˆ๋กœ๊ทธยท์ƒ์„ฑ๊ธฐยท3์ค‘ ์˜ค๋ผํด ๋Ÿฌ๋„ˆยท์ตœ์†Œํ™”)์™€ `tests/fixtures/fuzz_regressions` ํšŒ๊ท€ ๋ฐ•์ œ ์ˆ˜ํŠธ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋ ˆ์ด์•„์›ƒ ๊ทผ์‚ฌ ํ”„๋ฆฌ๋ทฐ ๋ Œ๋”๋Ÿฌ `hwpx.tools.layout_preview`๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค(ํŽ˜์ด์ง€ ๋ฐ•์Šคยทํ‘œยท์—ฌ๋ฐฑ ๊ทผ์‚ฌ HTML/PNG โ€” ์—์ด์ „ํŠธ ์ž๊ธฐ๊ฒ€์ฆ์šฉ).
  • section XML ๋ฐ”์ดํŠธ splice ๊ธฐ๋ฐ˜ ๋ฌธ๋‹จ ํŒจ์น˜ ๊ฒฝ๋กœ `hwpx.patch`๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค(๋ฏธ์ˆ˜์ • ์˜์—ญ ๋ฐ”์ดํŠธ ๋ณด์กด).
  • ๊ทธ๋ฆผ ์ž์‚ฐ ์•ˆ์ „ ์‚ฝ์ž…ยท์น˜ํ™˜ API(`add_picture` ๋ฐ ์น˜ํ™˜ ์›Œํฌํ”Œ๋กœ)์™€ manifest ๊ฒ€์ฆ์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๊ธฐ์กด ๋ฌธ์„œ ์„œ์‹ ํŽธ์ง‘ API๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค: ๋ฌธ๋‹จ ์ •๋ ฌยท์ค„๊ฐ„๊ฒฉยท๋“ค์—ฌ์“ฐ๊ธฐยท๋ฌธ๋‹จ ๊ฐ„๊ฒฉ, ์šฉ์ง€ยท์—ฌ๋ฐฑยท๋ฐฉํ–ฅ, ๋จธ๋ฆฌ๋ง/๊ผฌ๋ฆฌ๋งยท์ชฝ๋ฒˆํ˜ธ, ๋ถˆ๋ฆฟ/๋ฒˆํ˜ธ ํ˜•์‹.
  • ๋ˆ„๋ฆ„ํ‹€(ํด๋ฆญํžˆ์–ด ํ•„๋“œ) 1๊ธ‰ ์กฐํšŒยท์ฑ„์›€ API๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๊ณต๋ฌธ์„œ ์ž‘์„ฑ๊ทœ์ • lint `hwpx.tools.official_lint`(ํ•ญ๋ชฉ๊ธฐํ˜ธ ์œ„๊ณ„ยท"๋." ํ‘œ์‹œยท๋ถ™์ž„ยท๋‚ ์งœ ํ‘œ๊ธฐ)์™€ ๊ฒฐ์žฌ๋ž€ ํ”„๋ฆฌ์…‹์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๊ณ ๊ธ‰ ์ƒ์„ฑ๊ธฐ `hwpx.tools.advanced_generators`๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค: ์‚ฌ์ง„๋Œ€์ง€(`build_image_grid`)ยทํšŒ์˜ ๋ช…ํŒจ(`build_meeting_nameplates`)ยทํ‘œ ๊ธฐ๋ฐ˜ ์กฐ์ง๋„.
  • + 4 more

๐Ÿ“ฆ ๋ณ€๊ฒฝ

  • `hwpx.oxml.document` ๋ชจ๋†€๋ฆฌ์Šค(5,700์—ฌ ์ค„)๋ฅผ ์š”์†Œ๋ณ„ ๋ชจ๋“ˆ(`_document_impl` ์™ธ 18๊ฐœ)๋กœ ๋ถ„ํ• ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ณต๊ฐœ API๋Š” ๋ณ€ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๐Ÿ“ฆ ์ˆ˜์ •

  • ์‹ ๋ขฐํ•  ์ˆ˜ ์—†๋Š” ์ž…๋ ฅ ํŒŒ์‹ฑ์„ ๊ฐ•๊ฑดํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค(`hwpx.opc.security`): XML entity ์„ ์–ธ ๊ฑฐ๋ถ€์™€ ๊นŠ์ด/ํฌ๊ธฐ ํ•œ๋„, ZIP ์••์ถ•๋น„ยท๋ฉค๋ฒ„ ์ˆ˜ ํ•œ๋„๋ฅผ ์ ์šฉํ•ด entity ํญํƒ„ยท์••์ถ• ํญํƒ„ ์ž…๋ ฅ์„ ์•ˆ์ „ํ•˜๊ฒŒ ๊ฑฐ๋ถ€ํ•ฉ๋‹ˆ๋‹ค.
v2.10.3
github-actions[bot]github-actions[bot]ยท2w agoยทJune 9, 2026
GitHub

๐Ÿ“ฆ ์ถ”๊ฐ€

  • `hwpx.tools.validate_editor_open_safety()`์™€ `EditorOpenSafetyReport`๋ฅผ ์ถ”๊ฐ€ํ•ด package validation, document validation, ์žฌ์˜คํ”ˆ ๊ฒ€์ฆ์„ ํ•œ ๊ณณ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“ฆ ์ˆ˜์ •

  • ํ…์ŠคํŠธ๋ฅผ ์ค„์ด๋Š” ์ €์ˆ˜์ค€ ํŽธ์ง‘ ๋’ค stale `hp:linesegarray`๊ฐ€ ๋‚จ์•„ ํ•œ์ปด ํŽธ์ง‘๊ธฐ์—์„œ ์—ด๋ฆฌ์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ๋ฅผ ๋ง‰๊ธฐ ์œ„ํ•ด, ์ €์žฅ ์ง์ „ plain-text ๋ฌธ๋‹จ์˜ ๋ฌดํšจํ•œ layout cache๋ฅผ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.
  • ํŽธ์ง‘๋œ section๊ณผ public ์ €์ˆ˜์ค€ section write ๊ฒฝ๋กœ๋Š” ๋ชจ๋“  `hp:lineSegArray` layout cache๋ฅผ ์ œ๊ฑฐํ•ด, ๋ณตํ•ฉ ๋ฌธ๋‹จ์ฒ˜๋Ÿผ stale ์—ฌ๋ถ€๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๊ณ„์‚ฐํ•˜๊ธฐ ์–ด๋ ค์šด ๊ฒฝ์šฐ๋„ ํŽธ์ง‘๊ธฐ๊ฐ€ ๋‹ค์‹œ ๊ณ„์‚ฐํ•˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • public ์ €์ˆ˜์ค€ section/header XML write ๊ฒฝ๋กœ๋„ Hancom-compatible root namespace ์„ ์–ธ๊ณผ `standalone="yes"` XML declaration์„ ๋ณด์ •ํ•ด generic XML serializer ์ถœ๋ ฅ์ด ๊ทธ๋Œ€๋กœ ์ €์žฅ๋˜์ง€ ์•Š๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • `HwpxDocument.to_bytes()`, `save_to_path()`, `save_to_stream()`์ด ์ƒ์„ฑ๋œ ํŒจํ‚ค์ง€์˜ editor-open safety๋ฅผ ํ™•์ธํ•œ ๋’ค์—๋งŒ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ฑฐ๋‚˜ ์“ฐ๋„๋ก ๋ณด๊ฐ•ํ–ˆ์Šต๋‹ˆ๋‹ค. `save_to_path()`๋Š” safety ์‹คํŒจ ์‹œ ๊ธฐ์กด ๋Œ€์ƒ ํŒŒ์ผ์„ ๊ต์ฒดํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • `HwpxPackage.save()`๋„ editor-open safety๋ฅผ ๊ธฐ๋ณธ ๊ฒ€์ฆํ•ด ์ €์ˆ˜์ค€ package ์ง์ ‘ ํŽธ์ง‘์ด unsafe HWPX๋ฅผ bytes/path/stream์œผ๋กœ ๋‚ด๋ณด๋‚ด์ง€ ์•Š๋„๋ก ๋ง‰์Šต๋‹ˆ๋‹ค.
  • public `HwpxPackage.save()`์—์„œ editor-open safety ๊ฒ€์ฆ์„ ์šฐํšŒํ•˜๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š๋„๋ก ์ •๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ฒ€์ฆ ์‹คํŒจ ์ƒํƒœ์˜ bytes snapshot์€ package ๋‚ด๋ถ€ ์ง„๋‹จ ํ† ํฐ์ด ์žˆ๋Š” ๊ฒฝ๋กœ์—์„œ๋งŒ ์ƒ์„ฑํ•˜๋ฉฐ, unchecked ๊ฒฝ๋กœ๋Š” caller-provided file path/stream์— ์ง์ ‘ ์“ธ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
  • `HwpxDocument._to_bytes_raw()`์˜ open-safety bypass ์ธ์ž๋ฅผ ์ œ๊ฑฐํ•ด document ๊ฐ์ฒด์—์„œ unchecked bytes๋ฅผ ์–ป๋Š” ์‹ค์ˆ˜์„ฑ ์šฐํšŒ ๊ฒฝ๋กœ๋ฅผ ๋” ์ขํ˜”์Šต๋‹ˆ๋‹ค.
  • private archive writer๋„ save ๋‚ด๋ถ€ ์ปจํ…์ŠคํŠธ์—์„œ๋งŒ ๋™์ž‘ํ•˜๊ฒŒ ํ•ด, `_write_archive()`/`_write_zip_entry()` ์ง์ ‘ ํ˜ธ์ถœ๋กœ editor-open safety ๊ฒ€์ฆ์„ ๊ฑด๋„ˆ๋›ด ZIP์„ ๋งŒ๋“œ๋Š” ์‹ค์ˆ˜์„ฑ ์šฐํšŒ ๊ฒฝ๋กœ๋ฅผ ๋ง‰์•˜์Šต๋‹ˆ๋‹ค.
  • + 10 more
v2.10.2
github-actions[bot]github-actions[bot]ยท3w agoยทJune 6, 2026
GitHub

๐Ÿ“ฆ ์ถ”๊ฐ€

  • `hwpx.tools.markdown_export.export_markdown()`์™€ `HwpxDocument.export_rich_markdown()`์„ ์ถ”๊ฐ€ํ•ด ํ’๋ถ€ํ•œ Markdown ๋ณ€ํ™˜์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ธ๋ผ์ธ ์„œ์‹(๊ตต๊ฒŒ/๊ธฐ์šธ์ž„/์ทจ์†Œ์„ /์ƒ‰์ƒ/ํ•˜์ด๋ผ์ดํŠธ), ํ‘œ ๋ณ‘ํ•ฉ ์…€(colspan/rowspan HTML), ์ค‘์ฒฉ ํ‘œ ์žฌ๊ท€, `rect`/`ellipse`/`polygon` ๋„ํ˜• ๋‚ด๋ถ€ paragraph, BinData ์ด๋ฏธ์ง€ ์ถ”์ถœ, `โ… .`/`1.` ํŒจํ„ด ๊ธฐ๋ฐ˜ ํ—ค๋”ฉ ๊ฐ์ง€(`# `/`## `), ๊ฐ์ฃผยท๋ฏธ์ฃผ(์ •ํ™• ์œ„์น˜ ๋งˆ์ปค + `fn1`/`en1` ์ผ๋ จ๋ฒˆํ˜ธ + ๋ณธ๋ฌธ ์ธ๋ผ์ธ ์„œ์‹), ํ•˜์ดํผ๋งํฌ(`[text](url)`) ๋ณด์กด์„ ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ์กด `HwpxDocument.export_markdown()`์€ ๊ทธ๋Œ€๋กœ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.
  • `HwpxOxmlNote`์— ๋ณธ๋ฌธ paragraph ์ ‘๊ทผ/ํŽธ์ง‘ helper๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค: `body_paragraph` property, `add_run(text, *, char_pr_id_ref=..., bold=..., italic=..., underline=..., color=..., font=..., size=..., highlight=..., strike=..., attributes=...)`, `add_hyperlink(url, display_text, *, char_pr_id_ref=...)`. XML ์ง์ ‘ ์กฐ์ž‘ ์—†์ด ๊ฐ์ฃผ ๋ณธ๋ฌธ์— ํ˜ผํ•ฉ ์„œ์‹ run๊ณผ ํ•˜์ดํผ๋งํฌ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • `get_table_map()` ๊ฒฐ๊ณผ์— ๋ณธ๋ฌธ ํ‘œ anchor `location`, ์…€ ๋ฌธ๋‹จ๋ณ„ `table_cell_paragraph` location, `caption_text`, `preceding_paragraph_text`๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์ƒˆ ์ปจ๋ฒ„ํ„ฐ์™€ helper์— ๋Œ€ํ•œ ํšŒ๊ท€ ํ…Œ์ŠคํŠธ๋ฅผ `tests/test_markdown_export.py`์— ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“ฆ ๋ณ€๊ฒฝ

  • `HwpxOxmlTableCell.text`๊ฐ€ ์…€ ๋‚ด๋ถ€ ์—ฌ๋Ÿฌ ๋ฌธ๋‹จ์„ ์ค„๋ฐ”๊ฟˆ์œผ๋กœ ๋ณด์กดํ•˜๊ณ , `set_text(..., preserve_format=True, split_paragraphs=True)` ๊ฒฝ๋กœ์—์„œ ๊ธฐ์กด run `charPrIDRef`๋ฅผ ์œ ์ง€ํ•˜๋„๋ก ๊ฐœ์„ ํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“ฆ ์ˆ˜์ •

  • `HwpxOxmlParagraph.add_footnote()`/`add_endnote()`์˜ `char_pr_id_ref` ์ธ์ž๊ฐ€ ์™ธ๋ถ€ ํ˜ธ์ŠคํŒ… run์—๋งŒ ์ ์šฉ๋˜๊ณ  ๊ฐ์ฃผ ๋ณธ๋ฌธ run์€ ํ•ญ์ƒ `charPrIDRef="0"`์œผ๋กœ ํ•˜๋“œ์ฝ”๋”ฉ๋˜๋˜ ๋ฌธ์ œ๋ฅผ ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ธ์ž๊ฐ€ ์‚ฌ์šฉ์ž ์˜๋„๋Œ€๋กœ ๋ณธ๋ฌธ run์—๋„ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.
v2.10.1
github-actions[bot]github-actions[bot]ยท3w agoยทJune 4, 2026
GitHub

๐Ÿ“ฆ ์ถ”๊ฐ€

  • `document_plan` authoring์„ builder lowering ์ค‘์‹ฌ์œผ๋กœ ํ™•์žฅํ•˜๊ณ  v2 builder node, TOC, government_report preset์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
  • ์ •๋ถ€๋ณด๊ณ ์„œ ๊ณ„์‚ฐ/ํŒŒ์‹ฑ ์œ ํ‹ธ๋ฆฌํ‹ฐ(`hwpx.tools.report_utils`, `hwpx.tools.report_parser`)์™€ computed field ์น˜ํ™˜์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • generic element coverage inventory, table cleanup, table profile/caption/unit preservation, id reference integrity checker๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • `linesegarray`, `transMatrix`, `scaMatrix`, `rotMatrix`, edit/combo box control์„ first-class OXML ๋ชจ๋ธ๋กœ ์Šน๊ฒฉํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“ฆ ๋ณ€๊ฒฝ

  • builder save report์˜ hard gate๊ฐ€ id integrity๋ฅผ ์‹ค์ œ ๊ฒ€์‚ฌ ๊ฒฐ๊ณผ๋กœ ๋ฐ˜์˜ํ•˜๋„๋ก ๊ฐ•ํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ํŒจํ‚ค์ง€ rewrite ์‹œ `mimetype` ์—”ํŠธ๋ฆฌ๋ฅผ ๋ณด์กดํ•˜๋„๋ก OPC ์ €์žฅ ๊ฒฝ๋กœ๋ฅผ ์ •๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.
v2.10.0
github-actions[bot]github-actions[bot]ยท3w agoยทJune 2, 2026
GitHub

๐Ÿ“ฆ ์ถ”๊ฐ€

  • `hwpx.builder` ๊ณต๊ฐœ ํŒจํ‚ค์ง€๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. `Document`, `Section`, `Paragraph`, `Run`, `Heading`, `Bullet`, `NumberedList`, `Table`, `Image`, `Header`, `Footer`, `PageNumber`, `PageBreak`, `Metadata`, `PageSize`, `Margins` ๋…ธ๋“œ๋กœ ์กฐ๋ฆฝํ˜• HWPX ์ƒ์„ฑ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
  • `BuilderSaveReport`์™€ `ReopenReport`๋ฅผ ์ถ”๊ฐ€ํ•ด builder ์ €์žฅ ํ›„ package validation, document error/lint, reopen, feature flags, visual review ํ•„์š” ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋จธ๋ฆฌ๊ธ€/๋ฐ”๋‹ฅ๊ธ€ ๋ฆฌ์น˜ content, ์ž๋™ ์ชฝ๋ฒˆํ˜ธ, ๋ฆฌ์น˜ ๋Ÿฐ ์„œ์‹(color/font/size/highlight/strike), ๋‹ค๋‹จ๊ณ„ ๋ชฉ๋ก, ํ‘œ ๋ณ‘ํ•ฉ/์Œ์˜/์—ด๋„ˆ๋น„, ์ด๋ฏธ์ง€ ๋ฐฐ์น˜๋ฅผ ์œ„ํ•œ `HwpxDocument` facade ๋ฐ OXML wrapper ๋ฉ”์„œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • `hwpx.document_plan.v1`, ์šด์˜ ๊ณ„ํš์„œ ํ’ˆ์งˆ ํ”„๋กœํ•„, template form-fit authoring, proposal/form-fill ํ’ˆ์งˆ ๊ฒ€์ฆ ํ๋ฆ„์„ ๊ฐ•ํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • hwpxlib sample corpus ๊ธฐ๋ฐ˜ oracle fixture์™€ builder vertical slice ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • `src/hwpx/tools/_schemas/owpml/`์— 2011 Hancom ๋„ค์ž„์ŠคํŽ˜์ด์Šค์šฉ subset XSD ๋ฒˆ๋“ค์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค (`header.xsd`, `body.xsd`, `paralist.xsd`, `core.xsd`, `xml.xsd`, `NOTICE`).
  • `hwpx.oxml.load_compound_schema()`์™€ `SchemaImportError`๋ฅผ ์ถ”๊ฐ€ํ•ด offline compound XSD ๋กœ๋”ฉ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
  • fixture matrix ๊ธฐ๋ฐ˜ Phase 1 validation ๋ฆฌํฌํŠธ(`shared/hwpx/HWPX_STACK_VALIDATION_2026-04-20_pre-phase1.md`, `..._post-phase1.md`)์™€ ํšŒ๊ท€ ํ…Œ์ŠคํŠธ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“ฆ ๋ณ€๊ฒฝ

  • `validate_document().ok`๋Š” error ๊ธฐ์ค€์œผ๋กœ ์œ ์ง€ํ•˜๊ณ  schema warning์€ lint/warning์œผ๋กœ ๋ถ„๋ฆฌํ•ด ๊ฐ€์‹œํ™”ํ•ฉ๋‹ˆ๋‹ค.
  • `HwpxDocument.save_to_path()` ๊ธฐ๋ฐ˜ ์ €์žฅ/์žฌ์˜คํ”ˆ ๊ฒ€์ฆ ๊ฒฝ๋กœ๋ฅผ builder์™€ authoring workflow์—์„œ ์ผ๊ด€๋˜๊ฒŒ ์‚ฌ์šฉํ•˜๋„๋ก ์ •๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.
  • `hwpx-validate`๋Š” ์ด์ œ ๊ธฐ๋ณธ strict ๋ชจ๋“œ๋กœ Phase 1 subset schema bundle์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. `--no-strict`๋กœ warning-only ๋ถ„๋ฅ˜๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
  • `HwpxDocument.validate()`๋Š” ๊ธฐ๋ณธ `strict=False`๋กœ ๋™์ž‘ํ•˜๋ฉฐ, `validate_on_save_strict` ์˜ต์…˜์œผ๋กœ ์ €์žฅ ์‹œ strict ๊ฒ€์ฆ์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํŒจํ‚ค์ง€ ๋ฐฐํฌ๋ฌผ(sdist/wheel)์— OWPML subset schema bundle์ด ํฌํ•จ๋˜๋„๋ก package-data๋ฅผ ํ™•์žฅํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“ฆ ์ˆ˜์ •

  • split-run placeholder, template form-fit, proposal/document-plan ์ƒ์„ฑ ๊ฒฝ๋กœ์˜ ํšŒ๊ท€๋ฅผ ๋ณด๊ฐ•ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • builder vertical slice์—์„œ Hancom Office HWP ์žฌ์˜คํ”ˆ๊ณผ ๊ตฌ์กฐ hard gate๊ฐ€ ํ†ต๊ณผํ•˜๋„๋ก ๋จธ๋ฆฌ๊ธ€/๋ฐ”๋‹ฅ๊ธ€ lowering๊ณผ page number control ๋ฐฐ์น˜๋ฅผ ์ •๋ ฌํ–ˆ์Šต๋‹ˆ๋‹ค.
v2.9.1
github-actions[bot]github-actions[bot]ยท2mo agoยทApril 27, 2026
GitHub

๐Ÿ“ฆ [2.9.1] - 2026-04-27

  • ์ƒํ˜ธ์šด์šฉ์„ฑ(interop) ๋ฒ„๊ทธ ๋ฌถ์Œ ๋ฆด๋ฆฌ์ฆˆ์ž…๋‹ˆ๋‹ค. ์™ธ๋ถ€ ๊ธฐ์—ฌ์ž๋“ค์ด ๋ณด๊ณ ํ•˜๊ณ  ์ˆ˜์ •ํ•œ ์„ธ ๊ฐ€์ง€ ๋ฌธ์ œ๋ฅผ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“ฆ ์ˆ˜์ •

  • `HwpxOxmlTableCell._ensure_text_element`์™€ `ensure_run_style` ๋‚ด modifier๊ฐ€ lxml ์—˜๋ฆฌ๋จผํŠธ ์ƒ์—์„œ ๋˜ํ•œ `ET.SubElement`๋ฅผ ํ˜ธ์ถœํ•ด `TypeError`๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋˜ ๊ฒฝ๋กœ๋ฅผ ๊ธฐ๋ณธ ํ—ฌํผ `_append_child`๋กœ ์ •๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด์ œ `cell.text = ...`์™€ `paragraph.add_run(..., bold=True)`๊ฐ€ monkey-patch ์—†์ด ์ •์ƒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค (#30, [@hhy827](https://github.com/hhy827)).
  • `_paragraph_id` / `_object_id` / `_memo_id`๊ฐ€ `uuid4().int & 0xFFFFFFFF`๋กœ๋ถ€ํ„ฐ signed int32 ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚˜๋Š” ๊ฐ’์„ ์•ฝ 50% ํ™•๋ฅ ๋กœ ์ƒ์„ฑํ•˜๋˜ ๋ฌธ์ œ๋ฅผ ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. id ๊ฐ’์„ signed 32-bit ์–‘์ˆ˜ ๋ฒ”์œ„(`0 <= x < 2^31`)๋กœ ํด๋žจํ”„ํ•ด downstream ์†Œ๋น„์ž์™€์˜ ์ƒํ˜ธ์šด์šฉ์„ฑ์„ ํ™•๋ณดํ–ˆ์Šต๋‹ˆ๋‹ค (#34, [@seonghoony](https://github.com/seonghoony)).
  • `HwpxDocument.new()`์˜ seed๋กœ ์“ฐ์ด๋Š” ๋ฒˆ๋“ค `Skeleton.hwpx`์— signed int32 ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚˜๋Š” `<hp:p id="3121190098">`๊ฐ€ ํฌํ•จ๋ผ ์žˆ๋˜ ๋ฌธ์ œ๋ฅผ ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค (#35, [@seonghoony](https://github.com/seonghoony)).
  • `pyproject.toml`์— PEP 639 `license` expression๊ณผ ๊ฐ™์ด ๋‚จ์•„ ์žˆ๋˜ legacy `License :: OSI Approved :: Apache Software License` classifier๋ฅผ ์ œ๊ฑฐํ•ด `setuptools>=77`์—์„œ์˜ ์†Œ์Šค ์„ค์น˜/๋ฐ”์ด๋„ˆ๋ฆฌ ๋นŒ๋“œ ์‹คํŒจ๋ฅผ ํ•ด์†Œํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“ฆ ์ถ”๊ฐ€

  • ์œ„ ์„ธ ๋ฒ„๊ทธ์— ๋Œ€ํ•œ ํšŒ๊ท€ ํ…Œ์ŠคํŠธ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค (`tests/test_document_formatting.py`, `tests/test_id_generator_range.py`, `tests/test_skeleton_template_ids.py`).
  • ๋จธ์ง€๋œ ๊ธฐ์—ฌ๋ฅผ ์ธ์ •ํ•˜๋Š” `CONTRIBUTORS.md`๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  `README.md` / `CONTRIBUTING.md`์—์„œ ์—ฐ๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“ฆ ๋ณ€๊ฒฝ

  • License relicensed to Apache-2.0 (sole author, full consent). Previous license terms no longer apply to future releases.
v2.9.0
github-actions[bot]github-actions[bot]ยท2mo agoยทApril 2, 2026
GitHub

๐Ÿ“ฆ ์ถ”๊ฐ€

  • `HwpxDocument.get_table_map()`, `find_cell_by_label()`, `fill_by_path()`๋ฅผ ์ถ”๊ฐ€ํ•ด HWPX ์–‘์‹/ํ…œํ”Œ๋ฆฟ ํ‘œ๋ฅผ ๋ฌธ์„œ ์ˆœ์„œ ๊ธฐ๋ฐ˜์œผ๋กœ ํƒ์ƒ‰ํ•˜๊ณ  ์ฑ„์šธ ์ˆ˜ ์žˆ๊ฒŒ ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • `hwpx.tools.table_navigation` ๋ชจ๋“ˆ์„ ์ถ”๊ฐ€ํ•ด ์—”์ง„ ๋ ˆ๋ฒจ์—์„œ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ํ‘œ ํƒ์ƒ‰, ๋ผ๋ฒจ ์ •๊ทœํ™”, ๋ฐฉํ–ฅ ์ด๋™, ๋ฐฐ์น˜ ์ฑ„์šฐ๊ธฐ helper๋ฅผ ๊ณต๊ฐœํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“ฆ ๋ณ€๊ฒฝ

  • ๋ผ๋ฒจ ๋งค์นญ์ด ๊ณต๋ฐฑ ์ถ•์•ฝ, ๋Œ€์†Œ๋ฌธ์ž ๋ฌด์‹œ, ํ›„ํ–‰ ์ฝœ๋ก  ํ—ˆ์šฉ ๊ทœ์น™์„ ๋”ฐ๋ฅด๋„๋ก ์ •๊ทœํ™” ๋กœ์ง์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ํ‘œ ์ž๋™ํ™” API์— ๋Œ€ํ•œ ํšŒ๊ท€ ํ…Œ์ŠคํŠธ์™€ README/API ๋ ˆํผ๋Ÿฐ์Šค ๋ฌธ์„œ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
v2.8.3
github-actions[bot]github-actions[bot]ยท3mo agoยทMarch 10, 2026
GitHub

๐Ÿ“ฆ ๋ณ€๊ฒฝ

  • ์ €์žฅ์†Œ์™€ ๋ฐฐํฌ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์˜ ๋ผ์ด์„ ์Šค ํ‘œ๊ธฐ๋ฅผ ์‹ค์ œ `LICENSE` ํŒŒ์ผ๊ณผ ์ผ์น˜ํ•˜๋„๋ก ์ •๋ ฌํ–ˆ์Šต๋‹ˆ๋‹ค.
  • `pyproject.toml`์„ PEP 639 ๋ฐฉ์‹์˜ `LicenseRef-python-hwpx-NonCommercial` + `license-files` ๊ตฌ์„ฑ์œผ๋กœ ๊ฐฑ์‹ ํ•˜๊ณ , ์ž˜๋ชป๋œ MIT ๋ถ„๋ฅ˜์ž๋ฅผ ์ œ๊ฑฐํ–ˆ์Šต๋‹ˆ๋‹ค.
  • README ๋ผ์ด์„ ์Šค ๋ฐฐ์ง€/์„น์…˜์„ ์ปค์Šคํ…€ ๋น„์ƒ์—…์  ๋ผ์ด์„ ์Šค ๊ธฐ์ค€์œผ๋กœ ์ˆ˜์ •ํ•˜๊ณ , wheel/sdist ์‚ฐ์ถœ๋ฌผ์˜ ๋ผ์ด์„ ์Šค ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ๊ฒ€์ฆํ•˜๋Š” ํšŒ๊ท€ ํ…Œ์ŠคํŠธ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
v2.8.2
github-actions[bot]github-actions[bot]ยท3mo agoยทMarch 8, 2026
GitHub

๐Ÿ“ฆ ๋ณ€๊ฒฝ

  • README๋ฅผ ํ˜„์žฌ ๊ณต๊ฐœ API์™€ CLI ๋ฒ”์œ„์— ๋งž์ถฐ ์ •๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค. Quick start, ํ…์ŠคํŠธ ์ถ”์ถœ, ๊ฐ์ฒด ๊ฒ€์ƒ‰ ์˜ˆ์‹œ๋ฅผ ์‹ค์ œ ํ˜ธ์ถœ ๋ฐฉ์‹ ๊ธฐ์ค€์œผ๋กœ ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • `add_memo()`/`add_memo_with_anchor()`๊ฐ€ `HwpxDocument.new()`๋กœ ๋งŒ๋“  ์‹ค์ œ `lxml` ๊ธฐ๋ฐ˜ ๋ฌธ์„œ์—์„œ๋„ ๋™์ž‘ํ•˜๋„๋ก memo XML ์ƒ์„ฑ ๊ฒฝ๋กœ๋ฅผ ์—”์ง„ ํ˜ธํ™˜ ๋ฐฉ์‹์œผ๋กœ ์ •๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์‹ค์ œ ๋นˆ ๋ฌธ์„œ ํ…œํ”Œ๋ฆฟ์—์„œ ๋ฉ”๋ชจ ์ถ”๊ฐ€ ํ›„ roundtrip ๋˜๋Š” ํšŒ๊ท€ ํ…Œ์ŠคํŠธ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
v2.8
github-actions[bot]github-actions[bot]ยท3mo agoยทMarch 8, 2026
GitHub

๐Ÿ“ฆ ๋ณ€๊ฒฝ

  • `HwpxPackage`์™€ OXML ๋กœ๋”ฉ/์ €์žฅ์ด rootfile/manifest-relative ๊ฒฝ๋กœ๋ฅผ ์‹ค์ œ๋กœ ๋”ฐ๋ฅด๋„๋ก ์ •๋ ฌํ–ˆ์Šต๋‹ˆ๋‹ค.
  • `hwpx-analyze-template --extract-dir`๊ฐ€ pack-ready ์ž‘์—… ๋””๋ ‰ํ„ฐ๋ฆฌ์™€ `.hwpx-pack-metadata.json`์„ ์ƒ์„ฑํ•˜๋„๋ก ํ™•์žฅํ–ˆ์Šต๋‹ˆ๋‹ค.
  • `hwpx-validate-package`๋ฅผ ์—”์ง„ ์ •ํ•ฉ ๊ธฐ์ค€์œผ๋กœ ์žฌ์ž‘์„ฑํ•ด dynamic rootfile/manifest ๊ด€๊ณ„, CRC, fallback warning์„ ๊ตฌ๋ถ„ํ•˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • `hwpx-unpack` ๊ธฐ๋ณธ๊ฐ’์„ raw-byte preserving์œผ๋กœ ๋ฐ”๊พธ๊ณ  `--pretty-xml` opt-in์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • tooling/OPC ํšŒ๊ท€ ํ…Œ์ŠคํŠธ๋ฅผ ํ™•๋Œ€ํ•˜๊ณ , coverage threshold๋ฅผ 60์œผ๋กœ ์˜ฌ๋ ธ์œผ๋ฉฐ, pyright๋Š” touched OPC/tooling ๋ฒ”์œ„์—์„œ `basic`์œผ๋กœ ์ƒํ–ฅํ–ˆ์Šต๋‹ˆ๋‹ค.
v2.7.1
github-actions[bot]github-actions[bot]ยท3mo agoยทMarch 8, 2026
GitHub

๐Ÿ“ฆ ๋ณ€๊ฒฝ

  • ๊ณต๊ฐœ ์ €์žฅ์†Œ์™€ ๋ฐฐํฌ ์‚ฐ์ถœ๋ฌผ์—์„œ ๋‚ด๋ถ€ ๊ฐ์‚ฌ ๋ฌธ์„œ๋ฅผ ์ œ๊ฑฐํ–ˆ์Šต๋‹ˆ๋‹ค.
v2.7
github-actions[bot]github-actions[bot]ยท3mo agoยทMarch 8, 2026
GitHub

๐Ÿ“ฆ ์ถ”๊ฐ€

  • `hwpx-unpack`, `hwpx-pack`, `hwpx-analyze-template` CLI๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • `src/hwpx/tools/archive_cli.py`๋ฅผ ์ถ”๊ฐ€ํ•ด unpack/pack ์›Œํฌํ”Œ๋กœ๋ฅผ ํŒจํ‚ค์ง€ ๋ ˆ๋ฒจ ๋„๊ตฌ๋กœ ์Šน๊ฒฉํ–ˆ์Šต๋‹ˆ๋‹ค.
  • unpack ์‹œ `.hwpx-pack-metadata.json`์„ ๊ธฐ๋กํ•˜๊ณ , pack ์‹œ ์ด๋ฅผ ์‚ฌ์šฉํ•ด ์›๋ณธ ZIP ์—”ํŠธ๋ฆฌ ์ˆœ์„œ/์••์ถ• ๋ฐฉ์‹์„ ๊ฐ€๋Šฅํ•œ ๋ฒ”์œ„์—์„œ ๋ณด์กดํ•˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • `src/hwpx/tools/template_analyzer.py`์™€ `DevDoc/hwpxskill_gap_audit.md`๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“ฆ ๋ณ€๊ฒฝ

  • `scripts/office/unpack.py`, `scripts/office/pack.py`, `scripts/analyze_template.py`๋ฅผ ํŒจํ‚ค์ง€ ๋„๊ตฌ ๋ž˜ํผ๋กœ ์ •๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.
  • `page_guard`์— shape/control count ๋ฐ ํžˆ์Šคํ† ๊ทธ๋žจ ๋น„๊ต๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ , rendered page count๊ฐ€ ์•„๋‹Œ layout-drift proxy์ž„์„ ๋ฌธ์„œ์™€ CLI ์„ค๋ช…์— ๋ช…์‹œํ–ˆ์Šต๋‹ˆ๋‹ค.
  • README์™€ `docs/usage.md`์— ์ƒˆ CLI ์‚ฌ์šฉ ์˜ˆ์‹œ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์ƒˆ tooling์— ๋Œ€ํ•œ CLI/์ถ”์ถœ/overwrite/page-guard ํšŒ๊ท€ ํ…Œ์ŠคํŠธ๋ฅผ ๊ฐ•ํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค.
v2.6
github-actions[bot]github-actions[bot]ยท3mo agoยทMarch 8, 2026
GitHub

๐Ÿ“ฆ ์ถ”๊ฐ€

  • `hwpx-validate-package` CLI์™€ `hwpx.tools.package_validator`๋ฅผ ์ถ”๊ฐ€ํ•ด ZIP/OPC/HWPX ํŒจํ‚ค์ง€ ๊ตฌ์กฐ, `mimetype`, `container.xml`, manifest/spine ์ฐธ์กฐ, XML well-formedness๋ฅผ ์ ๊ฒ€ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • `hwpx-page-guard` CLI์™€ `hwpx.tools.page_guard`๋ฅผ ์ถ”๊ฐ€ํ•ด ์„น์…˜ ์ˆ˜, ๋‹จ๋ฝ ์ˆ˜, page/column break, ํ‘œ ๊ตฌ์กฐ, ํ…์ŠคํŠธ ๊ธธ์ด ๋ณ€ํ™”๋Ÿ‰์„ ๊ธฐ์ค€์œผ๋กœ ๋ฌธ์„œ ๋“œ๋ฆฌํ”„ํŠธ๋ฅผ ๋น„๊ตํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • `hwpx-text-extract` CLI๋ฅผ ์ถ”๊ฐ€ํ•ด ๊ธฐ์กด `TextExtractor` ๊ธฐ๋Šฅ์„ plain/markdown ํ˜•ํƒœ๋กœ ๋ฐ”๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • `scripts/office/unpack.py`, `scripts/office/pack.py`, `scripts/analyze_template.py`๋ฅผ ์ถ”๊ฐ€ํ•ด XML-first HWPX ์ž‘์—… ํ๋ฆ„์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
  • gap-closure ๋ฐ˜์˜๋ถ„์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค (`tests/test_gap_closure_tools.py`).

๐Ÿ“ฆ ์ˆ˜์ •

  • `HwpxDocument.validate()`๊ฐ€ ๋‚ด๋ถ€ ์ง๋ ฌํ™” ๊ณผ์ •์—์„œ dirty ์ƒํƒœ๋ฅผ ์ง€์›Œ ๋ฒ„๋ฆฌ๋˜ ๋ถ€์ž‘์šฉ์„ ์ œ๊ฑฐํ•ด, ๊ฒ€์ฆ ์ดํ›„์—๋„ ์ €์žฅ ํ•„์š” ์ƒํƒœ๊ฐ€ ์œ ์ง€๋˜๋„๋ก ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.