@kimdayoun/hwpx-mcp 0.3.0 → 0.3.3
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.
- package/CHANGELOG.md +148 -0
- package/README.md +17 -46
- package/dist/HangingIndentCalculator.js +136 -166
- package/dist/HwpxDocument.d.ts +152 -12
- package/dist/HwpxDocument.js +780 -444
- package/dist/HwpxParser.js +18 -2
- package/dist/index.js +95 -40
- package/dist/types.d.ts +5 -0
- package/package.json +3 -1
package/dist/HwpxDocument.d.ts
CHANGED
|
@@ -59,6 +59,12 @@ export declare class HwpxDocument {
|
|
|
59
59
|
private _zip;
|
|
60
60
|
private _content;
|
|
61
61
|
private _isDirty;
|
|
62
|
+
/**
|
|
63
|
+
* True once the section element list has changed shape since the XML was
|
|
64
|
+
* parsed (insert/delete/copy/move of paragraphs, tables or images).
|
|
65
|
+
* Parsed XML offsets are unusable from that point until the next save.
|
|
66
|
+
*/
|
|
67
|
+
private _structureChanged;
|
|
62
68
|
private _format;
|
|
63
69
|
private _undoStack;
|
|
64
70
|
private _redoStack;
|
|
@@ -69,6 +75,12 @@ export declare class HwpxDocument {
|
|
|
69
75
|
private _pendingImageInserts;
|
|
70
76
|
private _pendingCellImageInserts;
|
|
71
77
|
private _pendingTableInserts;
|
|
78
|
+
/**
|
|
79
|
+
* Monotonic counter shared by paragraph and table inserts. Both kinds are
|
|
80
|
+
* replayed into XML in this order so each insert sees exactly the elements
|
|
81
|
+
* that existed when it was made. Replaying all tables before all paragraphs
|
|
82
|
+
* wrote "A, table, A-2, table" to disk as "A, A-2, table, table".
|
|
83
|
+
*/
|
|
72
84
|
private _tableInsertCounter;
|
|
73
85
|
private _pendingImageDeletes;
|
|
74
86
|
private _pendingTableDeletes;
|
|
@@ -107,6 +119,13 @@ export declare class HwpxDocument {
|
|
|
107
119
|
static createNew(id: string, title?: string, creator?: string): HwpxDocument;
|
|
108
120
|
get id(): string;
|
|
109
121
|
get path(): string;
|
|
122
|
+
/** True once the document has a real location on disk. */
|
|
123
|
+
get hasPath(): boolean;
|
|
124
|
+
/**
|
|
125
|
+
* Record where the document now lives after a successful write, so the next
|
|
126
|
+
* save without an explicit path targets the same file.
|
|
127
|
+
*/
|
|
128
|
+
setPath(newPath: string): void;
|
|
110
129
|
get format(): DocumentFormat;
|
|
111
130
|
get isDirty(): boolean;
|
|
112
131
|
get zip(): JSZip | null;
|
|
@@ -128,6 +147,32 @@ export declare class HwpxDocument {
|
|
|
128
147
|
* Call this after any modification that changes document structure or content.
|
|
129
148
|
*/
|
|
130
149
|
private markModified;
|
|
150
|
+
/**
|
|
151
|
+
* Record that the section element list changed shape. Call from every method
|
|
152
|
+
* that inserts, removes, copies or moves a section-level element. Once set,
|
|
153
|
+
* paragraph edits stop trusting offsets cached at parse time and locate their
|
|
154
|
+
* target in the current XML instead.
|
|
155
|
+
*/
|
|
156
|
+
private markStructureChanged;
|
|
157
|
+
/**
|
|
158
|
+
* Resolve "after element N" into an id-based anchor using the memory model
|
|
159
|
+
* as it is right now (before the new element is spliced in).
|
|
160
|
+
*
|
|
161
|
+
* Returns null for "before everything" (N < 0). Elements without an XML
|
|
162
|
+
* paragraph/table of their own (images, shapes) are skipped backwards to the
|
|
163
|
+
* nearest paragraph or table, which is what the XML placement needs.
|
|
164
|
+
*
|
|
165
|
+
* The parser turns a paragraph that is only a line of ─/━/═ into an 'hr'
|
|
166
|
+
* element with a fresh id. That paragraph is still in the XML, so it still
|
|
167
|
+
* takes an occurrence slot there: skipping it here put later anchors one
|
|
168
|
+
* paragraph early (measured on Hancom files with divider lines).
|
|
169
|
+
*/
|
|
170
|
+
private resolveElementAnchor;
|
|
171
|
+
/**
|
|
172
|
+
* The XML node a memory element stands for, or null if it has none of its
|
|
173
|
+
* own. An 'hr' parsed from a divider paragraph stands for that paragraph.
|
|
174
|
+
*/
|
|
175
|
+
private anchorKeyOf;
|
|
131
176
|
getSerializableContent(): object;
|
|
132
177
|
getAllText(): string;
|
|
133
178
|
getStructure(): object;
|
|
@@ -137,11 +182,15 @@ export declare class HwpxDocument {
|
|
|
137
182
|
index: number;
|
|
138
183
|
text: string;
|
|
139
184
|
style?: ParagraphStyle;
|
|
185
|
+
paraPrIDRef?: number;
|
|
186
|
+
charPrIDRef?: number;
|
|
140
187
|
}>;
|
|
141
188
|
getParagraph(sectionIndex: number, paragraphIndex: number): {
|
|
142
189
|
text: string;
|
|
143
190
|
runs: TextRun[];
|
|
144
191
|
style?: ParagraphStyle;
|
|
192
|
+
paraPrIDRef?: number;
|
|
193
|
+
charPrIDRef?: number;
|
|
145
194
|
} | null;
|
|
146
195
|
updateParagraphText(sectionIndex: number, elementIndex: number, runIndex: number, text: string): void;
|
|
147
196
|
updateParagraphRuns(sectionIndex: number, elementIndex: number, runs: TextRun[]): void;
|
|
@@ -433,6 +482,12 @@ export declare class HwpxDocument {
|
|
|
433
482
|
col: number;
|
|
434
483
|
value: string;
|
|
435
484
|
}>;
|
|
485
|
+
failed: Array<{
|
|
486
|
+
row: number;
|
|
487
|
+
col: number;
|
|
488
|
+
value: string;
|
|
489
|
+
error: string;
|
|
490
|
+
}>;
|
|
436
491
|
updated: Array<{
|
|
437
492
|
row: number;
|
|
438
493
|
col: number;
|
|
@@ -568,6 +623,12 @@ export declare class HwpxDocument {
|
|
|
568
623
|
text: string;
|
|
569
624
|
cell: TableCell;
|
|
570
625
|
} | null;
|
|
626
|
+
/**
|
|
627
|
+
* Find the merged cell that covers (row, col), if that position is not itself
|
|
628
|
+
* a master cell. A covered cell has no <hp:tc> of its own in the saved XML,
|
|
629
|
+
* so writing to it succeeds in memory and then silently vanishes on save.
|
|
630
|
+
*/
|
|
631
|
+
private findCoveringMergedCell;
|
|
571
632
|
updateTableCell(sectionIndex: number, tableIndex: number, row: number, col: number, text: string, charShapeId?: number): boolean;
|
|
572
633
|
setCellProperties(sectionIndex: number, tableIndex: number, row: number, col: number, props: Partial<TableCell>): boolean;
|
|
573
634
|
insertTableRow(sectionIndex: number, tableIndex: number, afterRowIndex: number, cellTexts?: string[]): boolean;
|
|
@@ -880,10 +941,68 @@ export declare class HwpxDocument {
|
|
|
880
941
|
*/
|
|
881
942
|
private applyParagraphDeletesToXml;
|
|
882
943
|
/**
|
|
883
|
-
*
|
|
884
|
-
*
|
|
944
|
+
* Find the end offset of the section-level element an insert anchors to.
|
|
945
|
+
*
|
|
946
|
+
* Paragraphs are matched by their own <hp:p id>. Tables are matched by
|
|
947
|
+
* <hp:tbl id>, either wrapped in a paragraph (the insert goes after that
|
|
948
|
+
* paragraph) or placed directly in the section (move_table writes them that
|
|
949
|
+
* way).
|
|
950
|
+
*
|
|
951
|
+
* The returned offset is always the end of a TOP-LEVEL element, because that
|
|
952
|
+
* is the only place a new section-level element may go. But paragraph
|
|
953
|
+
* occurrences are counted over exactly the paragraphs the parser puts in the
|
|
954
|
+
* memory model (see parsedParagraphStarts), so they agree with the occurrence
|
|
955
|
+
* resolveElementAnchor recorded. The parser also lifts paragraphs out of
|
|
956
|
+
* headers, text boxes and shapes; Hancom reuses id="0" / id="2147483648"
|
|
957
|
+
* there too. Counting only top-level paragraphs put 47 of 131 sampled Hancom
|
|
958
|
+
* files' copies and moves on the wrong paragraph.
|
|
959
|
+
*
|
|
960
|
+
* Returns -1 if the anchor is not present in the current XML.
|
|
961
|
+
*/
|
|
962
|
+
private findAnchorEnd;
|
|
963
|
+
/**
|
|
964
|
+
* The exact XML range of the memory paragraph a paragraph anchor names,
|
|
965
|
+
* found by id + occurrence among parsedParagraphStarts. For a paragraph in a
|
|
966
|
+
* header or text box this is that paragraph alone, not its container.
|
|
885
967
|
*/
|
|
886
|
-
private
|
|
968
|
+
private findParsedParagraph;
|
|
969
|
+
/**
|
|
970
|
+
* Start offsets (in `xml`) of the paragraphs HwpxParser turns into memory
|
|
971
|
+
* paragraphs, in document order. Mirrors HwpxParser.parseSection:
|
|
972
|
+
*
|
|
973
|
+
* - MEMO fields, footnotes and endnotes are ignored;
|
|
974
|
+
* - paragraphs inside any table are skipped;
|
|
975
|
+
* - a paragraph that holds a table is kept only if it still has <hp:t>
|
|
976
|
+
* once its tables are removed.
|
|
977
|
+
*
|
|
978
|
+
* Offsets are mapped back to the original XML, so callers can slice it.
|
|
979
|
+
*/
|
|
980
|
+
private parsedParagraphStarts;
|
|
981
|
+
/** Every <hp:tbl> range at any depth (outer tables before their nested ones). */
|
|
982
|
+
private findAllTablesDeep;
|
|
983
|
+
/** End offset of the paragraph opening at `start`, counting nested <hp:p>. */
|
|
984
|
+
private findBalancedParagraphEnd;
|
|
985
|
+
/** True if this paragraph directly (not via a nested table) holds <hp:tbl id>. */
|
|
986
|
+
private wrapsTopLevelTable;
|
|
987
|
+
/**
|
|
988
|
+
* Offset for an insert with no anchor ("before everything"). The first
|
|
989
|
+
* paragraph carries <hp:secPr> (page and section settings) and must stay
|
|
990
|
+
* first, so new content goes right after it.
|
|
991
|
+
*/
|
|
992
|
+
private findSectionHeadEnd;
|
|
993
|
+
/** Build the XML for a table inserted by insertTable, wrapped in its own paragraph. */
|
|
994
|
+
private buildInsertedTableXml;
|
|
995
|
+
/**
|
|
996
|
+
* Replay structural edits — paragraph/table inserts and paragraph
|
|
997
|
+
* copies/moves — into the section XML in the order the calls were made.
|
|
998
|
+
*
|
|
999
|
+
* Every edit carries id-based anchors resolved at call time, so it lands
|
|
1000
|
+
* after the same element in the XML that it followed in memory. Replaying in
|
|
1001
|
+
* call order means each anchor already exists (or has already moved) by the
|
|
1002
|
+
* time a later edit needs it. Copies/moves may cross sections, so all
|
|
1003
|
+
* touched sections are held in memory and written once at the end.
|
|
1004
|
+
*/
|
|
1005
|
+
private applyStructuralInsertsToXml;
|
|
887
1006
|
/**
|
|
888
1007
|
* Apply table move/copy operations to XML.
|
|
889
1008
|
* Extracts table XML from source and inserts at target position.
|
|
@@ -898,11 +1017,6 @@ export declare class HwpxDocument {
|
|
|
898
1017
|
* Returns the position after the closing tag of the element.
|
|
899
1018
|
*/
|
|
900
1019
|
private findInsertPositionForElement;
|
|
901
|
-
/**
|
|
902
|
-
* Apply paragraph inserts to XML.
|
|
903
|
-
* Inserts new paragraphs at the specified positions.
|
|
904
|
-
*/
|
|
905
|
-
private applyParagraphInsertsToXml;
|
|
906
1020
|
/**
|
|
907
1021
|
* Apply nested table inserts to XML.
|
|
908
1022
|
* Inserts a new table inside a cell of an existing table.
|
|
@@ -911,6 +1025,24 @@ export declare class HwpxDocument {
|
|
|
911
1025
|
/**
|
|
912
1026
|
* Generate XML for a nested table.
|
|
913
1027
|
*/
|
|
1028
|
+
/**
|
|
1029
|
+
* Usable width inside a table cell, in hwpunit.
|
|
1030
|
+
*
|
|
1031
|
+
* A cell with hasMargin="0" takes its padding from the table's inMargin, so
|
|
1032
|
+
* the cell's own cellMargin is only authoritative when hasMargin="1".
|
|
1033
|
+
*
|
|
1034
|
+
* Every lookup is scoped to the cell's (or table's) own markup. A nested
|
|
1035
|
+
* table inside the cell carries its own cellSz/cellMargin/inMargin, and a
|
|
1036
|
+
* first-match regex over the whole cell would read those instead — which
|
|
1037
|
+
* sized a second nested table to the first one's column (7086 vs 21260).
|
|
1038
|
+
*/
|
|
1039
|
+
private getCellInnerWidth;
|
|
1040
|
+
/**
|
|
1041
|
+
* Generate XML for a nested table.
|
|
1042
|
+
*
|
|
1043
|
+
* @param innerWidth Usable width of the parent cell in hwpunit. The nested
|
|
1044
|
+
* table is sized to fit it exactly; columns share the width evenly.
|
|
1045
|
+
*/
|
|
914
1046
|
private generateNestedTableXml;
|
|
915
1047
|
/**
|
|
916
1048
|
* Insert a nested table XML into a cell XML.
|
|
@@ -1063,8 +1195,10 @@ export declare class HwpxDocument {
|
|
|
1063
1195
|
*/
|
|
1064
1196
|
private replaceMultipleRunsInElement;
|
|
1065
1197
|
/**
|
|
1066
|
-
*
|
|
1067
|
-
*
|
|
1198
|
+
* Occurrence index of the paragraph at `elementIndex` among paragraphs with
|
|
1199
|
+
* the same id — counted with the same rule as insert anchors
|
|
1200
|
+
* (resolveElementAnchor), so a text update finds the paragraph that
|
|
1201
|
+
* findParsedParagraph resolves. Divider paragraphs parsed as 'hr' count.
|
|
1068
1202
|
*/
|
|
1069
1203
|
private getParagraphOccurrence;
|
|
1070
1204
|
/**
|
|
@@ -1591,6 +1725,14 @@ export declare class HwpxDocument {
|
|
|
1591
1725
|
* @returns Cell XML content and its position, or null if not found
|
|
1592
1726
|
*/
|
|
1593
1727
|
private findTableCellInXml;
|
|
1728
|
+
/**
|
|
1729
|
+
* Clone a table cell for a newly inserted row: same cell attributes, same
|
|
1730
|
+
* first-paragraph formatting, but a single paragraph holding `text`.
|
|
1731
|
+
*
|
|
1732
|
+
* Nested tables and extra paragraphs are dropped. The first run's
|
|
1733
|
+
* charPrIDRef is kept so the new text matches the template cell's font.
|
|
1734
|
+
*/
|
|
1735
|
+
private cloneCellWithText;
|
|
1594
1736
|
private applyTableRowInsertsToXml;
|
|
1595
1737
|
private applyTableRowDeletesToXml;
|
|
1596
1738
|
private applyTableColumnInsertsToXml;
|
|
@@ -1600,8 +1742,6 @@ export declare class HwpxDocument {
|
|
|
1600
1742
|
* Returns elements with their complete XML (including closing tags).
|
|
1601
1743
|
*/
|
|
1602
1744
|
private findTopLevelFullElements;
|
|
1603
|
-
private applyParagraphCopiesToXml;
|
|
1604
|
-
private applyParagraphMovesToXml;
|
|
1605
1745
|
private applyHeaderFooterUpdatesToXml;
|
|
1606
1746
|
}
|
|
1607
1747
|
export {};
|