python-hwpx 4.2.0__py3-none-any.whl → 5.0.2__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/__init__.py +240 -95
- hwpx/_document/__init__.py +1 -1
- hwpx/_document/_units.py +1 -1
- hwpx/_document/fields.py +1 -1
- hwpx/_document/layout.py +1 -1
- hwpx/_document/media.py +1 -1
- hwpx/_document/memos.py +1 -1
- hwpx/_document/persistence.py +1 -1
- hwpx/_document/shapes.py +14 -3
- hwpx/_document/tracked.py +53 -9
- hwpx/body_patch.py +3 -3
- hwpx/document.py +35 -3
- hwpx/errors.py +9 -2
- hwpx/form_fit/engine.py +2 -2
- hwpx/form_fit/measure.py +1 -1
- hwpx/opc/package.py +51 -11
- hwpx/oxml/body.py +56 -0
- hwpx/oxml/color.py +60 -0
- hwpx/oxml/objects.py +140 -14
- hwpx/oxml/paragraph.py +95 -4
- hwpx/patch.py +22 -0
- hwpx/quality/rendering.py +136 -0
- hwpx/quality/report.py +1 -1
- hwpx/quality/save_pipeline.py +12 -10
- hwpx/table_patch.py +77 -15
- hwpx/tools/__init__.py +19 -63
- hwpx/tools/doc_diff.py +0 -39
- hwpx/tools/exporter.py +20 -7
- hwpx/tools/mail_merge.py +16 -12
- hwpx/tools/page_guard.py +11 -12
- hwpx/tools/read_fidelity.py +1 -1
- hwpx/tools/redline.py +81 -42
- hwpx/tools/template_analyzer.py +0 -46
- hwpx/tools/toc_author.py +1 -1
- hwpx/tools/toc_fidelity.py +45 -7
- python_hwpx-5.0.2.dist-info/METADATA +238 -0
- python_hwpx-5.0.2.dist-info/RECORD +112 -0
- {python_hwpx-4.2.0.dist-info → python_hwpx-5.0.2.dist-info}/entry_points.txt +0 -2
- hwpx/agent/__init__.py +0 -125
- hwpx/agent/_batch_verification.py +0 -383
- hwpx/agent/blueprint/__init__.py +0 -58
- hwpx/agent/blueprint/bundle.py +0 -282
- hwpx/agent/blueprint/catalog.py +0 -136
- hwpx/agent/blueprint/dump.py +0 -520
- hwpx/agent/blueprint/mapping.py +0 -312
- hwpx/agent/blueprint/model.py +0 -722
- hwpx/agent/blueprint/native.py +0 -621
- hwpx/agent/blueprint/replay.py +0 -622
- hwpx/agent/catalog.py +0 -252
- hwpx/agent/cli.py +0 -631
- hwpx/agent/commands.py +0 -1383
- hwpx/agent/document.py +0 -801
- hwpx/agent/form_plan.py +0 -1760
- hwpx/agent/model.py +0 -808
- hwpx/agent/path.py +0 -155
- hwpx/agent/query.py +0 -230
- hwpx/agent/story.py +0 -207
- hwpx/authoring.py +0 -3483
- hwpx/benchmark/__init__.py +0 -25
- hwpx/benchmark/blind_eval.py +0 -261
- hwpx/builder/__init__.py +0 -52
- hwpx/builder/core.py +0 -992
- hwpx/builder/report.py +0 -195
- hwpx/conformance/__init__.py +0 -54
- hwpx/conformance/badges.py +0 -198
- hwpx/conformance/corpus/corpus.json +0 -53
- hwpx/conformance/corpus/meeting_summary.hwpx +0 -0
- hwpx/conformance/corpus/notice.hwpx +0 -0
- hwpx/conformance/corpus/report_table.hwpx +0 -0
- hwpx/conformance/corpus.py +0 -260
- hwpx/conformance/report.py +0 -223
- hwpx/conformance/roundtrip_batch.py +0 -171
- hwpx/conformance/runner.py +0 -395
- hwpx/design/__init__.py +0 -30
- hwpx/design/_support.py +0 -144
- hwpx/design/composer.py +0 -282
- hwpx/design/harvest.py +0 -305
- hwpx/design/plan.py +0 -69
- hwpx/design/profile.py +0 -88
- hwpx/design/profiles/application_form/fragments/body.xml +0 -1
- hwpx/design/profiles/application_form/fragments/heading.xml +0 -1
- hwpx/design/profiles/application_form/fragments/info_table.xml +0 -1
- hwpx/design/profiles/application_form/fragments/title.xml +0 -1
- hwpx/design/profiles/application_form/profile.json +0 -25
- hwpx/design/profiles/application_form/template.hwpx +0 -0
- hwpx/design/profiles/home_notice/fragments/body.xml +0 -1
- hwpx/design/profiles/home_notice/fragments/heading.xml +0 -1
- hwpx/design/profiles/home_notice/fragments/title.xml +0 -1
- hwpx/design/profiles/home_notice/profile.json +0 -24
- hwpx/design/profiles/home_notice/template.hwpx +0 -0
- hwpx/design/profiles/official_notice/fragments/body.xml +0 -1
- hwpx/design/profiles/official_notice/fragments/heading.xml +0 -1
- hwpx/design/profiles/official_notice/fragments/info_table.xml +0 -1
- hwpx/design/profiles/official_notice/fragments/title.xml +0 -1
- hwpx/design/profiles/official_notice/profile.json +0 -25
- hwpx/design/profiles/official_notice/template.hwpx +0 -0
- hwpx/design/profiles/report/fragments/body.xml +0 -1
- hwpx/design/profiles/report/fragments/heading.xml +0 -1
- hwpx/design/profiles/report/fragments/info_table.xml +0 -1
- hwpx/design/profiles/report/fragments/title.xml +0 -1
- hwpx/design/profiles/report/profile.json +0 -25
- hwpx/design/profiles/report/template.hwpx +0 -0
- hwpx/design/validator.py +0 -107
- hwpx/evalplan_fill.py +0 -2727
- hwpx/exam/__init__.py +0 -22
- hwpx/exam/compose.py +0 -237
- hwpx/exam/ir.py +0 -41
- hwpx/exam/measure.py +0 -147
- hwpx/exam/parser.py +0 -145
- hwpx/exam/profile.py +0 -116
- hwpx/fill_residue.py +0 -200
- hwpx/form_fill.py +0 -333
- hwpx/form_fit/seal.py +0 -450
- hwpx/form_fit/wordbox.py +0 -1238
- hwpx/formfill_quality.py +0 -822
- hwpx/guidance_scan.py +0 -616
- hwpx/presets/__init__.py +0 -22
- hwpx/presets/proposal.py +0 -538
- hwpx/template_formfit.py +0 -627
- hwpx/tools/advanced_generators.py +0 -154
- hwpx/tools/fuzz/__init__.py +0 -36
- hwpx/tools/fuzz/__main__.py +0 -80
- hwpx/tools/fuzz/catalog.py +0 -320
- hwpx/tools/fuzz/generator.py +0 -195
- hwpx/tools/fuzz/minimize.py +0 -33
- hwpx/tools/fuzz/runner.py +0 -502
- hwpx/tools/official_lint.py +0 -478
- hwpx/tools/pii.py +0 -389
- hwpx/tools/report_parser.py +0 -135
- hwpx/tools/style_profile.py +0 -437
- hwpx/tools/table_compute.py +0 -477
- hwpx/visual/__init__.py +0 -115
- hwpx/visual/_hancom_open_rate.ps1 +0 -374
- hwpx/visual/_refresh_hwpx_mac.applescript +0 -162
- hwpx/visual/_render_hwpx.ps1 +0 -72
- hwpx/visual/_render_hwpx_mac.applescript +0 -249
- hwpx/visual/detectors.py +0 -151
- hwpx/visual/diff.py +0 -153
- hwpx/visual/fixture_corpus.py +0 -210
- hwpx/visual/hancom_worker.py +0 -287
- hwpx/visual/masks.py +0 -51
- hwpx/visual/oracle.py +0 -942
- hwpx/visual/page_qa.py +0 -244
- hwpx/visual/qa_contracts.py +0 -293
- hwpx/visual/qa_metrics.py +0 -236
- hwpx/visual/report.py +0 -47
- python_hwpx-4.2.0.dist-info/METADATA +0 -182
- python_hwpx-4.2.0.dist-info/RECORD +0 -218
- {python_hwpx-4.2.0.dist-info → python_hwpx-5.0.2.dist-info}/WHEEL +0 -0
- {python_hwpx-4.2.0.dist-info → python_hwpx-5.0.2.dist-info}/licenses/LICENSE +0 -0
- {python_hwpx-4.2.0.dist-info → python_hwpx-5.0.2.dist-info}/licenses/NOTICE +0 -0
- {python_hwpx-4.2.0.dist-info → python_hwpx-5.0.2.dist-info}/top_level.txt +0 -0
hwpx/__init__.py
CHANGED
|
@@ -13,13 +13,15 @@
|
|
|
13
13
|
|
|
14
14
|
import importlib
|
|
15
15
|
import warnings
|
|
16
|
-
from
|
|
16
|
+
from dataclasses import dataclass
|
|
17
|
+
from typing import Literal
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
def _resolve_version() -> str:
|
|
20
|
-
"""패키지 메타데이터에서
|
|
21
|
+
"""패키지 메타데이터에서 배포 버전을 조회합니다(import는 함수 스코프 유지)."""
|
|
22
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
21
23
|
try:
|
|
22
|
-
return
|
|
24
|
+
return version("python-hwpx")
|
|
23
25
|
except PackageNotFoundError:
|
|
24
26
|
return "0+unknown"
|
|
25
27
|
|
|
@@ -48,12 +50,11 @@ _EXPERIMENTAL_EXPORTS = {
|
|
|
48
50
|
"render_document_viewer": "hwpx.tools.document_viewer",
|
|
49
51
|
}
|
|
50
52
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
53
|
+
# Emptied in 5.0. The 4.x notice on these names said they would go in the next
|
|
54
|
+
# major, and this is it: analyze_template_formfit, apply_template_formfit and the
|
|
55
|
+
# two schema-version constants are gone with hwpx.template_formfit. Use
|
|
56
|
+
# hwpx.table_patch.fill_cells, or the MCP form-fill trio.
|
|
57
|
+
_DEPRECATED_EXPORTS: dict[str, str] = {}
|
|
57
58
|
|
|
58
59
|
# deprecated formfit 표면의 공통 대체 경로 안내.
|
|
59
60
|
_FORMFIT_REPLACEMENT = (
|
|
@@ -78,6 +79,197 @@ def _deprecated_message(name: str) -> str:
|
|
|
78
79
|
)
|
|
79
80
|
|
|
80
81
|
|
|
82
|
+
# --- 5.0에서 이동한 이름 -------------------------------------------------
|
|
83
|
+
#
|
|
84
|
+
# 응용 워크플로는 companion 패키지가 소유한다. 여기 남는 것은 **표뿐**이다 —
|
|
85
|
+
# 그 패키지를 import하지 않는다. 설치돼 있는지에 따라 core의 동작이 달라지면
|
|
86
|
+
# 의존 방향이 뒤집히고, 그건 5.0이 없애려던 바로 그 결합이다.
|
|
87
|
+
#
|
|
88
|
+
# 표가 하는 일은 오류 메시지 하나다. 기본 Python 오류는 이름이 사라졌다는 것만
|
|
89
|
+
# 알려주고 어디로 갔는지는 말해주지 않는다.
|
|
90
|
+
_SurfaceKind = Literal["symbol", "module", "renamed", "retired"]
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@dataclass(frozen=True, slots=True)
|
|
94
|
+
class _CompanionSurface:
|
|
95
|
+
"""One executable migration hint without importing the companion."""
|
|
96
|
+
|
|
97
|
+
kind: _SurfaceKind
|
|
98
|
+
target_module: str | None = None
|
|
99
|
+
target_name: str | None = None
|
|
100
|
+
replacement: str | None = None
|
|
101
|
+
|
|
102
|
+
def import_statement(self, legacy_name: str) -> str | None:
|
|
103
|
+
if self.kind == "retired":
|
|
104
|
+
return None
|
|
105
|
+
if self.target_module is None:
|
|
106
|
+
raise AssertionError(f"{legacy_name}: target_module is required")
|
|
107
|
+
if self.kind == "module" or (
|
|
108
|
+
self.kind == "renamed" and self.target_name is None
|
|
109
|
+
):
|
|
110
|
+
return f"import {self.target_module} as {legacy_name}"
|
|
111
|
+
target_name = self.target_name or legacy_name
|
|
112
|
+
alias = f" as {legacy_name}" if target_name != legacy_name else ""
|
|
113
|
+
return f"from {self.target_module} import {target_name}{alias}"
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _symbol(target_module: str, target_name: str | None = None) -> _CompanionSurface:
|
|
117
|
+
return _CompanionSurface("symbol", target_module, target_name)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _module(target_module: str) -> _CompanionSurface:
|
|
121
|
+
return _CompanionSurface("module", target_module)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _renamed(
|
|
125
|
+
target_module: str,
|
|
126
|
+
target_name: str | None = None,
|
|
127
|
+
) -> _CompanionSurface:
|
|
128
|
+
return _CompanionSurface("renamed", target_module, target_name)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
_MOVED_TO_COMPANION: dict[str, _CompanionSurface] = {
|
|
132
|
+
"AUTHORING_REPORT_VERSION": _symbol("hwpx_automation.office.authoring"),
|
|
133
|
+
"DEFAULT_STYLE_PRESET": _symbol("hwpx_automation.office.authoring"),
|
|
134
|
+
"DOCUMENT_PLAN_SCHEMA_VERSION": _symbol("hwpx_automation.office.authoring"),
|
|
135
|
+
"DocumentBlock": _symbol("hwpx_automation.office.authoring"),
|
|
136
|
+
"DocumentPlan": _symbol("hwpx_automation.office.authoring"),
|
|
137
|
+
"DocumentStylePreset": _symbol("hwpx_automation.office.authoring"),
|
|
138
|
+
"OFFICIAL_DOCUMENT_STYLE_REPORT_VERSION": _symbol(
|
|
139
|
+
"hwpx_automation.office.compliance.official_lint"
|
|
140
|
+
),
|
|
141
|
+
"PlanValidationIssue": _symbol("hwpx_automation.office.authoring"),
|
|
142
|
+
"PlanValidationReport": _symbol("hwpx_automation.office.authoring"),
|
|
143
|
+
"STYLE_PROFILE_COMPARISON_SCHEMA_VERSION": _symbol(
|
|
144
|
+
"hwpx_automation.office.authoring.style_profile"
|
|
145
|
+
),
|
|
146
|
+
"STYLE_PROFILE_SCHEMA_VERSION": _symbol(
|
|
147
|
+
"hwpx_automation.office.authoring.style_profile"
|
|
148
|
+
),
|
|
149
|
+
"TABLE_COMPUTE_REPORT_VERSION": _symbol("hwpx_automation.office.utilities"),
|
|
150
|
+
"TEMPLATE_REGISTRY_SCHEMA_VERSION": _symbol(
|
|
151
|
+
"hwpx_automation.office.authoring.style_profile"
|
|
152
|
+
),
|
|
153
|
+
"agent": _module("hwpx_automation.office.agent"),
|
|
154
|
+
"apply_style_profile_to_plan": _symbol(
|
|
155
|
+
"hwpx_automation.office.authoring.style_profile"
|
|
156
|
+
),
|
|
157
|
+
"approval_box": _symbol("hwpx_automation.office.authoring.builder"),
|
|
158
|
+
"authoring": _module("hwpx_automation.office.authoring"),
|
|
159
|
+
"build_comparison_table_plan": _symbol(
|
|
160
|
+
"hwpx_automation.office.document_ops"
|
|
161
|
+
),
|
|
162
|
+
"build_image_grid": _symbol("hwpx_automation.office.authoring"),
|
|
163
|
+
"build_meeting_nameplates": _symbol(
|
|
164
|
+
"hwpx_automation.office.authoring.advanced_generators"
|
|
165
|
+
),
|
|
166
|
+
"build_organization_chart": _symbol(
|
|
167
|
+
"hwpx_automation.office.authoring.advanced_generators"
|
|
168
|
+
),
|
|
169
|
+
"builder": _module("hwpx_automation.office.authoring.builder"),
|
|
170
|
+
"compare_style_profiles": _symbol(
|
|
171
|
+
"hwpx_automation.office.authoring.style_profile"
|
|
172
|
+
),
|
|
173
|
+
"create_document_from_plan": _symbol("hwpx_automation.office.authoring"),
|
|
174
|
+
"describe_template": _symbol(
|
|
175
|
+
"hwpx_automation.office.authoring.style_profile"
|
|
176
|
+
),
|
|
177
|
+
"design": _module("hwpx_automation.office.authoring.design"),
|
|
178
|
+
"evalplan_fill": _renamed("hwpx_automation.office.evalplan"),
|
|
179
|
+
"exam": _module("hwpx_automation.office.exam"),
|
|
180
|
+
"extract_style_profile": _symbol(
|
|
181
|
+
"hwpx_automation.office.authoring.style_profile"
|
|
182
|
+
),
|
|
183
|
+
"fill_residue": _module(
|
|
184
|
+
"hwpx_automation.office.form_fill.fill_residue"
|
|
185
|
+
),
|
|
186
|
+
"form_fill": _module("hwpx_automation.office.form_fill"),
|
|
187
|
+
"formfill_quality": _renamed(
|
|
188
|
+
"hwpx_automation.office.form_fill.quality"
|
|
189
|
+
),
|
|
190
|
+
"get_document_plan_schema": _symbol("hwpx_automation.office.authoring"),
|
|
191
|
+
"guidance_scan": _renamed(
|
|
192
|
+
"hwpx_automation.office.form_fill.guidance"
|
|
193
|
+
),
|
|
194
|
+
"inspect_document_authoring_quality": _symbol(
|
|
195
|
+
"hwpx_automation.office.authoring"
|
|
196
|
+
),
|
|
197
|
+
"inspect_official_document_style": _symbol(
|
|
198
|
+
"hwpx_automation.office.compliance.official_lint"
|
|
199
|
+
),
|
|
200
|
+
"inspect_operating_plan_quality": _symbol(
|
|
201
|
+
"hwpx_automation.office.authoring"
|
|
202
|
+
),
|
|
203
|
+
"list_templates": _symbol(
|
|
204
|
+
"hwpx_automation.office.authoring.style_profile"
|
|
205
|
+
),
|
|
206
|
+
"mail_merge": _renamed(
|
|
207
|
+
"hwpx_automation.office.document_ops",
|
|
208
|
+
"build_mail_merge",
|
|
209
|
+
),
|
|
210
|
+
"normalize_document_plan": _symbol("hwpx_automation.office.authoring"),
|
|
211
|
+
"placeholder_fill_report": _symbol(
|
|
212
|
+
"hwpx_automation.office.authoring.style_profile"
|
|
213
|
+
),
|
|
214
|
+
"presets": _module("hwpx_automation.office.authoring.presets"),
|
|
215
|
+
"register_template": _symbol(
|
|
216
|
+
"hwpx_automation.office.authoring.style_profile"
|
|
217
|
+
),
|
|
218
|
+
"table_compute": _symbol("hwpx_automation.office.utilities"),
|
|
219
|
+
"template_formfit": _module(
|
|
220
|
+
"hwpx_automation.office.form_fill.template_formfit"
|
|
221
|
+
),
|
|
222
|
+
"validate_document_plan": _symbol("hwpx_automation.office.authoring"),
|
|
223
|
+
"visual": _renamed("hwpx_automation.office.rendering"),
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
_RETIRED_SURFACES: dict[str, _CompanionSurface] = {
|
|
227
|
+
"analyze_template_formfit": _CompanionSurface(
|
|
228
|
+
"retired",
|
|
229
|
+
replacement=(
|
|
230
|
+
"MCP analyze_form_fill → apply_form_fill → verify_form_fill 경로를 "
|
|
231
|
+
"사용하세요."
|
|
232
|
+
),
|
|
233
|
+
),
|
|
234
|
+
"apply_template_formfit": _CompanionSurface(
|
|
235
|
+
"retired",
|
|
236
|
+
replacement=(
|
|
237
|
+
"MCP analyze_form_fill → apply_form_fill → verify_form_fill 경로를 "
|
|
238
|
+
"사용하세요."
|
|
239
|
+
),
|
|
240
|
+
),
|
|
241
|
+
"TEMPLATE_FORMFIT_BASELINE_SCHEMA_VERSION": _CompanionSurface(
|
|
242
|
+
"retired",
|
|
243
|
+
replacement="새 form-fill ToolSpec 스키마를 사용하세요.",
|
|
244
|
+
),
|
|
245
|
+
"TEMPLATE_FORMFIT_PLAN_SCHEMA_VERSION": _CompanionSurface(
|
|
246
|
+
"retired",
|
|
247
|
+
replacement="새 form-fill ToolSpec 스키마를 사용하세요.",
|
|
248
|
+
),
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class MovedToCompanion(ImportError):
|
|
253
|
+
"""이동한 이름에 접근했다.
|
|
254
|
+
|
|
255
|
+
ImportError인 이유. ``from hwpx import X``는 모듈 ``__getattr__``이 던진
|
|
256
|
+
AttributeError를 잡아 자기만의 밋밋한 ImportError로 바꿔버린다 — 행선지를
|
|
257
|
+
적어 넣어도 사용자에게 닿지 않는다는 뜻이다. ImportError로 던지면 그대로
|
|
258
|
+
올라간다. 공개 코드에서 깨지는 형태가 압도적으로 ``from hwpx import X``라
|
|
259
|
+
그쪽을 택했다.
|
|
260
|
+
|
|
261
|
+
대가는 ``hasattr(hwpx, "...")``이다. Python의 hasattr은 AttributeError만
|
|
262
|
+
잡으므로 False 대신 예외가 나간다. 둘 다 상속해서 해결하려 했지만
|
|
263
|
+
ImportError와 AttributeError는 C 레벨 레이아웃이 충돌해 불가능하다.
|
|
264
|
+
예외가 나가더라도 메시지가 이유를 말해주므로 조용히 False가 되는 것보다는
|
|
265
|
+
낫다고 판단했다.
|
|
266
|
+
"""
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
class RetiredSurface(ImportError):
|
|
270
|
+
"""A removed compatibility surface whose replacement is not another import."""
|
|
271
|
+
|
|
272
|
+
|
|
81
273
|
def __getattr__(name: str) -> object:
|
|
82
274
|
"""Resolve dynamic module attributes.
|
|
83
275
|
|
|
@@ -90,14 +282,43 @@ def __getattr__(name: str) -> object:
|
|
|
90
282
|
|
|
91
283
|
module_name = _EXPERIMENTAL_EXPORTS.get(name)
|
|
92
284
|
if module_name is not None:
|
|
285
|
+
if module_name != "hwpx" and not module_name.startswith("hwpx."):
|
|
286
|
+
raise AttributeError(
|
|
287
|
+
"hwpx lazy-export targets must remain inside the 'hwpx' package"
|
|
288
|
+
)
|
|
93
289
|
warnings.warn(_experimental_message(name), DeprecationWarning, stacklevel=2)
|
|
94
290
|
return getattr(importlib.import_module(module_name), name)
|
|
95
291
|
|
|
96
292
|
module_name = _DEPRECATED_EXPORTS.get(name)
|
|
97
293
|
if module_name is not None:
|
|
294
|
+
if module_name != "hwpx" and not module_name.startswith("hwpx."):
|
|
295
|
+
raise AttributeError(
|
|
296
|
+
"hwpx lazy-export targets must remain inside the 'hwpx' package"
|
|
297
|
+
)
|
|
98
298
|
warnings.warn(_deprecated_message(name), DeprecationWarning, stacklevel=2)
|
|
99
299
|
return getattr(importlib.import_module(module_name), name)
|
|
100
300
|
|
|
301
|
+
moved = _MOVED_TO_COMPANION.get(name)
|
|
302
|
+
if moved is not None:
|
|
303
|
+
statement = moved.import_statement(name)
|
|
304
|
+
assert statement is not None
|
|
305
|
+
raise MovedToCompanion(
|
|
306
|
+
f"{name}은(는) python-hwpx 5.0에서 companion 패키지로 이동했습니다.\n"
|
|
307
|
+
f" pip install python-hwpx-automation\n"
|
|
308
|
+
f" {statement}\n"
|
|
309
|
+
f'4.x를 계속 쓰려면: pip install "python-hwpx<5"\n'
|
|
310
|
+
f"전체 대체표: docs/migration-5.0.md"
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
retired = _RETIRED_SURFACES.get(name)
|
|
314
|
+
if retired is not None:
|
|
315
|
+
raise RetiredSurface(
|
|
316
|
+
f"{name}은(는) 예고대로 python-hwpx 5.0에서 제거되었습니다. "
|
|
317
|
+
f"{retired.replacement}\n"
|
|
318
|
+
f'4.x를 계속 쓰려면: pip install "python-hwpx<5"\n'
|
|
319
|
+
f"전체 대체표: docs/migration-5.0.md"
|
|
320
|
+
)
|
|
321
|
+
|
|
101
322
|
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
102
323
|
|
|
103
324
|
|
|
@@ -118,15 +339,9 @@ from .tools.text_extractor import (
|
|
|
118
339
|
TextExtractor,
|
|
119
340
|
)
|
|
120
341
|
from .tools.object_finder import FoundElement, ObjectFinder
|
|
121
|
-
from .tools.advanced_generators import (
|
|
122
|
-
build_image_grid,
|
|
123
|
-
build_meeting_nameplates,
|
|
124
|
-
build_organization_chart,
|
|
125
|
-
)
|
|
126
342
|
from .tools.doc_diff import (
|
|
127
343
|
DOC_DIFF_REPORT_VERSION,
|
|
128
344
|
REFERENCE_CONSISTENCY_REPORT_VERSION,
|
|
129
|
-
build_comparison_table_plan,
|
|
130
345
|
diff_paragraphs,
|
|
131
346
|
doc_diff,
|
|
132
347
|
inspect_reference_consistency,
|
|
@@ -135,15 +350,7 @@ from .tools.mail_merge import (
|
|
|
135
350
|
MAIL_MERGE_REPORT_VERSION,
|
|
136
351
|
inspect_mail_merge_placeholders,
|
|
137
352
|
load_mail_merge_rows,
|
|
138
|
-
|
|
139
|
-
)
|
|
140
|
-
from .tools.table_compute import (
|
|
141
|
-
TABLE_COMPUTE_REPORT_VERSION,
|
|
142
|
-
table_compute,
|
|
143
|
-
)
|
|
144
|
-
from .tools.official_lint import (
|
|
145
|
-
OFFICIAL_DOCUMENT_STYLE_REPORT_VERSION,
|
|
146
|
-
inspect_official_document_style,
|
|
353
|
+
merge_template_rows,
|
|
147
354
|
)
|
|
148
355
|
from .tools.package_validator import (
|
|
149
356
|
EditorOpenSafetyReport,
|
|
@@ -151,18 +358,6 @@ from .tools.package_validator import (
|
|
|
151
358
|
validate_editor_open_safety,
|
|
152
359
|
validate_package,
|
|
153
360
|
)
|
|
154
|
-
from .tools.style_profile import (
|
|
155
|
-
STYLE_PROFILE_COMPARISON_SCHEMA_VERSION,
|
|
156
|
-
STYLE_PROFILE_SCHEMA_VERSION,
|
|
157
|
-
TEMPLATE_REGISTRY_SCHEMA_VERSION,
|
|
158
|
-
apply_style_profile_to_plan,
|
|
159
|
-
compare_style_profiles,
|
|
160
|
-
describe_template,
|
|
161
|
-
extract_style_profile,
|
|
162
|
-
list_templates,
|
|
163
|
-
placeholder_fill_report,
|
|
164
|
-
register_template,
|
|
165
|
-
)
|
|
166
361
|
from .ingest import HwpxMarkdownConverter
|
|
167
362
|
from .errors import HwpxError
|
|
168
363
|
from .mutation_report import (
|
|
@@ -178,23 +373,6 @@ from .patch import (
|
|
|
178
373
|
)
|
|
179
374
|
from .document import HwpxDocument
|
|
180
375
|
from .package import HwpxPackage
|
|
181
|
-
from .authoring import (
|
|
182
|
-
AUTHORING_REPORT_VERSION,
|
|
183
|
-
DEFAULT_STYLE_PRESET,
|
|
184
|
-
DOCUMENT_PLAN_SCHEMA_VERSION,
|
|
185
|
-
DocumentBlock,
|
|
186
|
-
DocumentPlan,
|
|
187
|
-
DocumentStylePreset,
|
|
188
|
-
PlanValidationIssue,
|
|
189
|
-
PlanValidationReport,
|
|
190
|
-
create_document_from_plan,
|
|
191
|
-
get_document_plan_schema,
|
|
192
|
-
inspect_document_authoring_quality,
|
|
193
|
-
inspect_operating_plan_quality,
|
|
194
|
-
normalize_document_plan,
|
|
195
|
-
validate_document_plan,
|
|
196
|
-
)
|
|
197
|
-
from .builder import approval_box
|
|
198
376
|
from .quality import (
|
|
199
377
|
QualityPolicy,
|
|
200
378
|
SavePipeline,
|
|
@@ -202,18 +380,20 @@ from .quality import (
|
|
|
202
380
|
)
|
|
203
381
|
|
|
204
382
|
__all__ = [
|
|
383
|
+
"inspect_reference_consistency",
|
|
384
|
+
"doc_diff",
|
|
385
|
+
"diff_paragraphs",
|
|
386
|
+
"REFERENCE_CONSISTENCY_REPORT_VERSION",
|
|
387
|
+
"DOC_DIFF_REPORT_VERSION",
|
|
388
|
+
"merge_template_rows",
|
|
389
|
+
"load_mail_merge_rows",
|
|
390
|
+
"inspect_mail_merge_placeholders",
|
|
391
|
+
"MAIL_MERGE_REPORT_VERSION",
|
|
205
392
|
"QualityPolicy",
|
|
206
393
|
"SavePipeline",
|
|
207
394
|
"VisualCompleteReport",
|
|
208
395
|
"__version__",
|
|
209
|
-
"AUTHORING_REPORT_VERSION",
|
|
210
396
|
"DEFAULT_NAMESPACES",
|
|
211
|
-
"DEFAULT_STYLE_PRESET",
|
|
212
|
-
"DOCUMENT_PLAN_SCHEMA_VERSION",
|
|
213
|
-
"get_document_plan_schema",
|
|
214
|
-
"DocumentBlock",
|
|
215
|
-
"DocumentPlan",
|
|
216
|
-
"DocumentStylePreset",
|
|
217
397
|
"EditorOpenSafetyReport",
|
|
218
398
|
"ParagraphInfo",
|
|
219
399
|
"PackageValidationReport",
|
|
@@ -221,7 +401,6 @@ __all__ = [
|
|
|
221
401
|
"HwpxError",
|
|
222
402
|
"MutationReport",
|
|
223
403
|
"PreservationDowngradeError",
|
|
224
|
-
"PlanValidationReport",
|
|
225
404
|
"ParagraphTextPatch",
|
|
226
405
|
"PatchApplied",
|
|
227
406
|
"PatchSkipped",
|
|
@@ -230,43 +409,9 @@ __all__ = [
|
|
|
230
409
|
"FoundElement",
|
|
231
410
|
"HwpxMarkdownConverter",
|
|
232
411
|
"ObjectFinder",
|
|
233
|
-
"OFFICIAL_DOCUMENT_STYLE_REPORT_VERSION",
|
|
234
|
-
"DOC_DIFF_REPORT_VERSION",
|
|
235
|
-
"REFERENCE_CONSISTENCY_REPORT_VERSION",
|
|
236
|
-
"MAIL_MERGE_REPORT_VERSION",
|
|
237
|
-
"STYLE_PROFILE_COMPARISON_SCHEMA_VERSION",
|
|
238
|
-
"STYLE_PROFILE_SCHEMA_VERSION",
|
|
239
|
-
"TEMPLATE_REGISTRY_SCHEMA_VERSION",
|
|
240
|
-
"TABLE_COMPUTE_REPORT_VERSION",
|
|
241
|
-
"apply_style_profile_to_plan",
|
|
242
|
-
"build_comparison_table_plan",
|
|
243
|
-
"build_image_grid",
|
|
244
|
-
"build_meeting_nameplates",
|
|
245
|
-
"build_organization_chart",
|
|
246
|
-
"diff_paragraphs",
|
|
247
|
-
"doc_diff",
|
|
248
|
-
"compare_style_profiles",
|
|
249
|
-
"PlanValidationIssue",
|
|
250
412
|
"HwpxDocument",
|
|
251
413
|
"HwpxPackage",
|
|
252
|
-
"create_document_from_plan",
|
|
253
|
-
"approval_box",
|
|
254
|
-
"describe_template",
|
|
255
|
-
"extract_style_profile",
|
|
256
|
-
"inspect_document_authoring_quality",
|
|
257
|
-
"inspect_mail_merge_placeholders",
|
|
258
|
-
"inspect_official_document_style",
|
|
259
|
-
"inspect_reference_consistency",
|
|
260
|
-
"inspect_operating_plan_quality",
|
|
261
|
-
"list_templates",
|
|
262
|
-
"load_mail_merge_rows",
|
|
263
|
-
"mail_merge",
|
|
264
|
-
"normalize_document_plan",
|
|
265
|
-
"placeholder_fill_report",
|
|
266
|
-
"validate_document_plan",
|
|
267
414
|
"validate_editor_open_safety",
|
|
268
415
|
"validate_package",
|
|
269
416
|
"paragraph_patch",
|
|
270
|
-
"register_template",
|
|
271
|
-
"table_compute",
|
|
272
417
|
]
|
hwpx/_document/__init__.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
"""Domain owners behind the HwpxDocument facade
|
|
2
|
+
"""Domain owners behind the HwpxDocument facade."""
|
hwpx/_document/_units.py
CHANGED
hwpx/_document/fields.py
CHANGED
hwpx/_document/layout.py
CHANGED
hwpx/_document/media.py
CHANGED
hwpx/_document/memos.py
CHANGED
hwpx/_document/persistence.py
CHANGED
hwpx/_document/shapes.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
"""Shape/control/note domain owner behind the HwpxDocument facade
|
|
2
|
+
"""Shape/control/note domain owner behind the HwpxDocument facade."""
|
|
3
3
|
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
@@ -29,7 +29,13 @@ def add_shape(
|
|
|
29
29
|
run_attributes: dict[str, str] | None = None,
|
|
30
30
|
**extra_attrs: str,
|
|
31
31
|
) -> HwpxOxmlInlineObject:
|
|
32
|
-
"""Insert an inline shape into a new paragraph.
|
|
32
|
+
"""Insert an inline shape into a new paragraph.
|
|
33
|
+
|
|
34
|
+
Low-level escape hatch — writes only the element and attributes it is
|
|
35
|
+
handed, so the result is not openable by Hancom until the required OWPML
|
|
36
|
+
children are supplied. Warns while they are missing. Prefer
|
|
37
|
+
:func:`add_line`, :func:`add_rectangle`, and :func:`add_ellipse`.
|
|
38
|
+
"""
|
|
33
39
|
|
|
34
40
|
paragraph = doc.add_paragraph(
|
|
35
41
|
"",
|
|
@@ -62,7 +68,12 @@ def add_control(
|
|
|
62
68
|
run_attributes: dict[str, str] | None = None,
|
|
63
69
|
**extra_attrs: str,
|
|
64
70
|
) -> HwpxOxmlInlineObject:
|
|
65
|
-
"""Insert a control inline object into a new paragraph.
|
|
71
|
+
"""Insert a control inline object into a new paragraph.
|
|
72
|
+
|
|
73
|
+
Low-level escape hatch — an ``<hp:ctrl>`` means nothing without the child
|
|
74
|
+
element that carries the control, and Hancom refuses to open a document
|
|
75
|
+
containing an empty one, so this warns until a child is appended.
|
|
76
|
+
"""
|
|
66
77
|
|
|
67
78
|
paragraph = doc.add_paragraph(
|
|
68
79
|
"",
|
hwpx/_document/tracked.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
"""Tracked-change authoring owner behind the :class:`HwpxDocument` facade
|
|
2
|
+
"""Tracked-change authoring owner behind the :class:`HwpxDocument` facade."""
|
|
3
3
|
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
@@ -48,6 +48,36 @@ def _paragraph_has_deletable_text(
|
|
|
48
48
|
return False
|
|
49
49
|
|
|
50
50
|
|
|
51
|
+
def _paragraph_has_replaceable_text(
|
|
52
|
+
paragraph: "HwpxOxmlParagraph",
|
|
53
|
+
match: str,
|
|
54
|
+
) -> bool:
|
|
55
|
+
"""Return whether *match* can be wrapped inside one inline text segment.
|
|
56
|
+
|
|
57
|
+
A match that only exists after concatenating text across inline markup is
|
|
58
|
+
present but cannot be rewritten safely. Detect that before allocating
|
|
59
|
+
tracked-change header IDs so a rejected replacement leaves no orphan
|
|
60
|
+
metadata behind.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
crosses_inline_markup = False
|
|
64
|
+
for run in paragraph.runs:
|
|
65
|
+
model = run.to_model()
|
|
66
|
+
run_text = "".join(span.text for span in model.text_spans)
|
|
67
|
+
if match not in run_text:
|
|
68
|
+
continue
|
|
69
|
+
for span in model.text_spans:
|
|
70
|
+
if match in span.leading_text or any(
|
|
71
|
+
match in markup.trailing_text for markup in span.marks
|
|
72
|
+
):
|
|
73
|
+
return True
|
|
74
|
+
crosses_inline_markup = True
|
|
75
|
+
|
|
76
|
+
if crosses_inline_markup:
|
|
77
|
+
raise ValueError("match crosses inline markup and cannot be wrapped safely")
|
|
78
|
+
return False
|
|
79
|
+
|
|
80
|
+
|
|
51
81
|
def add_tracked_insert(
|
|
52
82
|
doc: "HwpxDocument",
|
|
53
83
|
paragraph: "HwpxOxmlParagraph",
|
|
@@ -111,16 +141,30 @@ def add_tracked_replace(
|
|
|
111
141
|
) -> tuple[int, int]:
|
|
112
142
|
"""Represent a replacement as tracked delete of *old* plus tracked insert of *new*."""
|
|
113
143
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
144
|
+
if old == "":
|
|
145
|
+
raise ValueError("match must be a non-empty string")
|
|
146
|
+
if not _paragraph_has_replaceable_text(paragraph, old):
|
|
147
|
+
raise ValueError("match text was not found in the paragraph")
|
|
148
|
+
sanitized = _sanitize_tracked_text(new)
|
|
149
|
+
if not sanitized:
|
|
150
|
+
raise ValueError("tracked insert text must be non-empty")
|
|
151
|
+
|
|
152
|
+
delete_change_id = doc.add_track_change(
|
|
153
|
+
"Delete",
|
|
154
|
+
author_name=author,
|
|
118
155
|
date=date,
|
|
119
156
|
)
|
|
120
|
-
insert_change_id = doc.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
author=author,
|
|
157
|
+
insert_change_id = doc.add_track_change(
|
|
158
|
+
"Insert",
|
|
159
|
+
author_name=author,
|
|
124
160
|
date=date,
|
|
125
161
|
)
|
|
162
|
+
first_mark_id = doc._root.next_track_change_mark_id()
|
|
163
|
+
paragraph._add_tracked_replace(
|
|
164
|
+
old,
|
|
165
|
+
sanitized,
|
|
166
|
+
delete_change_id=delete_change_id,
|
|
167
|
+
insert_change_id=insert_change_id,
|
|
168
|
+
first_mark_id=first_mark_id,
|
|
169
|
+
)
|
|
126
170
|
return delete_change_id, insert_change_id
|
hwpx/body_patch.py
CHANGED
|
@@ -451,8 +451,8 @@ def strip_runs_by_color(
|
|
|
451
451
|
sections = {n: z.read(n).decode("utf-8") for n in names if re.search(r"section\d+\.xml$", n)}
|
|
452
452
|
|
|
453
453
|
# 계열 매칭(잔존 게이트와 정렬): 대상 색의 _color_family에 드는 모든 charPr.
|
|
454
|
-
from .
|
|
455
|
-
target_families = {
|
|
454
|
+
from .oxml.color import color_family
|
|
455
|
+
target_families = {color_family(h) for h in targets}
|
|
456
456
|
ids = set()
|
|
457
457
|
for cm in re.finditer(r"<(?:[A-Za-z_][\w.-]*:)?charPr\b[^>]*?>", header_xml):
|
|
458
458
|
tag = cm.group(0)
|
|
@@ -461,7 +461,7 @@ def strip_runs_by_color(
|
|
|
461
461
|
if not (idm and colm):
|
|
462
462
|
continue
|
|
463
463
|
col = colm.group(1).upper()
|
|
464
|
-
if col in targets or
|
|
464
|
+
if col in targets or color_family(col) in target_families:
|
|
465
465
|
ids.add(idm.group(1))
|
|
466
466
|
transcript: list[dict[str, Any]] = []
|
|
467
467
|
changed: set[str] = set()
|