reamkit 1.30.0 → 1.31.0
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/README.md +60 -24
- package/dist/esm/core/converter/project.js +3 -1
- package/dist/esm/core/crypto/offcrypto.js +1 -1
- package/dist/esm/core/document-model/index.d.ts +1 -1
- package/dist/esm/core/document-model/types.d.ts +63 -0
- package/dist/esm/core/font/index.d.ts +2 -0
- package/dist/esm/core/font/ttf-build.d.ts +113 -0
- package/dist/esm/core/font/ttf-build.js +1224 -0
- package/dist/esm/core/font/ttf-subset.d.ts +19 -0
- package/dist/esm/core/font/ttf-subset.js +14 -1
- package/dist/esm/core/fonts/provider.d.ts +17 -0
- package/dist/esm/core/fonts/provider.js +27 -2
- package/dist/esm/core/fonts/remote-fonts.js +8 -1
- package/dist/esm/core/ir/flow.d.ts +71 -1
- package/dist/esm/core/numbering/index.d.ts +1 -1
- package/dist/esm/core/numbering/state.d.ts +11 -1
- package/dist/esm/core/numbering/state.js +10 -1
- package/dist/esm/core/style-cascade/resolver.js +35 -4
- package/dist/esm/core/style-cascade/types.d.ts +19 -1
- package/dist/esm/core/style-cascade/types.js +3 -0
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/layout/page-doc.d.ts +10 -3
- package/dist/esm/layout/styled-layout.d.ts +48 -7
- package/dist/esm/layout/styled-layout.js +881 -119
- package/dist/esm/layout/turned-section.d.ts +38 -0
- package/dist/esm/layout/turned-section.js +193 -0
- package/dist/esm/pdf/styled-page-emitter.js +78 -2
- package/dist/esm/pdf-reader/cff-outline.d.ts +27 -0
- package/dist/esm/pdf-reader/cff-outline.js +169 -21
- package/dist/esm/pdf-reader/content.d.ts +53 -0
- package/dist/esm/pdf-reader/content.js +9 -1
- package/dist/esm/pdf-reader/display.d.ts +36 -0
- package/dist/esm/pdf-reader/display.js +66 -1
- package/dist/esm/pdf-reader/document.js +5 -1
- package/dist/esm/pdf-reader/embedded-fonts.d.ts +2 -1
- package/dist/esm/pdf-reader/embedded-fonts.js +32 -2
- package/dist/esm/pdf-reader/encodings.d.ts +8 -0
- package/dist/esm/pdf-reader/encodings.js +25 -3
- package/dist/esm/pdf-reader/face-outlines.d.ts +78 -0
- package/dist/esm/pdf-reader/face-outlines.js +362 -0
- package/dist/esm/pdf-reader/figures.d.ts +52 -0
- package/dist/esm/pdf-reader/figures.js +433 -0
- package/dist/esm/pdf-reader/flow-build.d.ts +131 -5
- package/dist/esm/pdf-reader/flow-build.js +345 -26
- package/dist/esm/pdf-reader/font.js +321 -36
- package/dist/esm/pdf-reader/glyf-outline.d.ts +33 -0
- package/dist/esm/pdf-reader/glyf-outline.js +135 -1
- package/dist/esm/pdf-reader/glyph-names.js +154 -1
- package/dist/esm/pdf-reader/layout.d.ts +93 -0
- package/dist/esm/pdf-reader/layout.js +1703 -214
- package/dist/esm/pdf-reader/page-numbers.d.ts +53 -0
- package/dist/esm/pdf-reader/page-numbers.js +167 -0
- package/dist/esm/pdf-reader/tagged.js +182 -23
- package/dist/esm/pdf-reader/text.d.ts +6 -3
- package/dist/esm/pdf-reader/text.js +98 -9
- package/dist/esm/pdf-reader/type1-outline.d.ts +11 -0
- package/dist/esm/pdf-reader/type1-outline.js +63 -8
- package/dist/esm/pdf-reader/vector.js +71 -1
- package/dist/esm/word/doc/doc-reader.js +6 -2
- package/dist/esm/word/doc/doc-text.d.ts +6 -0
- package/dist/esm/word/doc/doc-text.js +19 -1
- package/dist/esm/word/document-parser.d.ts +2 -2
- package/dist/esm/word/document-parser.js +11 -1
- package/dist/esm/word/docx-reader.js +5 -3
- package/dist/esm/word/docx-writer.js +207 -21
- package/dist/esm/word/drawing-parser.d.ts +5 -3
- package/dist/esm/word/drawing-parser.js +49 -8
- package/dist/esm/word/font-embed.d.ts +30 -0
- package/dist/esm/word/font-embed.js +173 -0
- package/dist/esm/word/font-table.d.ts +10 -0
- package/dist/esm/word/font-table.js +13 -1
- package/dist/esm/word/index.js +1 -1
- package/dist/esm/word/numbering-parser.d.ts +3 -1
- package/dist/esm/word/numbering-parser.js +2 -1
- package/dist/esm/word/paragraph-properties.d.ts +7 -6
- package/dist/esm/word/paragraph-properties.js +14 -2
- package/dist/esm/word/run-properties.js +26 -0
- package/package.json +8 -3
|
@@ -6,6 +6,7 @@ import { buildOpcPackage } from "../core/opc/opc-writer.js";
|
|
|
6
6
|
import "../core/opc/index.js";
|
|
7
7
|
import { omathXml } from "./omml-serializer.js";
|
|
8
8
|
import { chartSpaceXml } from "../core/drawingml/chart-serializer.js";
|
|
9
|
+
import { OBFUSCATED_FONT_CONTENT_TYPE, embedFaces } from "./font-embed.js";
|
|
9
10
|
//#region src/word/docx-writer.ts
|
|
10
11
|
var encoder = new TextEncoder();
|
|
11
12
|
var DEFAULT_RUN = resolveRunProperties({}, {}, EMPTY_STYLE_SHEET);
|
|
@@ -20,6 +21,12 @@ var NUMBERING_PART = "word/numbering.xml";
|
|
|
20
21
|
var REL_FONT_TABLE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable";
|
|
21
22
|
var FONT_TABLE_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml";
|
|
22
23
|
var FONT_TABLE_PART = "word/fontTable.xml";
|
|
24
|
+
var REL_SETTINGS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings";
|
|
25
|
+
var SETTINGS_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml";
|
|
26
|
+
var SETTINGS_PART = "word/settings.xml";
|
|
27
|
+
var REL_STYLES = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles";
|
|
28
|
+
var STYLES_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml";
|
|
29
|
+
var STYLES_PART = "word/styles.xml";
|
|
23
30
|
var REL_FOOTNOTES = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes";
|
|
24
31
|
var REL_ENDNOTES = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/endnotes";
|
|
25
32
|
var FOOTNOTES_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml";
|
|
@@ -33,6 +40,20 @@ var REL_COMMENTS_EXTENDED = "http://schemas.microsoft.com/office/2011/relationsh
|
|
|
33
40
|
var COMMENTS_EXTENDED_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml";
|
|
34
41
|
var COMMENTS_EXTENDED_PART = "word/commentsExtended.xml";
|
|
35
42
|
var W14_NS = "http://schemas.microsoft.com/office/word/2010/wordml";
|
|
43
|
+
var MC_NS = "http://schemas.openxmlformats.org/markup-compatibility/2006";
|
|
44
|
+
/**
|
|
45
|
+
* The namespaces a part's root declares: WordprocessingML's and the
|
|
46
|
+
* relationships', and — where the part uses one — Word 2010's `w14`, which a
|
|
47
|
+
* reader that does not know it is told to pass over (ECMA-376 Part 3
|
|
48
|
+
* `mc:Ignorable`) rather than to refuse the file.
|
|
49
|
+
*
|
|
50
|
+
* @param inner The part's content, to see whether it uses `w14`.
|
|
51
|
+
* @returns The attributes, each with its leading space.
|
|
52
|
+
*/
|
|
53
|
+
function rootNamespaces(inner) {
|
|
54
|
+
const base = " xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"";
|
|
55
|
+
return inner.includes("<w14:") ? `${base} xmlns:w14="${W14_NS}" xmlns:mc="${MC_NS}" mc:Ignorable="w14"` : base;
|
|
56
|
+
}
|
|
36
57
|
var W15_NS = "http://schemas.microsoft.com/office/word/2012/wordml";
|
|
37
58
|
var REL_CHART = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart";
|
|
38
59
|
var CHART_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.drawingml.chart+xml";
|
|
@@ -133,6 +154,8 @@ function writeDocx(flow) {
|
|
|
133
154
|
...flow.charts ? { charts: flow.charts } : {},
|
|
134
155
|
...flow.faceFamilies ? { faceFamilies: flow.faceFamilies } : {},
|
|
135
156
|
familiesUsed: /* @__PURE__ */ new Map(),
|
|
157
|
+
...flow.faceOutlines ? { faceOutlines: flow.faceOutlines } : {},
|
|
158
|
+
facesUsed: /* @__PURE__ */ new Set(),
|
|
136
159
|
zRanks: zRanksOf(flow)
|
|
137
160
|
};
|
|
138
161
|
const docScope = newScope();
|
|
@@ -153,7 +176,8 @@ function writeDocx(flow) {
|
|
|
153
176
|
});
|
|
154
177
|
emitBody(body, flow.body, losses, state, docScope, sectPrByClosingIndex);
|
|
155
178
|
if (finalSectPr) body.push(finalSectPr);
|
|
156
|
-
const
|
|
179
|
+
const bodyXml = body.join("");
|
|
180
|
+
const documentXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:document${rootNamespaces(bodyXml)}><w:body>${bodyXml}</w:body></w:document>`;
|
|
157
181
|
const numberingPart = flow.body.some((el) => el.kind === "paragraph" && el.paragraph.properties.numbering !== void 0) && flow.numbering ? {
|
|
158
182
|
path: NUMBERING_PART,
|
|
159
183
|
data: encoder.encode(numberingXml(flow.numbering)),
|
|
@@ -185,9 +209,14 @@ function writeDocx(flow) {
|
|
|
185
209
|
}, state, losses, docScope, extraParts, extraPartRels);
|
|
186
210
|
const commentParaIds = emitComments(flow.comments, state, losses, docScope, extraParts, extraPartRels);
|
|
187
211
|
emitCommentsExtended(flow.comments, commentParaIds, docScope, extraParts);
|
|
212
|
+
const embedded = state.faceOutlines && state.faceFamilies ? embedFaces(state.facesUsed, state.faceOutlines, state.faceFamilies, losses) : void 0;
|
|
213
|
+
if (embedded && embedded.relationships.length > 0) extraPartRels.push({
|
|
214
|
+
sourcePart: FONT_TABLE_PART,
|
|
215
|
+
relationships: [...embedded.relationships]
|
|
216
|
+
});
|
|
188
217
|
const fontTablePart = state.familiesUsed.size > 0 ? {
|
|
189
218
|
path: FONT_TABLE_PART,
|
|
190
|
-
data: encoder.encode(fontTableXml(state.familiesUsed)),
|
|
219
|
+
data: encoder.encode(fontTableXml(state.familiesUsed, embedded?.elements)),
|
|
191
220
|
contentType: FONT_TABLE_CONTENT_TYPE
|
|
192
221
|
} : void 0;
|
|
193
222
|
if (fontTablePart) docScope.rels.push({
|
|
@@ -196,6 +225,29 @@ function writeDocx(flow) {
|
|
|
196
225
|
target: "fontTable.xml",
|
|
197
226
|
targetMode: "Internal"
|
|
198
227
|
});
|
|
228
|
+
const settings = settingsXml(flow, sections, (embedded?.parts.length ?? 0) > 0);
|
|
229
|
+
const settingsPart = settings !== void 0 ? {
|
|
230
|
+
path: SETTINGS_PART,
|
|
231
|
+
data: encoder.encode(settings),
|
|
232
|
+
contentType: SETTINGS_CONTENT_TYPE
|
|
233
|
+
} : void 0;
|
|
234
|
+
if (settingsPart) docScope.rels.push({
|
|
235
|
+
id: `rId${++docScope.relSeq}`,
|
|
236
|
+
type: REL_SETTINGS,
|
|
237
|
+
target: "settings.xml",
|
|
238
|
+
targetMode: "Internal"
|
|
239
|
+
});
|
|
240
|
+
const stylesPart = {
|
|
241
|
+
path: STYLES_PART,
|
|
242
|
+
data: encoder.encode(stylesXml()),
|
|
243
|
+
contentType: STYLES_CONTENT_TYPE
|
|
244
|
+
};
|
|
245
|
+
docScope.rels.push({
|
|
246
|
+
id: `rId${++docScope.relSeq}`,
|
|
247
|
+
type: REL_STYLES,
|
|
248
|
+
target: "styles.xml",
|
|
249
|
+
targetMode: "Internal"
|
|
250
|
+
});
|
|
199
251
|
const partRelationships = [...docScope.rels.length > 0 ? [{
|
|
200
252
|
sourcePart: "word/document.xml",
|
|
201
253
|
relationships: docScope.rels
|
|
@@ -208,12 +260,16 @@ function writeDocx(flow) {
|
|
|
208
260
|
data: encoder.encode(documentXml),
|
|
209
261
|
contentType: DOC_CONTENT_TYPE
|
|
210
262
|
},
|
|
263
|
+
stylesPart,
|
|
211
264
|
...numberingPart ? [numberingPart] : [],
|
|
212
265
|
...fontTablePart ? [fontTablePart] : [],
|
|
266
|
+
...settingsPart ? [settingsPart] : [],
|
|
213
267
|
...extraParts,
|
|
214
268
|
...state.chartParts,
|
|
215
|
-
...state.mediaParts
|
|
269
|
+
...state.mediaParts,
|
|
270
|
+
...embedded?.parts ?? []
|
|
216
271
|
],
|
|
272
|
+
...(embedded?.parts.length ?? 0) > 0 ? { defaultsByExtension: { odttf: OBFUSCATED_FONT_CONTENT_TYPE } } : {},
|
|
217
273
|
rootRelationships: [{
|
|
218
274
|
id: "rId1",
|
|
219
275
|
type: REL_OFFICE_DOCUMENT,
|
|
@@ -225,8 +281,50 @@ function writeDocx(flow) {
|
|
|
225
281
|
losses
|
|
226
282
|
};
|
|
227
283
|
}
|
|
228
|
-
|
|
229
|
-
|
|
284
|
+
/**
|
|
285
|
+
* §17.15.1.78 `w:settings` — the document-wide settings the model carries, in
|
|
286
|
+
* the order CT_Settings declares them: `w:embedTrueTypeFonts` and
|
|
287
|
+
* `w:saveSubsetFonts` (§17.15.1.42, .74) where the package embeds its faces,
|
|
288
|
+
* `w:gutterAtTop` (§17.15.1.49), `w:evenAndOddHeaders` (§17.15.1.36, which a
|
|
289
|
+
* section carries in the model), and inside `w:compat` §17.15.3.4's
|
|
290
|
+
* `w:doNotExpandShiftReturn` before the version of Word the document is laid
|
|
291
|
+
* out for ([MS-DOCX] `w:compatSetting` `compatibilityMode`), which CT_Compat
|
|
292
|
+
* keeps last.
|
|
293
|
+
*
|
|
294
|
+
* @param flow The document.
|
|
295
|
+
* @param sections The sections being written.
|
|
296
|
+
* @param embedsFonts Whether the package embeds fonts — which then stay
|
|
297
|
+
* embedded, as subsets, when the document is saved again.
|
|
298
|
+
* @returns The part's XML, or undefined where the document states none.
|
|
299
|
+
*/
|
|
300
|
+
function settingsXml(flow, sections, embedsFonts) {
|
|
301
|
+
const parts = [];
|
|
302
|
+
if (embedsFonts) parts.push("<w:embedTrueTypeFonts/><w:saveSubsetFonts/>");
|
|
303
|
+
if (flow.gutterAtTop === true) parts.push("<w:gutterAtTop/>");
|
|
304
|
+
if (sections.some((sec) => sec.properties.evenAndOddHeaders === true)) parts.push("<w:evenAndOddHeaders/>");
|
|
305
|
+
const compat = [...flow.doNotExpandShiftReturn === true ? ["<w:doNotExpandShiftReturn/>"] : [], ...flow.compatibilityMode !== void 0 ? [`<w:compatSetting w:name="compatibilityMode" w:uri="http://schemas.microsoft.com/office/word" w:val="${String(flow.compatibilityMode)}"/>`] : []];
|
|
306
|
+
if (compat.length > 0) parts.push(`<w:compat>${compat.join("")}</w:compat>`);
|
|
307
|
+
if (parts.length === 0) return void 0;
|
|
308
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:settings xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">${parts.join("")}</w:settings>`;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* §17.7.2 `w:styles` — what a property the document states nowhere IS: the
|
|
312
|
+
* values the model resolves an empty sheet to (`DEFAULT_RUN`, `DEFAULT_PARA`
|
|
313
|
+
* — the very values a run or paragraph is written without), as §17.7.5
|
|
314
|
+
* `w:docDefaults`, under an empty default paragraph style (§17.7.4.17).
|
|
315
|
+
*
|
|
316
|
+
* A package with no styles part leaves them to its reader, and Word fills
|
|
317
|
+
* them from its own template's Normal: 8pt after every paragraph, lines 1.08
|
|
318
|
+
* apart, runs in 12pt — so every paragraph of a reconstruction stood 8pt
|
|
319
|
+
* further down than its page set it, and a run of the model's own 11pt, which
|
|
320
|
+
* states no size, was set in 12.
|
|
321
|
+
*/
|
|
322
|
+
function stylesXml() {
|
|
323
|
+
const size = Math.round(DEFAULT_RUN.fontSizePt * 2);
|
|
324
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:styles xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"><w:docDefaults><w:rPrDefault><w:rPr><w:sz w:val="${size}"/><w:szCs w:val="${size}"/></w:rPr></w:rPrDefault><w:pPrDefault><w:pPr>${DEFAULT_PARA.widowControl ? "<w:widowControl/>" : "<w:widowControl w:val=\"0\"/>"}${`<w:spacing w:before="${twips(DEFAULT_PARA.spacingBefore)}" w:after="${twips(DEFAULT_PARA.spacingAfter)}" w:line="${twips(DEFAULT_PARA.spacingLine)}" w:lineRule="${DEFAULT_PARA.spacingLineRule}"/>`}</w:pPr></w:pPrDefault></w:docDefaults><w:style w:type="paragraph" w:default="1" w:styleId="Normal"><w:name w:val="Normal"/></w:style></w:styles>`;
|
|
325
|
+
}
|
|
326
|
+
function fontTableXml(families, embeds) {
|
|
327
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:fonts xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">${[...families.values()].map((f) => `<w:font w:name="${escapeAttr(f.family)}"><w:family w:val="${f.generic}"/><w:pitch w:val="${f.generic === "modern" ? "fixed" : "variable"}"/>${embeds?.get(f.family) ?? ""}</w:font>`).join("")}</w:fonts>`;
|
|
230
328
|
}
|
|
231
329
|
/**
|
|
232
330
|
* §20.4.2.3 `relativeHeight` — a float's z-order, written the way Word writes
|
|
@@ -277,7 +375,8 @@ function emitNotes(notes, cfg, state, losses, docScope, extraParts, extraPartRel
|
|
|
277
375
|
for (const el of content) emitBlock(inner, el, losses, state, scope);
|
|
278
376
|
noteXmls.push(`<${cfg.noteTag} w:id="${escapeAttr(id)}">${inner.join("") || "<w:p/>"}</${cfg.noteTag}>`);
|
|
279
377
|
}
|
|
280
|
-
const
|
|
378
|
+
const notesXml = noteXmls.join("");
|
|
379
|
+
const xml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><${cfg.rootTag}${rootNamespaces(notesXml)}>` + stub("separator", -1, "<w:separator/>") + stub("continuationSeparator", 0, "<w:continuationSeparator/>") + notesXml + `</${cfg.rootTag}>`;
|
|
281
380
|
extraParts.push({
|
|
282
381
|
path: cfg.partPath,
|
|
283
382
|
data: encoder.encode(xml),
|
|
@@ -383,7 +482,8 @@ function emitHeadersFooters(flow, section, state, docScope, extraParts, extraPar
|
|
|
383
482
|
const scope = newScope();
|
|
384
483
|
const inner = [];
|
|
385
484
|
for (const el of content) emitBlock(inner, el, losses, state, scope);
|
|
386
|
-
const
|
|
485
|
+
const bandXml = inner.join("");
|
|
486
|
+
const xml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><${root}${rootNamespaces(bandXml)}>${bandXml}</${root}>`;
|
|
387
487
|
extraParts.push({
|
|
388
488
|
path,
|
|
389
489
|
data: encoder.encode(xml),
|
|
@@ -427,7 +527,7 @@ function emitHeadersFooters(flow, section, state, docScope, extraParts, extraPar
|
|
|
427
527
|
var docxWriter = {
|
|
428
528
|
id: "docx",
|
|
429
529
|
consumes: "flow",
|
|
430
|
-
supports: new Set([FEATURES.text]),
|
|
530
|
+
supports: new Set([FEATURES.text, FEATURES.fontsEmbedding]),
|
|
431
531
|
write: (doc) => writeDocx(doc)
|
|
432
532
|
};
|
|
433
533
|
/**
|
|
@@ -636,13 +736,76 @@ function shapeDrawingXml(shape, losses, state, scope) {
|
|
|
636
736
|
const cy = Math.round(shape.height * EMU_PER_PT);
|
|
637
737
|
const id = ++state.drawingSeq;
|
|
638
738
|
const descr = shape.altText ? ` descr="${escapeAttr(shape.altText)}"` : "";
|
|
639
|
-
const
|
|
640
|
-
const
|
|
641
|
-
|
|
739
|
+
const members = shape.children ?? [];
|
|
740
|
+
const graphic = "<a:graphic xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">" + (members.length > 0 ? `<a:graphicData uri="${WPG_URI}">` + groupXml("wpg:wgp", members, {
|
|
741
|
+
x: 0,
|
|
742
|
+
y: 0,
|
|
743
|
+
cx,
|
|
744
|
+
cy
|
|
745
|
+
}, losses, state, scope) : `<a:graphicData uri="${WPS_URI}">` + wspXml(shape, cx, cy, void 0, losses, state, scope)) + "</a:graphicData></a:graphic>";
|
|
642
746
|
return drawingFrame(shape.float, cx, cy, `<wp:docPr id="${id}" name="Shape ${id}"${descr}/>`, graphic, state, shape.transform?.rotation60k);
|
|
643
747
|
}
|
|
644
|
-
|
|
645
|
-
|
|
748
|
+
/**
|
|
749
|
+
* `wps:wsp` — one shape: its box, geometry, fill and outline, and the text it
|
|
750
|
+
* holds. `id` is its own `wps:cNvPr` inside a group, where the members name
|
|
751
|
+
* themselves; a shape alone is named by the frame's `wp:docPr`.
|
|
752
|
+
*/
|
|
753
|
+
function wspXml(shape, cx, cy, member, losses, state, scope) {
|
|
754
|
+
const spPr = `<wps:spPr>${xfrmXml(shape.transform, cx, cy, member)}${geomXml(shape.geometry)}${fillXml(shape.fill)}${shape.line ? lineXml(shape.line) : ""}</wps:spPr>`;
|
|
755
|
+
const txbx = shape.text ? txbxXml(shape.text, losses, state, scope) : "";
|
|
756
|
+
return `<wps:wsp xmlns:wps="${WPS_URI}">` + (member ? `<wps:cNvPr id="${member.id}" name="Shape ${member.id}"/>` : "") + "<wps:cNvSpPr/>" + spPr + txbx + bodyPrXml(shape.text) + "</wps:wsp>";
|
|
757
|
+
}
|
|
758
|
+
var WPG_URI = "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup";
|
|
759
|
+
/**
|
|
760
|
+
* §20.5.2.17 `wpg:wgp` / `wpg:grpSp` — a group: its box, and its members in
|
|
761
|
+
* the order they are painted. The members are placed in a child space the
|
|
762
|
+
* same as the box, so a member's offset from the group's corner is its offset
|
|
763
|
+
* in the file. The inverse of drawing-parser's `groupChildren`.
|
|
764
|
+
*
|
|
765
|
+
* Written as one shape, the group came back an empty box: a figure's paths
|
|
766
|
+
* and labels were thrown away with it.
|
|
767
|
+
*/
|
|
768
|
+
function groupXml(tag, members, box, losses, state, scope) {
|
|
769
|
+
return `<${tag}${tag === "wpg:wgp" ? ` xmlns:wpg="${WPG_URI}"` : ""}><wpg:cNvGrpSpPr/><wpg:grpSpPr>${`<a:xfrm><a:off x="${box.x}" y="${box.y}"/><a:ext cx="${box.cx}" cy="${box.cy}"/><a:chOff x="0" y="0"/><a:chExt cx="${box.cx}" cy="${box.cy}"/></a:xfrm>`}</wpg:grpSpPr>${members.map((m) => memberXml(m, losses, state, scope)).join("")}</${tag}>`;
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* One member of a group: a group of its own, a picture (`pic:pic`, a shape
|
|
773
|
+
* whose fill is the picture — how the reader brings one in), or a shape.
|
|
774
|
+
*/
|
|
775
|
+
function memberXml(member, losses, state, scope) {
|
|
776
|
+
const s = member.shape;
|
|
777
|
+
const x = Math.round(member.xPt * EMU_PER_PT);
|
|
778
|
+
const y = Math.round(member.yPt * EMU_PER_PT);
|
|
779
|
+
const cx = Math.round(s.width * EMU_PER_PT);
|
|
780
|
+
const cy = Math.round(s.height * EMU_PER_PT);
|
|
781
|
+
if (s.children !== void 0 && s.children.length > 0) return groupXml("wpg:grpSp", s.children, {
|
|
782
|
+
x,
|
|
783
|
+
y,
|
|
784
|
+
cx,
|
|
785
|
+
cy
|
|
786
|
+
}, losses, state, scope);
|
|
787
|
+
const id = ++state.drawingSeq;
|
|
788
|
+
if (s.fill.kind !== "picture") return wspXml(s, cx, cy, {
|
|
789
|
+
id,
|
|
790
|
+
x,
|
|
791
|
+
y
|
|
792
|
+
}, losses, state, scope);
|
|
793
|
+
const relId = s.fill.imageResource !== void 0 ? mediaRelId(s.fill.imageResource, state, scope) : void 0;
|
|
794
|
+
if (relId === void 0) {
|
|
795
|
+
losses.push({
|
|
796
|
+
severity: "dropped",
|
|
797
|
+
feature: FEATURES.images,
|
|
798
|
+
detail: imageRefusal(s.fill.imageResource, state)
|
|
799
|
+
});
|
|
800
|
+
return "";
|
|
801
|
+
}
|
|
802
|
+
return `<pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:nvPicPr><pic:cNvPr id="${id}" name="Image ${id}"/><pic:cNvPicPr/></pic:nvPicPr><pic:blipFill><a:blip r:embed="${relId}">${s.fill.alpha !== void 0 && s.fill.alpha < 1 ? `<a:alphaModFix amt="${String(Math.round(Math.max(0, s.fill.alpha) * 1e5))}"/>` : ""}</a:blip>${srcRectXml(s.fill.imageCrop)}<a:stretch><a:fillRect/></a:stretch></pic:blipFill><pic:spPr>${xfrmXml(s.transform, cx, cy, {
|
|
803
|
+
x,
|
|
804
|
+
y
|
|
805
|
+
})}<a:prstGeom prst="rect"><a:avLst/></a:prstGeom></pic:spPr></pic:pic>`;
|
|
806
|
+
}
|
|
807
|
+
function xfrmXml(t, cx, cy, at) {
|
|
808
|
+
return `<a:xfrm${t?.rotation60k !== void 0 ? ` rot="${t.rotation60k}"` : ""}${t?.flipH ? " flipH=\"1\"" : ""}${t?.flipV ? " flipV=\"1\"" : ""}><a:off x="${at?.x ?? 0}" y="${at?.y ?? 0}"/><a:ext cx="${cx}" cy="${cy}"/></a:xfrm>`;
|
|
646
809
|
}
|
|
647
810
|
/**
|
|
648
811
|
* The factor that carries a path into whole units, and the space it lands in.
|
|
@@ -668,7 +831,7 @@ function pathScale(g) {
|
|
|
668
831
|
else if (c.cmd === "quad") values.push(c.x1, c.y1, c.x, c.y);
|
|
669
832
|
else if (c.cmd === "arc") values.push(c.wR, c.hR);
|
|
670
833
|
if (values.every((v) => Number.isInteger(v))) return 1;
|
|
671
|
-
const span =
|
|
834
|
+
const span = values.reduce((most, v) => Number.isFinite(v) ? Math.max(most, Math.abs(v)) : most, 1);
|
|
672
835
|
return Math.max(1, Math.floor(PATH_SPACE / span));
|
|
673
836
|
}
|
|
674
837
|
function geomXml(g) {
|
|
@@ -736,7 +899,7 @@ function bodyPrXml(text) {
|
|
|
736
899
|
if (!text) return "<wps:bodyPr/>";
|
|
737
900
|
const ins = (v, name) => v !== void 0 ? ` ${name}="${Math.round(v * EMU_PER_PT)}"` : "";
|
|
738
901
|
const anchor = text.anchor ? ` anchor="${text.anchor}"` : "";
|
|
739
|
-
return "<wps:bodyPr" + ins(text.insetLeft, "lIns") + ins(text.insetTop, "tIns") + ins(text.insetRight, "rIns") + ins(text.insetBottom, "bIns") + anchor + "/>";
|
|
902
|
+
return "<wps:bodyPr" + (text.noWrap === true ? " wrap=\"none\"" : "") + ins(text.insetLeft, "lIns") + ins(text.insetTop, "tIns") + ins(text.insetRight, "rIns") + ins(text.insetBottom, "bIns") + anchor + "/>";
|
|
740
903
|
}
|
|
741
904
|
function mediaRelId(resource, state, scope) {
|
|
742
905
|
const existingRel = scope.relIdByResource.get(resource);
|
|
@@ -787,6 +950,7 @@ function tblPrXml(p) {
|
|
|
787
950
|
out.push(`<w:tblW w:w="${w}" w:type="${p.widthType}"/>`);
|
|
788
951
|
}
|
|
789
952
|
if (p.alignment && p.alignment !== "left") out.push(`<w:jc w:val="${p.alignment}"/>`);
|
|
953
|
+
if (p.indentPt !== void 0 && p.indentPt !== 0) out.push(`<w:tblInd w:w="${twips(p.indentPt)}" w:type="dxa"/>`);
|
|
790
954
|
const borders = bordersXml("w:tblBorders", p.borders);
|
|
791
955
|
if (borders) out.push(borders);
|
|
792
956
|
if (p.layout === "fixed") out.push("<w:tblLayout w:type=\"fixed\"/>");
|
|
@@ -839,11 +1003,14 @@ function bordersXml(tag, borders) {
|
|
|
839
1003
|
if (tag === "w:pBdr" && key === "insideV") return "";
|
|
840
1004
|
const el = tag === "w:pBdr" && key === "insideH" ? "w:between" : name;
|
|
841
1005
|
const sz = b.width !== void 0 ? ` w:sz="${Math.round(b.width * 8)}"` : "";
|
|
1006
|
+
const space = b.spacePt !== void 0 && Math.round(b.spacePt) > 0 ? ` w:space="${Math.min(Math.round(b.spacePt), MOST_BORDER_SPACE_PT)}"` : "";
|
|
842
1007
|
const color = b.colorHex !== void 0 ? ` w:color="${b.colorHex}"` : "";
|
|
843
|
-
return `<${el} w:val="${b.style}"${sz}${color}/>`;
|
|
1008
|
+
return `<${el} w:val="${b.style}"${sz}${space}${color}/>`;
|
|
844
1009
|
}).join("");
|
|
845
1010
|
return sides ? `<${tag}>${sides}</${tag}>` : "";
|
|
846
1011
|
}
|
|
1012
|
+
/** §17.3.4 — the farthest a border may stand off its text, in points. */
|
|
1013
|
+
var MOST_BORDER_SPACE_PT = 31;
|
|
847
1014
|
function cellMarginsXml(tag, margins) {
|
|
848
1015
|
if (!margins) return "";
|
|
849
1016
|
const inner = [
|
|
@@ -914,7 +1081,8 @@ function runXml(run, state, scope) {
|
|
|
914
1081
|
return piece("<w:fldChar w:fldCharType=\"begin\"/>") + piece(`<w:instrText xml:space="preserve"> ${run.field} </w:instrText>`) + piece("<w:fldChar w:fldCharType=\"separate\"/>") + piece(`<w:t xml:space="preserve">${escapeXml(run.text)}</w:t>`) + piece("<w:fldChar w:fldCharType=\"end\"/>") + brk;
|
|
915
1082
|
}
|
|
916
1083
|
if (run.text === "") return brk ? `<w:r>${rPr}${brk}</w:r>` : "";
|
|
917
|
-
|
|
1084
|
+
const lineBreak = run.columnBreak ? "<w:br w:type=\"column\"/>" : "<w:br/>";
|
|
1085
|
+
return `<w:r>${rPr}${run.text.split(" ").map((piece) => piece.split("\n").map((line) => line === "" ? "" : `<w:t xml:space="preserve">${escapeXml(line)}</w:t>`).join(lineBreak)).join("<w:tab/>")}${brk}</w:r>`;
|
|
918
1086
|
}
|
|
919
1087
|
function rPrXml(r, state) {
|
|
920
1088
|
const out = [];
|
|
@@ -925,6 +1093,15 @@ function rPrXml(r, state) {
|
|
|
925
1093
|
if (states("italic")) out.push(toggle("w:i", r.italic), toggle("w:iCs", r.italic));
|
|
926
1094
|
if (states("strike")) out.push(toggle("w:strike", r.strike));
|
|
927
1095
|
if (states("colorHex")) out.push(`<w:color w:val="${r.colorHex}"/>`);
|
|
1096
|
+
if (r.letterSpacingPt !== void 0 && r.letterSpacingPt !== 0) {
|
|
1097
|
+
const twips = Math.round(r.letterSpacingPt * 20);
|
|
1098
|
+
if (twips !== 0) out.push(`<w:spacing w:val="${String(twips)}"/>`);
|
|
1099
|
+
}
|
|
1100
|
+
if (r.widthScale !== void 0) {
|
|
1101
|
+
const percent = Math.min(600, Math.max(1, Math.round(r.widthScale * 100)));
|
|
1102
|
+
if (percent !== 100) out.push(`<w:w w:val="${String(percent)}"/>`);
|
|
1103
|
+
}
|
|
1104
|
+
if (r.kerningMinPt !== void 0 && states("kerningMinPt")) out.push(`<w:kern w:val="${Math.round(r.kerningMinPt * 2)}"/>`);
|
|
928
1105
|
if (states("fontSizePt")) {
|
|
929
1106
|
const half = Math.round(r.fontSizePt * 2);
|
|
930
1107
|
out.push(`<w:sz w:val="${half}"/>`, `<w:szCs w:val="${half}"/>`);
|
|
@@ -934,6 +1111,7 @@ function rPrXml(r, state) {
|
|
|
934
1111
|
if (states("verticalAlign")) out.push(`<w:vertAlign w:val="${r.verticalAlign}"/>`);
|
|
935
1112
|
if (states("rtl")) out.push(toggle("w:rtl", r.rtl));
|
|
936
1113
|
if (r.lang !== void 0) out.push(`<w:lang w:val="${escapeAttr(r.lang)}"/>`);
|
|
1114
|
+
if (r.ligatures !== void 0 && states("ligatures")) out.push(`<w14:ligatures w14:val="${r.ligatures}"/>`);
|
|
937
1115
|
return out.length > 0 ? `<w:rPr>${out.join("")}</w:rPr>` : "";
|
|
938
1116
|
}
|
|
939
1117
|
function underlineXml(r) {
|
|
@@ -945,10 +1123,14 @@ function runShdXml(fill) {
|
|
|
945
1123
|
}
|
|
946
1124
|
function pPrBody(p) {
|
|
947
1125
|
const out = [];
|
|
1126
|
+
if (p.keepNext) out.push("<w:keepNext/>");
|
|
1127
|
+
if (p.keepLines) out.push("<w:keepLines/>");
|
|
948
1128
|
if (p.pageBreakBefore) out.push("<w:pageBreakBefore/>");
|
|
1129
|
+
if (p.widowControl === false) out.push("<w:widowControl w:val=\"0\"/>");
|
|
949
1130
|
if (p.numbering) out.push(`<w:numPr><w:ilvl w:val="${p.numbering.ilvl}"/><w:numId w:val="${escapeAttr(p.numbering.numId)}"/></w:numPr>`);
|
|
950
1131
|
const pBdr = bordersXml("w:pBdr", p.borders);
|
|
951
1132
|
if (pBdr) out.push(pBdr);
|
|
1133
|
+
if (p.shading) out.push(`<w:shd w:val="clear" w:color="auto" w:fill="${p.shading.colorHex}"/>`);
|
|
952
1134
|
const tabs = p.tabs ?? [];
|
|
953
1135
|
if (tabs.length > 0) {
|
|
954
1136
|
const stops = tabs.map((t) => {
|
|
@@ -1005,10 +1187,7 @@ function spacingXml(p) {
|
|
|
1005
1187
|
const attrs = [];
|
|
1006
1188
|
if (stated(p.spacingBefore) && p.spacingBefore !== DEFAULT_PARA.spacingBefore) attrs.push(`w:before="${twips(p.spacingBefore)}"`);
|
|
1007
1189
|
if (stated(p.spacingAfter) && p.spacingAfter !== DEFAULT_PARA.spacingAfter) attrs.push(`w:after="${twips(p.spacingAfter)}"`);
|
|
1008
|
-
if ((p.spacingLineRule !== DEFAULT_PARA.spacingLineRule || p.spacingLine !== DEFAULT_PARA.spacingLine) && p.spacingLine > 0) {
|
|
1009
|
-
const lineVal = p.spacingLineRule === "auto" ? Math.round(p.spacingLine * 12) : twips(p.spacingLine);
|
|
1010
|
-
attrs.push(`w:line="${lineVal}"`, `w:lineRule="${p.spacingLineRule}"`);
|
|
1011
|
-
}
|
|
1190
|
+
if ((p.spacingLineRule !== DEFAULT_PARA.spacingLineRule || p.spacingLine !== DEFAULT_PARA.spacingLine) && p.spacingLine > 0) attrs.push(`w:line="${twips(p.spacingLine)}"`, `w:lineRule="${p.spacingLineRule}"`);
|
|
1012
1191
|
return attrs.length > 0 ? `<w:spacing ${attrs.join(" ")}/>` : "";
|
|
1013
1192
|
}
|
|
1014
1193
|
function rFontsXml(fonts, state) {
|
|
@@ -1018,6 +1197,7 @@ function rFontsXml(fonts, state) {
|
|
|
1018
1197
|
const known = state?.faceFamilies?.get(name);
|
|
1019
1198
|
if (known === void 0) return name;
|
|
1020
1199
|
state?.familiesUsed.set(known.family, known);
|
|
1200
|
+
if (state?.faceOutlines?.has(name) === true) state.facesUsed.add(name);
|
|
1021
1201
|
return known.family;
|
|
1022
1202
|
};
|
|
1023
1203
|
const attrs = [];
|
|
@@ -1104,8 +1284,14 @@ function sectPrXml(s, hf) {
|
|
|
1104
1284
|
const footer = m.footer !== void 0 ? ` w:footer="${twips(m.footer)}"` : "";
|
|
1105
1285
|
parts.push(`<w:pgMar w:top="${twips(m.top)}" w:right="${twips(m.right)}" w:bottom="${twips(m.bottom)}" w:left="${twips(m.left)}"${header}${footer}/>`);
|
|
1106
1286
|
}
|
|
1287
|
+
if (s.pageNumberFormat !== void 0 || s.pageNumberStart !== void 0) {
|
|
1288
|
+
const fmt = s.pageNumberFormat !== void 0 ? ` w:fmt="${s.pageNumberFormat}"` : "";
|
|
1289
|
+
const start = s.pageNumberStart !== void 0 ? ` w:start="${String(s.pageNumberStart)}"` : "";
|
|
1290
|
+
parts.push(`<w:pgNumType${fmt}${start}/>`);
|
|
1291
|
+
}
|
|
1107
1292
|
if (s.columns) parts.push(colsXml(s.columns));
|
|
1108
1293
|
if (s.titlePg) parts.push("<w:titlePg/>");
|
|
1294
|
+
if (s.textDirection) parts.push(`<w:textDirection w:val="${s.textDirection}"/>`);
|
|
1109
1295
|
if (parts.length === 0) return "";
|
|
1110
1296
|
return `<w:sectPr>${parts.join("")}</w:sectPr>`;
|
|
1111
1297
|
}
|
|
@@ -104,7 +104,9 @@ export type DrawingContent = {
|
|
|
104
104
|
* ECMA-376 Part 3 (Markup Compatibility) — resolve an `<mc:AlternateContent>` to
|
|
105
105
|
* the children of the first `<mc:Choice>` whose `Requires` lists only namespaces
|
|
106
106
|
* we understand, else the `<mc:Fallback>` children, else nothing. (`Requires`
|
|
107
|
-
* holds space-separated namespace prefixes as declared in the document.)
|
|
107
|
+
* holds space-separated namespace prefixes as declared in the document.) The
|
|
108
|
+
* elements are known by their local names, whatever prefix the producer bound
|
|
109
|
+
* the namespace to: Word 2008 for Mac wrote `ve:`.
|
|
108
110
|
*
|
|
109
111
|
* @param altContent The `mc:AlternateContent` node.
|
|
110
112
|
* @returns The chosen branch's children.
|
|
@@ -126,12 +128,12 @@ export declare function expandMcChildren(children: ReadonlyArray<PoNode>): Array
|
|
|
126
128
|
* SmartArt diagram, or — falling through — an embedded picture from
|
|
127
129
|
* `a:blip @r:embed`.
|
|
128
130
|
*
|
|
129
|
-
* @param
|
|
131
|
+
* @param drawingNode The `w:drawing` node.
|
|
130
132
|
* @param resolveColor Resolver for theme/scheme colours used by shape fills/lines.
|
|
131
133
|
* @param parseBody Optional body parser for a shape's text box (omitted ⇒ no text).
|
|
132
134
|
* @returns The parsed content, or `null` when no anchor / recognizable graphic is found.
|
|
133
135
|
*/
|
|
134
|
-
export declare function parseDrawing(
|
|
136
|
+
export declare function parseDrawing(drawingNode: PoNode, resolveColor: ColorResolver, parseBody?: ParseBody, resolveImage?: (relId: string) => ResourceId | undefined, resolveChartPart?: (relId: string) => string | undefined, themeLineWidths?: ReadonlyArray<number>, themeStyles?: ThemeStyles, parseDrawingText?: ParseDrawingText): DrawingContent | null;
|
|
135
137
|
/**
|
|
136
138
|
* Parse a legacy `<w:pict>`/`<w:object>` VML picture (ISO/IEC 29500-1 §14, VML
|
|
137
139
|
* transitional) into an `image` {@link DrawingContent}. Modern files use
|
|
@@ -22,8 +22,6 @@ var UNDERSTOOD_NS = new Set([
|
|
|
22
22
|
]);
|
|
23
23
|
function parseFloatAnchor(anchor) {
|
|
24
24
|
if (!poIs(anchor, "wp:anchor")) return void 0;
|
|
25
|
-
const behindRaw = poAttr(anchor, "behindDoc");
|
|
26
|
-
const behind = behindRaw === "1" || behindRaw === "true";
|
|
27
25
|
let wrap = "none";
|
|
28
26
|
let wrapSide;
|
|
29
27
|
for (const child of poChildren(anchor)) {
|
|
@@ -35,6 +33,8 @@ function parseFloatAnchor(anchor) {
|
|
|
35
33
|
const side = poAttr(child, "wrapText");
|
|
36
34
|
wrapSide = side === "left" || side === "right" || side === "largest" ? side : "bothSides";
|
|
37
35
|
}
|
|
36
|
+
const behindRaw = poAttr(anchor, "behindDoc");
|
|
37
|
+
const behind = (behindRaw === "1" || behindRaw === "true") && wrap === "none";
|
|
38
38
|
const zOrder = poIntAttr(anchor, "relativeHeight");
|
|
39
39
|
const posH = parseAnchorPos(anchor, "wp:positionH", [
|
|
40
40
|
"margin",
|
|
@@ -163,18 +163,20 @@ function parseAnchorPos(anchor, tag, allowed) {
|
|
|
163
163
|
* ECMA-376 Part 3 (Markup Compatibility) — resolve an `<mc:AlternateContent>` to
|
|
164
164
|
* the children of the first `<mc:Choice>` whose `Requires` lists only namespaces
|
|
165
165
|
* we understand, else the `<mc:Fallback>` children, else nothing. (`Requires`
|
|
166
|
-
* holds space-separated namespace prefixes as declared in the document.)
|
|
166
|
+
* holds space-separated namespace prefixes as declared in the document.) The
|
|
167
|
+
* elements are known by their local names, whatever prefix the producer bound
|
|
168
|
+
* the namespace to: Word 2008 for Mac wrote `ve:`.
|
|
167
169
|
*
|
|
168
170
|
* @param altContent The `mc:AlternateContent` node.
|
|
169
171
|
* @returns The chosen branch's children.
|
|
170
172
|
*/
|
|
171
173
|
function resolveMc(altContent) {
|
|
172
174
|
for (const choice of poChildren(altContent)) {
|
|
173
|
-
if (!
|
|
175
|
+
if (!poIsLocal(choice, "Choice")) continue;
|
|
174
176
|
const requires = (poAttr(choice, "Requires") ?? "").split(/\s+/).filter(Boolean);
|
|
175
177
|
if (requires.length > 0 && requires.every((r) => UNDERSTOOD_NS.has(r))) return poChildren(choice);
|
|
176
178
|
}
|
|
177
|
-
const fallback = poChildren(altContent).find((c) =>
|
|
179
|
+
const fallback = poChildren(altContent).find((c) => poIsLocal(c, "Fallback"));
|
|
178
180
|
return fallback ? poChildren(fallback) : [];
|
|
179
181
|
}
|
|
180
182
|
/**
|
|
@@ -188,22 +190,55 @@ function resolveMc(altContent) {
|
|
|
188
190
|
*/
|
|
189
191
|
function expandMcChildren(children) {
|
|
190
192
|
const out = [];
|
|
191
|
-
for (const c of children) if (
|
|
193
|
+
for (const c of children) if (poIsLocal(c, "AlternateContent")) out.push(...resolveMc(c));
|
|
192
194
|
else out.push(c);
|
|
193
195
|
return out;
|
|
194
196
|
}
|
|
195
197
|
/**
|
|
198
|
+
* The node with every markup-compatibility block beneath it resolved (see
|
|
199
|
+
* {@link resolveMc}), so that a search through its descendants finds only
|
|
200
|
+
* the content a reader like this one is to take. Bug51170.docx's header offers
|
|
201
|
+
* its logo twice — a PDF for Word for Mac as the Choice, a PNG as the Fallback
|
|
202
|
+
* — and the first `a:blip` in document order was the PDF, which is not a
|
|
203
|
+
* picture a page can show: the logo was left out.
|
|
204
|
+
*
|
|
205
|
+
* @param node The node to resolve.
|
|
206
|
+
* @returns The node itself where nothing beneath it needed resolving.
|
|
207
|
+
*/
|
|
208
|
+
function resolveMcTree(node) {
|
|
209
|
+
const tag = poTag(node);
|
|
210
|
+
const children = poChildren(node);
|
|
211
|
+
if (tag === void 0 || children.length === 0) return node;
|
|
212
|
+
let changed = false;
|
|
213
|
+
const resolved = [];
|
|
214
|
+
for (const child of children) {
|
|
215
|
+
if (poIsLocal(child, "AlternateContent")) {
|
|
216
|
+
changed = true;
|
|
217
|
+
for (const chosen of resolveMc(child)) resolved.push(resolveMcTree(chosen));
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
const inner = resolveMcTree(child);
|
|
221
|
+
if (inner !== child) changed = true;
|
|
222
|
+
resolved.push(inner);
|
|
223
|
+
}
|
|
224
|
+
return changed ? {
|
|
225
|
+
...node,
|
|
226
|
+
[tag]: resolved
|
|
227
|
+
} : node;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
196
230
|
* Parse a `<w:drawing>` (ECMA-376 Part 1 §20) into a {@link DrawingContent}. The
|
|
197
231
|
* `a:graphicData` `@uri` selects the branch: a `wps:wsp` shape, a chart, a
|
|
198
232
|
* SmartArt diagram, or — falling through — an embedded picture from
|
|
199
233
|
* `a:blip @r:embed`.
|
|
200
234
|
*
|
|
201
|
-
* @param
|
|
235
|
+
* @param drawingNode The `w:drawing` node.
|
|
202
236
|
* @param resolveColor Resolver for theme/scheme colours used by shape fills/lines.
|
|
203
237
|
* @param parseBody Optional body parser for a shape's text box (omitted ⇒ no text).
|
|
204
238
|
* @returns The parsed content, or `null` when no anchor / recognizable graphic is found.
|
|
205
239
|
*/
|
|
206
|
-
function parseDrawing(
|
|
240
|
+
function parseDrawing(drawingNode, resolveColor, parseBody, resolveImage, resolveChartPart, themeLineWidths, themeStyles, parseDrawingText) {
|
|
241
|
+
const drawing = resolveMcTree(drawingNode);
|
|
207
242
|
const anchor = poChildren(drawing).find((c) => poIs(c, "wp:inline")) ?? poChildren(drawing).find((c) => poIs(c, "wp:anchor"));
|
|
208
243
|
if (!anchor) return null;
|
|
209
244
|
const extent = poFindDescendant(anchor, "wp:extent");
|
|
@@ -1083,6 +1118,12 @@ function vmlFloat(style, zIndexAttr, shape) {
|
|
|
1083
1118
|
const zIndex = prop("z-index") ?? zIndexAttr;
|
|
1084
1119
|
return {
|
|
1085
1120
|
wrap: vmlWrap(shape),
|
|
1121
|
+
wrapDist: {
|
|
1122
|
+
topPt: pt(prop("mso-wrap-distance-top") ?? 0),
|
|
1123
|
+
bottomPt: pt(prop("mso-wrap-distance-bottom") ?? 0),
|
|
1124
|
+
leftPt: pt(prop("mso-wrap-distance-left") ?? 9),
|
|
1125
|
+
rightPt: pt(prop("mso-wrap-distance-right") ?? 9)
|
|
1126
|
+
},
|
|
1086
1127
|
...poAttrLocal(shape, "allowincell") === "f" ? { inCell: false } : {},
|
|
1087
1128
|
...zIndex !== void 0 && zIndex < 0 ? { behind: true } : {},
|
|
1088
1129
|
...zIndex !== void 0 ? { zOrder: Math.abs(zIndex) } : {},
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { FaceFamily, FaceOutlines } from '../core/ir/flow.js';
|
|
2
|
+
import { Loss } from '../core/ir/index.js';
|
|
3
|
+
import { OpcPart, Relationship } from '../core/opc/index.js';
|
|
4
|
+
/** §17.8.1 — the content type of an obfuscated embedded font. */
|
|
5
|
+
export declare const OBFUSCATED_FONT_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.obfuscatedFont";
|
|
6
|
+
/** The faces written into a package, and how the font table reaches them. */
|
|
7
|
+
export interface EmbeddedFaces {
|
|
8
|
+
/** Family → the `w:embed*` elements its `w:font` carries (§17.8.3.3–6). */
|
|
9
|
+
readonly elements: ReadonlyMap<string, string>;
|
|
10
|
+
/** The obfuscated font parts, under `word/fonts/`. */
|
|
11
|
+
readonly parts: ReadonlyArray<OpcPart>;
|
|
12
|
+
/** The font table's relationships to them. */
|
|
13
|
+
readonly relationships: ReadonlyArray<Relationship>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Build the embedded fonts for the faces the runs name (§17.8.1).
|
|
17
|
+
*
|
|
18
|
+
* The faces are gathered by family and by slot — the style each face IS. Two
|
|
19
|
+
* faces in one slot (a SemiBold and a Bold, both written bold) become one font:
|
|
20
|
+
* the face with the most characters, and the other's characters it lacks.
|
|
21
|
+
*
|
|
22
|
+
* A face its licence keeps out is reported, not written.
|
|
23
|
+
*
|
|
24
|
+
* @param used The run font names the document's runs carry.
|
|
25
|
+
* @param outlines Run font name → the face's outlines.
|
|
26
|
+
* @param families Run font name → the family a word processor knows it by.
|
|
27
|
+
* @param losses Appended to for every face left out.
|
|
28
|
+
* @returns The fonts to write and the font table's references to them.
|
|
29
|
+
*/
|
|
30
|
+
export declare function embedFaces(used: Iterable<string>, outlines: ReadonlyMap<string, FaceOutlines>, families: ReadonlyMap<string, FaceFamily>, losses: Array<Loss>): EmbeddedFaces;
|