@maxoyed/ode-core 0.0.1
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/LICENSE +21 -0
- package/README.md +51 -0
- package/dist/chunk-OUPZEHJO.js +162 -0
- package/dist/chunk-OUPZEHJO.js.map +1 -0
- package/dist/chunk-SYMAPCOS.js +104 -0
- package/dist/chunk-SYMAPCOS.js.map +1 -0
- package/dist/chunk-TRNXHJAU.js +21 -0
- package/dist/chunk-TRNXHJAU.js.map +1 -0
- package/dist/docx/index.d.ts +44 -0
- package/dist/docx/index.js +540 -0
- package/dist/docx/index.js.map +1 -0
- package/dist/elements-Bvueu_TD.d.ts +69 -0
- package/dist/index.d.ts +176 -0
- package/dist/index.js +608 -0
- package/dist/index.js.map +1 -0
- package/dist/pagination/index.d.ts +131 -0
- package/dist/pagination/index.js +34 -0
- package/dist/pagination/index.js.map +1 -0
- package/dist/spec/index.d.ts +69 -0
- package/dist/spec/index.js +45 -0
- package/dist/spec/index.js.map +1 -0
- package/dist/styles.css +189 -0
- package/package.json +89 -0
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ELEMENT_SPEC,
|
|
3
|
+
LINE_HEIGHT_MM,
|
|
4
|
+
MARGIN_MM,
|
|
5
|
+
OFFICIAL_RED,
|
|
6
|
+
PAGE_A4_MM,
|
|
7
|
+
PT_TO_MM,
|
|
8
|
+
TYPE_AREA_MM,
|
|
9
|
+
toHalfPoint,
|
|
10
|
+
toPt
|
|
11
|
+
} from "../chunk-SYMAPCOS.js";
|
|
12
|
+
|
|
13
|
+
// src/docx/export.ts
|
|
14
|
+
import {
|
|
15
|
+
AlignmentType,
|
|
16
|
+
BorderStyle,
|
|
17
|
+
Document,
|
|
18
|
+
Footer,
|
|
19
|
+
HorizontalPositionAlign,
|
|
20
|
+
HorizontalPositionRelativeFrom,
|
|
21
|
+
ImageRun,
|
|
22
|
+
LineRuleType,
|
|
23
|
+
Packer,
|
|
24
|
+
PageNumber,
|
|
25
|
+
Paragraph,
|
|
26
|
+
Table,
|
|
27
|
+
TableCell,
|
|
28
|
+
TableRow,
|
|
29
|
+
TextRun,
|
|
30
|
+
TextWrappingType,
|
|
31
|
+
VerticalPositionAlign,
|
|
32
|
+
VerticalPositionRelativeFrom,
|
|
33
|
+
WidthType
|
|
34
|
+
} from "docx";
|
|
35
|
+
|
|
36
|
+
// src/docx/font-map.ts
|
|
37
|
+
var DOCX_FONT_NAME = {
|
|
38
|
+
xiaobiaosong: "\u65B9\u6B63\u5C0F\u6807\u5B8B\u7B80\u4F53",
|
|
39
|
+
fangsong: "\u4EFF\u5B8B_GB2312",
|
|
40
|
+
heiti: "\u9ED1\u4F53",
|
|
41
|
+
kaiti: "\u6977\u4F53_GB2312",
|
|
42
|
+
songti: "\u5B8B\u4F53"
|
|
43
|
+
};
|
|
44
|
+
var FONT_ROLE_BY_DOCX_NAME = Object.fromEntries(
|
|
45
|
+
Object.keys(DOCX_FONT_NAME).map((role) => [DOCX_FONT_NAME[role], role])
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
// src/docx/units.ts
|
|
49
|
+
function mmToTwip(mm) {
|
|
50
|
+
return Math.round(mm * 1440 / 25.4);
|
|
51
|
+
}
|
|
52
|
+
function ptToTwip(pt) {
|
|
53
|
+
return Math.round(pt * 20);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// src/docx/image.ts
|
|
57
|
+
var MIME_TO_TYPE = {
|
|
58
|
+
"image/png": "png",
|
|
59
|
+
"image/jpeg": "jpg",
|
|
60
|
+
"image/jpg": "jpg",
|
|
61
|
+
"image/gif": "gif",
|
|
62
|
+
"image/bmp": "bmp"
|
|
63
|
+
};
|
|
64
|
+
var TYPE_TO_MIME = {
|
|
65
|
+
png: "image/png",
|
|
66
|
+
jpg: "image/jpeg",
|
|
67
|
+
gif: "image/gif",
|
|
68
|
+
bmp: "image/bmp"
|
|
69
|
+
};
|
|
70
|
+
function base64ToBytes(b64) {
|
|
71
|
+
if (typeof atob === "function") {
|
|
72
|
+
const bin = atob(b64);
|
|
73
|
+
const out = new Uint8Array(bin.length);
|
|
74
|
+
for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
return new Uint8Array(Buffer.from(b64, "base64"));
|
|
78
|
+
}
|
|
79
|
+
function bytesToBase64(bytes) {
|
|
80
|
+
if (typeof btoa === "function") {
|
|
81
|
+
let bin = "";
|
|
82
|
+
for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);
|
|
83
|
+
return btoa(bin);
|
|
84
|
+
}
|
|
85
|
+
return Buffer.from(bytes).toString("base64");
|
|
86
|
+
}
|
|
87
|
+
function parseDataUrl(src) {
|
|
88
|
+
const m = /^data:([^;,]+)(;base64)?,(.*)$/s.exec(src);
|
|
89
|
+
if (!m) return null;
|
|
90
|
+
const mime = m[1].toLowerCase();
|
|
91
|
+
const type = MIME_TO_TYPE[mime];
|
|
92
|
+
if (!type || !m[2]) return null;
|
|
93
|
+
return { bytes: base64ToBytes(m[3]), mime, type };
|
|
94
|
+
}
|
|
95
|
+
function toDataUrl(type, bytes) {
|
|
96
|
+
return `data:${TYPE_TO_MIME[type]};base64,${bytesToBase64(bytes)}`;
|
|
97
|
+
}
|
|
98
|
+
function readImageSize(bytes) {
|
|
99
|
+
if (bytes.length >= 24 && bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78 && bytes[3] === 71) {
|
|
100
|
+
const dv = new DataView(bytes.buffer, bytes.byteOffset);
|
|
101
|
+
return { width: dv.getUint32(16), height: dv.getUint32(20), type: "png" };
|
|
102
|
+
}
|
|
103
|
+
if (bytes.length >= 10 && bytes[0] === 71 && bytes[1] === 73 && bytes[2] === 70) {
|
|
104
|
+
const dv = new DataView(bytes.buffer, bytes.byteOffset);
|
|
105
|
+
return { width: dv.getUint16(6, true), height: dv.getUint16(8, true), type: "gif" };
|
|
106
|
+
}
|
|
107
|
+
if (bytes.length >= 26 && bytes[0] === 66 && bytes[1] === 77) {
|
|
108
|
+
const dv = new DataView(bytes.buffer, bytes.byteOffset);
|
|
109
|
+
return { width: dv.getInt32(18, true), height: Math.abs(dv.getInt32(22, true)), type: "bmp" };
|
|
110
|
+
}
|
|
111
|
+
if (bytes.length >= 4 && bytes[0] === 255 && bytes[1] === 216) {
|
|
112
|
+
let off = 2;
|
|
113
|
+
const dv = new DataView(bytes.buffer, bytes.byteOffset);
|
|
114
|
+
while (off + 9 < bytes.length) {
|
|
115
|
+
if (bytes[off] !== 255) {
|
|
116
|
+
off++;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const marker = bytes[off + 1];
|
|
120
|
+
if (marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204) {
|
|
121
|
+
return { width: dv.getUint16(off + 7), height: dv.getUint16(off + 5), type: "jpg" };
|
|
122
|
+
}
|
|
123
|
+
const len = dv.getUint16(off + 2);
|
|
124
|
+
off += 2 + len;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// src/docx/export.ts
|
|
131
|
+
var TYPE_AREA_WIDTH_PX = TYPE_AREA_MM.width / 25.4 * 96;
|
|
132
|
+
var DEFAULT_ROLE = "body";
|
|
133
|
+
var STYLE_ID_PREFIX = "odoc-";
|
|
134
|
+
function styleIdFor(role) {
|
|
135
|
+
return `${STYLE_ID_PREFIX}${role}`;
|
|
136
|
+
}
|
|
137
|
+
function alignmentOf(spec) {
|
|
138
|
+
switch (spec.align) {
|
|
139
|
+
case "center":
|
|
140
|
+
return AlignmentType.CENTER;
|
|
141
|
+
case "right":
|
|
142
|
+
return AlignmentType.RIGHT;
|
|
143
|
+
case "justify":
|
|
144
|
+
return AlignmentType.JUSTIFIED;
|
|
145
|
+
case "left":
|
|
146
|
+
return AlignmentType.LEFT;
|
|
147
|
+
default:
|
|
148
|
+
return void 0;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function textOf(node) {
|
|
152
|
+
return (node.content ?? []).map((n) => n.type === "text" ? n.text ?? "" : "").join("");
|
|
153
|
+
}
|
|
154
|
+
function paragraphForRole(role, text) {
|
|
155
|
+
const spec = ELEMENT_SPEC[role] ?? ELEMENT_SPEC[DEFAULT_ROLE];
|
|
156
|
+
const sizePt = toPt(spec.size);
|
|
157
|
+
const fontName = DOCX_FONT_NAME[spec.font];
|
|
158
|
+
const indent = {
|
|
159
|
+
...spec.indent ? { firstLine: ptToTwip(spec.indent * sizePt) } : {},
|
|
160
|
+
...spec.marginLeft ? { left: ptToTwip(spec.marginLeft * sizePt) } : {},
|
|
161
|
+
...spec.marginRight ? { right: ptToTwip(spec.marginRight * sizePt) } : {}
|
|
162
|
+
};
|
|
163
|
+
return new Paragraph({
|
|
164
|
+
style: styleIdFor(role),
|
|
165
|
+
// 命名样式,便于 Word 识别与无损往返
|
|
166
|
+
alignment: alignmentOf(spec),
|
|
167
|
+
indent: Object.keys(indent).length ? indent : void 0,
|
|
168
|
+
// 正文固定行距按规范(每面 22 行);标题等沿用同一固定行距,保证版式稳定
|
|
169
|
+
spacing: { line: ptToTwip(LINE_HEIGHT_MM / PT_TO_MM), lineRule: LineRuleType.EXACT },
|
|
170
|
+
children: [
|
|
171
|
+
new TextRun({
|
|
172
|
+
text,
|
|
173
|
+
font: { ascii: fontName, eastAsia: fontName, hAnsi: fontName },
|
|
174
|
+
size: toHalfPoint(spec.size),
|
|
175
|
+
bold: spec.bold,
|
|
176
|
+
color: (spec.color ?? "#000000").replace("#", "")
|
|
177
|
+
})
|
|
178
|
+
]
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
function buildParagraphStyles() {
|
|
182
|
+
return Object.keys(ELEMENT_SPEC).map((role) => {
|
|
183
|
+
const spec = ELEMENT_SPEC[role];
|
|
184
|
+
const sizePt = toPt(spec.size);
|
|
185
|
+
const fontName = DOCX_FONT_NAME[spec.font];
|
|
186
|
+
const indent = {
|
|
187
|
+
...spec.indent ? { firstLine: ptToTwip(spec.indent * sizePt) } : {},
|
|
188
|
+
...spec.marginLeft ? { left: ptToTwip(spec.marginLeft * sizePt) } : {},
|
|
189
|
+
...spec.marginRight ? { right: ptToTwip(spec.marginRight * sizePt) } : {}
|
|
190
|
+
};
|
|
191
|
+
return {
|
|
192
|
+
id: styleIdFor(role),
|
|
193
|
+
name: `ODOC ${role}`,
|
|
194
|
+
basedOn: "Normal",
|
|
195
|
+
next: styleIdFor(DEFAULT_ROLE),
|
|
196
|
+
quickFormat: true,
|
|
197
|
+
run: {
|
|
198
|
+
font: { ascii: fontName, eastAsia: fontName, hAnsi: fontName },
|
|
199
|
+
size: toHalfPoint(spec.size),
|
|
200
|
+
bold: spec.bold,
|
|
201
|
+
color: (spec.color ?? "#000000").replace("#", "")
|
|
202
|
+
},
|
|
203
|
+
paragraph: {
|
|
204
|
+
alignment: alignmentOf(spec),
|
|
205
|
+
indent: Object.keys(indent).length ? indent : void 0,
|
|
206
|
+
spacing: { line: ptToTwip(LINE_HEIGHT_MM / PT_TO_MM), lineRule: LineRuleType.EXACT }
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
function tableForNode(node) {
|
|
212
|
+
const rows = (node.content ?? []).filter((r) => r.type === "tableRow");
|
|
213
|
+
const border = { style: BorderStyle.SINGLE, size: 4, color: "000000" };
|
|
214
|
+
return new Table({
|
|
215
|
+
width: { size: 100, type: WidthType.PERCENTAGE },
|
|
216
|
+
borders: { top: border, bottom: border, left: border, right: border, insideHorizontal: border, insideVertical: border },
|
|
217
|
+
rows: rows.map(
|
|
218
|
+
(row) => new TableRow({
|
|
219
|
+
children: (row.content ?? []).filter((c) => c.type === "tableCell" || c.type === "tableHeader").map((cell) => {
|
|
220
|
+
const paras = (cell.content ?? []).filter((n) => n.type === "paragraph");
|
|
221
|
+
const children = (paras.length ? paras : [{ type: "paragraph" }]).map(
|
|
222
|
+
(p) => paragraphForRole(
|
|
223
|
+
p.attrs?.officialRole ?? DEFAULT_ROLE,
|
|
224
|
+
textOf(p)
|
|
225
|
+
)
|
|
226
|
+
);
|
|
227
|
+
const colspan = Number(cell.attrs?.colspan ?? 1) || 1;
|
|
228
|
+
const rowspan = Number(cell.attrs?.rowspan ?? 1) || 1;
|
|
229
|
+
return new TableCell({
|
|
230
|
+
children,
|
|
231
|
+
columnSpan: colspan > 1 ? colspan : void 0,
|
|
232
|
+
rowSpan: rowspan > 1 ? rowspan : void 0
|
|
233
|
+
});
|
|
234
|
+
})
|
|
235
|
+
})
|
|
236
|
+
)
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
function imageParagraph(node) {
|
|
240
|
+
const src = node.attrs?.src ?? "";
|
|
241
|
+
const parsed = parseDataUrl(src);
|
|
242
|
+
if (!parsed) {
|
|
243
|
+
return paragraphForRole("body", node.attrs?.alt ? `[\u56FE\u7247\uFF1A${node.attrs.alt}]` : "[\u56FE\u7247]");
|
|
244
|
+
}
|
|
245
|
+
const size = readImageSize(parsed.bytes) ?? { width: 400, height: 300 };
|
|
246
|
+
const isSeal = !!node.attrs?.seal;
|
|
247
|
+
const maxWidth = isSeal ? 40 / 25.4 * 96 : TYPE_AREA_WIDTH_PX;
|
|
248
|
+
const scale = size.width > maxWidth ? maxWidth / size.width : 1;
|
|
249
|
+
return new Paragraph({
|
|
250
|
+
alignment: AlignmentType.CENTER,
|
|
251
|
+
children: [
|
|
252
|
+
new ImageRun({
|
|
253
|
+
data: parsed.bytes,
|
|
254
|
+
type: parsed.type,
|
|
255
|
+
transformation: {
|
|
256
|
+
width: Math.round(size.width * scale),
|
|
257
|
+
height: Math.round(size.height * scale)
|
|
258
|
+
},
|
|
259
|
+
// 印章导出为浮动图片,允许叠压于成文日期之上
|
|
260
|
+
...isSeal ? {
|
|
261
|
+
floating: {
|
|
262
|
+
horizontalPosition: {
|
|
263
|
+
relative: HorizontalPositionRelativeFrom.MARGIN,
|
|
264
|
+
align: HorizontalPositionAlign.CENTER
|
|
265
|
+
},
|
|
266
|
+
verticalPosition: {
|
|
267
|
+
relative: VerticalPositionRelativeFrom.PARAGRAPH,
|
|
268
|
+
align: VerticalPositionAlign.CENTER
|
|
269
|
+
},
|
|
270
|
+
allowOverlap: true,
|
|
271
|
+
behindDocument: false,
|
|
272
|
+
wrap: { type: TextWrappingType.NONE }
|
|
273
|
+
}
|
|
274
|
+
} : {}
|
|
275
|
+
})
|
|
276
|
+
]
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
function separatorParagraph(variant = "reverse") {
|
|
280
|
+
const isRecord = variant === "record";
|
|
281
|
+
return new Paragraph({
|
|
282
|
+
border: {
|
|
283
|
+
bottom: {
|
|
284
|
+
style: BorderStyle.SINGLE,
|
|
285
|
+
size: isRecord ? 6 : 12,
|
|
286
|
+
// 八分之一磅为单位 → 0.75pt / 1.5pt
|
|
287
|
+
color: isRecord ? "000000" : OFFICIAL_RED.replace("#", ""),
|
|
288
|
+
space: 1
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
children: [new TextRun("")]
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
function pageNumberFooter(align) {
|
|
295
|
+
const fontName = DOCX_FONT_NAME.songti;
|
|
296
|
+
return new Footer({
|
|
297
|
+
children: [
|
|
298
|
+
new Paragraph({
|
|
299
|
+
alignment: align,
|
|
300
|
+
children: [
|
|
301
|
+
new TextRun({
|
|
302
|
+
children: ["\u2014 ", PageNumber.CURRENT, " \u2014"],
|
|
303
|
+
font: { ascii: fontName, eastAsia: fontName, hAnsi: fontName },
|
|
304
|
+
size: toHalfPoint("\u56DB\u53F7")
|
|
305
|
+
})
|
|
306
|
+
]
|
|
307
|
+
})
|
|
308
|
+
]
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
function buildChildren(doc) {
|
|
312
|
+
const top = (doc.type === "doc" ? doc.content : [doc]) ?? [];
|
|
313
|
+
return top.map((node) => {
|
|
314
|
+
if (node.type === "horizontalRule")
|
|
315
|
+
return separatorParagraph(node.attrs?.variant ?? "reverse");
|
|
316
|
+
if (node.type === "image") return imageParagraph(node);
|
|
317
|
+
if (node.type === "table") return tableForNode(node);
|
|
318
|
+
const role = node.attrs?.officialRole ?? DEFAULT_ROLE;
|
|
319
|
+
return paragraphForRole(role, textOf(node));
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
function buildDocument(doc) {
|
|
323
|
+
const section = {
|
|
324
|
+
properties: {
|
|
325
|
+
page: {
|
|
326
|
+
size: {
|
|
327
|
+
width: mmToTwip(PAGE_A4_MM.width),
|
|
328
|
+
height: mmToTwip(PAGE_A4_MM.height)
|
|
329
|
+
},
|
|
330
|
+
margin: {
|
|
331
|
+
top: mmToTwip(MARGIN_MM.top),
|
|
332
|
+
right: mmToTwip(MARGIN_MM.right),
|
|
333
|
+
bottom: mmToTwip(MARGIN_MM.bottom),
|
|
334
|
+
left: mmToTwip(MARGIN_MM.left)
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
footers: {
|
|
339
|
+
default: pageNumberFooter(AlignmentType.RIGHT),
|
|
340
|
+
// 单页居右
|
|
341
|
+
even: pageNumberFooter(AlignmentType.LEFT)
|
|
342
|
+
// 双页居左
|
|
343
|
+
},
|
|
344
|
+
children: buildChildren(doc)
|
|
345
|
+
};
|
|
346
|
+
return new Document({
|
|
347
|
+
evenAndOddHeaderAndFooters: true,
|
|
348
|
+
styles: { paragraphStyles: buildParagraphStyles() },
|
|
349
|
+
sections: [section]
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
function toDocxBlob(doc) {
|
|
353
|
+
return Packer.toBlob(buildDocument(doc));
|
|
354
|
+
}
|
|
355
|
+
function toDocxBuffer(doc) {
|
|
356
|
+
return Packer.toBuffer(buildDocument(doc));
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// src/docx/import.ts
|
|
360
|
+
import { unzipSync, strFromU8 } from "fflate";
|
|
361
|
+
import { XMLParser } from "fast-xml-parser";
|
|
362
|
+
var DEFAULT_ROLE2 = "body";
|
|
363
|
+
var KNOWN_ROLES = new Set(Object.keys(ELEMENT_SPEC));
|
|
364
|
+
var tagOf = (n) => Object.keys(n).find((k) => k !== ":@") ?? "";
|
|
365
|
+
var asArray = (v) => Array.isArray(v) ? v : [];
|
|
366
|
+
var childrenOf = (n) => asArray(n[tagOf(n)]);
|
|
367
|
+
var kids = (n, tag) => asArray(n[tag]);
|
|
368
|
+
var attr = (n, name) => n ? n[":@"]?.[`@_${name}`] : void 0;
|
|
369
|
+
var find = (arr, tag) => arr.find((n) => tagOf(n) === tag);
|
|
370
|
+
var findAll = (arr, tag) => arr.filter((n) => tagOf(n) === tag);
|
|
371
|
+
function findDeep(nodes, tag) {
|
|
372
|
+
for (const n of nodes) {
|
|
373
|
+
if (tagOf(n) === tag) return n;
|
|
374
|
+
const found = findDeep(childrenOf(n), tag);
|
|
375
|
+
if (found) return found;
|
|
376
|
+
}
|
|
377
|
+
return void 0;
|
|
378
|
+
}
|
|
379
|
+
function collectText(nodes) {
|
|
380
|
+
let s = "";
|
|
381
|
+
for (const n of nodes) {
|
|
382
|
+
if ("#text" in n) s += String(n["#text"]);
|
|
383
|
+
else s += collectText(childrenOf(n));
|
|
384
|
+
}
|
|
385
|
+
return s;
|
|
386
|
+
}
|
|
387
|
+
function parseRels(xml) {
|
|
388
|
+
if (!xml) return {};
|
|
389
|
+
const parser = new XMLParser({
|
|
390
|
+
ignoreAttributes: false,
|
|
391
|
+
attributeNamePrefix: "@_",
|
|
392
|
+
isArray: (name) => name === "Relationship"
|
|
393
|
+
});
|
|
394
|
+
const tree = parser.parse(strFromU8(xml));
|
|
395
|
+
const list = tree?.Relationships?.Relationship ?? [];
|
|
396
|
+
const map = {};
|
|
397
|
+
for (const r of list) map[r["@_Id"]] = r["@_Target"];
|
|
398
|
+
return map;
|
|
399
|
+
}
|
|
400
|
+
function resolveImage(ctx, relId) {
|
|
401
|
+
const target = ctx.rels[relId];
|
|
402
|
+
if (!target) return null;
|
|
403
|
+
const rel = target.replace(/^\/+/, "");
|
|
404
|
+
const path = rel.startsWith("word/") ? rel : `word/${rel}`.replace(/word\/\.\.\//, "");
|
|
405
|
+
const bytes = ctx.files[path] ?? ctx.files[rel];
|
|
406
|
+
if (!bytes) return null;
|
|
407
|
+
const size = readImageSize(bytes);
|
|
408
|
+
return toDataUrl(size?.type ?? "png", bytes);
|
|
409
|
+
}
|
|
410
|
+
function normAlign(a) {
|
|
411
|
+
if (!a) return "left";
|
|
412
|
+
if (a === "justify" || a === "both") return "both";
|
|
413
|
+
return a;
|
|
414
|
+
}
|
|
415
|
+
function inferRole(props) {
|
|
416
|
+
const roleByFont = props.fontName ? FONT_ROLE_BY_DOCX_NAME[props.fontName] : void 0;
|
|
417
|
+
for (const role of Object.keys(ELEMENT_SPEC)) {
|
|
418
|
+
const spec = ELEMENT_SPEC[role];
|
|
419
|
+
if (roleByFont && spec.font !== roleByFont) continue;
|
|
420
|
+
if (props.sizeHalfPoint !== void 0 && toHalfPoint(spec.size) !== props.sizeHalfPoint) continue;
|
|
421
|
+
if (normAlign(spec.align) !== props.align) continue;
|
|
422
|
+
if (Boolean(spec.bold) !== props.bold) continue;
|
|
423
|
+
if (Boolean(spec.indent) !== props.hasIndent) continue;
|
|
424
|
+
return role;
|
|
425
|
+
}
|
|
426
|
+
return DEFAULT_ROLE2;
|
|
427
|
+
}
|
|
428
|
+
function paragraphToNode(pChildren, ctx) {
|
|
429
|
+
const blip = findDeep(pChildren, "a:blip");
|
|
430
|
+
if (blip) {
|
|
431
|
+
const embed = attr(blip, "r:embed") ?? attr(blip, "r:link");
|
|
432
|
+
const src = embed ? resolveImage(ctx, embed) : null;
|
|
433
|
+
if (src) {
|
|
434
|
+
const isSeal = !!findDeep(pChildren, "wp:anchor");
|
|
435
|
+
return { type: "image", attrs: isSeal ? { src, seal: true } : { src } };
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
const pPr = find(pChildren, "w:pPr");
|
|
439
|
+
const pPrKids = pPr ? childrenOf(pPr) : [];
|
|
440
|
+
const runs = findAll(pChildren, "w:r");
|
|
441
|
+
const text = runs.map((r) => collectText(childrenOf(r))).join("");
|
|
442
|
+
const pBdr = find(pPrKids, "w:pBdr");
|
|
443
|
+
if (pBdr && text.trim() === "") {
|
|
444
|
+
const color = attr(find(childrenOf(pBdr), "w:bottom"), "w:color");
|
|
445
|
+
const variant = color && color.toLowerCase() !== "e60012" ? "record" : "reverse";
|
|
446
|
+
return { type: "horizontalRule", attrs: { variant } };
|
|
447
|
+
}
|
|
448
|
+
const styleVal = attr(find(pPrKids, "w:pStyle"), "w:val");
|
|
449
|
+
let role;
|
|
450
|
+
if (styleVal && styleVal.startsWith(STYLE_ID_PREFIX)) {
|
|
451
|
+
const candidate = styleVal.slice(STYLE_ID_PREFIX.length);
|
|
452
|
+
if (KNOWN_ROLES.has(candidate)) role = candidate;
|
|
453
|
+
}
|
|
454
|
+
if (!role) {
|
|
455
|
+
const firstRpr = runs[0] ? find(childrenOf(runs[0]), "w:rPr") : void 0;
|
|
456
|
+
const rprKids = firstRpr ? childrenOf(firstRpr) : [];
|
|
457
|
+
const fontName = attr(find(rprKids, "w:rFonts"), "w:eastAsia");
|
|
458
|
+
const szVal = attr(find(rprKids, "w:sz"), "w:val");
|
|
459
|
+
const ind = find(pPrKids, "w:ind");
|
|
460
|
+
role = inferRole({
|
|
461
|
+
fontName,
|
|
462
|
+
sizeHalfPoint: szVal !== void 0 ? Number(szVal) : void 0,
|
|
463
|
+
align: normAlign(attr(find(pPrKids, "w:jc"), "w:val")),
|
|
464
|
+
hasIndent: attr(ind, "w:firstLine") !== void 0 || attr(ind, "w:firstLineChars") !== void 0,
|
|
465
|
+
bold: !!find(rprKids, "w:b")
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
const node = { type: "paragraph", attrs: { officialRole: role } };
|
|
469
|
+
if (text.length) node.content = [{ type: "text", text }];
|
|
470
|
+
return node;
|
|
471
|
+
}
|
|
472
|
+
function tableToNode(tblChildren, ctx) {
|
|
473
|
+
const rows = [];
|
|
474
|
+
const colOrigin = /* @__PURE__ */ new Map();
|
|
475
|
+
for (const tr of findAll(tblChildren, "w:tr")) {
|
|
476
|
+
const rowCells = [];
|
|
477
|
+
let col = 0;
|
|
478
|
+
for (const tc of findAll(kids(tr, "w:tr"), "w:tc")) {
|
|
479
|
+
const tcPr = find(kids(tc, "w:tc"), "w:tcPr");
|
|
480
|
+
const tcPrKids = tcPr ? childrenOf(tcPr) : [];
|
|
481
|
+
const gridSpan = Number(attr(find(tcPrKids, "w:gridSpan"), "w:val") ?? "1") || 1;
|
|
482
|
+
const vMerge = find(tcPrKids, "w:vMerge");
|
|
483
|
+
const isContinue = !!vMerge && attr(vMerge, "w:val") !== "restart";
|
|
484
|
+
if (isContinue) {
|
|
485
|
+
const origin = colOrigin.get(col);
|
|
486
|
+
if (origin?.attrs) origin.attrs.rowspan = Number(origin.attrs.rowspan ?? 1) + 1;
|
|
487
|
+
col += gridSpan;
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
const paras = findAll(kids(tc, "w:tc"), "w:p").map((p) => paragraphToNode(kids(p, "w:p"), ctx));
|
|
491
|
+
const cell = {
|
|
492
|
+
type: "tableCell",
|
|
493
|
+
attrs: { colspan: gridSpan, rowspan: 1 },
|
|
494
|
+
content: paras.length ? paras : [{ type: "paragraph" }]
|
|
495
|
+
};
|
|
496
|
+
rowCells.push(cell);
|
|
497
|
+
for (let k = 0; k < gridSpan; k++) colOrigin.set(col + k, cell);
|
|
498
|
+
col += gridSpan;
|
|
499
|
+
}
|
|
500
|
+
rows.push({ type: "tableRow", content: rowCells });
|
|
501
|
+
}
|
|
502
|
+
return { type: "table", content: rows };
|
|
503
|
+
}
|
|
504
|
+
function fromDocx(data) {
|
|
505
|
+
const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);
|
|
506
|
+
const files = unzipSync(bytes);
|
|
507
|
+
const docXml = files["word/document.xml"];
|
|
508
|
+
if (!docXml) throw new Error("\u65E0\u6548\u7684 docx\uFF1A\u672A\u627E\u5230 word/document.xml");
|
|
509
|
+
const ctx = {
|
|
510
|
+
files,
|
|
511
|
+
rels: parseRels(files["word/_rels/document.xml.rels"])
|
|
512
|
+
};
|
|
513
|
+
const parser = new XMLParser({
|
|
514
|
+
preserveOrder: true,
|
|
515
|
+
ignoreAttributes: false,
|
|
516
|
+
attributeNamePrefix: "@_"
|
|
517
|
+
});
|
|
518
|
+
const tree = parser.parse(strFromU8(docXml));
|
|
519
|
+
const docNode = find(tree, "w:document");
|
|
520
|
+
const bodyNode = docNode ? find(childrenOf(docNode), "w:body") : void 0;
|
|
521
|
+
const bodyChildren = bodyNode ? childrenOf(bodyNode) : [];
|
|
522
|
+
const content = [];
|
|
523
|
+
for (const child of bodyChildren) {
|
|
524
|
+
const tag = tagOf(child);
|
|
525
|
+
if (tag === "w:p") content.push(paragraphToNode(kids(child, "w:p"), ctx));
|
|
526
|
+
else if (tag === "w:tbl") content.push(tableToNode(kids(child, "w:tbl"), ctx));
|
|
527
|
+
}
|
|
528
|
+
return { type: "doc", content: content.length ? content : [{ type: "paragraph" }] };
|
|
529
|
+
}
|
|
530
|
+
export {
|
|
531
|
+
DOCX_FONT_NAME,
|
|
532
|
+
fromDocx,
|
|
533
|
+
parseDataUrl,
|
|
534
|
+
readImageSize,
|
|
535
|
+
styleIdFor,
|
|
536
|
+
toDataUrl,
|
|
537
|
+
toDocxBlob,
|
|
538
|
+
toDocxBuffer
|
|
539
|
+
};
|
|
540
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/docx/export.ts","../../src/docx/font-map.ts","../../src/docx/units.ts","../../src/docx/image.ts","../../src/docx/import.ts"],"sourcesContent":["/**\n * 公文 → docx 导出。\n *\n * 依 GB/T 9704-2012 生成符合公文版式的 .docx:A4 页面与版心边距、各要素字体/字号/\n * 对齐/缩进、正文固定行距,以及版心下方页码(单页居右、双页居左空一字)。\n * 纯前端可用(浏览器 toDocxBlob / Node toDocxBuffer),无需后端。\n */\nimport {\n AlignmentType,\n BorderStyle,\n Document,\n Footer,\n HorizontalPositionAlign,\n HorizontalPositionRelativeFrom,\n ImageRun,\n LineRuleType,\n Packer,\n PageNumber,\n Paragraph,\n Table,\n TableCell,\n TableRow,\n TextRun,\n TextWrappingType,\n VerticalPositionAlign,\n VerticalPositionRelativeFrom,\n WidthType,\n type IParagraphStyleOptions,\n type ISectionOptions,\n type IParagraphOptions,\n} from \"docx\";\nimport type { JSONContent } from \"@tiptap/core\";\nimport { ELEMENT_SPEC, OFFICIAL_RED, type ElementSpec, type OfficialElement } from \"../spec/elements\";\nimport { PAGE_A4_MM, MARGIN_MM, TYPE_AREA_MM, LINE_HEIGHT_MM } from \"../spec/layout\";\nimport { toHalfPoint, toPt, PT_TO_MM } from \"../spec/font-size\";\nimport { DOCX_FONT_NAME } from \"./font-map\";\nimport { mmToTwip, ptToTwip } from \"./units\";\nimport { parseDataUrl, readImageSize } from \"./image\";\n\n/** 版心宽度(px,96dpi),用于限制图片最大宽度。 */\nconst TYPE_AREA_WIDTH_PX = (TYPE_AREA_MM.width / 25.4) * 96;\n\nconst DEFAULT_ROLE: OfficialElement = \"body\";\n\n/** 段落样式 id:用于在 docx 中以命名样式标记公文要素,实现无损往返。 */\nexport const STYLE_ID_PREFIX = \"odoc-\";\nexport function styleIdFor(role: OfficialElement): string {\n return `${STYLE_ID_PREFIX}${role}`;\n}\n\nfunction alignmentOf(spec: ElementSpec): (typeof AlignmentType)[keyof typeof AlignmentType] | undefined {\n switch (spec.align) {\n case \"center\":\n return AlignmentType.CENTER;\n case \"right\":\n return AlignmentType.RIGHT;\n case \"justify\":\n return AlignmentType.JUSTIFIED;\n case \"left\":\n return AlignmentType.LEFT;\n default:\n return undefined;\n }\n}\n\nfunction textOf(node: JSONContent): string {\n return (node.content ?? [])\n .map((n) => (n.type === \"text\" ? (n.text ?? \"\") : \"\"))\n .join(\"\");\n}\n\n/** 将一个公文段落块转为 docx Paragraph。 */\nfunction paragraphForRole(role: OfficialElement, text: string): Paragraph {\n const spec = ELEMENT_SPEC[role] ?? ELEMENT_SPEC[DEFAULT_ROLE];\n const sizePt = toPt(spec.size);\n const fontName = DOCX_FONT_NAME[spec.font];\n\n const indent: NonNullable<IParagraphOptions[\"indent\"]> = {\n ...(spec.indent ? { firstLine: ptToTwip(spec.indent * sizePt) } : {}),\n ...(spec.marginLeft ? { left: ptToTwip(spec.marginLeft * sizePt) } : {}),\n ...(spec.marginRight ? { right: ptToTwip(spec.marginRight * sizePt) } : {}),\n };\n\n return new Paragraph({\n style: styleIdFor(role), // 命名样式,便于 Word 识别与无损往返\n alignment: alignmentOf(spec),\n indent: Object.keys(indent).length ? indent : undefined,\n // 正文固定行距按规范(每面 22 行);标题等沿用同一固定行距,保证版式稳定\n spacing: { line: ptToTwip(LINE_HEIGHT_MM / PT_TO_MM), lineRule: LineRuleType.EXACT },\n children: [\n new TextRun({\n text,\n font: { ascii: fontName, eastAsia: fontName, hAnsi: fontName },\n size: toHalfPoint(spec.size),\n bold: spec.bold,\n color: (spec.color ?? \"#000000\").replace(\"#\", \"\"),\n }),\n ],\n });\n}\n\n/** 依 ELEMENT_SPEC 生成全部公文要素的命名段落样式(写入 styles.xml)。 */\nfunction buildParagraphStyles(): IParagraphStyleOptions[] {\n return (Object.keys(ELEMENT_SPEC) as OfficialElement[]).map((role) => {\n const spec = ELEMENT_SPEC[role];\n const sizePt = toPt(spec.size);\n const fontName = DOCX_FONT_NAME[spec.font];\n const indent: NonNullable<IParagraphOptions[\"indent\"]> = {\n ...(spec.indent ? { firstLine: ptToTwip(spec.indent * sizePt) } : {}),\n ...(spec.marginLeft ? { left: ptToTwip(spec.marginLeft * sizePt) } : {}),\n ...(spec.marginRight ? { right: ptToTwip(spec.marginRight * sizePt) } : {}),\n };\n return {\n id: styleIdFor(role),\n name: `ODOC ${role}`,\n basedOn: \"Normal\",\n next: styleIdFor(DEFAULT_ROLE),\n quickFormat: true,\n run: {\n font: { ascii: fontName, eastAsia: fontName, hAnsi: fontName },\n size: toHalfPoint(spec.size),\n bold: spec.bold,\n color: (spec.color ?? \"#000000\").replace(\"#\", \"\"),\n },\n paragraph: {\n alignment: alignmentOf(spec),\n indent: Object.keys(indent).length ? indent : undefined,\n spacing: { line: ptToTwip(LINE_HEIGHT_MM / PT_TO_MM), lineRule: LineRuleType.EXACT },\n },\n } satisfies IParagraphStyleOptions;\n });\n}\n\n/** 表格节点 → docx Table(全框线,单元格内段落沿用其要素角色,缺省正文)。 */\nfunction tableForNode(node: JSONContent): Table {\n const rows = (node.content ?? []).filter((r) => r.type === \"tableRow\");\n const border = { style: BorderStyle.SINGLE, size: 4, color: \"000000\" } as const;\n return new Table({\n width: { size: 100, type: WidthType.PERCENTAGE },\n borders: { top: border, bottom: border, left: border, right: border, insideHorizontal: border, insideVertical: border },\n rows: rows.map(\n (row) =>\n new TableRow({\n children: (row.content ?? [])\n .filter((c) => c.type === \"tableCell\" || c.type === \"tableHeader\")\n .map((cell) => {\n const paras = (cell.content ?? []).filter((n) => n.type === \"paragraph\");\n const children = (paras.length ? paras : [{ type: \"paragraph\" } as JSONContent]).map(\n (p) =>\n paragraphForRole(\n (p.attrs?.officialRole as OfficialElement | undefined) ?? DEFAULT_ROLE,\n textOf(p),\n ),\n );\n const colspan = Number(cell.attrs?.colspan ?? 1) || 1;\n const rowspan = Number(cell.attrs?.rowspan ?? 1) || 1;\n return new TableCell({\n children,\n columnSpan: colspan > 1 ? colspan : undefined,\n rowSpan: rowspan > 1 ? rowspan : undefined,\n });\n }),\n }),\n ),\n });\n}\n\n/** 图片节点 → 居中段落 + ImageRun(按版心宽度等比缩放)。 */\nfunction imageParagraph(node: JSONContent): Paragraph {\n const src = (node.attrs?.src as string | undefined) ?? \"\";\n const parsed = parseDataUrl(src);\n if (!parsed) {\n // 非 base64 data URL(如外链)无法离线嵌入,导出为占位文字\n return paragraphForRole(\"body\", node.attrs?.alt ? `[图片:${node.attrs.alt}]` : \"[图片]\");\n }\n const size = readImageSize(parsed.bytes) ?? { width: 400, height: 300 };\n const isSeal = !!node.attrs?.seal;\n const maxWidth = isSeal ? (40 / 25.4) * 96 : TYPE_AREA_WIDTH_PX; // 印章约 40mm\n const scale = size.width > maxWidth ? maxWidth / size.width : 1;\n return new Paragraph({\n alignment: AlignmentType.CENTER,\n children: [\n new ImageRun({\n data: parsed.bytes,\n type: parsed.type,\n transformation: {\n width: Math.round(size.width * scale),\n height: Math.round(size.height * scale),\n },\n // 印章导出为浮动图片,允许叠压于成文日期之上\n ...(isSeal\n ? {\n floating: {\n horizontalPosition: {\n relative: HorizontalPositionRelativeFrom.MARGIN,\n align: HorizontalPositionAlign.CENTER,\n },\n verticalPosition: {\n relative: VerticalPositionRelativeFrom.PARAGRAPH,\n align: VerticalPositionAlign.CENTER,\n },\n allowOverlap: true,\n behindDocument: false,\n wrap: { type: TextWrappingType.NONE },\n },\n }\n : {}),\n }),\n ],\n });\n}\n\n/** 分隔线:用段落下边框表示。reverse=红头反线(红1.5pt),record=版记线(黑0.75pt)。 */\nfunction separatorParagraph(variant: \"reverse\" | \"record\" = \"reverse\"): Paragraph {\n const isRecord = variant === \"record\";\n return new Paragraph({\n border: {\n bottom: {\n style: BorderStyle.SINGLE,\n size: isRecord ? 6 : 12, // 八分之一磅为单位 → 0.75pt / 1.5pt\n color: isRecord ? \"000000\" : OFFICIAL_RED.replace(\"#\", \"\"),\n space: 1,\n },\n },\n children: [new TextRun(\"\")],\n });\n}\n\nfunction pageNumberFooter(align: (typeof AlignmentType)[keyof typeof AlignmentType]): Footer {\n const fontName = DOCX_FONT_NAME.songti;\n return new Footer({\n children: [\n new Paragraph({\n alignment: align,\n children: [\n new TextRun({\n children: [\"— \", PageNumber.CURRENT, \" —\"],\n font: { ascii: fontName, eastAsia: fontName, hAnsi: fontName },\n size: toHalfPoint(\"四号\"),\n }),\n ],\n }),\n ],\n });\n}\n\nfunction buildChildren(doc: JSONContent): (Paragraph | Table)[] {\n const top = (doc.type === \"doc\" ? doc.content : [doc]) ?? [];\n return top.map((node) => {\n if (node.type === \"horizontalRule\")\n return separatorParagraph((node.attrs?.variant as \"reverse\" | \"record\") ?? \"reverse\");\n if (node.type === \"image\") return imageParagraph(node);\n if (node.type === \"table\") return tableForNode(node);\n const role = (node.attrs?.officialRole as OfficialElement | undefined) ?? DEFAULT_ROLE;\n return paragraphForRole(role, textOf(node));\n });\n}\n\nfunction buildDocument(doc: JSONContent): Document {\n const section: ISectionOptions = {\n properties: {\n page: {\n size: {\n width: mmToTwip(PAGE_A4_MM.width),\n height: mmToTwip(PAGE_A4_MM.height),\n },\n margin: {\n top: mmToTwip(MARGIN_MM.top),\n right: mmToTwip(MARGIN_MM.right),\n bottom: mmToTwip(MARGIN_MM.bottom),\n left: mmToTwip(MARGIN_MM.left),\n },\n },\n },\n footers: {\n default: pageNumberFooter(AlignmentType.RIGHT), // 单页居右\n even: pageNumberFooter(AlignmentType.LEFT), // 双页居左\n },\n children: buildChildren(doc),\n };\n\n return new Document({\n evenAndOddHeaderAndFooters: true,\n styles: { paragraphStyles: buildParagraphStyles() },\n sections: [section],\n });\n}\n\n/** 导出为 Blob(浏览器,可直接触发下载)。 */\nexport function toDocxBlob(doc: JSONContent): Promise<Blob> {\n return Packer.toBlob(buildDocument(doc));\n}\n\n/** 导出为 Buffer/Uint8Array(Node,便于测试与服务端)。 */\nexport function toDocxBuffer(doc: JSONContent): Promise<Buffer> {\n return Packer.toBuffer(buildDocument(doc));\n}\n","/**\n * 公文字体角色 → Word(docx)字体名映射。\n *\n * 与 CSS 兜底栈不同:docx 面向 Word,目标机器通常已安装公文字体,故此处使用\n * 公文规范字体名本体(如“仿宋_GB2312”)。字体本身仍由使用方环境提供,本库不分发。\n */\nimport type { FontRole } from \"../spec/fonts\";\n\nexport const DOCX_FONT_NAME: Record<FontRole, string> = {\n xiaobiaosong: \"方正小标宋简体\",\n fangsong: \"仿宋_GB2312\",\n heiti: \"黑体\",\n kaiti: \"楷体_GB2312\",\n songti: \"宋体\",\n};\n\n/** 反向查找:docx 字体名 → 公文字体角色(用于导入时推断要素)。 */\nexport const FONT_ROLE_BY_DOCX_NAME: Record<string, FontRole> = Object.fromEntries(\n (Object.keys(DOCX_FONT_NAME) as FontRole[]).map((role) => [DOCX_FONT_NAME[role], role]),\n);\n","/**\n * docx(OOXML)单位换算。OOXML 长度多以 twip(1/20 pt = 1/1440 inch)为单位。\n */\n\n/** 毫米 → twip */\nexport function mmToTwip(mm: number): number {\n return Math.round((mm * 1440) / 25.4);\n}\n\n/** 磅(pt)→ twip */\nexport function ptToTwip(pt: number): number {\n return Math.round(pt * 20);\n}\n","/**\n * 图片工具:data URL 解析、字节 ↔ base64、以及从图片字节读取内置尺寸与类型。\n * 纯前端/Node 通用,无需依赖图片库。\n */\n\nexport type DocxImageType = \"png\" | \"jpg\" | \"gif\" | \"bmp\";\n\nexport interface ParsedImage {\n bytes: Uint8Array;\n mime: string;\n type: DocxImageType;\n}\n\nconst MIME_TO_TYPE: Record<string, DocxImageType> = {\n \"image/png\": \"png\",\n \"image/jpeg\": \"jpg\",\n \"image/jpg\": \"jpg\",\n \"image/gif\": \"gif\",\n \"image/bmp\": \"bmp\",\n};\n\nconst TYPE_TO_MIME: Record<DocxImageType, string> = {\n png: \"image/png\",\n jpg: \"image/jpeg\",\n gif: \"image/gif\",\n bmp: \"image/bmp\",\n};\n\nexport function base64ToBytes(b64: string): Uint8Array {\n if (typeof atob === \"function\") {\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n }\n // Node 环境\n return new Uint8Array(Buffer.from(b64, \"base64\"));\n}\n\nexport function bytesToBase64(bytes: Uint8Array): string {\n if (typeof btoa === \"function\") {\n let bin = \"\";\n for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);\n return btoa(bin);\n }\n return Buffer.from(bytes).toString(\"base64\");\n}\n\n/** 解析 data:[mime];base64,xxx;非 base64 data URL 返回 null。 */\nexport function parseDataUrl(src: string): ParsedImage | null {\n const m = /^data:([^;,]+)(;base64)?,(.*)$/s.exec(src);\n if (!m) return null;\n const mime = m[1].toLowerCase();\n const type = MIME_TO_TYPE[mime];\n if (!type || !m[2]) return null; // 仅支持已知类型的 base64 data URL\n return { bytes: base64ToBytes(m[3]), mime, type };\n}\n\n/** 由类型与字节拼装 data URL。 */\nexport function toDataUrl(type: DocxImageType, bytes: Uint8Array): string {\n return `data:${TYPE_TO_MIME[type]};base64,${bytesToBase64(bytes)}`;\n}\n\nexport interface ImageSize {\n width: number;\n height: number;\n type?: DocxImageType;\n}\n\n/** 从图片字节读取内置像素尺寸(支持 PNG / GIF / JPEG / BMP)。 */\nexport function readImageSize(bytes: Uint8Array): ImageSize | null {\n // PNG: 89 50 4E 47 0D 0A 1A 0A,IHDR 宽高在偏移 16..24(大端)\n if (bytes.length >= 24 && bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4e && bytes[3] === 0x47) {\n const dv = new DataView(bytes.buffer, bytes.byteOffset);\n return { width: dv.getUint32(16), height: dv.getUint32(20), type: \"png\" };\n }\n // GIF: 'GIF8',宽高在偏移 6..10(小端)\n if (bytes.length >= 10 && bytes[0] === 0x47 && bytes[1] === 0x49 && bytes[2] === 0x46) {\n const dv = new DataView(bytes.buffer, bytes.byteOffset);\n return { width: dv.getUint16(6, true), height: dv.getUint16(8, true), type: \"gif\" };\n }\n // BMP: 'BM',宽高在偏移 18..26(小端)\n if (bytes.length >= 26 && bytes[0] === 0x42 && bytes[1] === 0x4d) {\n const dv = new DataView(bytes.buffer, bytes.byteOffset);\n return { width: dv.getInt32(18, true), height: Math.abs(dv.getInt32(22, true)), type: \"bmp\" };\n }\n // JPEG: FF D8,扫描 SOF 标记取宽高\n if (bytes.length >= 4 && bytes[0] === 0xff && bytes[1] === 0xd8) {\n let off = 2;\n const dv = new DataView(bytes.buffer, bytes.byteOffset);\n while (off + 9 < bytes.length) {\n if (bytes[off] !== 0xff) {\n off++;\n continue;\n }\n const marker = bytes[off + 1];\n // SOF0..SOF15 除 DHT(C4)/JPG(C8)/DAC(CC)\n if (marker >= 0xc0 && marker <= 0xcf && marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc) {\n return { width: dv.getUint16(off + 7), height: dv.getUint16(off + 5), type: \"jpg\" };\n }\n const len = dv.getUint16(off + 2);\n off += 2 + len;\n }\n }\n return null;\n}\n","/**\n * docx → 公文导入。\n *\n * 纯前端解析:fflate 解压 .docx(zip),fast-xml-parser 以 preserveOrder 读取\n * word/document.xml(保留段落/表格/图片的相对顺序)。优先用命名样式 w:pStyle(odoc-*)\n * 无损还原公文要素角色;缺失时退回按字体/字号/对齐/缩进推断。支持表格、分隔线与图片\n * (通过关系文件 word/_rels/document.xml.rels 解析 r:embed,从 word/media 抽取媒体)。\n */\nimport { unzipSync, strFromU8 } from \"fflate\";\nimport { XMLParser } from \"fast-xml-parser\";\nimport type { JSONContent } from \"@tiptap/core\";\nimport { ELEMENT_SPEC, type OfficialElement } from \"../spec/elements\";\nimport { toHalfPoint } from \"../spec/font-size\";\nimport { FONT_ROLE_BY_DOCX_NAME } from \"./font-map\";\nimport { STYLE_ID_PREFIX } from \"./export\";\nimport { readImageSize, toDataUrl } from \"./image\";\n\nconst DEFAULT_ROLE: OfficialElement = \"body\";\nconst KNOWN_ROLES = new Set(Object.keys(ELEMENT_SPEC));\n\n// ---- preserveOrder 遍历辅助 ----\ntype PO = Record<string, unknown>;\nconst tagOf = (n: PO): string => Object.keys(n).find((k) => k !== \":@\") ?? \"\";\nconst asArray = (v: unknown): PO[] => (Array.isArray(v) ? (v as PO[]) : []);\nconst childrenOf = (n: PO): PO[] => asArray(n[tagOf(n)]);\nconst kids = (n: PO, tag: string): PO[] => asArray(n[tag]);\nconst attr = (n: PO | undefined, name: string): string | undefined =>\n n ? ((n[\":@\"] as Record<string, string> | undefined)?.[`@_${name}`]) : undefined;\nconst find = (arr: PO[], tag: string): PO | undefined => arr.find((n) => tagOf(n) === tag);\nconst findAll = (arr: PO[], tag: string): PO[] => arr.filter((n) => tagOf(n) === tag);\nfunction findDeep(nodes: PO[], tag: string): PO | undefined {\n for (const n of nodes) {\n if (tagOf(n) === tag) return n;\n const found = findDeep(childrenOf(n), tag);\n if (found) return found;\n }\n return undefined;\n}\n\nfunction collectText(nodes: PO[]): string {\n let s = \"\";\n for (const n of nodes) {\n if (\"#text\" in n) s += String((n as { \"#text\": unknown })[\"#text\"]);\n else s += collectText(childrenOf(n));\n }\n return s;\n}\n\ninterface ImportCtx {\n files: Record<string, Uint8Array>;\n rels: Record<string, string>;\n}\n\nfunction parseRels(xml?: Uint8Array): Record<string, string> {\n if (!xml) return {};\n const parser = new XMLParser({\n ignoreAttributes: false,\n attributeNamePrefix: \"@_\",\n isArray: (name) => name === \"Relationship\",\n });\n const tree = parser.parse(strFromU8(xml));\n const list = tree?.Relationships?.Relationship ?? [];\n const map: Record<string, string> = {};\n for (const r of list) map[r[\"@_Id\"]] = r[\"@_Target\"];\n return map;\n}\n\nfunction resolveImage(ctx: ImportCtx, relId: string): string | null {\n const target = ctx.rels[relId];\n if (!target) return null;\n const rel = target.replace(/^\\/+/, \"\");\n const path = rel.startsWith(\"word/\") ? rel : `word/${rel}`.replace(/word\\/\\.\\.\\//, \"\");\n const bytes = ctx.files[path] ?? ctx.files[rel];\n if (!bytes) return null;\n const size = readImageSize(bytes);\n return toDataUrl(size?.type ?? \"png\", bytes);\n}\n\nfunction normAlign(a?: string): string {\n if (!a) return \"left\";\n if (a === \"justify\" || a === \"both\") return \"both\";\n return a;\n}\n\ninterface ParaProps {\n fontName?: string;\n sizeHalfPoint?: number;\n align: string;\n hasIndent: boolean;\n bold: boolean;\n}\n\nfunction inferRole(props: ParaProps): OfficialElement {\n const roleByFont = props.fontName ? FONT_ROLE_BY_DOCX_NAME[props.fontName] : undefined;\n for (const role of Object.keys(ELEMENT_SPEC) as OfficialElement[]) {\n const spec = ELEMENT_SPEC[role];\n if (roleByFont && spec.font !== roleByFont) continue;\n if (props.sizeHalfPoint !== undefined && toHalfPoint(spec.size) !== props.sizeHalfPoint) continue;\n if (normAlign(spec.align) !== props.align) continue;\n if (Boolean(spec.bold) !== props.bold) continue;\n if (Boolean(spec.indent) !== props.hasIndent) continue;\n return role;\n }\n return DEFAULT_ROLE;\n}\n\nfunction paragraphToNode(pChildren: PO[], ctx: ImportCtx): JSONContent {\n // 图片:段落内含 w:drawing → a:blip r:embed。浮动锚定(wp:anchor)还原为印章\n const blip = findDeep(pChildren, \"a:blip\");\n if (blip) {\n const embed = attr(blip, \"r:embed\") ?? attr(blip, \"r:link\");\n const src = embed ? resolveImage(ctx, embed) : null;\n if (src) {\n const isSeal = !!findDeep(pChildren, \"wp:anchor\");\n return { type: \"image\", attrs: isSeal ? { src, seal: true } : { src } };\n }\n }\n\n const pPr = find(pChildren, \"w:pPr\");\n const pPrKids = pPr ? childrenOf(pPr) : [];\n const runs = findAll(pChildren, \"w:r\");\n const text = runs.map((r) => collectText(childrenOf(r))).join(\"\");\n\n // 分隔线:段落下边框且无文字。按边框颜色区分红头反线 / 版记黑线\n const pBdr = find(pPrKids, \"w:pBdr\");\n if (pBdr && text.trim() === \"\") {\n const color = attr(find(childrenOf(pBdr), \"w:bottom\"), \"w:color\");\n const variant = color && color.toLowerCase() !== \"e60012\" ? \"record\" : \"reverse\";\n return { type: \"horizontalRule\", attrs: { variant } };\n }\n\n // 优先命名样式(无损)\n const styleVal = attr(find(pPrKids, \"w:pStyle\"), \"w:val\");\n let role: OfficialElement | undefined;\n if (styleVal && styleVal.startsWith(STYLE_ID_PREFIX)) {\n const candidate = styleVal.slice(STYLE_ID_PREFIX.length);\n if (KNOWN_ROLES.has(candidate)) role = candidate as OfficialElement;\n }\n\n if (!role) {\n const firstRpr = runs[0] ? find(childrenOf(runs[0]), \"w:rPr\") : undefined;\n const rprKids = firstRpr ? childrenOf(firstRpr) : [];\n const fontName = attr(find(rprKids, \"w:rFonts\"), \"w:eastAsia\");\n const szVal = attr(find(rprKids, \"w:sz\"), \"w:val\");\n const ind = find(pPrKids, \"w:ind\");\n role = inferRole({\n fontName,\n sizeHalfPoint: szVal !== undefined ? Number(szVal) : undefined,\n align: normAlign(attr(find(pPrKids, \"w:jc\"), \"w:val\")),\n hasIndent: attr(ind, \"w:firstLine\") !== undefined || attr(ind, \"w:firstLineChars\") !== undefined,\n bold: !!find(rprKids, \"w:b\"),\n });\n }\n\n const node: JSONContent = { type: \"paragraph\", attrs: { officialRole: role } };\n if (text.length) node.content = [{ type: \"text\", text }];\n return node;\n}\n\nfunction tableToNode(tblChildren: PO[], ctx: ImportCtx): JSONContent {\n const rows: JSONContent[] = [];\n // grid 列 → 当前占据该列的合并起始单元格(用于纵向合并 vMerge=continue 累加 rowspan)\n const colOrigin = new Map<number, JSONContent>();\n\n for (const tr of findAll(tblChildren, \"w:tr\")) {\n const rowCells: JSONContent[] = [];\n let col = 0;\n for (const tc of findAll(kids(tr, \"w:tr\"), \"w:tc\")) {\n const tcPr = find(kids(tc, \"w:tc\"), \"w:tcPr\");\n const tcPrKids = tcPr ? childrenOf(tcPr) : [];\n const gridSpan = Number(attr(find(tcPrKids, \"w:gridSpan\"), \"w:val\") ?? \"1\") || 1;\n const vMerge = find(tcPrKids, \"w:vMerge\");\n const isContinue = !!vMerge && attr(vMerge, \"w:val\") !== \"restart\";\n\n if (isContinue) {\n // 纵向合并的延续格:累加起始格 rowspan,不产出单元格\n const origin = colOrigin.get(col);\n if (origin?.attrs) origin.attrs.rowspan = Number(origin.attrs.rowspan ?? 1) + 1;\n col += gridSpan;\n continue;\n }\n\n const paras = findAll(kids(tc, \"w:tc\"), \"w:p\").map((p) => paragraphToNode(kids(p, \"w:p\"), ctx));\n const cell: JSONContent = {\n type: \"tableCell\",\n attrs: { colspan: gridSpan, rowspan: 1 },\n content: paras.length ? paras : [{ type: \"paragraph\" }],\n };\n rowCells.push(cell);\n for (let k = 0; k < gridSpan; k++) colOrigin.set(col + k, cell);\n col += gridSpan;\n }\n rows.push({ type: \"tableRow\", content: rowCells });\n }\n return { type: \"table\", content: rows };\n}\n\n/** 解析 docx 数据为 Tiptap JSON 文档。 */\nexport function fromDocx(data: ArrayBuffer | Uint8Array): JSONContent {\n const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);\n const files = unzipSync(bytes);\n const docXml = files[\"word/document.xml\"];\n if (!docXml) throw new Error(\"无效的 docx:未找到 word/document.xml\");\n\n const ctx: ImportCtx = {\n files,\n rels: parseRels(files[\"word/_rels/document.xml.rels\"]),\n };\n\n const parser = new XMLParser({\n preserveOrder: true,\n ignoreAttributes: false,\n attributeNamePrefix: \"@_\",\n });\n const tree = parser.parse(strFromU8(docXml)) as PO[];\n\n const docNode = find(tree, \"w:document\");\n const bodyNode = docNode ? find(childrenOf(docNode), \"w:body\") : undefined;\n const bodyChildren = bodyNode ? childrenOf(bodyNode) : [];\n\n const content: JSONContent[] = [];\n for (const child of bodyChildren) {\n const tag = tagOf(child);\n if (tag === \"w:p\") content.push(paragraphToNode(kids(child, \"w:p\"), ctx));\n else if (tag === \"w:tbl\") content.push(tableToNode(kids(child, \"w:tbl\"), ctx));\n // 跳过 w:sectPr 等\n }\n\n return { type: \"doc\", content: content.length ? content : [{ type: \"paragraph\" }] };\n}\n"],"mappings":";;;;;;;;;;;;;AAOA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIK;;;ACtBA,IAAM,iBAA2C;AAAA,EACtD,cAAc;AAAA,EACd,UAAU;AAAA,EACV,OAAO;AAAA,EACP,OAAO;AAAA,EACP,QAAQ;AACV;AAGO,IAAM,yBAAmD,OAAO;AAAA,EACpE,OAAO,KAAK,cAAc,EAAiB,IAAI,CAAC,SAAS,CAAC,eAAe,IAAI,GAAG,IAAI,CAAC;AACxF;;;ACdO,SAAS,SAAS,IAAoB;AAC3C,SAAO,KAAK,MAAO,KAAK,OAAQ,IAAI;AACtC;AAGO,SAAS,SAAS,IAAoB;AAC3C,SAAO,KAAK,MAAM,KAAK,EAAE;AAC3B;;;ACCA,IAAM,eAA8C;AAAA,EAClD,aAAa;AAAA,EACb,cAAc;AAAA,EACd,aAAa;AAAA,EACb,aAAa;AAAA,EACb,aAAa;AACf;AAEA,IAAM,eAA8C;AAAA,EAClD,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AACP;AAEO,SAAS,cAAc,KAAyB;AACrD,MAAI,OAAO,SAAS,YAAY;AAC9B,UAAM,MAAM,KAAK,GAAG;AACpB,UAAM,MAAM,IAAI,WAAW,IAAI,MAAM;AACrC,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,IAAK,KAAI,CAAC,IAAI,IAAI,WAAW,CAAC;AAC9D,WAAO;AAAA,EACT;AAEA,SAAO,IAAI,WAAW,OAAO,KAAK,KAAK,QAAQ,CAAC;AAClD;AAEO,SAAS,cAAc,OAA2B;AACvD,MAAI,OAAO,SAAS,YAAY;AAC9B,QAAI,MAAM;AACV,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,IAAK,QAAO,OAAO,aAAa,MAAM,CAAC,CAAC;AAC1E,WAAO,KAAK,GAAG;AAAA,EACjB;AACA,SAAO,OAAO,KAAK,KAAK,EAAE,SAAS,QAAQ;AAC7C;AAGO,SAAS,aAAa,KAAiC;AAC5D,QAAM,IAAI,kCAAkC,KAAK,GAAG;AACpD,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,OAAO,EAAE,CAAC,EAAE,YAAY;AAC9B,QAAM,OAAO,aAAa,IAAI;AAC9B,MAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAG,QAAO;AAC3B,SAAO,EAAE,OAAO,cAAc,EAAE,CAAC,CAAC,GAAG,MAAM,KAAK;AAClD;AAGO,SAAS,UAAU,MAAqB,OAA2B;AACxE,SAAO,QAAQ,aAAa,IAAI,CAAC,WAAW,cAAc,KAAK,CAAC;AAClE;AASO,SAAS,cAAc,OAAqC;AAEjE,MAAI,MAAM,UAAU,MAAM,MAAM,CAAC,MAAM,OAAQ,MAAM,CAAC,MAAM,MAAQ,MAAM,CAAC,MAAM,MAAQ,MAAM,CAAC,MAAM,IAAM;AAC1G,UAAM,KAAK,IAAI,SAAS,MAAM,QAAQ,MAAM,UAAU;AACtD,WAAO,EAAE,OAAO,GAAG,UAAU,EAAE,GAAG,QAAQ,GAAG,UAAU,EAAE,GAAG,MAAM,MAAM;AAAA,EAC1E;AAEA,MAAI,MAAM,UAAU,MAAM,MAAM,CAAC,MAAM,MAAQ,MAAM,CAAC,MAAM,MAAQ,MAAM,CAAC,MAAM,IAAM;AACrF,UAAM,KAAK,IAAI,SAAS,MAAM,QAAQ,MAAM,UAAU;AACtD,WAAO,EAAE,OAAO,GAAG,UAAU,GAAG,IAAI,GAAG,QAAQ,GAAG,UAAU,GAAG,IAAI,GAAG,MAAM,MAAM;AAAA,EACpF;AAEA,MAAI,MAAM,UAAU,MAAM,MAAM,CAAC,MAAM,MAAQ,MAAM,CAAC,MAAM,IAAM;AAChE,UAAM,KAAK,IAAI,SAAS,MAAM,QAAQ,MAAM,UAAU;AACtD,WAAO,EAAE,OAAO,GAAG,SAAS,IAAI,IAAI,GAAG,QAAQ,KAAK,IAAI,GAAG,SAAS,IAAI,IAAI,CAAC,GAAG,MAAM,MAAM;AAAA,EAC9F;AAEA,MAAI,MAAM,UAAU,KAAK,MAAM,CAAC,MAAM,OAAQ,MAAM,CAAC,MAAM,KAAM;AAC/D,QAAI,MAAM;AACV,UAAM,KAAK,IAAI,SAAS,MAAM,QAAQ,MAAM,UAAU;AACtD,WAAO,MAAM,IAAI,MAAM,QAAQ;AAC7B,UAAI,MAAM,GAAG,MAAM,KAAM;AACvB;AACA;AAAA,MACF;AACA,YAAM,SAAS,MAAM,MAAM,CAAC;AAE5B,UAAI,UAAU,OAAQ,UAAU,OAAQ,WAAW,OAAQ,WAAW,OAAQ,WAAW,KAAM;AAC7F,eAAO,EAAE,OAAO,GAAG,UAAU,MAAM,CAAC,GAAG,QAAQ,GAAG,UAAU,MAAM,CAAC,GAAG,MAAM,MAAM;AAAA,MACpF;AACA,YAAM,MAAM,GAAG,UAAU,MAAM,CAAC;AAChC,aAAO,IAAI;AAAA,IACb;AAAA,EACF;AACA,SAAO;AACT;;;AHjEA,IAAM,qBAAsB,aAAa,QAAQ,OAAQ;AAEzD,IAAM,eAAgC;AAG/B,IAAM,kBAAkB;AACxB,SAAS,WAAW,MAA+B;AACxD,SAAO,GAAG,eAAe,GAAG,IAAI;AAClC;AAEA,SAAS,YAAY,MAAmF;AACtG,UAAQ,KAAK,OAAO;AAAA,IAClB,KAAK;AACH,aAAO,cAAc;AAAA,IACvB,KAAK;AACH,aAAO,cAAc;AAAA,IACvB,KAAK;AACH,aAAO,cAAc;AAAA,IACvB,KAAK;AACH,aAAO,cAAc;AAAA,IACvB;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,OAAO,MAA2B;AACzC,UAAQ,KAAK,WAAW,CAAC,GACtB,IAAI,CAAC,MAAO,EAAE,SAAS,SAAU,EAAE,QAAQ,KAAM,EAAG,EACpD,KAAK,EAAE;AACZ;AAGA,SAAS,iBAAiB,MAAuB,MAAyB;AACxE,QAAM,OAAO,aAAa,IAAI,KAAK,aAAa,YAAY;AAC5D,QAAM,SAAS,KAAK,KAAK,IAAI;AAC7B,QAAM,WAAW,eAAe,KAAK,IAAI;AAEzC,QAAM,SAAmD;AAAA,IACvD,GAAI,KAAK,SAAS,EAAE,WAAW,SAAS,KAAK,SAAS,MAAM,EAAE,IAAI,CAAC;AAAA,IACnE,GAAI,KAAK,aAAa,EAAE,MAAM,SAAS,KAAK,aAAa,MAAM,EAAE,IAAI,CAAC;AAAA,IACtE,GAAI,KAAK,cAAc,EAAE,OAAO,SAAS,KAAK,cAAc,MAAM,EAAE,IAAI,CAAC;AAAA,EAC3E;AAEA,SAAO,IAAI,UAAU;AAAA,IACnB,OAAO,WAAW,IAAI;AAAA;AAAA,IACtB,WAAW,YAAY,IAAI;AAAA,IAC3B,QAAQ,OAAO,KAAK,MAAM,EAAE,SAAS,SAAS;AAAA;AAAA,IAE9C,SAAS,EAAE,MAAM,SAAS,iBAAiB,QAAQ,GAAG,UAAU,aAAa,MAAM;AAAA,IACnF,UAAU;AAAA,MACR,IAAI,QAAQ;AAAA,QACV;AAAA,QACA,MAAM,EAAE,OAAO,UAAU,UAAU,UAAU,OAAO,SAAS;AAAA,QAC7D,MAAM,YAAY,KAAK,IAAI;AAAA,QAC3B,MAAM,KAAK;AAAA,QACX,QAAQ,KAAK,SAAS,WAAW,QAAQ,KAAK,EAAE;AAAA,MAClD,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AACH;AAGA,SAAS,uBAAiD;AACxD,SAAQ,OAAO,KAAK,YAAY,EAAwB,IAAI,CAAC,SAAS;AACpE,UAAM,OAAO,aAAa,IAAI;AAC9B,UAAM,SAAS,KAAK,KAAK,IAAI;AAC7B,UAAM,WAAW,eAAe,KAAK,IAAI;AACzC,UAAM,SAAmD;AAAA,MACvD,GAAI,KAAK,SAAS,EAAE,WAAW,SAAS,KAAK,SAAS,MAAM,EAAE,IAAI,CAAC;AAAA,MACnE,GAAI,KAAK,aAAa,EAAE,MAAM,SAAS,KAAK,aAAa,MAAM,EAAE,IAAI,CAAC;AAAA,MACtE,GAAI,KAAK,cAAc,EAAE,OAAO,SAAS,KAAK,cAAc,MAAM,EAAE,IAAI,CAAC;AAAA,IAC3E;AACA,WAAO;AAAA,MACL,IAAI,WAAW,IAAI;AAAA,MACnB,MAAM,QAAQ,IAAI;AAAA,MAClB,SAAS;AAAA,MACT,MAAM,WAAW,YAAY;AAAA,MAC7B,aAAa;AAAA,MACb,KAAK;AAAA,QACH,MAAM,EAAE,OAAO,UAAU,UAAU,UAAU,OAAO,SAAS;AAAA,QAC7D,MAAM,YAAY,KAAK,IAAI;AAAA,QAC3B,MAAM,KAAK;AAAA,QACX,QAAQ,KAAK,SAAS,WAAW,QAAQ,KAAK,EAAE;AAAA,MAClD;AAAA,MACA,WAAW;AAAA,QACT,WAAW,YAAY,IAAI;AAAA,QAC3B,QAAQ,OAAO,KAAK,MAAM,EAAE,SAAS,SAAS;AAAA,QAC9C,SAAS,EAAE,MAAM,SAAS,iBAAiB,QAAQ,GAAG,UAAU,aAAa,MAAM;AAAA,MACrF;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAGA,SAAS,aAAa,MAA0B;AAC9C,QAAM,QAAQ,KAAK,WAAW,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,SAAS,UAAU;AACrE,QAAM,SAAS,EAAE,OAAO,YAAY,QAAQ,MAAM,GAAG,OAAO,SAAS;AACrE,SAAO,IAAI,MAAM;AAAA,IACf,OAAO,EAAE,MAAM,KAAK,MAAM,UAAU,WAAW;AAAA,IAC/C,SAAS,EAAE,KAAK,QAAQ,QAAQ,QAAQ,MAAM,QAAQ,OAAO,QAAQ,kBAAkB,QAAQ,gBAAgB,OAAO;AAAA,IACtH,MAAM,KAAK;AAAA,MACT,CAAC,QACC,IAAI,SAAS;AAAA,QACX,WAAW,IAAI,WAAW,CAAC,GACxB,OAAO,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,SAAS,aAAa,EAChE,IAAI,CAAC,SAAS;AACb,gBAAM,SAAS,KAAK,WAAW,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,SAAS,WAAW;AACvE,gBAAM,YAAY,MAAM,SAAS,QAAQ,CAAC,EAAE,MAAM,YAAY,CAAgB,GAAG;AAAA,YAC/E,CAAC,MACC;AAAA,cACG,EAAE,OAAO,gBAAgD;AAAA,cAC1D,OAAO,CAAC;AAAA,YACV;AAAA,UACJ;AACA,gBAAM,UAAU,OAAO,KAAK,OAAO,WAAW,CAAC,KAAK;AACpD,gBAAM,UAAU,OAAO,KAAK,OAAO,WAAW,CAAC,KAAK;AACpD,iBAAO,IAAI,UAAU;AAAA,YACnB;AAAA,YACA,YAAY,UAAU,IAAI,UAAU;AAAA,YACpC,SAAS,UAAU,IAAI,UAAU;AAAA,UACnC,CAAC;AAAA,QACH,CAAC;AAAA,MACL,CAAC;AAAA,IACL;AAAA,EACF,CAAC;AACH;AAGA,SAAS,eAAe,MAA8B;AACpD,QAAM,MAAO,KAAK,OAAO,OAA8B;AACvD,QAAM,SAAS,aAAa,GAAG;AAC/B,MAAI,CAAC,QAAQ;AAEX,WAAO,iBAAiB,QAAQ,KAAK,OAAO,MAAM,sBAAO,KAAK,MAAM,GAAG,MAAM,gBAAM;AAAA,EACrF;AACA,QAAM,OAAO,cAAc,OAAO,KAAK,KAAK,EAAE,OAAO,KAAK,QAAQ,IAAI;AACtE,QAAM,SAAS,CAAC,CAAC,KAAK,OAAO;AAC7B,QAAM,WAAW,SAAU,KAAK,OAAQ,KAAK;AAC7C,QAAM,QAAQ,KAAK,QAAQ,WAAW,WAAW,KAAK,QAAQ;AAC9D,SAAO,IAAI,UAAU;AAAA,IACnB,WAAW,cAAc;AAAA,IACzB,UAAU;AAAA,MACR,IAAI,SAAS;AAAA,QACX,MAAM,OAAO;AAAA,QACb,MAAM,OAAO;AAAA,QACb,gBAAgB;AAAA,UACd,OAAO,KAAK,MAAM,KAAK,QAAQ,KAAK;AAAA,UACpC,QAAQ,KAAK,MAAM,KAAK,SAAS,KAAK;AAAA,QACxC;AAAA;AAAA,QAEA,GAAI,SACA;AAAA,UACE,UAAU;AAAA,YACR,oBAAoB;AAAA,cAClB,UAAU,+BAA+B;AAAA,cACzC,OAAO,wBAAwB;AAAA,YACjC;AAAA,YACA,kBAAkB;AAAA,cAChB,UAAU,6BAA6B;AAAA,cACvC,OAAO,sBAAsB;AAAA,YAC/B;AAAA,YACA,cAAc;AAAA,YACd,gBAAgB;AAAA,YAChB,MAAM,EAAE,MAAM,iBAAiB,KAAK;AAAA,UACtC;AAAA,QACF,IACA,CAAC;AAAA,MACP,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AACH;AAGA,SAAS,mBAAmB,UAAgC,WAAsB;AAChF,QAAM,WAAW,YAAY;AAC7B,SAAO,IAAI,UAAU;AAAA,IACnB,QAAQ;AAAA,MACN,QAAQ;AAAA,QACN,OAAO,YAAY;AAAA,QACnB,MAAM,WAAW,IAAI;AAAA;AAAA,QACrB,OAAO,WAAW,WAAW,aAAa,QAAQ,KAAK,EAAE;AAAA,QACzD,OAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,UAAU,CAAC,IAAI,QAAQ,EAAE,CAAC;AAAA,EAC5B,CAAC;AACH;AAEA,SAAS,iBAAiB,OAAmE;AAC3F,QAAM,WAAW,eAAe;AAChC,SAAO,IAAI,OAAO;AAAA,IAChB,UAAU;AAAA,MACR,IAAI,UAAU;AAAA,QACZ,WAAW;AAAA,QACX,UAAU;AAAA,UACR,IAAI,QAAQ;AAAA,YACV,UAAU,CAAC,WAAM,WAAW,SAAS,SAAI;AAAA,YACzC,MAAM,EAAE,OAAO,UAAU,UAAU,UAAU,OAAO,SAAS;AAAA,YAC7D,MAAM,YAAY,cAAI;AAAA,UACxB,CAAC;AAAA,QACH;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AACH;AAEA,SAAS,cAAc,KAAyC;AAC9D,QAAM,OAAO,IAAI,SAAS,QAAQ,IAAI,UAAU,CAAC,GAAG,MAAM,CAAC;AAC3D,SAAO,IAAI,IAAI,CAAC,SAAS;AACvB,QAAI,KAAK,SAAS;AAChB,aAAO,mBAAoB,KAAK,OAAO,WAAoC,SAAS;AACtF,QAAI,KAAK,SAAS,QAAS,QAAO,eAAe,IAAI;AACrD,QAAI,KAAK,SAAS,QAAS,QAAO,aAAa,IAAI;AACnD,UAAM,OAAQ,KAAK,OAAO,gBAAgD;AAC1E,WAAO,iBAAiB,MAAM,OAAO,IAAI,CAAC;AAAA,EAC5C,CAAC;AACH;AAEA,SAAS,cAAc,KAA4B;AACjD,QAAM,UAA2B;AAAA,IAC/B,YAAY;AAAA,MACV,MAAM;AAAA,QACJ,MAAM;AAAA,UACJ,OAAO,SAAS,WAAW,KAAK;AAAA,UAChC,QAAQ,SAAS,WAAW,MAAM;AAAA,QACpC;AAAA,QACA,QAAQ;AAAA,UACN,KAAK,SAAS,UAAU,GAAG;AAAA,UAC3B,OAAO,SAAS,UAAU,KAAK;AAAA,UAC/B,QAAQ,SAAS,UAAU,MAAM;AAAA,UACjC,MAAM,SAAS,UAAU,IAAI;AAAA,QAC/B;AAAA,MACF;AAAA,IACF;AAAA,IACA,SAAS;AAAA,MACP,SAAS,iBAAiB,cAAc,KAAK;AAAA;AAAA,MAC7C,MAAM,iBAAiB,cAAc,IAAI;AAAA;AAAA,IAC3C;AAAA,IACA,UAAU,cAAc,GAAG;AAAA,EAC7B;AAEA,SAAO,IAAI,SAAS;AAAA,IAClB,4BAA4B;AAAA,IAC5B,QAAQ,EAAE,iBAAiB,qBAAqB,EAAE;AAAA,IAClD,UAAU,CAAC,OAAO;AAAA,EACpB,CAAC;AACH;AAGO,SAAS,WAAW,KAAiC;AAC1D,SAAO,OAAO,OAAO,cAAc,GAAG,CAAC;AACzC;AAGO,SAAS,aAAa,KAAmC;AAC9D,SAAO,OAAO,SAAS,cAAc,GAAG,CAAC;AAC3C;;;AIhSA,SAAS,WAAW,iBAAiB;AACrC,SAAS,iBAAiB;AAQ1B,IAAMA,gBAAgC;AACtC,IAAM,cAAc,IAAI,IAAI,OAAO,KAAK,YAAY,CAAC;AAIrD,IAAM,QAAQ,CAAC,MAAkB,OAAO,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,MAAM,IAAI,KAAK;AAC3E,IAAM,UAAU,CAAC,MAAsB,MAAM,QAAQ,CAAC,IAAK,IAAa,CAAC;AACzE,IAAM,aAAa,CAAC,MAAgB,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;AACvD,IAAM,OAAO,CAAC,GAAO,QAAsB,QAAQ,EAAE,GAAG,CAAC;AACzD,IAAM,OAAO,CAAC,GAAmB,SAC/B,IAAM,EAAE,IAAI,IAA2C,KAAK,IAAI,EAAE,IAAK;AACzE,IAAM,OAAO,CAAC,KAAW,QAAgC,IAAI,KAAK,CAAC,MAAM,MAAM,CAAC,MAAM,GAAG;AACzF,IAAM,UAAU,CAAC,KAAW,QAAsB,IAAI,OAAO,CAAC,MAAM,MAAM,CAAC,MAAM,GAAG;AACpF,SAAS,SAAS,OAAa,KAA6B;AAC1D,aAAW,KAAK,OAAO;AACrB,QAAI,MAAM,CAAC,MAAM,IAAK,QAAO;AAC7B,UAAM,QAAQ,SAAS,WAAW,CAAC,GAAG,GAAG;AACzC,QAAI,MAAO,QAAO;AAAA,EACpB;AACA,SAAO;AACT;AAEA,SAAS,YAAY,OAAqB;AACxC,MAAI,IAAI;AACR,aAAW,KAAK,OAAO;AACrB,QAAI,WAAW,EAAG,MAAK,OAAQ,EAA2B,OAAO,CAAC;AAAA,QAC7D,MAAK,YAAY,WAAW,CAAC,CAAC;AAAA,EACrC;AACA,SAAO;AACT;AAOA,SAAS,UAAU,KAA0C;AAC3D,MAAI,CAAC,IAAK,QAAO,CAAC;AAClB,QAAM,SAAS,IAAI,UAAU;AAAA,IAC3B,kBAAkB;AAAA,IAClB,qBAAqB;AAAA,IACrB,SAAS,CAAC,SAAS,SAAS;AAAA,EAC9B,CAAC;AACD,QAAM,OAAO,OAAO,MAAM,UAAU,GAAG,CAAC;AACxC,QAAM,OAAO,MAAM,eAAe,gBAAgB,CAAC;AACnD,QAAM,MAA8B,CAAC;AACrC,aAAW,KAAK,KAAM,KAAI,EAAE,MAAM,CAAC,IAAI,EAAE,UAAU;AACnD,SAAO;AACT;AAEA,SAAS,aAAa,KAAgB,OAA8B;AAClE,QAAM,SAAS,IAAI,KAAK,KAAK;AAC7B,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,MAAM,OAAO,QAAQ,QAAQ,EAAE;AACrC,QAAM,OAAO,IAAI,WAAW,OAAO,IAAI,MAAM,QAAQ,GAAG,GAAG,QAAQ,gBAAgB,EAAE;AACrF,QAAM,QAAQ,IAAI,MAAM,IAAI,KAAK,IAAI,MAAM,GAAG;AAC9C,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,OAAO,cAAc,KAAK;AAChC,SAAO,UAAU,MAAM,QAAQ,OAAO,KAAK;AAC7C;AAEA,SAAS,UAAU,GAAoB;AACrC,MAAI,CAAC,EAAG,QAAO;AACf,MAAI,MAAM,aAAa,MAAM,OAAQ,QAAO;AAC5C,SAAO;AACT;AAUA,SAAS,UAAU,OAAmC;AACpD,QAAM,aAAa,MAAM,WAAW,uBAAuB,MAAM,QAAQ,IAAI;AAC7E,aAAW,QAAQ,OAAO,KAAK,YAAY,GAAwB;AACjE,UAAM,OAAO,aAAa,IAAI;AAC9B,QAAI,cAAc,KAAK,SAAS,WAAY;AAC5C,QAAI,MAAM,kBAAkB,UAAa,YAAY,KAAK,IAAI,MAAM,MAAM,cAAe;AACzF,QAAI,UAAU,KAAK,KAAK,MAAM,MAAM,MAAO;AAC3C,QAAI,QAAQ,KAAK,IAAI,MAAM,MAAM,KAAM;AACvC,QAAI,QAAQ,KAAK,MAAM,MAAM,MAAM,UAAW;AAC9C,WAAO;AAAA,EACT;AACA,SAAOA;AACT;AAEA,SAAS,gBAAgB,WAAiB,KAA6B;AAErE,QAAM,OAAO,SAAS,WAAW,QAAQ;AACzC,MAAI,MAAM;AACR,UAAM,QAAQ,KAAK,MAAM,SAAS,KAAK,KAAK,MAAM,QAAQ;AAC1D,UAAM,MAAM,QAAQ,aAAa,KAAK,KAAK,IAAI;AAC/C,QAAI,KAAK;AACP,YAAM,SAAS,CAAC,CAAC,SAAS,WAAW,WAAW;AAChD,aAAO,EAAE,MAAM,SAAS,OAAO,SAAS,EAAE,KAAK,MAAM,KAAK,IAAI,EAAE,IAAI,EAAE;AAAA,IACxE;AAAA,EACF;AAEA,QAAM,MAAM,KAAK,WAAW,OAAO;AACnC,QAAM,UAAU,MAAM,WAAW,GAAG,IAAI,CAAC;AACzC,QAAM,OAAO,QAAQ,WAAW,KAAK;AACrC,QAAM,OAAO,KAAK,IAAI,CAAC,MAAM,YAAY,WAAW,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE;AAGhE,QAAM,OAAO,KAAK,SAAS,QAAQ;AACnC,MAAI,QAAQ,KAAK,KAAK,MAAM,IAAI;AAC9B,UAAM,QAAQ,KAAK,KAAK,WAAW,IAAI,GAAG,UAAU,GAAG,SAAS;AAChE,UAAM,UAAU,SAAS,MAAM,YAAY,MAAM,WAAW,WAAW;AACvE,WAAO,EAAE,MAAM,kBAAkB,OAAO,EAAE,QAAQ,EAAE;AAAA,EACtD;AAGA,QAAM,WAAW,KAAK,KAAK,SAAS,UAAU,GAAG,OAAO;AACxD,MAAI;AACJ,MAAI,YAAY,SAAS,WAAW,eAAe,GAAG;AACpD,UAAM,YAAY,SAAS,MAAM,gBAAgB,MAAM;AACvD,QAAI,YAAY,IAAI,SAAS,EAAG,QAAO;AAAA,EACzC;AAEA,MAAI,CAAC,MAAM;AACT,UAAM,WAAW,KAAK,CAAC,IAAI,KAAK,WAAW,KAAK,CAAC,CAAC,GAAG,OAAO,IAAI;AAChE,UAAM,UAAU,WAAW,WAAW,QAAQ,IAAI,CAAC;AACnD,UAAM,WAAW,KAAK,KAAK,SAAS,UAAU,GAAG,YAAY;AAC7D,UAAM,QAAQ,KAAK,KAAK,SAAS,MAAM,GAAG,OAAO;AACjD,UAAM,MAAM,KAAK,SAAS,OAAO;AACjC,WAAO,UAAU;AAAA,MACf;AAAA,MACA,eAAe,UAAU,SAAY,OAAO,KAAK,IAAI;AAAA,MACrD,OAAO,UAAU,KAAK,KAAK,SAAS,MAAM,GAAG,OAAO,CAAC;AAAA,MACrD,WAAW,KAAK,KAAK,aAAa,MAAM,UAAa,KAAK,KAAK,kBAAkB,MAAM;AAAA,MACvF,MAAM,CAAC,CAAC,KAAK,SAAS,KAAK;AAAA,IAC7B,CAAC;AAAA,EACH;AAEA,QAAM,OAAoB,EAAE,MAAM,aAAa,OAAO,EAAE,cAAc,KAAK,EAAE;AAC7E,MAAI,KAAK,OAAQ,MAAK,UAAU,CAAC,EAAE,MAAM,QAAQ,KAAK,CAAC;AACvD,SAAO;AACT;AAEA,SAAS,YAAY,aAAmB,KAA6B;AACnE,QAAM,OAAsB,CAAC;AAE7B,QAAM,YAAY,oBAAI,IAAyB;AAE/C,aAAW,MAAM,QAAQ,aAAa,MAAM,GAAG;AAC7C,UAAM,WAA0B,CAAC;AACjC,QAAI,MAAM;AACV,eAAW,MAAM,QAAQ,KAAK,IAAI,MAAM,GAAG,MAAM,GAAG;AAClD,YAAM,OAAO,KAAK,KAAK,IAAI,MAAM,GAAG,QAAQ;AAC5C,YAAM,WAAW,OAAO,WAAW,IAAI,IAAI,CAAC;AAC5C,YAAM,WAAW,OAAO,KAAK,KAAK,UAAU,YAAY,GAAG,OAAO,KAAK,GAAG,KAAK;AAC/E,YAAM,SAAS,KAAK,UAAU,UAAU;AACxC,YAAM,aAAa,CAAC,CAAC,UAAU,KAAK,QAAQ,OAAO,MAAM;AAEzD,UAAI,YAAY;AAEd,cAAM,SAAS,UAAU,IAAI,GAAG;AAChC,YAAI,QAAQ,MAAO,QAAO,MAAM,UAAU,OAAO,OAAO,MAAM,WAAW,CAAC,IAAI;AAC9E,eAAO;AACP;AAAA,MACF;AAEA,YAAM,QAAQ,QAAQ,KAAK,IAAI,MAAM,GAAG,KAAK,EAAE,IAAI,CAAC,MAAM,gBAAgB,KAAK,GAAG,KAAK,GAAG,GAAG,CAAC;AAC9F,YAAM,OAAoB;AAAA,QACxB,MAAM;AAAA,QACN,OAAO,EAAE,SAAS,UAAU,SAAS,EAAE;AAAA,QACvC,SAAS,MAAM,SAAS,QAAQ,CAAC,EAAE,MAAM,YAAY,CAAC;AAAA,MACxD;AACA,eAAS,KAAK,IAAI;AAClB,eAAS,IAAI,GAAG,IAAI,UAAU,IAAK,WAAU,IAAI,MAAM,GAAG,IAAI;AAC9D,aAAO;AAAA,IACT;AACA,SAAK,KAAK,EAAE,MAAM,YAAY,SAAS,SAAS,CAAC;AAAA,EACnD;AACA,SAAO,EAAE,MAAM,SAAS,SAAS,KAAK;AACxC;AAGO,SAAS,SAAS,MAA6C;AACpE,QAAM,QAAQ,gBAAgB,aAAa,OAAO,IAAI,WAAW,IAAI;AACrE,QAAM,QAAQ,UAAU,KAAK;AAC7B,QAAM,SAAS,MAAM,mBAAmB;AACxC,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,mEAAgC;AAE7D,QAAM,MAAiB;AAAA,IACrB;AAAA,IACA,MAAM,UAAU,MAAM,8BAA8B,CAAC;AAAA,EACvD;AAEA,QAAM,SAAS,IAAI,UAAU;AAAA,IAC3B,eAAe;AAAA,IACf,kBAAkB;AAAA,IAClB,qBAAqB;AAAA,EACvB,CAAC;AACD,QAAM,OAAO,OAAO,MAAM,UAAU,MAAM,CAAC;AAE3C,QAAM,UAAU,KAAK,MAAM,YAAY;AACvC,QAAM,WAAW,UAAU,KAAK,WAAW,OAAO,GAAG,QAAQ,IAAI;AACjE,QAAM,eAAe,WAAW,WAAW,QAAQ,IAAI,CAAC;AAExD,QAAM,UAAyB,CAAC;AAChC,aAAW,SAAS,cAAc;AAChC,UAAM,MAAM,MAAM,KAAK;AACvB,QAAI,QAAQ,MAAO,SAAQ,KAAK,gBAAgB,KAAK,OAAO,KAAK,GAAG,GAAG,CAAC;AAAA,aAC/D,QAAQ,QAAS,SAAQ,KAAK,YAAY,KAAK,OAAO,OAAO,GAAG,GAAG,CAAC;AAAA,EAE/E;AAEA,SAAO,EAAE,MAAM,OAAO,SAAS,QAAQ,SAAS,UAAU,CAAC,EAAE,MAAM,YAAY,CAAC,EAAE;AACpF;","names":["DEFAULT_ROLE"]}
|