App and Document

The two-class v2 facade

The split

hwpapi v2 divides the public surface into two top-level classes:

Class Owns Obtained from
App COM engine lifecycle, window visibility, open/save/close/quit App()
Document Everything scoped to one open document β€” text, cursor, collections, page layout app.doc

This is the central design choice of v2. The v1 App carried 82+ public members; the v2 App has ≀ 15. Every document-scoped member moved to Document.

When to use which

  • App β€” connect/disconnect, open a file, save or export, toggle window visibility, reach the escape hatch.
  • Document (app.doc) β€” read/write text, navigate the cursor, insert things, access collections (fields, bookmarks, images, …), configure page layout.
from hwpapi import App

app = App()

# Lifecycle on App
app.open("report.hwp")
app.save()
app.save_as("report.pdf")

# Content on app.doc
text = app.doc.text
app.doc.insert_text("new paragraph\n")
app.doc.fields["author"] = "홍길동"
app.doc.page.setup(width="210mm", height="297mm")

app.close()
app.quit()

App β€” the lifecycle layer

The full v2 App public surface is:

Member Kind Purpose
App(new_app=False, is_visible=True, dll_path=None, engine=None) constructor Connect to HWP
open(path) method Open a .hwp / .hwpx file
new() method Create a blank document
close() method Close the active document
save() method Save in place
save_as(path, *, as_block=False) method Save to a new file (format inferred from extension)
reload() method Rebind to an existing COM engine
quit() method Tear down the COM engine
visible property (r/w) Window visibility
doc property The active Document
engine property The Engine β€” escape hatch
api property Raw HWP COM handle β€” ultimate escape hatch
actions property The action catalogue β€” escape hatch
__enter__ / __exit__ context manager Auto-closes on exit

Context-manager usage handles cleanup for you:

from hwpapi import App

with App() as app:
    app.open("template.hwp")
    app.doc.insert_text("Generated by hwpapi\n")
    app.save_as("generated.pdf")
# app.close() + app.quit() happen here even if an exception was raised

Document β€” the per-document layer

app.doc returns the same Document instance on every access (@cached_property). It owns:

  • Text β€” doc.text (read/write), doc.get_text(), doc.insert_text(s), doc.get_selected_text(), doc.selection
  • Navigation β€” doc.cursor.goto_page(n), doc.cursor.move_to_field(name), doc.cursor.in_table(), doc.cursor.get_charshape(), doc.cursor.get_parashape()
  • Clipboard β€” doc.copy(), doc.cut(), doc.paste(), doc.delete()
  • Undo β€” doc.undo(), doc.redo()
  • Search β€” doc.find_text(q), doc.replace_all(find, replace)
  • Structure β€” doc.insert_page_break(), doc.insert_line_break(), doc.insert_paragraph_break(), doc.insert_tab(), doc.insert_heading(level, text)
  • Inserts β€” doc.insert_file(path), doc.insert_picture(path)
  • Page setup β€” doc.page.setup(...)
  • Counters β€” doc.page_count, doc.current_page, doc.get_filepath(), doc.get_hwnd()
  • Collections (dict-like) β€” doc.fields, doc.bookmarks, doc.hyperlinks, doc.images, doc.styles, doc.paragraphs, doc.tables
  • Scanner β€” doc.scan(...) context manager

Why not merge them?

The v1 App was a god-object: 82 public members mixing lifecycle, content, formatting, navigation, and low-level utilities. The split serves three goals:

  1. Discoverability. dir(app) now fits on one screen; everything else lives under app.doc.* with IDE tab completion.
  2. Lifetime. A Document stops being useful when the underlying file closes. Splitting the surface makes the invalidation boundary explicit β€” future v2.1 work on multi-document support targets app.documents[…] returning a new Document, without touching App.
  3. Teachability. β€œApp = COM engine, Document = contents” is a sentence. β€œApp = everything HWP-related” was a paragraph.

See also

Back to top