python-hwpx-automation 6.0.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hwpx_automation/__init__.py +61 -0
- hwpx_automation/__init__.pyi +27 -0
- hwpx_automation/__main__.py +8 -0
- hwpx_automation/agent_document.py +392 -0
- hwpx_automation/api.py +136 -0
- hwpx_automation/blind_eval.py +407 -0
- hwpx_automation/capabilities.py +110 -0
- hwpx_automation/compat.py +48 -0
- hwpx_automation/configuration.py +60 -0
- hwpx_automation/core/__init__.py +2 -0
- hwpx_automation/core/content.py +762 -0
- hwpx_automation/core/context.py +111 -0
- hwpx_automation/core/diff.py +53 -0
- hwpx_automation/core/document.py +37 -0
- hwpx_automation/core/formatting.py +513 -0
- hwpx_automation/core/handles.py +24 -0
- hwpx_automation/core/locations.py +205 -0
- hwpx_automation/core/locator.py +162 -0
- hwpx_automation/core/plan.py +680 -0
- hwpx_automation/core/resources.py +42 -0
- hwpx_automation/core/search.py +296 -0
- hwpx_automation/core/transactions.py +434 -0
- hwpx_automation/core/txn.py +48 -0
- hwpx_automation/document_state.py +95 -0
- hwpx_automation/errors.py +174 -0
- hwpx_automation/execution_lock.py +15 -0
- hwpx_automation/fastmcp_adapter.py +672 -0
- hwpx_automation/form_fill.py +1177 -0
- hwpx_automation/form_output_models.py +223 -0
- hwpx_automation/handlers/__init__.py +2 -0
- hwpx_automation/handlers/_shared.py +377 -0
- hwpx_automation/handlers/agent_document.py +257 -0
- hwpx_automation/handlers/authoring.py +750 -0
- hwpx_automation/handlers/content_edit.py +1078 -0
- hwpx_automation/handlers/form_fill.py +607 -0
- hwpx_automation/handlers/layout_style.py +660 -0
- hwpx_automation/handlers/quality_render.py +566 -0
- hwpx_automation/handlers/read_export.py +1295 -0
- hwpx_automation/handlers/specialized.py +624 -0
- hwpx_automation/handlers/tracked_changes.py +589 -0
- hwpx_automation/handlers/workflow.py +105 -0
- hwpx_automation/hwp_converter.py +227 -0
- hwpx_automation/hwp_support.py +94 -0
- hwpx_automation/hwpx_ops.py +1439 -0
- hwpx_automation/identity.json +263 -0
- hwpx_automation/identity.py +18 -0
- hwpx_automation/ingest_adapters.py +85 -0
- hwpx_automation/markdown_plan.py +216 -0
- hwpx_automation/mcp_cli.py +29 -0
- hwpx_automation/metadata/tools_meta.py +40 -0
- hwpx_automation/mixed_form.py +3007 -0
- hwpx_automation/mutation_models.py +401 -0
- hwpx_automation/network_policy.py +232 -0
- hwpx_automation/office/__init__.py +14 -0
- hwpx_automation/office/agent/__init__.py +125 -0
- hwpx_automation/office/agent/_batch_verification.py +383 -0
- hwpx_automation/office/agent/blueprint/__init__.py +58 -0
- hwpx_automation/office/agent/blueprint/bundle.py +282 -0
- hwpx_automation/office/agent/blueprint/catalog.py +136 -0
- hwpx_automation/office/agent/blueprint/dump.py +520 -0
- hwpx_automation/office/agent/blueprint/mapping.py +312 -0
- hwpx_automation/office/agent/blueprint/model.py +722 -0
- hwpx_automation/office/agent/blueprint/native.py +621 -0
- hwpx_automation/office/agent/blueprint/replay.py +622 -0
- hwpx_automation/office/agent/catalog.py +252 -0
- hwpx_automation/office/agent/cli.py +647 -0
- hwpx_automation/office/agent/commands.py +1383 -0
- hwpx_automation/office/agent/document.py +801 -0
- hwpx_automation/office/agent/form_plan.py +1760 -0
- hwpx_automation/office/agent/model.py +808 -0
- hwpx_automation/office/agent/path.py +155 -0
- hwpx_automation/office/agent/query.py +230 -0
- hwpx_automation/office/agent/story.py +207 -0
- hwpx_automation/office/authoring/__init__.py +3542 -0
- hwpx_automation/office/authoring/advanced_generators.py +154 -0
- hwpx_automation/office/authoring/builder/__init__.py +52 -0
- hwpx_automation/office/authoring/builder/core.py +996 -0
- hwpx_automation/office/authoring/builder/report.py +195 -0
- hwpx_automation/office/authoring/design/__init__.py +30 -0
- hwpx_automation/office/authoring/design/_support.py +144 -0
- hwpx_automation/office/authoring/design/composer.py +282 -0
- hwpx_automation/office/authoring/design/harvest.py +305 -0
- hwpx_automation/office/authoring/design/plan.py +69 -0
- hwpx_automation/office/authoring/design/profile.py +88 -0
- hwpx_automation/office/authoring/design/profiles/application_form/fragments/body.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/application_form/fragments/heading.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/application_form/fragments/info_table.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/application_form/fragments/title.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/application_form/profile.json +25 -0
- hwpx_automation/office/authoring/design/profiles/application_form/template.hwpx +0 -0
- hwpx_automation/office/authoring/design/profiles/home_notice/fragments/body.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/home_notice/fragments/heading.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/home_notice/fragments/title.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/home_notice/profile.json +24 -0
- hwpx_automation/office/authoring/design/profiles/home_notice/template.hwpx +0 -0
- hwpx_automation/office/authoring/design/profiles/official_notice/fragments/body.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/official_notice/fragments/heading.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/official_notice/fragments/info_table.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/official_notice/fragments/title.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/official_notice/profile.json +25 -0
- hwpx_automation/office/authoring/design/profiles/official_notice/template.hwpx +0 -0
- hwpx_automation/office/authoring/design/profiles/report/fragments/body.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/report/fragments/heading.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/report/fragments/info_table.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/report/fragments/title.xml +1 -0
- hwpx_automation/office/authoring/design/profiles/report/profile.json +25 -0
- hwpx_automation/office/authoring/design/profiles/report/template.hwpx +0 -0
- hwpx_automation/office/authoring/design/validator.py +107 -0
- hwpx_automation/office/authoring/presets/__init__.py +22 -0
- hwpx_automation/office/authoring/presets/proposal.py +538 -0
- hwpx_automation/office/authoring/report_parser.py +141 -0
- hwpx_automation/office/authoring/style_profile.py +437 -0
- hwpx_automation/office/authoring/template_analyzer.py +657 -0
- hwpx_automation/office/compliance/__init__.py +38 -0
- hwpx_automation/office/compliance/official_lint.py +478 -0
- hwpx_automation/office/compliance/pii.py +388 -0
- hwpx_automation/office/document_ops/__init__.py +13 -0
- hwpx_automation/office/document_ops/comparison.py +62 -0
- hwpx_automation/office/document_ops/mail_merge.py +73 -0
- hwpx_automation/office/document_ops/redline.py +35 -0
- hwpx_automation/office/evalplan/__init__.py +36 -0
- hwpx_automation/office/evalplan/runtime.py +2762 -0
- hwpx_automation/office/exam/__init__.py +44 -0
- hwpx_automation/office/exam/compose.py +282 -0
- hwpx_automation/office/exam/ir.py +44 -0
- hwpx_automation/office/exam/measure.py +163 -0
- hwpx_automation/office/exam/parser.py +150 -0
- hwpx_automation/office/exam/profile.py +123 -0
- hwpx_automation/office/form_fill/__init__.py +66 -0
- hwpx_automation/office/form_fill/classification.py +108 -0
- hwpx_automation/office/form_fill/fill_residue.py +242 -0
- hwpx_automation/office/form_fill/fit/__init__.py +36 -0
- hwpx_automation/office/form_fill/fit/apply.py +24 -0
- hwpx_automation/office/form_fill/fit/engine.py +24 -0
- hwpx_automation/office/form_fill/fit/measure.py +50 -0
- hwpx_automation/office/form_fill/fit/policy.py +28 -0
- hwpx_automation/office/form_fill/fit/report.py +28 -0
- hwpx_automation/office/form_fill/fit/seal.py +457 -0
- hwpx_automation/office/form_fill/fit/wordbox.py +1343 -0
- hwpx_automation/office/form_fill/guidance.py +704 -0
- hwpx_automation/office/form_fill/quality.py +961 -0
- hwpx_automation/office/form_fill/split_run.py +333 -0
- hwpx_automation/office/form_fill/template_formfit.py +656 -0
- hwpx_automation/office/house_style/__init__.py +196 -0
- hwpx_automation/office/house_style/composition.py +68 -0
- hwpx_automation/office/house_style/data/bank.json +625 -0
- hwpx_automation/office/house_style/data/genres.json +43 -0
- hwpx_automation/office/quality/__init__.py +14 -0
- hwpx_automation/office/quality/page_guard.py +277 -0
- hwpx_automation/office/rendering/__init__.py +145 -0
- hwpx_automation/office/rendering/_hancom_open_rate.ps1 +374 -0
- hwpx_automation/office/rendering/_refresh_hwpx_mac.applescript +162 -0
- hwpx_automation/office/rendering/_render_hwpx.ps1 +72 -0
- hwpx_automation/office/rendering/_render_hwpx_mac.applescript +249 -0
- hwpx_automation/office/rendering/block_splits.py +76 -0
- hwpx_automation/office/rendering/detectors.py +151 -0
- hwpx_automation/office/rendering/diff.py +153 -0
- hwpx_automation/office/rendering/fixture_corpus.py +215 -0
- hwpx_automation/office/rendering/oracle.py +909 -0
- hwpx_automation/office/rendering/page_qa.py +245 -0
- hwpx_automation/office/rendering/qa_contracts.py +293 -0
- hwpx_automation/office/rendering/qa_metrics.py +241 -0
- hwpx_automation/office/rendering/worker.py +290 -0
- hwpx_automation/office/utilities/__init__.py +12 -0
- hwpx_automation/office/utilities/table_compute.py +477 -0
- hwpx_automation/ops_services/__init__.py +1 -0
- hwpx_automation/ops_services/_border_fill.py +283 -0
- hwpx_automation/ops_services/composition.py +55 -0
- hwpx_automation/ops_services/content_layout.py +322 -0
- hwpx_automation/ops_services/context.py +213 -0
- hwpx_automation/ops_services/form_fields.py +557 -0
- hwpx_automation/ops_services/media.py +178 -0
- hwpx_automation/ops_services/memo_style.py +477 -0
- hwpx_automation/ops_services/package_validation.py +166 -0
- hwpx_automation/ops_services/planning.py +201 -0
- hwpx_automation/ops_services/preview_export.py +585 -0
- hwpx_automation/ops_services/read_query.py +601 -0
- hwpx_automation/ops_services/save_policy.py +604 -0
- hwpx_automation/ops_services/tables.py +539 -0
- hwpx_automation/ops_services/transactions.py +616 -0
- hwpx_automation/preview_output_models.py +69 -0
- hwpx_automation/public-modules.json +206 -0
- hwpx_automation/py.typed +1 -0
- hwpx_automation/quality.py +351 -0
- hwpx_automation/quality_generation.py +725 -0
- hwpx_automation/runtime.py +321 -0
- hwpx_automation/runtime_services.py +100 -0
- hwpx_automation/server.py +259 -0
- hwpx_automation/storage.py +747 -0
- hwpx_automation/tool_bindings.py +170 -0
- hwpx_automation/tool_contract.py +982 -0
- hwpx_automation/upstream.py +755 -0
- hwpx_automation/utils/__init__.py +2 -0
- hwpx_automation/utils/helpers.py +29 -0
- hwpx_automation/visual_qa.py +667 -0
- hwpx_automation/workflow/__init__.py +55 -0
- hwpx_automation/workflow/adapters.py +482 -0
- hwpx_automation/workflow/dispatcher.py +213 -0
- hwpx_automation/workflow/models.py +243 -0
- hwpx_automation/workflow/policy.py +198 -0
- hwpx_automation/workflow/render_contracts.py +173 -0
- hwpx_automation/workflow/render_metrics.py +196 -0
- hwpx_automation/workflow/render_queue.py +482 -0
- hwpx_automation/workflow/render_security.py +172 -0
- hwpx_automation/workflow/render_transport.py +369 -0
- hwpx_automation/workflow/rendering.py +206 -0
- hwpx_automation/workflow/service.py +758 -0
- hwpx_automation/workflow/state_machine.py +65 -0
- hwpx_automation/workflow/store.py +747 -0
- hwpx_automation/workspace.py +1694 -0
- python_hwpx_automation-6.0.3.dist-info/METADATA +279 -0
- python_hwpx_automation-6.0.3.dist-info/RECORD +217 -0
- python_hwpx_automation-6.0.3.dist-info/WHEEL +5 -0
- python_hwpx_automation-6.0.3.dist-info/entry_points.txt +3 -0
- python_hwpx_automation-6.0.3.dist-info/licenses/LICENSE +178 -0
- python_hwpx_automation-6.0.3.dist-info/licenses/NOTICE +14 -0
- python_hwpx_automation-6.0.3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import logging
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any, Callable, Dict, List, Optional, Tuple
|
|
9
|
+
|
|
10
|
+
from ..hwp_support import HwpBinaryError, extract_hwp_text
|
|
11
|
+
from ..core.locator import RegisteredHandle
|
|
12
|
+
from ..core.context import default_session_lifecycle_policy
|
|
13
|
+
from ..storage import DocumentStorage, LocalDocumentStorage
|
|
14
|
+
from ..workspace import (
|
|
15
|
+
WorkspacePathError,
|
|
16
|
+
)
|
|
17
|
+
from ..upstream import (
|
|
18
|
+
HwpxDocument,
|
|
19
|
+
HwpxOxmlParagraph,
|
|
20
|
+
HwpxOxmlTable,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger("hwpx_automation.hwpx_ops")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class DocumentContext:
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
*,
|
|
30
|
+
storage: DocumentStorage,
|
|
31
|
+
paging_paragraph_limit: int,
|
|
32
|
+
error_type: Callable[..., RuntimeError],
|
|
33
|
+
handle_error_type: type[RuntimeError],
|
|
34
|
+
) -> None:
|
|
35
|
+
self.storage = storage
|
|
36
|
+
self.base_directory = storage.base_directory
|
|
37
|
+
self.paging_limit = max(1, paging_paragraph_limit)
|
|
38
|
+
self._registered_handles: Dict[str, RegisteredHandle] = {}
|
|
39
|
+
self._error_type = error_type
|
|
40
|
+
self._handle_error_type = handle_error_type
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def registered_handles(self) -> Dict[str, RegisteredHandle]:
|
|
44
|
+
return self._registered_handles
|
|
45
|
+
|
|
46
|
+
def _local_storage(self) -> LocalDocumentStorage:
|
|
47
|
+
"""Return :attr:`storage` narrowed to the local filesystem backend.
|
|
48
|
+
|
|
49
|
+
Guarded exact-sidecar publication (``atomic_publish_bytes`` /
|
|
50
|
+
``read_guarded_bytes`` / ``remove_guarded_output`` /
|
|
51
|
+
``materialize_output_guard`` / ``cleanup_owned_parent_directories``) is a
|
|
52
|
+
filesystem-workspace capability only :class:`LocalDocumentStorage`
|
|
53
|
+
provides; non-local backends route through their own fallback paths.
|
|
54
|
+
Every caller reaches these methods only after an ``isinstance`` gate or
|
|
55
|
+
``_capture_exact_sidecar_guard`` has already established the local
|
|
56
|
+
backend, so this re-expresses that invariant for the type checker and
|
|
57
|
+
fails closed with the same error the sidecar machinery already raises.
|
|
58
|
+
"""
|
|
59
|
+
storage = self.storage
|
|
60
|
+
if not isinstance(storage, LocalDocumentStorage):
|
|
61
|
+
raise TypeError("exact sidecar operations require local storage")
|
|
62
|
+
return storage
|
|
63
|
+
|
|
64
|
+
def _new_error(
|
|
65
|
+
self,
|
|
66
|
+
code: str,
|
|
67
|
+
message: str,
|
|
68
|
+
*,
|
|
69
|
+
details: Optional[Dict[str, Any]] = None,
|
|
70
|
+
hint: Optional[str] = None,
|
|
71
|
+
) -> RuntimeError:
|
|
72
|
+
return self._error_type(message, code=code, details=details, hint=hint)
|
|
73
|
+
|
|
74
|
+
def _resolve_path(self, path: str, *, must_exist: bool = True) -> Path:
|
|
75
|
+
try:
|
|
76
|
+
resolved = self.storage.resolve_path(path, must_exist=must_exist)
|
|
77
|
+
except FileNotFoundError as exc:
|
|
78
|
+
raise self._new_error(
|
|
79
|
+
"DOCUMENT_NOT_FOUND",
|
|
80
|
+
"요청한 문서를 허용된 작업공간에서 찾을 수 없습니다.",
|
|
81
|
+
details={"requestedName": Path(path).name},
|
|
82
|
+
) from exc
|
|
83
|
+
except WorkspacePathError as exc:
|
|
84
|
+
raise self._new_error(
|
|
85
|
+
exc.code,
|
|
86
|
+
"요청한 경로가 허용된 HWPX 작업공간 경계를 벗어났습니다.",
|
|
87
|
+
details=exc.safe_details(),
|
|
88
|
+
) from exc
|
|
89
|
+
except PermissionError as exc:
|
|
90
|
+
raise self._new_error(
|
|
91
|
+
"PERMISSION_DENIED",
|
|
92
|
+
"요청한 문서에 접근할 권한이 없습니다.",
|
|
93
|
+
details={"requestedName": Path(path).name},
|
|
94
|
+
) from exc
|
|
95
|
+
self._register_handle(path, resolved)
|
|
96
|
+
return resolved
|
|
97
|
+
|
|
98
|
+
def _make_handle_id(self, path: str, backend: Optional[str] = None) -> str:
|
|
99
|
+
seed = f"{backend or 'local'}::{path}"
|
|
100
|
+
digest = hashlib.sha256(seed.encode("utf-8")).hexdigest()[:16]
|
|
101
|
+
return f"h_{digest}"
|
|
102
|
+
|
|
103
|
+
def _register_handle(self, path: str, resolved: Path) -> RegisteredHandle:
|
|
104
|
+
relative = self._relative_path(resolved)
|
|
105
|
+
handle_id = self._make_handle_id(relative)
|
|
106
|
+
handle = RegisteredHandle(type="handle", handleId=handle_id, path=relative)
|
|
107
|
+
self._registered_handles[handle_id] = handle
|
|
108
|
+
return handle
|
|
109
|
+
|
|
110
|
+
def list_registered_handles(self) -> List[RegisteredHandle]:
|
|
111
|
+
return sorted(
|
|
112
|
+
self._registered_handles.values(), key=lambda item: item.handle_id
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
def open_document_handle(self, path: str) -> Dict[str, Any]:
|
|
116
|
+
resolved = self._resolve_path(path)
|
|
117
|
+
handle = self._register_handle(path, resolved)
|
|
118
|
+
return {"handle": handle.model_dump(by_alias=True)}
|
|
119
|
+
|
|
120
|
+
def list_open_documents(self) -> Dict[str, Any]:
|
|
121
|
+
policy = default_session_lifecycle_policy()
|
|
122
|
+
return {
|
|
123
|
+
"documents": [
|
|
124
|
+
handle.model_dump(by_alias=True)
|
|
125
|
+
for handle in self.list_registered_handles()
|
|
126
|
+
],
|
|
127
|
+
"sessionPolicy": policy.as_dict(),
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
def close_document_handle(self, handle_id: str) -> Dict[str, Any]:
|
|
131
|
+
removed = self._registered_handles.pop(handle_id, None)
|
|
132
|
+
return {"closed": removed is not None}
|
|
133
|
+
|
|
134
|
+
def get_registered_handle(self, handle_id: str) -> RegisteredHandle:
|
|
135
|
+
handle = self._registered_handles.get(handle_id)
|
|
136
|
+
if handle is None:
|
|
137
|
+
raise self._handle_error_type(f"등록되지 않은 handleId입니다: {handle_id}")
|
|
138
|
+
return handle
|
|
139
|
+
|
|
140
|
+
def resolve_document_path(
|
|
141
|
+
self,
|
|
142
|
+
*,
|
|
143
|
+
path: Optional[str] = None,
|
|
144
|
+
handle_id: Optional[str] = None,
|
|
145
|
+
) -> str:
|
|
146
|
+
if path:
|
|
147
|
+
return path
|
|
148
|
+
if handle_id:
|
|
149
|
+
return self.get_registered_handle(handle_id).path
|
|
150
|
+
raise self._new_error(
|
|
151
|
+
"DOCUMENT_LOCATOR_REQUIRED",
|
|
152
|
+
"path 또는 handleId 중 하나를 제공해야 합니다.",
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
def _resolve_output_path(self, path: str) -> Path:
|
|
156
|
+
return self.storage.resolve_output_path(path)
|
|
157
|
+
|
|
158
|
+
def _relative_path(self, path: Path) -> str:
|
|
159
|
+
return self.storage.relative_path(path)
|
|
160
|
+
|
|
161
|
+
def _open_document(self, path: str) -> Tuple[HwpxDocument, Path]:
|
|
162
|
+
resolved = self._resolve_path(path)
|
|
163
|
+
if resolved.suffix.lower() == ".hwp":
|
|
164
|
+
raise self._new_error(
|
|
165
|
+
"READ_ONLY_HWP_DOCUMENT",
|
|
166
|
+
"HWP 파일은 편집이 불가합니다. 먼저 convert_hwp_to_hwpx 도구로 HWPX 변환 후 편집하세요.",
|
|
167
|
+
)
|
|
168
|
+
try:
|
|
169
|
+
document, resolved = self.storage.open_document(path)
|
|
170
|
+
except FileNotFoundError as exc:
|
|
171
|
+
raise self._new_error(
|
|
172
|
+
"DOCUMENT_NOT_FOUND",
|
|
173
|
+
"요청한 문서를 허용된 작업공간에서 찾을 수 없습니다.",
|
|
174
|
+
details={"requestedName": Path(path).name},
|
|
175
|
+
) from exc
|
|
176
|
+
except WorkspacePathError as exc:
|
|
177
|
+
raise self._new_error(
|
|
178
|
+
exc.code,
|
|
179
|
+
"요청한 경로가 허용된 HWPX 작업공간 경계를 벗어났습니다.",
|
|
180
|
+
details=exc.safe_details(),
|
|
181
|
+
) from exc
|
|
182
|
+
except PermissionError as exc:
|
|
183
|
+
raise self._new_error(
|
|
184
|
+
"PERMISSION_DENIED",
|
|
185
|
+
"요청한 문서에 접근할 권한이 없습니다.",
|
|
186
|
+
details={"requestedName": Path(path).name},
|
|
187
|
+
) from exc
|
|
188
|
+
except Exception as exc: # pragma: no cover - delegated to backend
|
|
189
|
+
raise self._new_error(
|
|
190
|
+
"DOCUMENT_OPEN_FAILED",
|
|
191
|
+
f"failed to open '{path}': {exc}",
|
|
192
|
+
details={"path": path},
|
|
193
|
+
) from exc
|
|
194
|
+
return document, resolved
|
|
195
|
+
|
|
196
|
+
def _read_only_hwp_paragraphs(self, path: str) -> Tuple[List[str], Path, str]:
|
|
197
|
+
resolved = self._resolve_path(path)
|
|
198
|
+
try:
|
|
199
|
+
snapshot = extract_hwp_text(resolved)
|
|
200
|
+
except HwpBinaryError as exc:
|
|
201
|
+
raise self._new_error(
|
|
202
|
+
"HWP_TEXT_EXTRACT_FAILED", f"HWP 텍스트 추출 실패: {exc}"
|
|
203
|
+
) from exc
|
|
204
|
+
return snapshot.paragraphs, resolved, snapshot.source
|
|
205
|
+
|
|
206
|
+
def _iter_paragraphs(self, document: HwpxDocument) -> List[HwpxOxmlParagraph]:
|
|
207
|
+
return list(document.paragraphs)
|
|
208
|
+
|
|
209
|
+
def _iter_tables(self, document: HwpxDocument) -> List[HwpxOxmlTable]:
|
|
210
|
+
tables: List[HwpxOxmlTable] = []
|
|
211
|
+
for paragraph in document.paragraphs:
|
|
212
|
+
tables.extend(paragraph.tables)
|
|
213
|
+
return tables
|