@json-to-office/core-docx 1.0.0 → 1.3.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/dist/components/visual.d.ts +6 -6
- package/dist/components/visual.d.ts.map +1 -1
- package/dist/core/desugarExternals.d.ts.map +1 -1
- package/dist/core/flattenVisuals.d.ts.map +1 -1
- package/dist/core/imageResources.d.ts.map +1 -1
- package/dist/core/prerasterizeVisuals.d.ts +10 -4
- package/dist/core/prerasterizeVisuals.d.ts.map +1 -1
- package/dist/index.js +1222 -98
- package/dist/index.js.map +1 -1
- package/dist/ir/compiler.d.ts.map +1 -1
- package/dist/ir/features.d.ts +1 -1
- package/dist/ir/features.d.ts.map +1 -1
- package/dist/ir/nativeVisual.d.ts +79 -0
- package/dist/ir/nativeVisual.d.ts.map +1 -0
- package/dist/ir/types.d.ts +155 -1
- package/dist/ir/types.d.ts.map +1 -1
- package/dist/ir/units.d.ts +12 -0
- package/dist/ir/units.d.ts.map +1 -1
- package/dist/ir/validation.d.ts.map +1 -1
- package/dist/plugin/example/index.js +1215 -96
- package/dist/plugin/example/index.js.map +1 -1
- package/dist/renderers/docxjs/emit.d.ts.map +1 -1
- package/dist/renderers/docxjs/index.d.ts.map +1 -1
- package/dist/renderers/office-open/chartParts.d.ts +28 -0
- package/dist/renderers/office-open/chartParts.d.ts.map +1 -0
- package/dist/renderers/office-open/emit.d.ts +47 -6
- package/dist/renderers/office-open/emit.d.ts.map +1 -1
- package/dist/renderers/office-open/index.d.ts +2 -2
- package/dist/renderers/office-open/index.d.ts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/chartWorkbook.d.ts +19 -0
- package/dist/utils/chartWorkbook.d.ts.map +1 -0
- package/dist/utils/packageDocument.d.ts +2 -0
- package/dist/utils/packageDocument.d.ts.map +1 -1
- package/package.json +3 -3
|
@@ -1977,6 +1977,12 @@ function pointsToEighthPoints(points) {
|
|
|
1977
1977
|
function inchesToTwips(inches) {
|
|
1978
1978
|
return Math.round(inches * TWIPS_PER_INCH);
|
|
1979
1979
|
}
|
|
1980
|
+
function inchesToEmu(inches) {
|
|
1981
|
+
return Math.round(inches * EMU_PER_INCH);
|
|
1982
|
+
}
|
|
1983
|
+
function pointsToEmu(points) {
|
|
1984
|
+
return Math.round(points * EMU_PER_INCH / 72);
|
|
1985
|
+
}
|
|
1980
1986
|
function twipsToEmu(twips) {
|
|
1981
1987
|
return Math.round(twips * EMU_PER_TWIP);
|
|
1982
1988
|
}
|
|
@@ -2179,6 +2185,14 @@ function inlineChildren(children, resources = /* @__PURE__ */ new Map()) {
|
|
|
2179
2185
|
case "shape":
|
|
2180
2186
|
out.push(emitShape(child, resources));
|
|
2181
2187
|
break;
|
|
2188
|
+
case "drawingGroup":
|
|
2189
|
+
throw new Error(
|
|
2190
|
+
"the docxjs renderer has no emitter for a drawing group; this document should have been refused by the capability check"
|
|
2191
|
+
);
|
|
2192
|
+
case "chart":
|
|
2193
|
+
throw new Error(
|
|
2194
|
+
"the docxjs renderer has no emitter for a chart; this document should have been refused by the capability check"
|
|
2195
|
+
);
|
|
2182
2196
|
case "noteReference":
|
|
2183
2197
|
out.push(
|
|
2184
2198
|
child.noteKind === "endnote" ? new EndnoteReferenceRun(child.id) : new FootnoteReferenceRun(child.id)
|
|
@@ -2280,7 +2294,7 @@ function paragraphOptions(formatting) {
|
|
|
2280
2294
|
const options = {};
|
|
2281
2295
|
if (!formatting) return options;
|
|
2282
2296
|
if (formatting.alignment) {
|
|
2283
|
-
options.alignment =
|
|
2297
|
+
options.alignment = ALIGNMENT2[formatting.alignment];
|
|
2284
2298
|
}
|
|
2285
2299
|
if (formatting.spacing) {
|
|
2286
2300
|
const spacing = {};
|
|
@@ -2514,11 +2528,11 @@ function emitBorder(border3) {
|
|
|
2514
2528
|
color: border3.color?.hex ?? "000000"
|
|
2515
2529
|
};
|
|
2516
2530
|
}
|
|
2517
|
-
var
|
|
2531
|
+
var ALIGNMENT2, PAGE_FIELD, WRAP_TYPE, VERTICAL_ALIGN, TABLE_ANCHOR, HORIZONTAL_POSITION, VERTICAL_POSITION, BORDER_STYLE;
|
|
2518
2532
|
var init_emit = __esm({
|
|
2519
2533
|
"src/renderers/docxjs/emit.ts"() {
|
|
2520
2534
|
"use strict";
|
|
2521
|
-
|
|
2535
|
+
ALIGNMENT2 = {
|
|
2522
2536
|
left: AlignmentType.LEFT,
|
|
2523
2537
|
center: AlignmentType.CENTER,
|
|
2524
2538
|
right: AlignmentType.RIGHT,
|
|
@@ -2641,7 +2655,7 @@ function paragraphProperties(formatting) {
|
|
|
2641
2655
|
out.spacing = spacing;
|
|
2642
2656
|
}
|
|
2643
2657
|
if (formatting.alignment !== void 0) {
|
|
2644
|
-
out.alignment =
|
|
2658
|
+
out.alignment = ALIGNMENT2[formatting.alignment] ?? formatting.alignment;
|
|
2645
2659
|
}
|
|
2646
2660
|
if (formatting.keepNext !== void 0) out.keepNext = formatting.keepNext;
|
|
2647
2661
|
if (formatting.keepLines !== void 0) out.keepLines = formatting.keepLines;
|
|
@@ -2770,6 +2784,26 @@ var init_features = __esm({
|
|
|
2770
2784
|
"text-frames",
|
|
2771
2785
|
/** Native shape text boxes. */
|
|
2772
2786
|
"text-boxes",
|
|
2787
|
+
/**
|
|
2788
|
+
* A DrawingML group: shapes, text boxes and pictures sharing one child
|
|
2789
|
+
* coordinate space, drawn as a single anchored or inline object.
|
|
2790
|
+
*
|
|
2791
|
+
* Separate from `text-boxes` because a lone `wps:wsp` run is a far smaller
|
|
2792
|
+
* ask than `wpg:wgp` with child transforms, preset geometry and grouped
|
|
2793
|
+
* pictures — a backend can plausibly have the first and not the second.
|
|
2794
|
+
*/
|
|
2795
|
+
"drawing-groups",
|
|
2796
|
+
/**
|
|
2797
|
+
* A native chart part: a `c:chartSpace` with its own embedded workbook.
|
|
2798
|
+
*
|
|
2799
|
+
* Separate from `images` because a chart is not a picture with extra data —
|
|
2800
|
+
* it is its own part, its own relationship and its own workbook, and a
|
|
2801
|
+
* backend either writes all three or writes none. docx.js has no chart
|
|
2802
|
+
* primitive at all, which is what turns a `chart` component sent to that
|
|
2803
|
+
* backend into a named capability error rather than a document missing a
|
|
2804
|
+
* figure.
|
|
2805
|
+
*/
|
|
2806
|
+
"charts",
|
|
2773
2807
|
/** A table-of-contents field. */
|
|
2774
2808
|
"toc",
|
|
2775
2809
|
/** TOC entries baked in so an unrefreshed reader still shows them. */
|
|
@@ -3016,7 +3050,7 @@ function numberingConfig(numbering) {
|
|
|
3016
3050
|
level: level.level,
|
|
3017
3051
|
format: level.format,
|
|
3018
3052
|
text: level.text,
|
|
3019
|
-
alignment: level.alignment ?
|
|
3053
|
+
alignment: level.alignment ? ALIGNMENT2[level.alignment] : void 0,
|
|
3020
3054
|
...level.suffix ? { suffix: level.suffix } : {},
|
|
3021
3055
|
style: {
|
|
3022
3056
|
...level.indent ? {
|
|
@@ -3234,7 +3268,16 @@ var init_docxjs = __esm({
|
|
|
3234
3268
|
"cached-fields",
|
|
3235
3269
|
"shading",
|
|
3236
3270
|
"borders",
|
|
3237
|
-
"rtl"
|
|
3271
|
+
"rtl",
|
|
3272
|
+
// `drawing-groups` is the one exclusion that is a real backend gap rather
|
|
3273
|
+
// than a slice boundary: docx.js has no `wpg:wgp`. Leaving it out is what
|
|
3274
|
+
// turns a native `visual` sent to this backend into a named capability
|
|
3275
|
+
// error instead of a document with the graphic missing.
|
|
3276
|
+
"drawing-groups",
|
|
3277
|
+
// `charts` is the second real backend gap: docx.js has no chart primitive at
|
|
3278
|
+
// all, so a `chart` component sent here becomes a named capability error
|
|
3279
|
+
// rather than a document missing a figure.
|
|
3280
|
+
"charts"
|
|
3238
3281
|
]);
|
|
3239
3282
|
DOCXJS_CAPABILITIES = new Set(
|
|
3240
3283
|
[...ALL_DOCX_FEATURES].filter((feature) => !NOT_YET_EMITTED.has(feature))
|
|
@@ -3242,11 +3285,105 @@ var init_docxjs = __esm({
|
|
|
3242
3285
|
}
|
|
3243
3286
|
});
|
|
3244
3287
|
|
|
3288
|
+
// src/utils/chartWorkbook.ts
|
|
3289
|
+
import AdmZip3 from "adm-zip";
|
|
3290
|
+
import { chartWorkbookParts } from "@json-to-office/shared/rendering";
|
|
3291
|
+
import {
|
|
3292
|
+
CHART_WORKBOOK_SHEET_NAME,
|
|
3293
|
+
categoryReference,
|
|
3294
|
+
columnLetter,
|
|
3295
|
+
seriesNameReference,
|
|
3296
|
+
seriesValueReference
|
|
3297
|
+
} from "@json-to-office/shared/rendering";
|
|
3298
|
+
function buildChartWorkbook(series) {
|
|
3299
|
+
const zip = new AdmZip3();
|
|
3300
|
+
for (const [name, content] of chartWorkbookParts(series)) {
|
|
3301
|
+
zip.addFile(name, Buffer.from(content, "utf8"));
|
|
3302
|
+
}
|
|
3303
|
+
for (const entry of zip.getEntries()) {
|
|
3304
|
+
entry.header.timeval = WORKBOOK_TIMESTAMP;
|
|
3305
|
+
}
|
|
3306
|
+
return new Uint8Array(zip.toBuffer());
|
|
3307
|
+
}
|
|
3308
|
+
var WORKBOOK_TIMESTAMP;
|
|
3309
|
+
var init_chartWorkbook = __esm({
|
|
3310
|
+
"src/utils/chartWorkbook.ts"() {
|
|
3311
|
+
"use strict";
|
|
3312
|
+
init_packageDocument();
|
|
3313
|
+
WORKBOOK_TIMESTAMP = toDosTime(DEFAULT_GENERATION_DATE);
|
|
3314
|
+
}
|
|
3315
|
+
});
|
|
3316
|
+
|
|
3317
|
+
// src/renderers/office-open/chartParts.ts
|
|
3318
|
+
import {
|
|
3319
|
+
CHART_WORKBOOK_CONTENT_TYPE,
|
|
3320
|
+
chartWorkbookRelsXml,
|
|
3321
|
+
matchChartParts,
|
|
3322
|
+
spliceChartXml
|
|
3323
|
+
} from "@json-to-office/shared/rendering";
|
|
3324
|
+
function spliceInput(chart) {
|
|
3325
|
+
return {
|
|
3326
|
+
chartType: chart.chartType,
|
|
3327
|
+
series: chart.series,
|
|
3328
|
+
colors: chart.colors,
|
|
3329
|
+
...chart.legendPosition ? { legendPosition: chart.legendPosition } : {},
|
|
3330
|
+
...chart.categoryAxisTitle ? { categoryAxis: { title: chart.categoryAxisTitle } } : {},
|
|
3331
|
+
...chart.valueAxisTitle ? { valueAxis: { title: chart.valueAxisTitle } } : {}
|
|
3332
|
+
};
|
|
3333
|
+
}
|
|
3334
|
+
function declareWorkbookContentType(zip) {
|
|
3335
|
+
const entry = zip.getEntry("[Content_Types].xml");
|
|
3336
|
+
if (!entry) return;
|
|
3337
|
+
const xml = entry.getData().toString("utf8");
|
|
3338
|
+
if (xml.includes(`Extension="xlsx"`)) return;
|
|
3339
|
+
zip.updateFile(
|
|
3340
|
+
entry,
|
|
3341
|
+
Buffer.from(
|
|
3342
|
+
xml.replace(
|
|
3343
|
+
'<Default Extension="xml"',
|
|
3344
|
+
`<Default Extension="xlsx" ContentType="${CHART_WORKBOOK_CONTENT_TYPE}"/><Default Extension="xml"`
|
|
3345
|
+
),
|
|
3346
|
+
"utf8"
|
|
3347
|
+
)
|
|
3348
|
+
);
|
|
3349
|
+
}
|
|
3350
|
+
function spliceChartParts(zip, charts) {
|
|
3351
|
+
if (charts.length === 0) return;
|
|
3352
|
+
const parts = zip.getEntries().map((entry) => entry.entryName).map((name) => name.match(/^word\/charts\/chart(\d+)\.xml$/)).filter((match) => match !== null).map(
|
|
3353
|
+
(match) => [
|
|
3354
|
+
Number(match[1]),
|
|
3355
|
+
zip.getEntry(match[0]).getData().toString("utf8")
|
|
3356
|
+
]
|
|
3357
|
+
);
|
|
3358
|
+
for (const { ordinal, xml, chart } of matchChartParts(parts, charts)) {
|
|
3359
|
+
const workbookName = `chart${ordinal}.xlsx`;
|
|
3360
|
+
zip.updateFile(
|
|
3361
|
+
zip.getEntry(`word/charts/chart${ordinal}.xml`),
|
|
3362
|
+
Buffer.from(spliceChartXml(xml, spliceInput(chart)), "utf8")
|
|
3363
|
+
);
|
|
3364
|
+
zip.addFile(
|
|
3365
|
+
`word/embeddings/${workbookName}`,
|
|
3366
|
+
Buffer.from(buildChartWorkbook(chart.series))
|
|
3367
|
+
);
|
|
3368
|
+
zip.addFile(
|
|
3369
|
+
`word/charts/_rels/chart${ordinal}.xml.rels`,
|
|
3370
|
+
Buffer.from(chartWorkbookRelsXml(workbookName), "utf8")
|
|
3371
|
+
);
|
|
3372
|
+
}
|
|
3373
|
+
declareWorkbookContentType(zip);
|
|
3374
|
+
}
|
|
3375
|
+
var init_chartParts = __esm({
|
|
3376
|
+
"src/renderers/office-open/chartParts.ts"() {
|
|
3377
|
+
"use strict";
|
|
3378
|
+
init_chartWorkbook();
|
|
3379
|
+
}
|
|
3380
|
+
});
|
|
3381
|
+
|
|
3245
3382
|
// src/renderers/office-open/emit.ts
|
|
3246
3383
|
import { assertNever as assertNever2 } from "@json-to-office/shared/rendering";
|
|
3247
3384
|
function emptyContext() {
|
|
3248
3385
|
let next = 1;
|
|
3249
|
-
return { pictures: /* @__PURE__ */ new Map(), nextDrawingId: () => next
|
|
3386
|
+
return { pictures: /* @__PURE__ */ new Map(), nextDrawingId: () => next++, charts: [] };
|
|
3250
3387
|
}
|
|
3251
3388
|
function simpleField(instruction, cachedText) {
|
|
3252
3389
|
return {
|
|
@@ -3345,15 +3482,15 @@ function inlineChildren2(children, ctx = emptyContext()) {
|
|
|
3345
3482
|
out.push({ bookmarkEnd: { id: child.id } });
|
|
3346
3483
|
break;
|
|
3347
3484
|
case "image": {
|
|
3348
|
-
const build = ctx.pictures.get(child.resourceId);
|
|
3349
|
-
if (!build) {
|
|
3350
|
-
throw new Error(
|
|
3351
|
-
`no image was prepared for resource "${child.resourceId}"`
|
|
3352
|
-
);
|
|
3353
|
-
}
|
|
3354
3485
|
const pending = breakOption();
|
|
3355
3486
|
if (pending.break) out.push(pending);
|
|
3356
|
-
out.push({ picture:
|
|
3487
|
+
out.push({ picture: pictureOptions(child, ctx) });
|
|
3488
|
+
break;
|
|
3489
|
+
}
|
|
3490
|
+
case "drawingGroup": {
|
|
3491
|
+
const pending = breakOption();
|
|
3492
|
+
if (pending.break) out.push(pending);
|
|
3493
|
+
out.push({ wpgGroup: drawingGroupOptions(child, ctx) });
|
|
3357
3494
|
break;
|
|
3358
3495
|
}
|
|
3359
3496
|
case "hyperlink":
|
|
@@ -3382,6 +3519,10 @@ function inlineChildren2(children, ctx = emptyContext()) {
|
|
|
3382
3519
|
case "shape":
|
|
3383
3520
|
out.push({ wpsShape: shapeOptions(child, ctx) });
|
|
3384
3521
|
break;
|
|
3522
|
+
case "chart":
|
|
3523
|
+
ctx.charts?.push(child);
|
|
3524
|
+
out.push({ chart: chartOptions(child, ctx) });
|
|
3525
|
+
break;
|
|
3385
3526
|
case "noteReference":
|
|
3386
3527
|
out.push(
|
|
3387
3528
|
child.noteKind === "endnote" ? { endnoteReference: child.id } : { footnoteReference: child.id }
|
|
@@ -3448,6 +3589,185 @@ function floatingOptions2(floating) {
|
|
|
3448
3589
|
zIndex: floating.zIndex
|
|
3449
3590
|
};
|
|
3450
3591
|
}
|
|
3592
|
+
function imageMedia(ctx, resourceId, placement) {
|
|
3593
|
+
const build = ctx.pictures.get(resourceId);
|
|
3594
|
+
if (!build) {
|
|
3595
|
+
throw new Error(`no image was prepared for resource "${resourceId}"`);
|
|
3596
|
+
}
|
|
3597
|
+
return build(placement);
|
|
3598
|
+
}
|
|
3599
|
+
function pictureOptions(image, ctx) {
|
|
3600
|
+
const media = imageMedia(ctx, image.resourceId, image);
|
|
3601
|
+
return {
|
|
3602
|
+
type: media.type,
|
|
3603
|
+
data: media.data,
|
|
3604
|
+
// No `fileName`: at run level the backend allocates one, and stating our
|
|
3605
|
+
// own would only fight it.
|
|
3606
|
+
...media.fallback ? { fallback: media.fallback } : {},
|
|
3607
|
+
// Raw numbers are EMUs in @office-open/docx. Passing pixels here makes a
|
|
3608
|
+
// normal image only a few hundred EMUs wide, effectively a dot.
|
|
3609
|
+
transformation: { width: image.widthEmu, height: image.heightEmu },
|
|
3610
|
+
// The id is stated rather than left to the backend's process-global
|
|
3611
|
+
// counter. `name` stays empty, which is what it was before and what the
|
|
3612
|
+
// backend falls back to.
|
|
3613
|
+
altText: { id: String(ctx.nextDrawingId()) },
|
|
3614
|
+
...image.floating ? { floating: floatingOptions2(image.floating) } : {}
|
|
3615
|
+
// No `description` or `title`: no DOCX this pipeline has produced carries
|
|
3616
|
+
// `wp:docPr` alt text, and the compiler warns so the gap is visible rather
|
|
3617
|
+
// than silent.
|
|
3618
|
+
};
|
|
3619
|
+
}
|
|
3620
|
+
function drawingGroupOptions(group, ctx) {
|
|
3621
|
+
const id = ctx.nextDrawingId();
|
|
3622
|
+
return {
|
|
3623
|
+
altText: {
|
|
3624
|
+
id: String(id),
|
|
3625
|
+
...group.altText ? { description: group.altText } : {}
|
|
3626
|
+
},
|
|
3627
|
+
transformation: { width: group.widthEmu, height: group.heightEmu },
|
|
3628
|
+
childOffset: { x: 0, y: 0 },
|
|
3629
|
+
childExtent: { cx: group.canvasWidthEmu, cy: group.canvasHeightEmu },
|
|
3630
|
+
children: group.children.map((child) => groupChild(child, ctx)),
|
|
3631
|
+
...group.floating ? { floating: floatingOptions2(group.floating) } : {}
|
|
3632
|
+
};
|
|
3633
|
+
}
|
|
3634
|
+
function chartOptions(chart, ctx) {
|
|
3635
|
+
const id = ctx.nextDrawingId();
|
|
3636
|
+
return {
|
|
3637
|
+
type: chart.chartType,
|
|
3638
|
+
categories: chart.series[0]?.labels ?? [],
|
|
3639
|
+
series: chart.series.map((entry, index) => ({
|
|
3640
|
+
name: entry.name ?? `Series ${index + 1}`,
|
|
3641
|
+
values: entry.values
|
|
3642
|
+
})),
|
|
3643
|
+
...chart.title && chart.showTitle !== false ? { title: chart.title } : {},
|
|
3644
|
+
...chart.showLegend !== void 0 ? { showLegend: chart.showLegend } : {},
|
|
3645
|
+
transformation: { width: chart.widthEmu, height: chart.heightEmu },
|
|
3646
|
+
altText: {
|
|
3647
|
+
id: String(id),
|
|
3648
|
+
...chart.altText ? { description: chart.altText } : {}
|
|
3649
|
+
},
|
|
3650
|
+
...chart.floating ? { floating: floatingOptions2(chart.floating) } : {}
|
|
3651
|
+
};
|
|
3652
|
+
}
|
|
3653
|
+
function groupChild(child, ctx) {
|
|
3654
|
+
return child.kind === "shape" ? groupShape(child, ctx) : groupPicture(child, ctx);
|
|
3655
|
+
}
|
|
3656
|
+
function groupShape(shape, ctx) {
|
|
3657
|
+
const id = ctx.nextDrawingId();
|
|
3658
|
+
return {
|
|
3659
|
+
type: "wps",
|
|
3660
|
+
transformation: childTransformation(shape.frame),
|
|
3661
|
+
data: {
|
|
3662
|
+
nonVisualProperties: {
|
|
3663
|
+
id,
|
|
3664
|
+
...shape.name ? { name: shape.name } : {},
|
|
3665
|
+
// `txBox="1"` is how Word tells a text box from a shape that happens
|
|
3666
|
+
// to hold text, and it changes how the object behaves on selection.
|
|
3667
|
+
...shape.isTextBox ? { textBox: "1" } : {}
|
|
3668
|
+
},
|
|
3669
|
+
presetGeometry: { preset: shape.geometry },
|
|
3670
|
+
...shape.fill ? { fill: drawingFill(shape.fill) } : {},
|
|
3671
|
+
...shape.outline ? { outline: drawingOutline(shape.outline) } : {},
|
|
3672
|
+
children: (shape.text?.paragraphs ?? []).map(
|
|
3673
|
+
(child) => paragraph(child, ctx)
|
|
3674
|
+
),
|
|
3675
|
+
...shape.text ? { bodyProperties: bodyProperties(shape.text) } : {}
|
|
3676
|
+
}
|
|
3677
|
+
};
|
|
3678
|
+
}
|
|
3679
|
+
function groupPicture(picture2, ctx) {
|
|
3680
|
+
const id = ctx.nextDrawingId();
|
|
3681
|
+
const media = imageMedia(ctx, picture2.resourceId, {
|
|
3682
|
+
widthEmu: picture2.frame.widthEmu,
|
|
3683
|
+
heightEmu: picture2.frame.heightEmu
|
|
3684
|
+
});
|
|
3685
|
+
return {
|
|
3686
|
+
type: media.type,
|
|
3687
|
+
data: media.data,
|
|
3688
|
+
fileName: media.fileName,
|
|
3689
|
+
...media.fallback ? {
|
|
3690
|
+
fallback: {
|
|
3691
|
+
...media.fallback,
|
|
3692
|
+
fileName: media.fallbackFileName
|
|
3693
|
+
}
|
|
3694
|
+
} : {},
|
|
3695
|
+
transformation: childTransformation(picture2.frame),
|
|
3696
|
+
nonVisualProperties: {
|
|
3697
|
+
id,
|
|
3698
|
+
...picture2.name ? { name: picture2.name } : {},
|
|
3699
|
+
...picture2.altText ? { description: picture2.altText } : {}
|
|
3700
|
+
},
|
|
3701
|
+
...picture2.crop ? { sourceRectangle: sourceRectangle(picture2.crop) } : {}
|
|
3702
|
+
};
|
|
3703
|
+
}
|
|
3704
|
+
function childTransformation(frame) {
|
|
3705
|
+
const flip = {
|
|
3706
|
+
...frame.flipHorizontal ? { horizontal: true } : {},
|
|
3707
|
+
...frame.flipVertical ? { vertical: true } : {}
|
|
3708
|
+
};
|
|
3709
|
+
return {
|
|
3710
|
+
offset: {
|
|
3711
|
+
emus: { x: frame.xEmu, y: frame.yEmu },
|
|
3712
|
+
pixels: {
|
|
3713
|
+
x: Math.round(emuToPixels(frame.xEmu)),
|
|
3714
|
+
y: Math.round(emuToPixels(frame.yEmu))
|
|
3715
|
+
}
|
|
3716
|
+
},
|
|
3717
|
+
emus: { x: frame.widthEmu, y: frame.heightEmu },
|
|
3718
|
+
pixels: {
|
|
3719
|
+
x: Math.round(emuToPixels(frame.widthEmu)),
|
|
3720
|
+
y: Math.round(emuToPixels(frame.heightEmu))
|
|
3721
|
+
},
|
|
3722
|
+
// Degrees: the backend multiplies by 60000 on the way into `@rot`.
|
|
3723
|
+
...frame.rotationDegrees !== void 0 ? { rotation: frame.rotationDegrees } : {},
|
|
3724
|
+
...Object.keys(flip).length > 0 ? { flip } : {}
|
|
3725
|
+
};
|
|
3726
|
+
}
|
|
3727
|
+
function drawingFill(fill) {
|
|
3728
|
+
if (fill.kind === "none") return { type: "none" };
|
|
3729
|
+
return {
|
|
3730
|
+
type: "solid",
|
|
3731
|
+
color: {
|
|
3732
|
+
type: "rgb",
|
|
3733
|
+
value: fill.color.hex,
|
|
3734
|
+
// Transparency becomes an alpha transform on the colour. DrawingML
|
|
3735
|
+
// states *opacity*, so the value is the complement of what the author
|
|
3736
|
+
// wrote; the backend scales the percentage into `a:alpha`'s thousandths
|
|
3737
|
+
// itself, so it must be handed a plain percentage here.
|
|
3738
|
+
...fill.transparencyPercent !== void 0 ? { transforms: { alpha: 100 - fill.transparencyPercent } } : {}
|
|
3739
|
+
}
|
|
3740
|
+
};
|
|
3741
|
+
}
|
|
3742
|
+
function drawingOutline(outline) {
|
|
3743
|
+
return {
|
|
3744
|
+
...outline.widthEmu !== void 0 ? { width: outline.widthEmu } : {},
|
|
3745
|
+
...outline.dash ? { dash: outline.dash } : {},
|
|
3746
|
+
...outline.color ? { type: "solidFill", color: { value: outline.color.hex } } : {}
|
|
3747
|
+
};
|
|
3748
|
+
}
|
|
3749
|
+
function bodyProperties(text) {
|
|
3750
|
+
const insets = text.insetsEmu;
|
|
3751
|
+
return {
|
|
3752
|
+
...text.anchor ? { anchor: BODY_ANCHOR[text.anchor] } : {},
|
|
3753
|
+
// `lIns`/`tIns`/`rIns`/`bIns` is the vocabulary `a:bodyPr` actually has;
|
|
3754
|
+
// the backend also accepts a `margins` object and folds it into the same
|
|
3755
|
+
// attributes.
|
|
3756
|
+
...insets?.left !== void 0 ? { lIns: insets.left } : {},
|
|
3757
|
+
...insets?.top !== void 0 ? { tIns: insets.top } : {},
|
|
3758
|
+
...insets?.right !== void 0 ? { rIns: insets.right } : {},
|
|
3759
|
+
...insets?.bottom !== void 0 ? { bIns: insets.bottom } : {}
|
|
3760
|
+
};
|
|
3761
|
+
}
|
|
3762
|
+
function sourceRectangle(crop) {
|
|
3763
|
+
const thousandths = (fraction) => Math.round(fraction * 1e5);
|
|
3764
|
+
return {
|
|
3765
|
+
...crop.left !== void 0 ? { left: thousandths(crop.left) } : {},
|
|
3766
|
+
...crop.top !== void 0 ? { top: thousandths(crop.top) } : {},
|
|
3767
|
+
...crop.right !== void 0 ? { right: thousandths(crop.right) } : {},
|
|
3768
|
+
...crop.bottom !== void 0 ? { bottom: thousandths(crop.bottom) } : {}
|
|
3769
|
+
};
|
|
3770
|
+
}
|
|
3451
3771
|
function shapeOptions(shape, ctx) {
|
|
3452
3772
|
const id = String(ctx.nextDrawingId());
|
|
3453
3773
|
return {
|
|
@@ -3465,23 +3785,8 @@ function shapeOptions(shape, ctx) {
|
|
|
3465
3785
|
color: { type: "rgb", value: shape.fill.hex }
|
|
3466
3786
|
}
|
|
3467
3787
|
} : {},
|
|
3468
|
-
...shape.outline ? {
|
|
3469
|
-
|
|
3470
|
-
...shape.outline.widthEmu !== void 0 ? { width: shape.outline.widthEmu } : {},
|
|
3471
|
-
fill: {
|
|
3472
|
-
type: "solid",
|
|
3473
|
-
color: { type: "rgb", value: shape.outline.color.hex }
|
|
3474
|
-
}
|
|
3475
|
-
}
|
|
3476
|
-
} : {},
|
|
3477
|
-
...shape.insetsEmu ? {
|
|
3478
|
-
bodyProperties: {
|
|
3479
|
-
...shape.insetsEmu.top !== void 0 ? { topInset: shape.insetsEmu.top } : {},
|
|
3480
|
-
...shape.insetsEmu.bottom !== void 0 ? { bottomInset: shape.insetsEmu.bottom } : {},
|
|
3481
|
-
...shape.insetsEmu.left !== void 0 ? { leftInset: shape.insetsEmu.left } : {},
|
|
3482
|
-
...shape.insetsEmu.right !== void 0 ? { rightInset: shape.insetsEmu.right } : {}
|
|
3483
|
-
}
|
|
3484
|
-
} : {},
|
|
3788
|
+
...shape.outline ? { outline: drawingOutline(shape.outline) } : {},
|
|
3789
|
+
...shape.insetsEmu ? { bodyProperties: bodyProperties({ insetsEmu: shape.insetsEmu }) } : {},
|
|
3485
3790
|
...shape.floating ? { floating: floatingOptions2(shape.floating) } : {}
|
|
3486
3791
|
};
|
|
3487
3792
|
}
|
|
@@ -3838,7 +4143,7 @@ function numberingConfig2(numbering) {
|
|
|
3838
4143
|
}))
|
|
3839
4144
|
};
|
|
3840
4145
|
}
|
|
3841
|
-
var WRAP_TYPE2, TOC_PAGE_TAB_TWIPS;
|
|
4146
|
+
var WRAP_TYPE2, BODY_ANCHOR, TOC_PAGE_TAB_TWIPS;
|
|
3842
4147
|
var init_emit2 = __esm({
|
|
3843
4148
|
"src/renderers/office-open/emit.ts"() {
|
|
3844
4149
|
"use strict";
|
|
@@ -3849,6 +4154,11 @@ var init_emit2 = __esm({
|
|
|
3849
4154
|
tight: 2,
|
|
3850
4155
|
topAndBottom: 3
|
|
3851
4156
|
};
|
|
4157
|
+
BODY_ANCHOR = {
|
|
4158
|
+
top: "t",
|
|
4159
|
+
middle: "ctr",
|
|
4160
|
+
bottom: "b"
|
|
4161
|
+
};
|
|
3852
4162
|
TOC_PAGE_TAB_TWIPS = 9025;
|
|
3853
4163
|
}
|
|
3854
4164
|
});
|
|
@@ -3912,6 +4222,7 @@ __export(office_open_exports, {
|
|
|
3912
4222
|
buildDocumentOptions: () => buildDocumentOptions,
|
|
3913
4223
|
createOfficeOpenDocxRenderer: () => createOfficeOpenDocxRenderer
|
|
3914
4224
|
});
|
|
4225
|
+
import AdmZip4 from "adm-zip";
|
|
3915
4226
|
async function createOfficeOpenDocxRenderer() {
|
|
3916
4227
|
const backend = await import(
|
|
3917
4228
|
/* @vite-ignore */
|
|
@@ -3927,13 +4238,19 @@ async function createOfficeOpenDocxRenderer() {
|
|
|
3927
4238
|
format: "docx",
|
|
3928
4239
|
capabilities: OFFICE_OPEN_CAPABILITIES,
|
|
3929
4240
|
async render(ir, options) {
|
|
3930
|
-
const
|
|
4241
|
+
const charts = [];
|
|
4242
|
+
const document = await buildDocumentOptions(ir, charts);
|
|
3931
4243
|
const bytes = await backend.generateDocument(document, {
|
|
3932
4244
|
type: "uint8array"
|
|
3933
4245
|
});
|
|
3934
|
-
|
|
4246
|
+
let raw = Buffer.from(
|
|
3935
4247
|
bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes)
|
|
3936
4248
|
);
|
|
4249
|
+
if (charts.length > 0) {
|
|
4250
|
+
const zip = new AdmZip4(raw);
|
|
4251
|
+
spliceChartParts(zip, charts);
|
|
4252
|
+
raw = zip.toBuffer();
|
|
4253
|
+
}
|
|
3937
4254
|
if (options?.deterministic === false) return new Uint8Array(raw);
|
|
3938
4255
|
return new Uint8Array(
|
|
3939
4256
|
canonicalizeDocxBuffer(
|
|
@@ -3947,11 +4264,12 @@ async function createOfficeOpenDocxRenderer() {
|
|
|
3947
4264
|
}
|
|
3948
4265
|
};
|
|
3949
4266
|
}
|
|
3950
|
-
async function buildDocumentOptions(ir) {
|
|
4267
|
+
async function buildDocumentOptions(ir, charts = []) {
|
|
3951
4268
|
let nextDrawingId = 1;
|
|
3952
4269
|
const ctx = {
|
|
3953
4270
|
pictures: await prepareImages2(ir),
|
|
3954
|
-
nextDrawingId: () => nextDrawingId
|
|
4271
|
+
nextDrawingId: () => nextDrawingId++,
|
|
4272
|
+
charts
|
|
3955
4273
|
};
|
|
3956
4274
|
return {
|
|
3957
4275
|
styles: emitStyles2(ir.styles),
|
|
@@ -4026,18 +4344,9 @@ async function prepareImages2(ir) {
|
|
|
4026
4344
|
index === 0 ? data : distinguishImageBytes(data, type, size)
|
|
4027
4345
|
);
|
|
4028
4346
|
});
|
|
4029
|
-
resources.set(resource.id, (
|
|
4030
|
-
const
|
|
4031
|
-
|
|
4032
|
-
height: emuToPixels(image.heightEmu)
|
|
4033
|
-
};
|
|
4034
|
-
const sizeKey = `${rasterSize.width}x${rasterSize.height}`;
|
|
4035
|
-
const transformation = {
|
|
4036
|
-
// Raw numbers are EMUs in @office-open/docx. Passing pixels here makes
|
|
4037
|
-
// a normal image only a few hundred EMUs wide, effectively a dot.
|
|
4038
|
-
width: image.widthEmu,
|
|
4039
|
-
height: image.heightEmu
|
|
4040
|
-
};
|
|
4347
|
+
resources.set(resource.id, (placement) => {
|
|
4348
|
+
const sizeKey = placementKey(placement);
|
|
4349
|
+
const stem = `${resource.id}-${sizeKey}`;
|
|
4041
4350
|
return {
|
|
4042
4351
|
type,
|
|
4043
4352
|
// @office-open/docx stores a drawing transformation on its deduplicated
|
|
@@ -4045,29 +4354,28 @@ async function prepareImages2(ir) {
|
|
|
4045
4354
|
// reuse the first size, so each distinct placement size gets equivalent
|
|
4046
4355
|
// image bytes carrying a harmless format-native marker.
|
|
4047
4356
|
data: placementData.get(sizeKey) ?? data,
|
|
4048
|
-
|
|
4049
|
-
//
|
|
4050
|
-
//
|
|
4051
|
-
|
|
4052
|
-
altText: { id: String(drawingId) },
|
|
4357
|
+
// Named after the resource and the size it is drawn at, so two
|
|
4358
|
+
// placements of one image at one size share a single part and a third
|
|
4359
|
+
// at another size gets its own.
|
|
4360
|
+
fileName: `${stem}.${type}`,
|
|
4053
4361
|
...type === "svg" ? {
|
|
4054
4362
|
// Word before 2016 draws the fallback rather than the vector. A
|
|
4055
4363
|
// raster that could not be produced falls back to the SVG bytes,
|
|
4056
4364
|
// which is what this pipeline has always shipped.
|
|
4057
4365
|
fallback: {
|
|
4058
4366
|
type: "png",
|
|
4059
|
-
data: rasters.get(`${
|
|
4060
|
-
}
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
// No `description` or `title`: no DOCX this pipeline has produced
|
|
4064
|
-
// carries `wp:docPr` alt text, and the compiler warns so the gap is
|
|
4065
|
-
// visible rather than silent.
|
|
4367
|
+
data: rasters.get(`${resource.id}:${sizeKey}`) ?? data
|
|
4368
|
+
},
|
|
4369
|
+
fallbackFileName: `${stem}-fallback.png`
|
|
4370
|
+
} : {}
|
|
4066
4371
|
};
|
|
4067
4372
|
});
|
|
4068
4373
|
}
|
|
4069
4374
|
return resources;
|
|
4070
4375
|
}
|
|
4376
|
+
function placementKey(placement) {
|
|
4377
|
+
return `${emuToPixels(placement.widthEmu)}x${emuToPixels(placement.heightEmu)}`;
|
|
4378
|
+
}
|
|
4071
4379
|
function distinguishImageBytes(data, mediaType, placement) {
|
|
4072
4380
|
const marker = Buffer.from(`json-to-office:${placement}`, "utf8");
|
|
4073
4381
|
switch (mediaType) {
|
|
@@ -4166,12 +4474,27 @@ function crc32(data) {
|
|
|
4166
4474
|
}
|
|
4167
4475
|
function collectImagePlacements2(ir) {
|
|
4168
4476
|
const placements = /* @__PURE__ */ new Map();
|
|
4477
|
+
const record = (resourceId, placement) => {
|
|
4478
|
+
const sizes = placements.get(resourceId) ?? /* @__PURE__ */ new Set();
|
|
4479
|
+
sizes.add(placementKey(placement));
|
|
4480
|
+
placements.set(resourceId, sizes);
|
|
4481
|
+
};
|
|
4169
4482
|
const visitInline = (inline) => {
|
|
4170
4483
|
if (inline.kind === "image") {
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4484
|
+
record(inline.resourceId, inline);
|
|
4485
|
+
return;
|
|
4486
|
+
}
|
|
4487
|
+
if (inline.kind === "drawingGroup") {
|
|
4488
|
+
for (const child of inline.children) {
|
|
4489
|
+
if (child.kind === "picture") {
|
|
4490
|
+
record(child.resourceId, {
|
|
4491
|
+
widthEmu: child.frame.widthEmu,
|
|
4492
|
+
heightEmu: child.frame.heightEmu
|
|
4493
|
+
});
|
|
4494
|
+
continue;
|
|
4495
|
+
}
|
|
4496
|
+
child.text?.paragraphs.forEach(visitBlock);
|
|
4497
|
+
}
|
|
4175
4498
|
return;
|
|
4176
4499
|
}
|
|
4177
4500
|
if (inline.kind === "hyperlink" || inline.kind === "revision") {
|
|
@@ -4221,6 +4544,7 @@ var init_office_open = __esm({
|
|
|
4221
4544
|
"src/renderers/office-open/index.ts"() {
|
|
4222
4545
|
"use strict";
|
|
4223
4546
|
init_features();
|
|
4547
|
+
init_chartParts();
|
|
4224
4548
|
init_imageUtils();
|
|
4225
4549
|
init_packageDocument();
|
|
4226
4550
|
init_emit2();
|
|
@@ -4556,6 +4880,9 @@ import {
|
|
|
4556
4880
|
DEFAULT_VISUAL_DPI,
|
|
4557
4881
|
MAX_RASTERIZE_BATCH_SLIDES
|
|
4558
4882
|
} from "@json-to-office/shared";
|
|
4883
|
+
import {
|
|
4884
|
+
isNativeVisualProps
|
|
4885
|
+
} from "@json-to-office/shared-docx";
|
|
4559
4886
|
|
|
4560
4887
|
// src/components/visual.ts
|
|
4561
4888
|
import { createHash } from "crypto";
|
|
@@ -4753,7 +5080,9 @@ function collectVisualProps(root) {
|
|
|
4753
5080
|
const obj = node;
|
|
4754
5081
|
if (typeof obj.name === "string" && obj.enabled === false) return;
|
|
4755
5082
|
if (obj.name === "visual" && obj.props && typeof obj.props === "object") {
|
|
4756
|
-
|
|
5083
|
+
if (!isNativeVisualProps(obj.props)) {
|
|
5084
|
+
found.push(obj.props);
|
|
5085
|
+
}
|
|
4757
5086
|
return;
|
|
4758
5087
|
}
|
|
4759
5088
|
for (const value of Object.values(obj)) visit(value);
|
|
@@ -5033,6 +5362,9 @@ import {
|
|
|
5033
5362
|
clampVisualDpi as clampVisualDpi2,
|
|
5034
5363
|
DEFAULT_VISUAL_DPI as DEFAULT_VISUAL_DPI2
|
|
5035
5364
|
} from "@json-to-office/shared";
|
|
5365
|
+
import {
|
|
5366
|
+
isNativeVisualProps as isNativeVisualProps2
|
|
5367
|
+
} from "@json-to-office/shared-docx";
|
|
5036
5368
|
|
|
5037
5369
|
// src/components/highcharts.ts
|
|
5038
5370
|
init_colorUtils();
|
|
@@ -5194,6 +5526,7 @@ async function desugarExternals(document, options) {
|
|
|
5194
5526
|
return transformComponents(document, async (node) => {
|
|
5195
5527
|
if (node.enabled === false) return void 0;
|
|
5196
5528
|
if (node.name === "visual") {
|
|
5529
|
+
if (isNativeVisualProps2(node.props)) return void 0;
|
|
5197
5530
|
return withNodeIdentity(node, {
|
|
5198
5531
|
name: "image",
|
|
5199
5532
|
props: await visualImageProps(
|
|
@@ -5246,6 +5579,9 @@ async function visualImageProps(props, prerastered, options) {
|
|
|
5246
5579
|
|
|
5247
5580
|
// src/core/imageResources.ts
|
|
5248
5581
|
init_imageUtils();
|
|
5582
|
+
import {
|
|
5583
|
+
isNativeVisualProps as isNativeVisualProps3
|
|
5584
|
+
} from "@json-to-office/shared-docx";
|
|
5249
5585
|
async function loadImageResources(components) {
|
|
5250
5586
|
const sources = /* @__PURE__ */ new Set();
|
|
5251
5587
|
for (const component of components) collectSources(component, sources);
|
|
@@ -5284,6 +5620,9 @@ function collectSources(component, out) {
|
|
|
5284
5620
|
const children = component.children;
|
|
5285
5621
|
for (const child of children ?? []) collectSources(child, out);
|
|
5286
5622
|
const props = component.props ?? {};
|
|
5623
|
+
if (component.name === "visual" && isNativeVisualProps3(props)) {
|
|
5624
|
+
collectNativeVisualSources(props, out);
|
|
5625
|
+
}
|
|
5287
5626
|
for (const key of ["header", "footer"]) {
|
|
5288
5627
|
const part = props[key];
|
|
5289
5628
|
if (Array.isArray(part)) {
|
|
@@ -5305,6 +5644,23 @@ function collectSources(component, out) {
|
|
|
5305
5644
|
}
|
|
5306
5645
|
}
|
|
5307
5646
|
}
|
|
5647
|
+
function collectNativeVisualSources(props, out) {
|
|
5648
|
+
const canvas = props.canvas;
|
|
5649
|
+
const background = canvas?.background?.image;
|
|
5650
|
+
if (background) {
|
|
5651
|
+
const source = resolveImageSource(background);
|
|
5652
|
+
if (source) out.add(source);
|
|
5653
|
+
}
|
|
5654
|
+
const elements = props.elements;
|
|
5655
|
+
if (!Array.isArray(elements)) return;
|
|
5656
|
+
for (const element of elements) {
|
|
5657
|
+
if (!element || element.name !== "image") continue;
|
|
5658
|
+
const source = resolveImageSource(
|
|
5659
|
+
element.props ?? {}
|
|
5660
|
+
);
|
|
5661
|
+
if (source) out.add(source);
|
|
5662
|
+
}
|
|
5663
|
+
}
|
|
5308
5664
|
function collectCellSource(content, out) {
|
|
5309
5665
|
if (content && typeof content === "object") {
|
|
5310
5666
|
collectSources(content, out);
|
|
@@ -5316,7 +5672,520 @@ init_colorUtils();
|
|
|
5316
5672
|
import {
|
|
5317
5673
|
FeatureRequirementCollector
|
|
5318
5674
|
} from "@json-to-office/shared/rendering";
|
|
5319
|
-
import {
|
|
5675
|
+
import {
|
|
5676
|
+
synthesizeFamilyName,
|
|
5677
|
+
DEFAULT_CHART_THEME_COLORS as DEFAULT_CHART_THEME_COLORS2
|
|
5678
|
+
} from "@json-to-office/shared";
|
|
5679
|
+
import {
|
|
5680
|
+
isNativeVisualProps as isNativeVisualProps4
|
|
5681
|
+
} from "@json-to-office/shared-docx";
|
|
5682
|
+
|
|
5683
|
+
// src/ir/nativeVisual.ts
|
|
5684
|
+
init_units();
|
|
5685
|
+
init_imageUtils();
|
|
5686
|
+
var PIXELS_PER_INCH2 = 96;
|
|
5687
|
+
var DEFAULT_WIDTH_FRACTION = 0.75;
|
|
5688
|
+
var ASSUMED_FONT_SIZE_POINTS = 18;
|
|
5689
|
+
var MIN_DERIVED_TEXT_HEIGHT_INCHES = 0.5;
|
|
5690
|
+
var DERIVED_LINE_HEIGHT_RATIO = 1.6;
|
|
5691
|
+
var GEOMETRY_ALIASES = {
|
|
5692
|
+
arrow: "rightArrow",
|
|
5693
|
+
lightning: "lightningBolt"
|
|
5694
|
+
};
|
|
5695
|
+
var DASH_VALUES = /* @__PURE__ */ new Set([
|
|
5696
|
+
"solid",
|
|
5697
|
+
"dash",
|
|
5698
|
+
"dot",
|
|
5699
|
+
"dashDot"
|
|
5700
|
+
]);
|
|
5701
|
+
var ANCHOR = {
|
|
5702
|
+
top: "top",
|
|
5703
|
+
middle: "middle",
|
|
5704
|
+
bottom: "bottom"
|
|
5705
|
+
};
|
|
5706
|
+
var ALIGNMENT = {
|
|
5707
|
+
left: "left",
|
|
5708
|
+
center: "center",
|
|
5709
|
+
right: "right",
|
|
5710
|
+
justify: "justified"
|
|
5711
|
+
};
|
|
5712
|
+
function compileNativeVisualGroup(props, outer) {
|
|
5713
|
+
const canvas = {
|
|
5714
|
+
widthEmu: inchesToEmu(props.canvas.width),
|
|
5715
|
+
heightEmu: inchesToEmu(props.canvas.height)
|
|
5716
|
+
};
|
|
5717
|
+
const children = [];
|
|
5718
|
+
let refused = false;
|
|
5719
|
+
const deps = {
|
|
5720
|
+
...outer,
|
|
5721
|
+
reject: (detail) => {
|
|
5722
|
+
refused = true;
|
|
5723
|
+
outer.reject(detail);
|
|
5724
|
+
}
|
|
5725
|
+
};
|
|
5726
|
+
const background = props.canvas.background;
|
|
5727
|
+
if (background?.color) {
|
|
5728
|
+
const color = deps.color(background.color);
|
|
5729
|
+
if (!color) {
|
|
5730
|
+
deps.reject(`canvas.background.color "${background.color}"`);
|
|
5731
|
+
} else {
|
|
5732
|
+
children.push({
|
|
5733
|
+
kind: "shape",
|
|
5734
|
+
frame: fullBleed(canvas),
|
|
5735
|
+
geometry: "rect",
|
|
5736
|
+
fill: { kind: "solid", color },
|
|
5737
|
+
name: "Canvas background"
|
|
5738
|
+
});
|
|
5739
|
+
}
|
|
5740
|
+
}
|
|
5741
|
+
if (background?.image) {
|
|
5742
|
+
const source = resolveImageSource(background.image);
|
|
5743
|
+
const resource = source ? deps.picture(source) : void 0;
|
|
5744
|
+
if (!resource) {
|
|
5745
|
+
deps.reject("canvas.background.image could not be loaded");
|
|
5746
|
+
} else {
|
|
5747
|
+
children.push({
|
|
5748
|
+
kind: "picture",
|
|
5749
|
+
frame: fullBleed(canvas),
|
|
5750
|
+
resourceId: resource.resourceId,
|
|
5751
|
+
name: "Canvas background"
|
|
5752
|
+
});
|
|
5753
|
+
}
|
|
5754
|
+
}
|
|
5755
|
+
for (const [index, element] of (props.elements ?? []).entries()) {
|
|
5756
|
+
if (element.enabled === false) continue;
|
|
5757
|
+
const child = compileElement(element, index, canvas, deps);
|
|
5758
|
+
if (child) children.push(child);
|
|
5759
|
+
}
|
|
5760
|
+
return refused ? void 0 : { canvas, children };
|
|
5761
|
+
}
|
|
5762
|
+
function compileElement(element, index, canvas, deps) {
|
|
5763
|
+
const at = `elements[${index}]`;
|
|
5764
|
+
switch (element.name) {
|
|
5765
|
+
case "text":
|
|
5766
|
+
return compileText(element.props, at, canvas, deps);
|
|
5767
|
+
case "shape":
|
|
5768
|
+
return compileShape(element.props, at, canvas, deps);
|
|
5769
|
+
case "image":
|
|
5770
|
+
return compileImage(element.props, at, canvas, deps);
|
|
5771
|
+
default:
|
|
5772
|
+
deps.reject(`${at} is a "${element.name}"`);
|
|
5773
|
+
return void 0;
|
|
5774
|
+
}
|
|
5775
|
+
}
|
|
5776
|
+
function compileText(props, at, canvas, deps) {
|
|
5777
|
+
const runs = textRuns(props);
|
|
5778
|
+
if (!runs) {
|
|
5779
|
+
deps.warn(
|
|
5780
|
+
`[core-docx] native visual ${at} has neither "text" nor "runs"; nothing was drawn for it.`
|
|
5781
|
+
);
|
|
5782
|
+
return void 0;
|
|
5783
|
+
}
|
|
5784
|
+
const colors = resolveColors(
|
|
5785
|
+
[
|
|
5786
|
+
["color", props.color],
|
|
5787
|
+
["fill.color", props.fill?.color],
|
|
5788
|
+
...runs.map((run, i) => [`runs[${i}].color`, run.color]),
|
|
5789
|
+
...runs.map(
|
|
5790
|
+
(run, i) => [
|
|
5791
|
+
`runs[${i}].underline.color`,
|
|
5792
|
+
underlineColor(run.underline)
|
|
5793
|
+
]
|
|
5794
|
+
),
|
|
5795
|
+
["underline.color", underlineColor(props.underline)]
|
|
5796
|
+
],
|
|
5797
|
+
at,
|
|
5798
|
+
deps
|
|
5799
|
+
);
|
|
5800
|
+
if (!colors) return void 0;
|
|
5801
|
+
const frame = resolveFrame(props, canvas, {
|
|
5802
|
+
defaultHeightInches: derivedTextHeightInches(props, runs)
|
|
5803
|
+
});
|
|
5804
|
+
const paragraphs = textParagraphs(runs, props, colors, at);
|
|
5805
|
+
return {
|
|
5806
|
+
kind: "shape",
|
|
5807
|
+
frame,
|
|
5808
|
+
geometry: "rect",
|
|
5809
|
+
isTextBox: true,
|
|
5810
|
+
// A text box with no fill is transparent, which is what the rasterized
|
|
5811
|
+
// path draws; saying so explicitly stops Word applying its own default.
|
|
5812
|
+
fill: props.fill?.color ? solidFill(colors.get("fill.color"), props.fill.transparency) : { kind: "none" },
|
|
5813
|
+
text: {
|
|
5814
|
+
paragraphs,
|
|
5815
|
+
// pptx anchors a text box at the top unless told otherwise, and a
|
|
5816
|
+
// drawing that changes anchor between render modes moves its text.
|
|
5817
|
+
anchor: ANCHOR[props.valign ?? "top"] ?? "top",
|
|
5818
|
+
// A text box's insets default to nothing, again matching pptx. A shape
|
|
5819
|
+
// deliberately leaves them unstated so OOXML's own defaults apply.
|
|
5820
|
+
insetsEmu: marginInsets(props.margin) ?? ZERO_INSETS
|
|
5821
|
+
},
|
|
5822
|
+
name: `Text ${at}`
|
|
5823
|
+
};
|
|
5824
|
+
}
|
|
5825
|
+
var ZERO_INSETS = { top: 0, bottom: 0, left: 0, right: 0 };
|
|
5826
|
+
function textRuns(props) {
|
|
5827
|
+
if (props.runs?.length) return props.runs;
|
|
5828
|
+
if (props.text === void 0) return void 0;
|
|
5829
|
+
return [{ text: props.text }];
|
|
5830
|
+
}
|
|
5831
|
+
function textParagraphs(runs, props, colors, at) {
|
|
5832
|
+
const paragraphs = [];
|
|
5833
|
+
let current = [];
|
|
5834
|
+
const flush = () => {
|
|
5835
|
+
paragraphs.push({
|
|
5836
|
+
kind: "paragraph",
|
|
5837
|
+
id: `${at}.p${paragraphs.length}`,
|
|
5838
|
+
path: `${at}.paragraphs[${paragraphs.length}]`,
|
|
5839
|
+
children: current,
|
|
5840
|
+
formatting: {
|
|
5841
|
+
...props.align ? { alignment: ALIGNMENT[props.align] } : {},
|
|
5842
|
+
// A drawing's text is set flush against the shape; the document's
|
|
5843
|
+
// paragraph spacing would push it away from the top edge, which is not
|
|
5844
|
+
// what an absolutely-placed box means.
|
|
5845
|
+
spacing: { beforeTwips: 0, afterTwips: 0 }
|
|
5846
|
+
}
|
|
5847
|
+
});
|
|
5848
|
+
current = [];
|
|
5849
|
+
};
|
|
5850
|
+
runs.forEach((run, index) => {
|
|
5851
|
+
const formatting = runFormatting(run, props, colors, index);
|
|
5852
|
+
const lines = run.text.split("\n");
|
|
5853
|
+
lines.forEach((line, lineIndex) => {
|
|
5854
|
+
if (lineIndex > 0) current.push({ kind: "lineBreak" });
|
|
5855
|
+
if (line) {
|
|
5856
|
+
current.push({ kind: "text", text: line, ...formatting });
|
|
5857
|
+
}
|
|
5858
|
+
});
|
|
5859
|
+
if (run.breakLine && index < runs.length - 1) flush();
|
|
5860
|
+
});
|
|
5861
|
+
flush();
|
|
5862
|
+
return paragraphs;
|
|
5863
|
+
}
|
|
5864
|
+
function runFormatting(run, props, colors, index) {
|
|
5865
|
+
const fontFamily = run.fontFace ?? props.fontFace;
|
|
5866
|
+
const sizePoints = run.fontSize ?? props.fontSize;
|
|
5867
|
+
const color = colors.get(`runs[${index}].color`) ?? (run.color === void 0 ? colors.get("color") : void 0);
|
|
5868
|
+
const bold = run.bold ?? props.bold;
|
|
5869
|
+
const italic = run.italic ?? props.italic;
|
|
5870
|
+
const strike = run.strike ?? props.strike;
|
|
5871
|
+
const underline = compileUnderline(
|
|
5872
|
+
run.underline ?? props.underline,
|
|
5873
|
+
colors.get(
|
|
5874
|
+
run.underline !== void 0 ? `runs[${index}].underline.color` : "underline.color"
|
|
5875
|
+
)
|
|
5876
|
+
);
|
|
5877
|
+
const formatting = {
|
|
5878
|
+
...fontFamily ? { fontFamily } : {},
|
|
5879
|
+
...sizePoints !== void 0 ? { sizeHalfPoints: pointsToHalfPoints(sizePoints) } : {},
|
|
5880
|
+
...color ? { color } : {},
|
|
5881
|
+
...bold !== void 0 ? { bold } : {},
|
|
5882
|
+
...italic !== void 0 ? { italic } : {},
|
|
5883
|
+
...strike !== void 0 ? { strike } : {},
|
|
5884
|
+
...underline ? { underline } : {}
|
|
5885
|
+
};
|
|
5886
|
+
return Object.keys(formatting).length > 0 ? { formatting } : {};
|
|
5887
|
+
}
|
|
5888
|
+
var UNDERLINE_TYPES = {
|
|
5889
|
+
sng: "single",
|
|
5890
|
+
dbl: "double",
|
|
5891
|
+
dash: "dash",
|
|
5892
|
+
dotted: "dotted"
|
|
5893
|
+
};
|
|
5894
|
+
function compileUnderline(value, color) {
|
|
5895
|
+
if (value === void 0 || value === false) return void 0;
|
|
5896
|
+
const style = value === true ? "sng" : value.style ?? "sng";
|
|
5897
|
+
return {
|
|
5898
|
+
type: UNDERLINE_TYPES[style] ?? "single",
|
|
5899
|
+
...color ? { color } : {}
|
|
5900
|
+
};
|
|
5901
|
+
}
|
|
5902
|
+
function underlineColor(value) {
|
|
5903
|
+
return typeof value === "object" ? value.color : void 0;
|
|
5904
|
+
}
|
|
5905
|
+
function derivedTextHeightInches(props, runs) {
|
|
5906
|
+
const fontSize = props.fontSize ?? ASSUMED_FONT_SIZE_POINTS;
|
|
5907
|
+
const lineCount = runs.reduce(
|
|
5908
|
+
(total, run) => total + (run.breakLine ? 1 : 0) + (run.text.match(/\n/g)?.length ?? 0),
|
|
5909
|
+
1
|
|
5910
|
+
);
|
|
5911
|
+
return Math.max(
|
|
5912
|
+
MIN_DERIVED_TEXT_HEIGHT_INCHES,
|
|
5913
|
+
fontSize / 72 * DERIVED_LINE_HEIGHT_RATIO * lineCount
|
|
5914
|
+
);
|
|
5915
|
+
}
|
|
5916
|
+
function compileShape(props, at, canvas, deps) {
|
|
5917
|
+
const segments = shapeSegments(props);
|
|
5918
|
+
const colors = resolveColors(
|
|
5919
|
+
[
|
|
5920
|
+
["fill.color", props.fill?.color],
|
|
5921
|
+
["line.color", props.line?.color],
|
|
5922
|
+
["fontColor", props.fontColor],
|
|
5923
|
+
...segments.map(
|
|
5924
|
+
(segment, i) => [`text[${i}].color`, segment.color]
|
|
5925
|
+
)
|
|
5926
|
+
],
|
|
5927
|
+
at,
|
|
5928
|
+
deps
|
|
5929
|
+
);
|
|
5930
|
+
if (!colors) return void 0;
|
|
5931
|
+
const frame = resolveFrame(props, canvas, { defaultHeightInches: 0 });
|
|
5932
|
+
if (props.flipH) frame.flipHorizontal = true;
|
|
5933
|
+
if (props.flipV) frame.flipVertical = true;
|
|
5934
|
+
const dash = props.line?.dashType;
|
|
5935
|
+
if (dash !== void 0 && !DASH_VALUES.has(dash)) {
|
|
5936
|
+
deps.reject(`${at}.line.dashType "${dash}"`);
|
|
5937
|
+
return void 0;
|
|
5938
|
+
}
|
|
5939
|
+
const outline = props.line ? {
|
|
5940
|
+
...colors.get("line.color") ? { color: colors.get("line.color") } : {},
|
|
5941
|
+
...props.line.width !== void 0 ? { widthEmu: pointsToEmu(props.line.width) } : {},
|
|
5942
|
+
...dash && dash !== "solid" ? { dash } : {}
|
|
5943
|
+
} : void 0;
|
|
5944
|
+
const text = segments.length ? {
|
|
5945
|
+
paragraphs: shapeParagraphs(segments, props, colors, at),
|
|
5946
|
+
anchor: ANCHOR[props.valign ?? "middle"] ?? "middle",
|
|
5947
|
+
// Unstated insets are left unstated so OOXML's own shape defaults
|
|
5948
|
+
// apply, which is what the raster path draws.
|
|
5949
|
+
...marginInsets(props.margin) ? { insetsEmu: marginInsets(props.margin) } : {}
|
|
5950
|
+
} : void 0;
|
|
5951
|
+
return {
|
|
5952
|
+
kind: "shape",
|
|
5953
|
+
frame,
|
|
5954
|
+
geometry: GEOMETRY_ALIASES[props.type] ?? props.type,
|
|
5955
|
+
...props.fill ? {
|
|
5956
|
+
fill: props.fill.color ? solidFill(colors.get("fill.color"), props.fill.transparency) : { kind: "none" }
|
|
5957
|
+
} : {},
|
|
5958
|
+
...outline && Object.keys(outline).length > 0 ? { outline } : {},
|
|
5959
|
+
...text ? { text } : {},
|
|
5960
|
+
name: `Shape ${at}`
|
|
5961
|
+
};
|
|
5962
|
+
}
|
|
5963
|
+
function shapeSegments(props) {
|
|
5964
|
+
if (props.text === void 0) return [];
|
|
5965
|
+
if (typeof props.text === "string") {
|
|
5966
|
+
return props.text ? [{ text: props.text }] : [];
|
|
5967
|
+
}
|
|
5968
|
+
return props.text;
|
|
5969
|
+
}
|
|
5970
|
+
function shapeParagraphs(segments, props, colors, at) {
|
|
5971
|
+
const paragraphs = [];
|
|
5972
|
+
let current = [];
|
|
5973
|
+
const flush = () => {
|
|
5974
|
+
paragraphs.push({
|
|
5975
|
+
kind: "paragraph",
|
|
5976
|
+
id: `${at}.p${paragraphs.length}`,
|
|
5977
|
+
path: `${at}.paragraphs[${paragraphs.length}]`,
|
|
5978
|
+
children: current,
|
|
5979
|
+
formatting: {
|
|
5980
|
+
// A shape centres its text unless told otherwise, which is what the
|
|
5981
|
+
// rasterized shape draws.
|
|
5982
|
+
alignment: props.align ? ALIGNMENT[props.align] : "center",
|
|
5983
|
+
spacing: { beforeTwips: 0, afterTwips: 0 }
|
|
5984
|
+
}
|
|
5985
|
+
});
|
|
5986
|
+
current = [];
|
|
5987
|
+
};
|
|
5988
|
+
segments.forEach((segment, index) => {
|
|
5989
|
+
const fontFamily = segment.fontFace ?? props.fontFace;
|
|
5990
|
+
const sizePoints = segment.fontSize ?? props.fontSize;
|
|
5991
|
+
const color = colors.get(`text[${index}].color`) ?? (segment.color === void 0 ? colors.get("fontColor") : void 0);
|
|
5992
|
+
const bold = segment.bold ?? props.bold;
|
|
5993
|
+
const italic = segment.italic ?? props.italic;
|
|
5994
|
+
const formatting = {
|
|
5995
|
+
...fontFamily ? { fontFamily } : {},
|
|
5996
|
+
...sizePoints !== void 0 ? { sizeHalfPoints: pointsToHalfPoints(sizePoints) } : {},
|
|
5997
|
+
...color ? { color } : {},
|
|
5998
|
+
...bold !== void 0 ? { bold } : {},
|
|
5999
|
+
...italic !== void 0 ? { italic } : {}
|
|
6000
|
+
};
|
|
6001
|
+
const wrapped = Object.keys(formatting).length > 0 ? { formatting } : void 0;
|
|
6002
|
+
segment.text.split("\n").forEach((line, lineIndex) => {
|
|
6003
|
+
if (lineIndex > 0) current.push({ kind: "lineBreak" });
|
|
6004
|
+
if (line) current.push({ kind: "text", text: line, ...wrapped });
|
|
6005
|
+
});
|
|
6006
|
+
if (segment.breakLine && index < segments.length - 1) flush();
|
|
6007
|
+
});
|
|
6008
|
+
flush();
|
|
6009
|
+
return paragraphs;
|
|
6010
|
+
}
|
|
6011
|
+
function compileImage(props, at, canvas, deps) {
|
|
6012
|
+
const source = resolveImageSource(props);
|
|
6013
|
+
if (!source) {
|
|
6014
|
+
deps.reject(`${at} has none of "path", "base64" or "svg"`);
|
|
6015
|
+
return void 0;
|
|
6016
|
+
}
|
|
6017
|
+
const resource = deps.picture(source);
|
|
6018
|
+
if (!resource) {
|
|
6019
|
+
deps.reject(`${at} image could not be loaded`);
|
|
6020
|
+
return void 0;
|
|
6021
|
+
}
|
|
6022
|
+
const aspect = resource.intrinsic ? resource.intrinsic.width / resource.intrinsic.height : void 0;
|
|
6023
|
+
const intrinsicInches = resource.intrinsic ? {
|
|
6024
|
+
width: resource.intrinsic.width / PIXELS_PER_INCH2,
|
|
6025
|
+
height: resource.intrinsic.height / PIXELS_PER_INCH2
|
|
6026
|
+
} : void 0;
|
|
6027
|
+
const box = resolveImageBox(props, canvas, aspect, intrinsicInches);
|
|
6028
|
+
const sizing = props.sizing;
|
|
6029
|
+
if (!sizing || !aspect) {
|
|
6030
|
+
if (sizing && !aspect) {
|
|
6031
|
+
deps.warn(
|
|
6032
|
+
`[core-docx] native visual ${at} asks for "${sizing.type}" sizing, but the image's stored size could not be read; it was stretched to its box instead.`
|
|
6033
|
+
);
|
|
6034
|
+
}
|
|
6035
|
+
return picture(box, props, resource, at);
|
|
6036
|
+
}
|
|
6037
|
+
const boxAspect = box.widthEmu / box.heightEmu;
|
|
6038
|
+
if (sizing.type === "contain") {
|
|
6039
|
+
const fitted = boxAspect > aspect ? {
|
|
6040
|
+
widthEmu: Math.round(box.heightEmu * aspect),
|
|
6041
|
+
heightEmu: box.heightEmu
|
|
6042
|
+
} : {
|
|
6043
|
+
widthEmu: box.widthEmu,
|
|
6044
|
+
heightEmu: Math.round(box.widthEmu / aspect)
|
|
6045
|
+
};
|
|
6046
|
+
return picture(
|
|
6047
|
+
{
|
|
6048
|
+
...box,
|
|
6049
|
+
xEmu: box.xEmu + Math.round((box.widthEmu - fitted.widthEmu) / 2),
|
|
6050
|
+
yEmu: box.yEmu + Math.round((box.heightEmu - fitted.heightEmu) / 2),
|
|
6051
|
+
...fitted
|
|
6052
|
+
},
|
|
6053
|
+
props,
|
|
6054
|
+
resource,
|
|
6055
|
+
at
|
|
6056
|
+
);
|
|
6057
|
+
}
|
|
6058
|
+
const visible = boxAspect > aspect ? { widthFraction: 1, heightFraction: aspect / boxAspect } : { widthFraction: boxAspect / aspect, heightFraction: 1 };
|
|
6059
|
+
const crop = sizing.type === "cover" ? {
|
|
6060
|
+
...visible.widthFraction < 1 ? {
|
|
6061
|
+
left: (1 - visible.widthFraction) / 2,
|
|
6062
|
+
right: (1 - visible.widthFraction) / 2
|
|
6063
|
+
} : {},
|
|
6064
|
+
...visible.heightFraction < 1 ? {
|
|
6065
|
+
top: (1 - visible.heightFraction) / 2,
|
|
6066
|
+
bottom: (1 - visible.heightFraction) / 2
|
|
6067
|
+
} : {}
|
|
6068
|
+
} : {
|
|
6069
|
+
...visible.widthFraction < 1 ? { right: 1 - visible.widthFraction } : {},
|
|
6070
|
+
...visible.heightFraction < 1 ? { bottom: 1 - visible.heightFraction } : {}
|
|
6071
|
+
};
|
|
6072
|
+
return picture(box, props, resource, at, crop);
|
|
6073
|
+
}
|
|
6074
|
+
function picture(frame, props, resource, at, crop) {
|
|
6075
|
+
return {
|
|
6076
|
+
kind: "picture",
|
|
6077
|
+
frame,
|
|
6078
|
+
resourceId: resource.resourceId,
|
|
6079
|
+
...props.alt ? { altText: props.alt } : {},
|
|
6080
|
+
...crop && Object.keys(crop).length > 0 ? { crop } : {},
|
|
6081
|
+
name: `Image ${at}`
|
|
6082
|
+
};
|
|
6083
|
+
}
|
|
6084
|
+
function resolveImageBox(props, canvas, aspect, intrinsicInches) {
|
|
6085
|
+
const width = props.sizing?.w ?? props.w;
|
|
6086
|
+
const height = props.sizing?.h ?? props.h;
|
|
6087
|
+
const widthEmu = width !== void 0 ? resolveExtent(width, canvas.widthEmu) : void 0;
|
|
6088
|
+
const heightEmu = height !== void 0 ? resolveExtent(height, canvas.heightEmu) : void 0;
|
|
6089
|
+
const resolved = (() => {
|
|
6090
|
+
if (widthEmu !== void 0 && heightEmu !== void 0) {
|
|
6091
|
+
return { widthEmu, heightEmu };
|
|
6092
|
+
}
|
|
6093
|
+
if (widthEmu !== void 0) {
|
|
6094
|
+
return {
|
|
6095
|
+
widthEmu,
|
|
6096
|
+
heightEmu: aspect ? Math.round(widthEmu / aspect) : Math.round(widthEmu * (3 / 4))
|
|
6097
|
+
};
|
|
6098
|
+
}
|
|
6099
|
+
if (heightEmu !== void 0) {
|
|
6100
|
+
return {
|
|
6101
|
+
widthEmu: aspect ? Math.round(heightEmu * aspect) : Math.round(heightEmu * (4 / 3)),
|
|
6102
|
+
heightEmu
|
|
6103
|
+
};
|
|
6104
|
+
}
|
|
6105
|
+
if (intrinsicInches) {
|
|
6106
|
+
return {
|
|
6107
|
+
widthEmu: inchesToEmu(intrinsicInches.width),
|
|
6108
|
+
heightEmu: inchesToEmu(intrinsicInches.height)
|
|
6109
|
+
};
|
|
6110
|
+
}
|
|
6111
|
+
const fallbackWidth = Math.round(canvas.widthEmu * DEFAULT_WIDTH_FRACTION);
|
|
6112
|
+
return {
|
|
6113
|
+
widthEmu: fallbackWidth,
|
|
6114
|
+
heightEmu: Math.round(fallbackWidth * (3 / 4))
|
|
6115
|
+
};
|
|
6116
|
+
})();
|
|
6117
|
+
return {
|
|
6118
|
+
xEmu: props.x !== void 0 ? resolveOffset(props.x, canvas.widthEmu) : 0,
|
|
6119
|
+
yEmu: props.y !== void 0 ? resolveOffset(props.y, canvas.heightEmu) : 0,
|
|
6120
|
+
...resolved,
|
|
6121
|
+
...rotation(props.rotate)
|
|
6122
|
+
};
|
|
6123
|
+
}
|
|
6124
|
+
function fullBleed(canvas) {
|
|
6125
|
+
return {
|
|
6126
|
+
xEmu: 0,
|
|
6127
|
+
yEmu: 0,
|
|
6128
|
+
widthEmu: canvas.widthEmu,
|
|
6129
|
+
heightEmu: canvas.heightEmu
|
|
6130
|
+
};
|
|
6131
|
+
}
|
|
6132
|
+
function resolveFrame(props, canvas, options) {
|
|
6133
|
+
return {
|
|
6134
|
+
xEmu: props.x !== void 0 ? resolveOffset(props.x, canvas.widthEmu) : 0,
|
|
6135
|
+
yEmu: props.y !== void 0 ? resolveOffset(props.y, canvas.heightEmu) : 0,
|
|
6136
|
+
widthEmu: props.w !== void 0 ? resolveExtent(props.w, canvas.widthEmu) : Math.round(canvas.widthEmu * DEFAULT_WIDTH_FRACTION),
|
|
6137
|
+
heightEmu: props.h !== void 0 ? resolveExtent(props.h, canvas.heightEmu) : inchesToEmu(options.defaultHeightInches),
|
|
6138
|
+
...rotation(props.rotate)
|
|
6139
|
+
};
|
|
6140
|
+
}
|
|
6141
|
+
function rotation(degrees) {
|
|
6142
|
+
if (degrees === void 0 || degrees % 360 === 0) return {};
|
|
6143
|
+
return { rotationDegrees: degrees };
|
|
6144
|
+
}
|
|
6145
|
+
function resolveOffset(value, axisEmu) {
|
|
6146
|
+
return resolveLength(value, axisEmu);
|
|
6147
|
+
}
|
|
6148
|
+
function resolveExtent(value, axisEmu) {
|
|
6149
|
+
return Math.max(0, resolveLength(value, axisEmu));
|
|
6150
|
+
}
|
|
6151
|
+
function resolveLength(value, axisEmu) {
|
|
6152
|
+
if (typeof value === "number") return inchesToEmu(value);
|
|
6153
|
+
const percent = Number.parseFloat(value);
|
|
6154
|
+
if (!Number.isFinite(percent)) return 0;
|
|
6155
|
+
return Math.round(percent / 100 * axisEmu);
|
|
6156
|
+
}
|
|
6157
|
+
function marginInsets(margin) {
|
|
6158
|
+
if (margin === void 0) return void 0;
|
|
6159
|
+
const [top, right, bottom, left] = Array.isArray(margin) ? margin : [margin, margin, margin, margin];
|
|
6160
|
+
return {
|
|
6161
|
+
top: pointsToEmu(top ?? 0),
|
|
6162
|
+
right: pointsToEmu(right ?? 0),
|
|
6163
|
+
bottom: pointsToEmu(bottom ?? 0),
|
|
6164
|
+
left: pointsToEmu(left ?? 0)
|
|
6165
|
+
};
|
|
6166
|
+
}
|
|
6167
|
+
function solidFill(color, transparency) {
|
|
6168
|
+
return {
|
|
6169
|
+
kind: "solid",
|
|
6170
|
+
color,
|
|
6171
|
+
...transparency !== void 0 && transparency > 0 ? { transparencyPercent: transparency } : {}
|
|
6172
|
+
};
|
|
6173
|
+
}
|
|
6174
|
+
function resolveColors(entries, at, deps) {
|
|
6175
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
6176
|
+
let failed = false;
|
|
6177
|
+
for (const [key, value] of entries) {
|
|
6178
|
+
if (value === void 0) continue;
|
|
6179
|
+
const color = deps.color(value);
|
|
6180
|
+
if (!color) {
|
|
6181
|
+
deps.reject(`${at}.${key} "${value}"`);
|
|
6182
|
+
failed = true;
|
|
6183
|
+
continue;
|
|
6184
|
+
}
|
|
6185
|
+
resolved.set(key, color);
|
|
6186
|
+
}
|
|
6187
|
+
return failed ? void 0 : resolved;
|
|
6188
|
+
}
|
|
5320
6189
|
|
|
5321
6190
|
// src/styles/themeToStyles.ts
|
|
5322
6191
|
init_themes();
|
|
@@ -7159,7 +8028,7 @@ function decoratedStyle(match, base) {
|
|
|
7159
8028
|
function pushSegment(out, text, options, override = {}) {
|
|
7160
8029
|
const bold = override.bold ?? options.base.bold;
|
|
7161
8030
|
const italic = override.italic ?? options.base.italic;
|
|
7162
|
-
const formatting =
|
|
8031
|
+
const formatting = runFormatting2(options, { bold, italic });
|
|
7163
8032
|
const lines = text.split("\n");
|
|
7164
8033
|
for (const [lineIndex, line] of lines.entries()) {
|
|
7165
8034
|
if (!line && lineIndex === 0) continue;
|
|
@@ -7237,7 +8106,7 @@ function pushNoProofWords(out, text, formatting, options) {
|
|
|
7237
8106
|
out.push({ kind: "text", text: text.slice(lastIndex), formatting });
|
|
7238
8107
|
}
|
|
7239
8108
|
}
|
|
7240
|
-
function
|
|
8109
|
+
function runFormatting2(options, state) {
|
|
7241
8110
|
const formatting = { ...options.base };
|
|
7242
8111
|
if (state.bold !== void 0) formatting.bold = state.bold;
|
|
7243
8112
|
if (state.italic !== void 0) formatting.italic = state.italic;
|
|
@@ -7609,7 +8478,11 @@ function compileComponent(component, scope) {
|
|
|
7609
8478
|
case "columns":
|
|
7610
8479
|
return compileColumns(component, scope);
|
|
7611
8480
|
case "image":
|
|
7612
|
-
return
|
|
8481
|
+
return compileImage2(component, scope);
|
|
8482
|
+
case "visual":
|
|
8483
|
+
return compileVisual(component, scope);
|
|
8484
|
+
case "chart":
|
|
8485
|
+
return compileChart(component, scope);
|
|
7613
8486
|
case "table":
|
|
7614
8487
|
return compileTable(component, scope);
|
|
7615
8488
|
default:
|
|
@@ -7853,7 +8726,7 @@ function compileTextBox(component, scope) {
|
|
|
7853
8726
|
const style = props.style;
|
|
7854
8727
|
const padding = style?.padding;
|
|
7855
8728
|
if (props.renderAs === "shape") {
|
|
7856
|
-
const shape =
|
|
8729
|
+
const shape = compileShape2(props, children, scope);
|
|
7857
8730
|
if (shape) return shape;
|
|
7858
8731
|
}
|
|
7859
8732
|
ctx.features.require("tables", path3);
|
|
@@ -7907,7 +8780,7 @@ function compileTextBox(component, scope) {
|
|
|
7907
8780
|
];
|
|
7908
8781
|
}
|
|
7909
8782
|
var EMU_PER_PIXEL = 9525;
|
|
7910
|
-
function
|
|
8783
|
+
function compileShape2(props, children, scope) {
|
|
7911
8784
|
const { ctx, path: path3 } = scope;
|
|
7912
8785
|
const width = shapeSize(props.width, "width", ctx);
|
|
7913
8786
|
const height = shapeSize(props.height, "height", ctx);
|
|
@@ -8401,7 +9274,7 @@ function reportUnlowered(component, props, text, scope) {
|
|
|
8401
9274
|
}
|
|
8402
9275
|
function compileRuns(props, text, ctx, path3, mode = "inline") {
|
|
8403
9276
|
const font = props.font ?? {};
|
|
8404
|
-
const base =
|
|
9277
|
+
const base = runFormatting3(font, props, ctx);
|
|
8405
9278
|
if (base.language) ctx.features.require("proofing-language", path3);
|
|
8406
9279
|
if (base.noProof) ctx.features.require("proofing-language", path3);
|
|
8407
9280
|
const words = mergeNoProofWords(noProofWords(ctx.theme), props.noProofWords);
|
|
@@ -8671,7 +9544,7 @@ function isoDate(date) {
|
|
|
8671
9544
|
function isoDateTime(date) {
|
|
8672
9545
|
return date.toISOString().replace("T", " ").replace(/\.\d{3}Z$/, "Z");
|
|
8673
9546
|
}
|
|
8674
|
-
function
|
|
9547
|
+
function runFormatting3(font, props, ctx) {
|
|
8675
9548
|
const hasWeightRequest = font.fontWeight != null || font.bold === true;
|
|
8676
9549
|
const effectiveFamily = font.family ?? (hasWeightRequest ? resolveBodyFamily(ctx.theme) : void 0);
|
|
8677
9550
|
const weighted = applyFontWeightAlias({
|
|
@@ -8834,8 +9707,107 @@ function headingLevel(level) {
|
|
|
8834
9707
|
const value = typeof level === "number" ? level : 1;
|
|
8835
9708
|
return value >= 1 && value <= 6 ? value : 1;
|
|
8836
9709
|
}
|
|
9710
|
+
function compileVisual(component, scope) {
|
|
9711
|
+
const { ctx, path: path3 } = scope;
|
|
9712
|
+
const props = component.props ?? {};
|
|
9713
|
+
if (!isNativeVisualProps4(props)) {
|
|
9714
|
+
ctx.unsupported.push({
|
|
9715
|
+
name: "visual",
|
|
9716
|
+
path: path3,
|
|
9717
|
+
detail: "a raster visual reached the compiler; it should have been rasterized during desugaring"
|
|
9718
|
+
});
|
|
9719
|
+
return [];
|
|
9720
|
+
}
|
|
9721
|
+
const group = compileNativeVisualGroup(
|
|
9722
|
+
props,
|
|
9723
|
+
nativeVisualDeps(ctx, path3)
|
|
9724
|
+
);
|
|
9725
|
+
if (!group) return [];
|
|
9726
|
+
const size = visualPlacementPixels(props, group.canvas, {
|
|
9727
|
+
widthPx: Math.round(twipsToPixels(getAvailableWidthTwips(ctx.theme))),
|
|
9728
|
+
heightPx: Math.round(twipsToPixels(getAvailableHeightTwips(ctx.theme)))
|
|
9729
|
+
});
|
|
9730
|
+
const drawing = {
|
|
9731
|
+
kind: "drawingGroup",
|
|
9732
|
+
widthEmu: pixelsToEmu(size.width),
|
|
9733
|
+
heightEmu: pixelsToEmu(size.height),
|
|
9734
|
+
canvasWidthEmu: group.canvas.widthEmu,
|
|
9735
|
+
canvasHeightEmu: group.canvas.heightEmu,
|
|
9736
|
+
children: group.children,
|
|
9737
|
+
...typeof props.alt === "string" && props.alt ? { altText: props.alt } : {},
|
|
9738
|
+
...props.floating ? { floating: compileFloating(props.floating, ctx, path3) } : {}
|
|
9739
|
+
};
|
|
9740
|
+
ctx.features.require("drawing-groups", path3);
|
|
9741
|
+
if (props.floating) ctx.features.require("floating-images", path3);
|
|
9742
|
+
const spacing = {};
|
|
9743
|
+
if (props.spacing?.before !== void 0) {
|
|
9744
|
+
spacing.beforeTwips = pointsToTwips2(props.spacing.before);
|
|
9745
|
+
}
|
|
9746
|
+
if (props.spacing?.after !== void 0) {
|
|
9747
|
+
spacing.afterTwips = pointsToTwips2(props.spacing.after);
|
|
9748
|
+
}
|
|
9749
|
+
const blocks = [
|
|
9750
|
+
{
|
|
9751
|
+
kind: "paragraph",
|
|
9752
|
+
id: scope.id,
|
|
9753
|
+
path: path3,
|
|
9754
|
+
children: [drawing],
|
|
9755
|
+
formatting: {
|
|
9756
|
+
// A floating drawing is anchored, so aligning its paragraph would move
|
|
9757
|
+
// the anchor rather than the graphic — the same rule an image follows.
|
|
9758
|
+
...props.floating ? {} : { alignment: compileAlignment(props.alignment) ?? "center" },
|
|
9759
|
+
...Object.keys(spacing).length > 0 ? { spacing } : {},
|
|
9760
|
+
...props.keepNext !== void 0 ? { keepNext: props.keepNext } : {},
|
|
9761
|
+
...props.keepLines !== void 0 ? { keepLines: props.keepLines } : {}
|
|
9762
|
+
}
|
|
9763
|
+
}
|
|
9764
|
+
];
|
|
9765
|
+
const caption = captionBlock(props.caption, scope, "visual");
|
|
9766
|
+
if (caption === void 0) return [];
|
|
9767
|
+
if (caption) blocks.push(caption);
|
|
9768
|
+
return blocks;
|
|
9769
|
+
}
|
|
9770
|
+
function nativeVisualDeps(ctx, path3) {
|
|
9771
|
+
return {
|
|
9772
|
+
color: (value) => {
|
|
9773
|
+
try {
|
|
9774
|
+
return irColor(resolveColor(value, ctx.theme));
|
|
9775
|
+
} catch {
|
|
9776
|
+
return void 0;
|
|
9777
|
+
}
|
|
9778
|
+
},
|
|
9779
|
+
picture: (source) => {
|
|
9780
|
+
const loaded = ctx.images.get(source);
|
|
9781
|
+
if (!loaded) return void 0;
|
|
9782
|
+
const mediaType = detectImageType(source, loaded.contentType);
|
|
9783
|
+
if (mediaType === "svg") ctx.features.require("svg-images", path3);
|
|
9784
|
+
return {
|
|
9785
|
+
resourceId: declareResource(loaded, mediaType, ctx),
|
|
9786
|
+
mediaType,
|
|
9787
|
+
...loaded.intrinsic ? { intrinsic: loaded.intrinsic } : {}
|
|
9788
|
+
};
|
|
9789
|
+
},
|
|
9790
|
+
reject: (detail) => ctx.unsupported.push({ name: "visual", path: path3, detail }),
|
|
9791
|
+
warn: (message) => warnOnce(ctx, `visual:${message}`, message)
|
|
9792
|
+
};
|
|
9793
|
+
}
|
|
9794
|
+
function visualPlacementPixels(props, canvas, reference) {
|
|
9795
|
+
const canvasWidthPx = Math.round(emuToPixels(canvas.widthEmu));
|
|
9796
|
+
const canvasHeightPx = Math.round(emuToPixels(canvas.heightEmu));
|
|
9797
|
+
const targetWidth = props.width !== void 0 ? parseWidthValue(props.width, reference.widthPx) : void 0;
|
|
9798
|
+
const targetHeight = props.height !== void 0 ? parseDimensionValue(props.height, reference.heightPx) : void 0;
|
|
9799
|
+
if (targetWidth === void 0 && targetHeight === void 0) {
|
|
9800
|
+
return { width: canvasWidthPx, height: canvasHeightPx };
|
|
9801
|
+
}
|
|
9802
|
+
return calculateMissingDimension(
|
|
9803
|
+
canvasWidthPx,
|
|
9804
|
+
canvasHeightPx,
|
|
9805
|
+
targetWidth,
|
|
9806
|
+
targetHeight
|
|
9807
|
+
);
|
|
9808
|
+
}
|
|
8837
9809
|
var UNLOWERED_IMAGE_PROPS = ["comment"];
|
|
8838
|
-
function
|
|
9810
|
+
function compileImage2(component, scope) {
|
|
8839
9811
|
const { ctx, path: path3 } = scope;
|
|
8840
9812
|
const props = component.props ?? {};
|
|
8841
9813
|
for (const prop of UNLOWERED_IMAGE_PROPS) {
|
|
@@ -8896,32 +9868,142 @@ function compileImage(component, scope) {
|
|
|
8896
9868
|
}
|
|
8897
9869
|
}
|
|
8898
9870
|
];
|
|
8899
|
-
|
|
8900
|
-
|
|
8901
|
-
|
|
8902
|
-
|
|
8903
|
-
|
|
8904
|
-
|
|
9871
|
+
const caption = captionBlock(props.caption, scope, "image");
|
|
9872
|
+
if (caption === void 0) return [];
|
|
9873
|
+
if (caption) blocks.push(caption);
|
|
9874
|
+
return blocks;
|
|
9875
|
+
}
|
|
9876
|
+
var DEFAULT_CHART_HEIGHT_INCHES = 3;
|
|
9877
|
+
function compileChart(component, scope) {
|
|
9878
|
+
const { ctx, path: path3 } = scope;
|
|
9879
|
+
const props = component.props ?? {};
|
|
9880
|
+
if (props.type === "bubble") {
|
|
9881
|
+
throw new Error(
|
|
9882
|
+
`Chart at ${path3} is a bubble chart, which no docx renderer draws; use the pptx \`chart\` component on the pptxgenjs renderer for one.`
|
|
9883
|
+
);
|
|
9884
|
+
}
|
|
9885
|
+
const rawSeries = Array.isArray(props.data) ? props.data : [];
|
|
9886
|
+
if (rawSeries.length === 0) {
|
|
9887
|
+
throw new Error(`Chart at ${path3} has no data series.`);
|
|
9888
|
+
}
|
|
9889
|
+
const series = rawSeries.map((entry, index) => {
|
|
9890
|
+
const raw = entry ?? {};
|
|
9891
|
+
const label = typeof raw.name === "string" ? raw.name : `series ${index}`;
|
|
9892
|
+
const labels = raw.labels;
|
|
9893
|
+
const values = raw.values;
|
|
9894
|
+
if (!Array.isArray(labels) || !Array.isArray(values)) {
|
|
9895
|
+
throw new Error(
|
|
9896
|
+
`Chart series "${label}" at ${path3} needs both "labels" and "values"; every series carries its own, not just the first.`
|
|
9897
|
+
);
|
|
8905
9898
|
}
|
|
8906
|
-
|
|
8907
|
-
|
|
8908
|
-
|
|
8909
|
-
|
|
8910
|
-
|
|
8911
|
-
|
|
8912
|
-
|
|
8913
|
-
|
|
8914
|
-
|
|
8915
|
-
|
|
8916
|
-
|
|
8917
|
-
|
|
8918
|
-
|
|
8919
|
-
|
|
8920
|
-
|
|
8921
|
-
|
|
9899
|
+
if (labels.length !== values.length) {
|
|
9900
|
+
throw new Error(
|
|
9901
|
+
`Chart series "${label}" at ${path3} has ${labels.length} labels and ${values.length} values; they must be the same length.`
|
|
9902
|
+
);
|
|
9903
|
+
}
|
|
9904
|
+
return {
|
|
9905
|
+
...typeof raw.name === "string" ? { name: raw.name } : {},
|
|
9906
|
+
labels: labels.map((value) => String(value)),
|
|
9907
|
+
values: values.map((value) => Number(value))
|
|
9908
|
+
};
|
|
9909
|
+
});
|
|
9910
|
+
const categories = series[0].labels;
|
|
9911
|
+
for (const [index, entry] of series.entries()) {
|
|
9912
|
+
if (index === 0) continue;
|
|
9913
|
+
if (entry.labels.length !== categories.length || entry.labels.some((label, i) => label !== categories[i])) {
|
|
9914
|
+
throw new Error(
|
|
9915
|
+
`Chart series "${entry.name ?? `series ${index}`}" at ${path3} has different labels from the first series. A chart has one category axis, so every series must name the same categories in the same order.`
|
|
9916
|
+
);
|
|
9917
|
+
}
|
|
9918
|
+
}
|
|
9919
|
+
const colors = Array.isArray(props.chartColors) ? props.chartColors.map(
|
|
9920
|
+
(color) => resolveColor(String(color), ctx.theme)
|
|
9921
|
+
) : DEFAULT_CHART_THEME_COLORS2.map((token) => {
|
|
9922
|
+
const value = ctx.theme?.colors?.[token];
|
|
9923
|
+
if (typeof value !== "string" || value.length === 0) return void 0;
|
|
9924
|
+
try {
|
|
9925
|
+
return resolveColor(token, ctx.theme);
|
|
9926
|
+
} catch {
|
|
9927
|
+
return void 0;
|
|
9928
|
+
}
|
|
9929
|
+
}).filter((color) => color !== void 0);
|
|
9930
|
+
const page = getPageSetup(ctx.theme);
|
|
9931
|
+
const contentWidthInches = (page.size.width - page.margin.left - page.margin.right) / 1440;
|
|
9932
|
+
const widthInches = typeof props.width === "number" ? props.width : contentWidthInches;
|
|
9933
|
+
const heightInches = typeof props.height === "number" ? props.height : DEFAULT_CHART_HEIGHT_INCHES;
|
|
9934
|
+
const chart = {
|
|
9935
|
+
kind: "chart",
|
|
9936
|
+
chartType: props.type,
|
|
9937
|
+
series,
|
|
9938
|
+
colors,
|
|
9939
|
+
widthEmu: inchesToEmu(widthInches),
|
|
9940
|
+
heightEmu: inchesToEmu(heightInches),
|
|
9941
|
+
...typeof props.title === "string" ? { title: props.title } : {},
|
|
9942
|
+
...props.showTitle !== void 0 ? { showTitle: props.showTitle } : {},
|
|
9943
|
+
...props.showLegend !== void 0 ? { showLegend: props.showLegend } : {},
|
|
9944
|
+
...props.legendPos ? { legendPosition: props.legendPos } : {},
|
|
9945
|
+
...typeof props.catAxisTitle === "string" ? { categoryAxisTitle: props.catAxisTitle } : {},
|
|
9946
|
+
...typeof props.valAxisTitle === "string" ? { valueAxisTitle: props.valAxisTitle } : {},
|
|
9947
|
+
...typeof props.alt === "string" && props.alt ? { altText: props.alt } : {},
|
|
9948
|
+
...props.floating ? { floating: compileFloating(props.floating, ctx, path3) } : {}
|
|
9949
|
+
};
|
|
9950
|
+
ctx.features.require("charts", path3);
|
|
9951
|
+
if (props.floating) ctx.features.require("floating-images", path3);
|
|
9952
|
+
const spacing = {};
|
|
9953
|
+
if (props.spacing?.before !== void 0) {
|
|
9954
|
+
spacing.beforeTwips = pointsToTwips2(props.spacing.before);
|
|
8922
9955
|
}
|
|
9956
|
+
if (props.spacing?.after !== void 0) {
|
|
9957
|
+
spacing.afterTwips = pointsToTwips2(props.spacing.after);
|
|
9958
|
+
}
|
|
9959
|
+
const blocks = [
|
|
9960
|
+
{
|
|
9961
|
+
kind: "paragraph",
|
|
9962
|
+
id: scope.id,
|
|
9963
|
+
path: path3,
|
|
9964
|
+
children: [chart],
|
|
9965
|
+
formatting: {
|
|
9966
|
+
// An anchored chart moves with its anchor, so aligning the paragraph
|
|
9967
|
+
// would move the anchor rather than the chart — the same reasoning as
|
|
9968
|
+
// a floating image.
|
|
9969
|
+
...props.floating ? {} : { alignment: compileAlignment(props.alignment) ?? "center" },
|
|
9970
|
+
...Object.keys(spacing).length > 0 ? { spacing } : {},
|
|
9971
|
+
...props.keepNext !== void 0 ? { keepNext: props.keepNext } : {},
|
|
9972
|
+
...props.keepLines !== void 0 ? { keepLines: props.keepLines } : {}
|
|
9973
|
+
}
|
|
9974
|
+
}
|
|
9975
|
+
];
|
|
9976
|
+
const caption = captionBlock(props.caption, scope, "chart");
|
|
9977
|
+
if (caption === void 0) return [];
|
|
9978
|
+
if (caption) blocks.push(caption);
|
|
8923
9979
|
return blocks;
|
|
8924
9980
|
}
|
|
9981
|
+
function captionBlock(value, scope, componentName) {
|
|
9982
|
+
if (!value) return null;
|
|
9983
|
+
const { ctx, path: path3 } = scope;
|
|
9984
|
+
const caption = String(value);
|
|
9985
|
+
const syntax = containsUnsupportedSyntax(caption);
|
|
9986
|
+
if (syntax) {
|
|
9987
|
+
ctx.unsupported.push({ name: componentName, path: path3, detail: syntax });
|
|
9988
|
+
return void 0;
|
|
9989
|
+
}
|
|
9990
|
+
return {
|
|
9991
|
+
kind: "paragraph",
|
|
9992
|
+
id: `${scope.id}:caption`,
|
|
9993
|
+
path: `${path3}.caption`,
|
|
9994
|
+
styleId: "Normal",
|
|
9995
|
+
// Captions default to left alignment, whatever the figure did.
|
|
9996
|
+
formatting: { alignment: "left" },
|
|
9997
|
+
// A caption reaches the parser only when it carries a decorator, which
|
|
9998
|
+
// is why a link in an otherwise plain caption stays literal.
|
|
9999
|
+
children: DECORATED.test(caption) ? parseInline(caption, {
|
|
10000
|
+
base: {},
|
|
10001
|
+
hyperlinks: true,
|
|
10002
|
+
resolvePlaceholder: placeholderResolver(ctx),
|
|
10003
|
+
resolveCrossReference: crossReferenceResolver(ctx, path3)
|
|
10004
|
+
}) : parseLiteral(caption, { base: {} })
|
|
10005
|
+
};
|
|
10006
|
+
}
|
|
8925
10007
|
function relativeFrom(value, axis) {
|
|
8926
10008
|
const allowed = axis === "horizontal" ? ["character", "column", "margin", "page"] : ["margin", "page", "paragraph", "line"];
|
|
8927
10009
|
return typeof value === "string" && allowed.includes(value) ? value : void 0;
|
|
@@ -9345,6 +10427,9 @@ function cellChildren(cell, baseStyle, scope, rowRevision) {
|
|
|
9345
10427
|
if (content.name === "image") {
|
|
9346
10428
|
return wrap(cellImage(content, base, ctx));
|
|
9347
10429
|
}
|
|
10430
|
+
if (content.name === "visual") {
|
|
10431
|
+
return wrap(cellVisual(content, scope));
|
|
10432
|
+
}
|
|
9348
10433
|
if (content.name !== "paragraph") {
|
|
9349
10434
|
return wrap([
|
|
9350
10435
|
{
|
|
@@ -9384,6 +10469,40 @@ function cellChildren(cell, baseStyle, scope, rowRevision) {
|
|
|
9384
10469
|
})
|
|
9385
10470
|
);
|
|
9386
10471
|
}
|
|
10472
|
+
function cellVisual(component, scope) {
|
|
10473
|
+
const { ctx, path: path3 } = scope;
|
|
10474
|
+
const props = component.props ?? {};
|
|
10475
|
+
if (!isNativeVisualProps4(props)) {
|
|
10476
|
+
ctx.unsupported.push({
|
|
10477
|
+
name: "visual",
|
|
10478
|
+
path: path3,
|
|
10479
|
+
detail: "a raster visual reached the compiler; it should have been rasterized during desugaring"
|
|
10480
|
+
});
|
|
10481
|
+
return [];
|
|
10482
|
+
}
|
|
10483
|
+
const group = compileNativeVisualGroup(
|
|
10484
|
+
props,
|
|
10485
|
+
nativeVisualDeps(ctx, path3)
|
|
10486
|
+
);
|
|
10487
|
+
if (!group) return [];
|
|
10488
|
+
const size = visualPlacementPixels(props, group.canvas, {
|
|
10489
|
+
widthPx: CELL_DRAWING_REFERENCE.width,
|
|
10490
|
+
heightPx: CELL_DRAWING_REFERENCE.height
|
|
10491
|
+
});
|
|
10492
|
+
ctx.features.require("drawing-groups", path3);
|
|
10493
|
+
return [
|
|
10494
|
+
{
|
|
10495
|
+
kind: "drawingGroup",
|
|
10496
|
+
widthEmu: pixelsToEmu(size.width),
|
|
10497
|
+
heightEmu: pixelsToEmu(size.height),
|
|
10498
|
+
canvasWidthEmu: group.canvas.widthEmu,
|
|
10499
|
+
canvasHeightEmu: group.canvas.heightEmu,
|
|
10500
|
+
children: group.children,
|
|
10501
|
+
...typeof props.alt === "string" && props.alt ? { altText: props.alt } : {}
|
|
10502
|
+
}
|
|
10503
|
+
];
|
|
10504
|
+
}
|
|
10505
|
+
var CELL_DRAWING_REFERENCE = { width: 300, height: 200 };
|
|
9387
10506
|
function cellImage(component, base, ctx) {
|
|
9388
10507
|
const props = component.props ?? {};
|
|
9389
10508
|
const source = resolveImageSource(props);
|