@json-to-office/core-docx 1.0.0 → 1.1.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 +963 -94
- 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 +110 -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 +956 -92
- 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/emit.d.ts +38 -6
- package/dist/renderers/office-open/emit.d.ts.map +1 -1
- package/dist/renderers/office-open/index.d.ts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -2005,6 +2005,12 @@ function pointsToEighthPoints(points) {
|
|
|
2005
2005
|
function inchesToTwips(inches) {
|
|
2006
2006
|
return Math.round(inches * TWIPS_PER_INCH);
|
|
2007
2007
|
}
|
|
2008
|
+
function inchesToEmu(inches) {
|
|
2009
|
+
return Math.round(inches * EMU_PER_INCH);
|
|
2010
|
+
}
|
|
2011
|
+
function pointsToEmu(points) {
|
|
2012
|
+
return Math.round(points * EMU_PER_INCH / 72);
|
|
2013
|
+
}
|
|
2008
2014
|
function twipsToEmu(twips) {
|
|
2009
2015
|
return Math.round(twips * EMU_PER_TWIP);
|
|
2010
2016
|
}
|
|
@@ -2207,6 +2213,10 @@ function inlineChildren(children, resources = /* @__PURE__ */ new Map()) {
|
|
|
2207
2213
|
case "shape":
|
|
2208
2214
|
out.push(emitShape(child, resources));
|
|
2209
2215
|
break;
|
|
2216
|
+
case "drawingGroup":
|
|
2217
|
+
throw new Error(
|
|
2218
|
+
"the docxjs renderer has no emitter for a drawing group; this document should have been refused by the capability check"
|
|
2219
|
+
);
|
|
2210
2220
|
case "noteReference":
|
|
2211
2221
|
out.push(
|
|
2212
2222
|
child.noteKind === "endnote" ? new EndnoteReferenceRun(child.id) : new FootnoteReferenceRun(child.id)
|
|
@@ -2308,7 +2318,7 @@ function paragraphOptions(formatting) {
|
|
|
2308
2318
|
const options = {};
|
|
2309
2319
|
if (!formatting) return options;
|
|
2310
2320
|
if (formatting.alignment) {
|
|
2311
|
-
options.alignment =
|
|
2321
|
+
options.alignment = ALIGNMENT2[formatting.alignment];
|
|
2312
2322
|
}
|
|
2313
2323
|
if (formatting.spacing) {
|
|
2314
2324
|
const spacing = {};
|
|
@@ -2542,11 +2552,11 @@ function emitBorder(border3) {
|
|
|
2542
2552
|
color: border3.color?.hex ?? "000000"
|
|
2543
2553
|
};
|
|
2544
2554
|
}
|
|
2545
|
-
var
|
|
2555
|
+
var ALIGNMENT2, PAGE_FIELD, WRAP_TYPE, VERTICAL_ALIGN, TABLE_ANCHOR, HORIZONTAL_POSITION, VERTICAL_POSITION, BORDER_STYLE;
|
|
2546
2556
|
var init_emit = __esm({
|
|
2547
2557
|
"src/renderers/docxjs/emit.ts"() {
|
|
2548
2558
|
"use strict";
|
|
2549
|
-
|
|
2559
|
+
ALIGNMENT2 = {
|
|
2550
2560
|
left: AlignmentType.LEFT,
|
|
2551
2561
|
center: AlignmentType.CENTER,
|
|
2552
2562
|
right: AlignmentType.RIGHT,
|
|
@@ -2669,7 +2679,7 @@ function paragraphProperties(formatting) {
|
|
|
2669
2679
|
out.spacing = spacing;
|
|
2670
2680
|
}
|
|
2671
2681
|
if (formatting.alignment !== void 0) {
|
|
2672
|
-
out.alignment =
|
|
2682
|
+
out.alignment = ALIGNMENT2[formatting.alignment] ?? formatting.alignment;
|
|
2673
2683
|
}
|
|
2674
2684
|
if (formatting.keepNext !== void 0) out.keepNext = formatting.keepNext;
|
|
2675
2685
|
if (formatting.keepLines !== void 0) out.keepLines = formatting.keepLines;
|
|
@@ -2798,6 +2808,15 @@ var init_features = __esm({
|
|
|
2798
2808
|
"text-frames",
|
|
2799
2809
|
/** Native shape text boxes. */
|
|
2800
2810
|
"text-boxes",
|
|
2811
|
+
/**
|
|
2812
|
+
* A DrawingML group: shapes, text boxes and pictures sharing one child
|
|
2813
|
+
* coordinate space, drawn as a single anchored or inline object.
|
|
2814
|
+
*
|
|
2815
|
+
* Separate from `text-boxes` because a lone `wps:wsp` run is a far smaller
|
|
2816
|
+
* ask than `wpg:wgp` with child transforms, preset geometry and grouped
|
|
2817
|
+
* pictures — a backend can plausibly have the first and not the second.
|
|
2818
|
+
*/
|
|
2819
|
+
"drawing-groups",
|
|
2801
2820
|
/** A table-of-contents field. */
|
|
2802
2821
|
"toc",
|
|
2803
2822
|
/** TOC entries baked in so an unrefreshed reader still shows them. */
|
|
@@ -3044,7 +3063,7 @@ function numberingConfig(numbering) {
|
|
|
3044
3063
|
level: level.level,
|
|
3045
3064
|
format: level.format,
|
|
3046
3065
|
text: level.text,
|
|
3047
|
-
alignment: level.alignment ?
|
|
3066
|
+
alignment: level.alignment ? ALIGNMENT2[level.alignment] : void 0,
|
|
3048
3067
|
...level.suffix ? { suffix: level.suffix } : {},
|
|
3049
3068
|
style: {
|
|
3050
3069
|
...level.indent ? {
|
|
@@ -3262,7 +3281,12 @@ var init_docxjs = __esm({
|
|
|
3262
3281
|
"cached-fields",
|
|
3263
3282
|
"shading",
|
|
3264
3283
|
"borders",
|
|
3265
|
-
"rtl"
|
|
3284
|
+
"rtl",
|
|
3285
|
+
// `drawing-groups` is the one exclusion that is a real backend gap rather
|
|
3286
|
+
// than a slice boundary: docx.js has no `wpg:wgp`. Leaving it out is what
|
|
3287
|
+
// turns a native `visual` sent to this backend into a named capability
|
|
3288
|
+
// error instead of a document with the graphic missing.
|
|
3289
|
+
"drawing-groups"
|
|
3266
3290
|
]);
|
|
3267
3291
|
DOCXJS_CAPABILITIES = new Set(
|
|
3268
3292
|
[...ALL_DOCX_FEATURES].filter((feature) => !NOT_YET_EMITTED.has(feature))
|
|
@@ -3373,15 +3397,15 @@ function inlineChildren2(children, ctx = emptyContext()) {
|
|
|
3373
3397
|
out.push({ bookmarkEnd: { id: child.id } });
|
|
3374
3398
|
break;
|
|
3375
3399
|
case "image": {
|
|
3376
|
-
const build = ctx.pictures.get(child.resourceId);
|
|
3377
|
-
if (!build) {
|
|
3378
|
-
throw new Error(
|
|
3379
|
-
`no image was prepared for resource "${child.resourceId}"`
|
|
3380
|
-
);
|
|
3381
|
-
}
|
|
3382
3400
|
const pending = breakOption();
|
|
3383
3401
|
if (pending.break) out.push(pending);
|
|
3384
|
-
out.push({ picture:
|
|
3402
|
+
out.push({ picture: pictureOptions(child, ctx) });
|
|
3403
|
+
break;
|
|
3404
|
+
}
|
|
3405
|
+
case "drawingGroup": {
|
|
3406
|
+
const pending = breakOption();
|
|
3407
|
+
if (pending.break) out.push(pending);
|
|
3408
|
+
out.push({ wpgGroup: drawingGroupOptions(child, ctx) });
|
|
3385
3409
|
break;
|
|
3386
3410
|
}
|
|
3387
3411
|
case "hyperlink":
|
|
@@ -3476,6 +3500,166 @@ function floatingOptions2(floating) {
|
|
|
3476
3500
|
zIndex: floating.zIndex
|
|
3477
3501
|
};
|
|
3478
3502
|
}
|
|
3503
|
+
function imageMedia(ctx, resourceId, placement) {
|
|
3504
|
+
const build = ctx.pictures.get(resourceId);
|
|
3505
|
+
if (!build) {
|
|
3506
|
+
throw new Error(`no image was prepared for resource "${resourceId}"`);
|
|
3507
|
+
}
|
|
3508
|
+
return build(placement);
|
|
3509
|
+
}
|
|
3510
|
+
function pictureOptions(image, ctx) {
|
|
3511
|
+
const media = imageMedia(ctx, image.resourceId, image);
|
|
3512
|
+
return {
|
|
3513
|
+
type: media.type,
|
|
3514
|
+
data: media.data,
|
|
3515
|
+
// No `fileName`: at run level the backend allocates one, and stating our
|
|
3516
|
+
// own would only fight it.
|
|
3517
|
+
...media.fallback ? { fallback: media.fallback } : {},
|
|
3518
|
+
// Raw numbers are EMUs in @office-open/docx. Passing pixels here makes a
|
|
3519
|
+
// normal image only a few hundred EMUs wide, effectively a dot.
|
|
3520
|
+
transformation: { width: image.widthEmu, height: image.heightEmu },
|
|
3521
|
+
// The id is stated rather than left to the backend's process-global
|
|
3522
|
+
// counter. `name` stays empty, which is what it was before and what the
|
|
3523
|
+
// backend falls back to.
|
|
3524
|
+
altText: { id: String(ctx.nextDrawingId()) },
|
|
3525
|
+
...image.floating ? { floating: floatingOptions2(image.floating) } : {}
|
|
3526
|
+
// No `description` or `title`: no DOCX this pipeline has produced carries
|
|
3527
|
+
// `wp:docPr` alt text, and the compiler warns so the gap is visible rather
|
|
3528
|
+
// than silent.
|
|
3529
|
+
};
|
|
3530
|
+
}
|
|
3531
|
+
function drawingGroupOptions(group, ctx) {
|
|
3532
|
+
const id = ctx.nextDrawingId();
|
|
3533
|
+
return {
|
|
3534
|
+
altText: {
|
|
3535
|
+
id: String(id),
|
|
3536
|
+
...group.altText ? { description: group.altText } : {}
|
|
3537
|
+
},
|
|
3538
|
+
transformation: { width: group.widthEmu, height: group.heightEmu },
|
|
3539
|
+
childOffset: { x: 0, y: 0 },
|
|
3540
|
+
childExtent: { cx: group.canvasWidthEmu, cy: group.canvasHeightEmu },
|
|
3541
|
+
children: group.children.map((child) => groupChild(child, ctx)),
|
|
3542
|
+
...group.floating ? { floating: floatingOptions2(group.floating) } : {}
|
|
3543
|
+
};
|
|
3544
|
+
}
|
|
3545
|
+
function groupChild(child, ctx) {
|
|
3546
|
+
return child.kind === "shape" ? groupShape(child, ctx) : groupPicture(child, ctx);
|
|
3547
|
+
}
|
|
3548
|
+
function groupShape(shape, ctx) {
|
|
3549
|
+
const id = ctx.nextDrawingId();
|
|
3550
|
+
return {
|
|
3551
|
+
type: "wps",
|
|
3552
|
+
transformation: childTransformation(shape.frame),
|
|
3553
|
+
data: {
|
|
3554
|
+
nonVisualProperties: {
|
|
3555
|
+
id,
|
|
3556
|
+
...shape.name ? { name: shape.name } : {},
|
|
3557
|
+
// `txBox="1"` is how Word tells a text box from a shape that happens
|
|
3558
|
+
// to hold text, and it changes how the object behaves on selection.
|
|
3559
|
+
...shape.isTextBox ? { textBox: "1" } : {}
|
|
3560
|
+
},
|
|
3561
|
+
presetGeometry: { preset: shape.geometry },
|
|
3562
|
+
...shape.fill ? { fill: drawingFill(shape.fill) } : {},
|
|
3563
|
+
...shape.outline ? { outline: drawingOutline(shape.outline) } : {},
|
|
3564
|
+
children: (shape.text?.paragraphs ?? []).map(
|
|
3565
|
+
(child) => paragraph(child, ctx)
|
|
3566
|
+
),
|
|
3567
|
+
...shape.text ? { bodyProperties: bodyProperties(shape.text) } : {}
|
|
3568
|
+
}
|
|
3569
|
+
};
|
|
3570
|
+
}
|
|
3571
|
+
function groupPicture(picture2, ctx) {
|
|
3572
|
+
const id = ctx.nextDrawingId();
|
|
3573
|
+
const media = imageMedia(ctx, picture2.resourceId, {
|
|
3574
|
+
widthEmu: picture2.frame.widthEmu,
|
|
3575
|
+
heightEmu: picture2.frame.heightEmu
|
|
3576
|
+
});
|
|
3577
|
+
return {
|
|
3578
|
+
type: media.type,
|
|
3579
|
+
data: media.data,
|
|
3580
|
+
fileName: media.fileName,
|
|
3581
|
+
...media.fallback ? {
|
|
3582
|
+
fallback: {
|
|
3583
|
+
...media.fallback,
|
|
3584
|
+
fileName: media.fallbackFileName
|
|
3585
|
+
}
|
|
3586
|
+
} : {},
|
|
3587
|
+
transformation: childTransformation(picture2.frame),
|
|
3588
|
+
nonVisualProperties: {
|
|
3589
|
+
id,
|
|
3590
|
+
...picture2.name ? { name: picture2.name } : {},
|
|
3591
|
+
...picture2.altText ? { description: picture2.altText } : {}
|
|
3592
|
+
},
|
|
3593
|
+
...picture2.crop ? { sourceRectangle: sourceRectangle(picture2.crop) } : {}
|
|
3594
|
+
};
|
|
3595
|
+
}
|
|
3596
|
+
function childTransformation(frame) {
|
|
3597
|
+
const flip = {
|
|
3598
|
+
...frame.flipHorizontal ? { horizontal: true } : {},
|
|
3599
|
+
...frame.flipVertical ? { vertical: true } : {}
|
|
3600
|
+
};
|
|
3601
|
+
return {
|
|
3602
|
+
offset: {
|
|
3603
|
+
emus: { x: frame.xEmu, y: frame.yEmu },
|
|
3604
|
+
pixels: {
|
|
3605
|
+
x: Math.round(emuToPixels(frame.xEmu)),
|
|
3606
|
+
y: Math.round(emuToPixels(frame.yEmu))
|
|
3607
|
+
}
|
|
3608
|
+
},
|
|
3609
|
+
emus: { x: frame.widthEmu, y: frame.heightEmu },
|
|
3610
|
+
pixels: {
|
|
3611
|
+
x: Math.round(emuToPixels(frame.widthEmu)),
|
|
3612
|
+
y: Math.round(emuToPixels(frame.heightEmu))
|
|
3613
|
+
},
|
|
3614
|
+
// Degrees: the backend multiplies by 60000 on the way into `@rot`.
|
|
3615
|
+
...frame.rotationDegrees !== void 0 ? { rotation: frame.rotationDegrees } : {},
|
|
3616
|
+
...Object.keys(flip).length > 0 ? { flip } : {}
|
|
3617
|
+
};
|
|
3618
|
+
}
|
|
3619
|
+
function drawingFill(fill) {
|
|
3620
|
+
if (fill.kind === "none") return { type: "none" };
|
|
3621
|
+
return {
|
|
3622
|
+
type: "solid",
|
|
3623
|
+
color: {
|
|
3624
|
+
type: "rgb",
|
|
3625
|
+
value: fill.color.hex,
|
|
3626
|
+
// Transparency becomes an alpha transform on the colour. DrawingML
|
|
3627
|
+
// states *opacity*, so the value is the complement of what the author
|
|
3628
|
+
// wrote; the backend scales the percentage into `a:alpha`'s thousandths
|
|
3629
|
+
// itself, so it must be handed a plain percentage here.
|
|
3630
|
+
...fill.transparencyPercent !== void 0 ? { transforms: { alpha: 100 - fill.transparencyPercent } } : {}
|
|
3631
|
+
}
|
|
3632
|
+
};
|
|
3633
|
+
}
|
|
3634
|
+
function drawingOutline(outline) {
|
|
3635
|
+
return {
|
|
3636
|
+
...outline.widthEmu !== void 0 ? { width: outline.widthEmu } : {},
|
|
3637
|
+
...outline.dash ? { dash: outline.dash } : {},
|
|
3638
|
+
...outline.color ? { type: "solidFill", color: { value: outline.color.hex } } : {}
|
|
3639
|
+
};
|
|
3640
|
+
}
|
|
3641
|
+
function bodyProperties(text) {
|
|
3642
|
+
const insets = text.insetsEmu;
|
|
3643
|
+
return {
|
|
3644
|
+
...text.anchor ? { anchor: BODY_ANCHOR[text.anchor] } : {},
|
|
3645
|
+
// `lIns`/`tIns`/`rIns`/`bIns` is the vocabulary `a:bodyPr` actually has;
|
|
3646
|
+
// the backend also accepts a `margins` object and folds it into the same
|
|
3647
|
+
// attributes.
|
|
3648
|
+
...insets?.left !== void 0 ? { lIns: insets.left } : {},
|
|
3649
|
+
...insets?.top !== void 0 ? { tIns: insets.top } : {},
|
|
3650
|
+
...insets?.right !== void 0 ? { rIns: insets.right } : {},
|
|
3651
|
+
...insets?.bottom !== void 0 ? { bIns: insets.bottom } : {}
|
|
3652
|
+
};
|
|
3653
|
+
}
|
|
3654
|
+
function sourceRectangle(crop) {
|
|
3655
|
+
const thousandths = (fraction) => Math.round(fraction * 1e5);
|
|
3656
|
+
return {
|
|
3657
|
+
...crop.left !== void 0 ? { left: thousandths(crop.left) } : {},
|
|
3658
|
+
...crop.top !== void 0 ? { top: thousandths(crop.top) } : {},
|
|
3659
|
+
...crop.right !== void 0 ? { right: thousandths(crop.right) } : {},
|
|
3660
|
+
...crop.bottom !== void 0 ? { bottom: thousandths(crop.bottom) } : {}
|
|
3661
|
+
};
|
|
3662
|
+
}
|
|
3479
3663
|
function shapeOptions(shape, ctx) {
|
|
3480
3664
|
const id = String(ctx.nextDrawingId());
|
|
3481
3665
|
return {
|
|
@@ -3493,23 +3677,8 @@ function shapeOptions(shape, ctx) {
|
|
|
3493
3677
|
color: { type: "rgb", value: shape.fill.hex }
|
|
3494
3678
|
}
|
|
3495
3679
|
} : {},
|
|
3496
|
-
...shape.outline ? {
|
|
3497
|
-
|
|
3498
|
-
...shape.outline.widthEmu !== void 0 ? { width: shape.outline.widthEmu } : {},
|
|
3499
|
-
fill: {
|
|
3500
|
-
type: "solid",
|
|
3501
|
-
color: { type: "rgb", value: shape.outline.color.hex }
|
|
3502
|
-
}
|
|
3503
|
-
}
|
|
3504
|
-
} : {},
|
|
3505
|
-
...shape.insetsEmu ? {
|
|
3506
|
-
bodyProperties: {
|
|
3507
|
-
...shape.insetsEmu.top !== void 0 ? { topInset: shape.insetsEmu.top } : {},
|
|
3508
|
-
...shape.insetsEmu.bottom !== void 0 ? { bottomInset: shape.insetsEmu.bottom } : {},
|
|
3509
|
-
...shape.insetsEmu.left !== void 0 ? { leftInset: shape.insetsEmu.left } : {},
|
|
3510
|
-
...shape.insetsEmu.right !== void 0 ? { rightInset: shape.insetsEmu.right } : {}
|
|
3511
|
-
}
|
|
3512
|
-
} : {},
|
|
3680
|
+
...shape.outline ? { outline: drawingOutline(shape.outline) } : {},
|
|
3681
|
+
...shape.insetsEmu ? { bodyProperties: bodyProperties({ insetsEmu: shape.insetsEmu }) } : {},
|
|
3513
3682
|
...shape.floating ? { floating: floatingOptions2(shape.floating) } : {}
|
|
3514
3683
|
};
|
|
3515
3684
|
}
|
|
@@ -3866,7 +4035,7 @@ function numberingConfig2(numbering) {
|
|
|
3866
4035
|
}))
|
|
3867
4036
|
};
|
|
3868
4037
|
}
|
|
3869
|
-
var WRAP_TYPE2, TOC_PAGE_TAB_TWIPS;
|
|
4038
|
+
var WRAP_TYPE2, BODY_ANCHOR, TOC_PAGE_TAB_TWIPS;
|
|
3870
4039
|
var init_emit2 = __esm({
|
|
3871
4040
|
"src/renderers/office-open/emit.ts"() {
|
|
3872
4041
|
"use strict";
|
|
@@ -3877,6 +4046,11 @@ var init_emit2 = __esm({
|
|
|
3877
4046
|
tight: 2,
|
|
3878
4047
|
topAndBottom: 3
|
|
3879
4048
|
};
|
|
4049
|
+
BODY_ANCHOR = {
|
|
4050
|
+
top: "t",
|
|
4051
|
+
middle: "ctr",
|
|
4052
|
+
bottom: "b"
|
|
4053
|
+
};
|
|
3880
4054
|
TOC_PAGE_TAB_TWIPS = 9025;
|
|
3881
4055
|
}
|
|
3882
4056
|
});
|
|
@@ -4054,18 +4228,9 @@ async function prepareImages2(ir) {
|
|
|
4054
4228
|
index === 0 ? data : distinguishImageBytes(data, type, size)
|
|
4055
4229
|
);
|
|
4056
4230
|
});
|
|
4057
|
-
resources.set(resource.id, (
|
|
4058
|
-
const
|
|
4059
|
-
|
|
4060
|
-
height: emuToPixels(image.heightEmu)
|
|
4061
|
-
};
|
|
4062
|
-
const sizeKey = `${rasterSize.width}x${rasterSize.height}`;
|
|
4063
|
-
const transformation = {
|
|
4064
|
-
// Raw numbers are EMUs in @office-open/docx. Passing pixels here makes
|
|
4065
|
-
// a normal image only a few hundred EMUs wide, effectively a dot.
|
|
4066
|
-
width: image.widthEmu,
|
|
4067
|
-
height: image.heightEmu
|
|
4068
|
-
};
|
|
4231
|
+
resources.set(resource.id, (placement) => {
|
|
4232
|
+
const sizeKey = placementKey(placement);
|
|
4233
|
+
const stem = `${resource.id}-${sizeKey}`;
|
|
4069
4234
|
return {
|
|
4070
4235
|
type,
|
|
4071
4236
|
// @office-open/docx stores a drawing transformation on its deduplicated
|
|
@@ -4073,29 +4238,28 @@ async function prepareImages2(ir) {
|
|
|
4073
4238
|
// reuse the first size, so each distinct placement size gets equivalent
|
|
4074
4239
|
// image bytes carrying a harmless format-native marker.
|
|
4075
4240
|
data: placementData.get(sizeKey) ?? data,
|
|
4076
|
-
|
|
4077
|
-
//
|
|
4078
|
-
//
|
|
4079
|
-
|
|
4080
|
-
altText: { id: String(drawingId) },
|
|
4241
|
+
// Named after the resource and the size it is drawn at, so two
|
|
4242
|
+
// placements of one image at one size share a single part and a third
|
|
4243
|
+
// at another size gets its own.
|
|
4244
|
+
fileName: `${stem}.${type}`,
|
|
4081
4245
|
...type === "svg" ? {
|
|
4082
4246
|
// Word before 2016 draws the fallback rather than the vector. A
|
|
4083
4247
|
// raster that could not be produced falls back to the SVG bytes,
|
|
4084
4248
|
// which is what this pipeline has always shipped.
|
|
4085
4249
|
fallback: {
|
|
4086
4250
|
type: "png",
|
|
4087
|
-
data: rasters.get(`${
|
|
4088
|
-
}
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
// No `description` or `title`: no DOCX this pipeline has produced
|
|
4092
|
-
// carries `wp:docPr` alt text, and the compiler warns so the gap is
|
|
4093
|
-
// visible rather than silent.
|
|
4251
|
+
data: rasters.get(`${resource.id}:${sizeKey}`) ?? data
|
|
4252
|
+
},
|
|
4253
|
+
fallbackFileName: `${stem}-fallback.png`
|
|
4254
|
+
} : {}
|
|
4094
4255
|
};
|
|
4095
4256
|
});
|
|
4096
4257
|
}
|
|
4097
4258
|
return resources;
|
|
4098
4259
|
}
|
|
4260
|
+
function placementKey(placement) {
|
|
4261
|
+
return `${emuToPixels(placement.widthEmu)}x${emuToPixels(placement.heightEmu)}`;
|
|
4262
|
+
}
|
|
4099
4263
|
function distinguishImageBytes(data, mediaType, placement) {
|
|
4100
4264
|
const marker = Buffer.from(`json-to-office:${placement}`, "utf8");
|
|
4101
4265
|
switch (mediaType) {
|
|
@@ -4194,12 +4358,27 @@ function crc32(data) {
|
|
|
4194
4358
|
}
|
|
4195
4359
|
function collectImagePlacements2(ir) {
|
|
4196
4360
|
const placements = /* @__PURE__ */ new Map();
|
|
4361
|
+
const record = (resourceId, placement) => {
|
|
4362
|
+
const sizes = placements.get(resourceId) ?? /* @__PURE__ */ new Set();
|
|
4363
|
+
sizes.add(placementKey(placement));
|
|
4364
|
+
placements.set(resourceId, sizes);
|
|
4365
|
+
};
|
|
4197
4366
|
const visitInline = (inline) => {
|
|
4198
4367
|
if (inline.kind === "image") {
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4368
|
+
record(inline.resourceId, inline);
|
|
4369
|
+
return;
|
|
4370
|
+
}
|
|
4371
|
+
if (inline.kind === "drawingGroup") {
|
|
4372
|
+
for (const child of inline.children) {
|
|
4373
|
+
if (child.kind === "picture") {
|
|
4374
|
+
record(child.resourceId, {
|
|
4375
|
+
widthEmu: child.frame.widthEmu,
|
|
4376
|
+
heightEmu: child.frame.heightEmu
|
|
4377
|
+
});
|
|
4378
|
+
continue;
|
|
4379
|
+
}
|
|
4380
|
+
child.text?.paragraphs.forEach(visitBlock);
|
|
4381
|
+
}
|
|
4203
4382
|
return;
|
|
4204
4383
|
}
|
|
4205
4384
|
if (inline.kind === "hyperlink" || inline.kind === "revision") {
|
|
@@ -4372,6 +4551,9 @@ import {
|
|
|
4372
4551
|
DEFAULT_VISUAL_DPI,
|
|
4373
4552
|
MAX_RASTERIZE_BATCH_SLIDES
|
|
4374
4553
|
} from "@json-to-office/shared";
|
|
4554
|
+
import {
|
|
4555
|
+
isNativeVisualProps
|
|
4556
|
+
} from "@json-to-office/shared-docx";
|
|
4375
4557
|
|
|
4376
4558
|
// src/components/visual.ts
|
|
4377
4559
|
import { createHash } from "crypto";
|
|
@@ -4584,7 +4766,9 @@ function collectVisualProps(root) {
|
|
|
4584
4766
|
const obj = node;
|
|
4585
4767
|
if (typeof obj.name === "string" && obj.enabled === false) return;
|
|
4586
4768
|
if (obj.name === "visual" && obj.props && typeof obj.props === "object") {
|
|
4587
|
-
|
|
4769
|
+
if (!isNativeVisualProps(obj.props)) {
|
|
4770
|
+
found.push(obj.props);
|
|
4771
|
+
}
|
|
4588
4772
|
return;
|
|
4589
4773
|
}
|
|
4590
4774
|
for (const value of Object.values(obj)) visit(value);
|
|
@@ -4864,6 +5048,9 @@ import {
|
|
|
4864
5048
|
clampVisualDpi as clampVisualDpi2,
|
|
4865
5049
|
DEFAULT_VISUAL_DPI as DEFAULT_VISUAL_DPI2
|
|
4866
5050
|
} from "@json-to-office/shared";
|
|
5051
|
+
import {
|
|
5052
|
+
isNativeVisualProps as isNativeVisualProps2
|
|
5053
|
+
} from "@json-to-office/shared-docx";
|
|
4867
5054
|
|
|
4868
5055
|
// src/components/highcharts.ts
|
|
4869
5056
|
init_colorUtils();
|
|
@@ -5025,6 +5212,7 @@ async function desugarExternals(document, options) {
|
|
|
5025
5212
|
return transformComponents(document, async (node) => {
|
|
5026
5213
|
if (node.enabled === false) return void 0;
|
|
5027
5214
|
if (node.name === "visual") {
|
|
5215
|
+
if (isNativeVisualProps2(node.props)) return void 0;
|
|
5028
5216
|
return withNodeIdentity(node, {
|
|
5029
5217
|
name: "image",
|
|
5030
5218
|
props: await visualImageProps(
|
|
@@ -5077,6 +5265,9 @@ async function visualImageProps(props, prerastered, options) {
|
|
|
5077
5265
|
|
|
5078
5266
|
// src/core/imageResources.ts
|
|
5079
5267
|
init_imageUtils();
|
|
5268
|
+
import {
|
|
5269
|
+
isNativeVisualProps as isNativeVisualProps3
|
|
5270
|
+
} from "@json-to-office/shared-docx";
|
|
5080
5271
|
async function loadImageResources(components) {
|
|
5081
5272
|
const sources = /* @__PURE__ */ new Set();
|
|
5082
5273
|
for (const component of components) collectSources(component, sources);
|
|
@@ -5115,6 +5306,9 @@ function collectSources(component, out) {
|
|
|
5115
5306
|
const children = component.children;
|
|
5116
5307
|
for (const child of children ?? []) collectSources(child, out);
|
|
5117
5308
|
const props = component.props ?? {};
|
|
5309
|
+
if (component.name === "visual" && isNativeVisualProps3(props)) {
|
|
5310
|
+
collectNativeVisualSources(props, out);
|
|
5311
|
+
}
|
|
5118
5312
|
for (const key of ["header", "footer"]) {
|
|
5119
5313
|
const part = props[key];
|
|
5120
5314
|
if (Array.isArray(part)) {
|
|
@@ -5136,6 +5330,23 @@ function collectSources(component, out) {
|
|
|
5136
5330
|
}
|
|
5137
5331
|
}
|
|
5138
5332
|
}
|
|
5333
|
+
function collectNativeVisualSources(props, out) {
|
|
5334
|
+
const canvas = props.canvas;
|
|
5335
|
+
const background = canvas?.background?.image;
|
|
5336
|
+
if (background) {
|
|
5337
|
+
const source = resolveImageSource(background);
|
|
5338
|
+
if (source) out.add(source);
|
|
5339
|
+
}
|
|
5340
|
+
const elements = props.elements;
|
|
5341
|
+
if (!Array.isArray(elements)) return;
|
|
5342
|
+
for (const element of elements) {
|
|
5343
|
+
if (!element || element.name !== "image") continue;
|
|
5344
|
+
const source = resolveImageSource(
|
|
5345
|
+
element.props ?? {}
|
|
5346
|
+
);
|
|
5347
|
+
if (source) out.add(source);
|
|
5348
|
+
}
|
|
5349
|
+
}
|
|
5139
5350
|
function collectCellSource(content, out) {
|
|
5140
5351
|
if (content && typeof content === "object") {
|
|
5141
5352
|
collectSources(content, out);
|
|
@@ -5148,6 +5359,516 @@ import {
|
|
|
5148
5359
|
FeatureRequirementCollector
|
|
5149
5360
|
} from "@json-to-office/shared/rendering";
|
|
5150
5361
|
import { synthesizeFamilyName } from "@json-to-office/shared";
|
|
5362
|
+
import {
|
|
5363
|
+
isNativeVisualProps as isNativeVisualProps4
|
|
5364
|
+
} from "@json-to-office/shared-docx";
|
|
5365
|
+
|
|
5366
|
+
// src/ir/nativeVisual.ts
|
|
5367
|
+
init_units();
|
|
5368
|
+
init_imageUtils();
|
|
5369
|
+
var PIXELS_PER_INCH2 = 96;
|
|
5370
|
+
var DEFAULT_WIDTH_FRACTION = 0.75;
|
|
5371
|
+
var ASSUMED_FONT_SIZE_POINTS = 18;
|
|
5372
|
+
var MIN_DERIVED_TEXT_HEIGHT_INCHES = 0.5;
|
|
5373
|
+
var DERIVED_LINE_HEIGHT_RATIO = 1.6;
|
|
5374
|
+
var GEOMETRY_ALIASES = {
|
|
5375
|
+
arrow: "rightArrow",
|
|
5376
|
+
lightning: "lightningBolt"
|
|
5377
|
+
};
|
|
5378
|
+
var DASH_VALUES = /* @__PURE__ */ new Set([
|
|
5379
|
+
"solid",
|
|
5380
|
+
"dash",
|
|
5381
|
+
"dot",
|
|
5382
|
+
"dashDot"
|
|
5383
|
+
]);
|
|
5384
|
+
var ANCHOR = {
|
|
5385
|
+
top: "top",
|
|
5386
|
+
middle: "middle",
|
|
5387
|
+
bottom: "bottom"
|
|
5388
|
+
};
|
|
5389
|
+
var ALIGNMENT = {
|
|
5390
|
+
left: "left",
|
|
5391
|
+
center: "center",
|
|
5392
|
+
right: "right",
|
|
5393
|
+
justify: "justified"
|
|
5394
|
+
};
|
|
5395
|
+
function compileNativeVisualGroup(props, outer) {
|
|
5396
|
+
const canvas = {
|
|
5397
|
+
widthEmu: inchesToEmu(props.canvas.width),
|
|
5398
|
+
heightEmu: inchesToEmu(props.canvas.height)
|
|
5399
|
+
};
|
|
5400
|
+
const children = [];
|
|
5401
|
+
let refused = false;
|
|
5402
|
+
const deps = {
|
|
5403
|
+
...outer,
|
|
5404
|
+
reject: (detail) => {
|
|
5405
|
+
refused = true;
|
|
5406
|
+
outer.reject(detail);
|
|
5407
|
+
}
|
|
5408
|
+
};
|
|
5409
|
+
const background = props.canvas.background;
|
|
5410
|
+
if (background?.color) {
|
|
5411
|
+
const color = deps.color(background.color);
|
|
5412
|
+
if (!color) {
|
|
5413
|
+
deps.reject(`canvas.background.color "${background.color}"`);
|
|
5414
|
+
} else {
|
|
5415
|
+
children.push({
|
|
5416
|
+
kind: "shape",
|
|
5417
|
+
frame: fullBleed(canvas),
|
|
5418
|
+
geometry: "rect",
|
|
5419
|
+
fill: { kind: "solid", color },
|
|
5420
|
+
name: "Canvas background"
|
|
5421
|
+
});
|
|
5422
|
+
}
|
|
5423
|
+
}
|
|
5424
|
+
if (background?.image) {
|
|
5425
|
+
const source = resolveImageSource(background.image);
|
|
5426
|
+
const resource = source ? deps.picture(source) : void 0;
|
|
5427
|
+
if (!resource) {
|
|
5428
|
+
deps.reject("canvas.background.image could not be loaded");
|
|
5429
|
+
} else {
|
|
5430
|
+
children.push({
|
|
5431
|
+
kind: "picture",
|
|
5432
|
+
frame: fullBleed(canvas),
|
|
5433
|
+
resourceId: resource.resourceId,
|
|
5434
|
+
name: "Canvas background"
|
|
5435
|
+
});
|
|
5436
|
+
}
|
|
5437
|
+
}
|
|
5438
|
+
for (const [index, element] of (props.elements ?? []).entries()) {
|
|
5439
|
+
if (element.enabled === false) continue;
|
|
5440
|
+
const child = compileElement(element, index, canvas, deps);
|
|
5441
|
+
if (child) children.push(child);
|
|
5442
|
+
}
|
|
5443
|
+
return refused ? void 0 : { canvas, children };
|
|
5444
|
+
}
|
|
5445
|
+
function compileElement(element, index, canvas, deps) {
|
|
5446
|
+
const at = `elements[${index}]`;
|
|
5447
|
+
switch (element.name) {
|
|
5448
|
+
case "text":
|
|
5449
|
+
return compileText(element.props, at, canvas, deps);
|
|
5450
|
+
case "shape":
|
|
5451
|
+
return compileShape(element.props, at, canvas, deps);
|
|
5452
|
+
case "image":
|
|
5453
|
+
return compileImage(element.props, at, canvas, deps);
|
|
5454
|
+
default:
|
|
5455
|
+
deps.reject(`${at} is a "${element.name}"`);
|
|
5456
|
+
return void 0;
|
|
5457
|
+
}
|
|
5458
|
+
}
|
|
5459
|
+
function compileText(props, at, canvas, deps) {
|
|
5460
|
+
const runs = textRuns(props);
|
|
5461
|
+
if (!runs) {
|
|
5462
|
+
deps.warn(
|
|
5463
|
+
`[core-docx] native visual ${at} has neither "text" nor "runs"; nothing was drawn for it.`
|
|
5464
|
+
);
|
|
5465
|
+
return void 0;
|
|
5466
|
+
}
|
|
5467
|
+
const colors = resolveColors(
|
|
5468
|
+
[
|
|
5469
|
+
["color", props.color],
|
|
5470
|
+
["fill.color", props.fill?.color],
|
|
5471
|
+
...runs.map((run, i) => [`runs[${i}].color`, run.color]),
|
|
5472
|
+
...runs.map(
|
|
5473
|
+
(run, i) => [
|
|
5474
|
+
`runs[${i}].underline.color`,
|
|
5475
|
+
underlineColor(run.underline)
|
|
5476
|
+
]
|
|
5477
|
+
),
|
|
5478
|
+
["underline.color", underlineColor(props.underline)]
|
|
5479
|
+
],
|
|
5480
|
+
at,
|
|
5481
|
+
deps
|
|
5482
|
+
);
|
|
5483
|
+
if (!colors) return void 0;
|
|
5484
|
+
const frame = resolveFrame(props, canvas, {
|
|
5485
|
+
defaultHeightInches: derivedTextHeightInches(props, runs)
|
|
5486
|
+
});
|
|
5487
|
+
const paragraphs = textParagraphs(runs, props, colors, at);
|
|
5488
|
+
return {
|
|
5489
|
+
kind: "shape",
|
|
5490
|
+
frame,
|
|
5491
|
+
geometry: "rect",
|
|
5492
|
+
isTextBox: true,
|
|
5493
|
+
// A text box with no fill is transparent, which is what the rasterized
|
|
5494
|
+
// path draws; saying so explicitly stops Word applying its own default.
|
|
5495
|
+
fill: props.fill?.color ? solidFill(colors.get("fill.color"), props.fill.transparency) : { kind: "none" },
|
|
5496
|
+
text: {
|
|
5497
|
+
paragraphs,
|
|
5498
|
+
// pptx anchors a text box at the top unless told otherwise, and a
|
|
5499
|
+
// drawing that changes anchor between render modes moves its text.
|
|
5500
|
+
anchor: ANCHOR[props.valign ?? "top"] ?? "top",
|
|
5501
|
+
// A text box's insets default to nothing, again matching pptx. A shape
|
|
5502
|
+
// deliberately leaves them unstated so OOXML's own defaults apply.
|
|
5503
|
+
insetsEmu: marginInsets(props.margin) ?? ZERO_INSETS
|
|
5504
|
+
},
|
|
5505
|
+
name: `Text ${at}`
|
|
5506
|
+
};
|
|
5507
|
+
}
|
|
5508
|
+
var ZERO_INSETS = { top: 0, bottom: 0, left: 0, right: 0 };
|
|
5509
|
+
function textRuns(props) {
|
|
5510
|
+
if (props.runs?.length) return props.runs;
|
|
5511
|
+
if (props.text === void 0) return void 0;
|
|
5512
|
+
return [{ text: props.text }];
|
|
5513
|
+
}
|
|
5514
|
+
function textParagraphs(runs, props, colors, at) {
|
|
5515
|
+
const paragraphs = [];
|
|
5516
|
+
let current = [];
|
|
5517
|
+
const flush = () => {
|
|
5518
|
+
paragraphs.push({
|
|
5519
|
+
kind: "paragraph",
|
|
5520
|
+
id: `${at}.p${paragraphs.length}`,
|
|
5521
|
+
path: `${at}.paragraphs[${paragraphs.length}]`,
|
|
5522
|
+
children: current,
|
|
5523
|
+
formatting: {
|
|
5524
|
+
...props.align ? { alignment: ALIGNMENT[props.align] } : {},
|
|
5525
|
+
// A drawing's text is set flush against the shape; the document's
|
|
5526
|
+
// paragraph spacing would push it away from the top edge, which is not
|
|
5527
|
+
// what an absolutely-placed box means.
|
|
5528
|
+
spacing: { beforeTwips: 0, afterTwips: 0 }
|
|
5529
|
+
}
|
|
5530
|
+
});
|
|
5531
|
+
current = [];
|
|
5532
|
+
};
|
|
5533
|
+
runs.forEach((run, index) => {
|
|
5534
|
+
const formatting = runFormatting(run, props, colors, index);
|
|
5535
|
+
const lines = run.text.split("\n");
|
|
5536
|
+
lines.forEach((line, lineIndex) => {
|
|
5537
|
+
if (lineIndex > 0) current.push({ kind: "lineBreak" });
|
|
5538
|
+
if (line) {
|
|
5539
|
+
current.push({ kind: "text", text: line, ...formatting });
|
|
5540
|
+
}
|
|
5541
|
+
});
|
|
5542
|
+
if (run.breakLine && index < runs.length - 1) flush();
|
|
5543
|
+
});
|
|
5544
|
+
flush();
|
|
5545
|
+
return paragraphs;
|
|
5546
|
+
}
|
|
5547
|
+
function runFormatting(run, props, colors, index) {
|
|
5548
|
+
const fontFamily = run.fontFace ?? props.fontFace;
|
|
5549
|
+
const sizePoints = run.fontSize ?? props.fontSize;
|
|
5550
|
+
const color = colors.get(`runs[${index}].color`) ?? (run.color === void 0 ? colors.get("color") : void 0);
|
|
5551
|
+
const bold = run.bold ?? props.bold;
|
|
5552
|
+
const italic = run.italic ?? props.italic;
|
|
5553
|
+
const strike = run.strike ?? props.strike;
|
|
5554
|
+
const underline = compileUnderline(
|
|
5555
|
+
run.underline ?? props.underline,
|
|
5556
|
+
colors.get(
|
|
5557
|
+
run.underline !== void 0 ? `runs[${index}].underline.color` : "underline.color"
|
|
5558
|
+
)
|
|
5559
|
+
);
|
|
5560
|
+
const formatting = {
|
|
5561
|
+
...fontFamily ? { fontFamily } : {},
|
|
5562
|
+
...sizePoints !== void 0 ? { sizeHalfPoints: pointsToHalfPoints(sizePoints) } : {},
|
|
5563
|
+
...color ? { color } : {},
|
|
5564
|
+
...bold !== void 0 ? { bold } : {},
|
|
5565
|
+
...italic !== void 0 ? { italic } : {},
|
|
5566
|
+
...strike !== void 0 ? { strike } : {},
|
|
5567
|
+
...underline ? { underline } : {}
|
|
5568
|
+
};
|
|
5569
|
+
return Object.keys(formatting).length > 0 ? { formatting } : {};
|
|
5570
|
+
}
|
|
5571
|
+
var UNDERLINE_TYPES = {
|
|
5572
|
+
sng: "single",
|
|
5573
|
+
dbl: "double",
|
|
5574
|
+
dash: "dash",
|
|
5575
|
+
dotted: "dotted"
|
|
5576
|
+
};
|
|
5577
|
+
function compileUnderline(value, color) {
|
|
5578
|
+
if (value === void 0 || value === false) return void 0;
|
|
5579
|
+
const style = value === true ? "sng" : value.style ?? "sng";
|
|
5580
|
+
return {
|
|
5581
|
+
type: UNDERLINE_TYPES[style] ?? "single",
|
|
5582
|
+
...color ? { color } : {}
|
|
5583
|
+
};
|
|
5584
|
+
}
|
|
5585
|
+
function underlineColor(value) {
|
|
5586
|
+
return typeof value === "object" ? value.color : void 0;
|
|
5587
|
+
}
|
|
5588
|
+
function derivedTextHeightInches(props, runs) {
|
|
5589
|
+
const fontSize = props.fontSize ?? ASSUMED_FONT_SIZE_POINTS;
|
|
5590
|
+
const lineCount = runs.reduce(
|
|
5591
|
+
(total, run) => total + (run.breakLine ? 1 : 0) + (run.text.match(/\n/g)?.length ?? 0),
|
|
5592
|
+
1
|
|
5593
|
+
);
|
|
5594
|
+
return Math.max(
|
|
5595
|
+
MIN_DERIVED_TEXT_HEIGHT_INCHES,
|
|
5596
|
+
fontSize / 72 * DERIVED_LINE_HEIGHT_RATIO * lineCount
|
|
5597
|
+
);
|
|
5598
|
+
}
|
|
5599
|
+
function compileShape(props, at, canvas, deps) {
|
|
5600
|
+
const segments = shapeSegments(props);
|
|
5601
|
+
const colors = resolveColors(
|
|
5602
|
+
[
|
|
5603
|
+
["fill.color", props.fill?.color],
|
|
5604
|
+
["line.color", props.line?.color],
|
|
5605
|
+
["fontColor", props.fontColor],
|
|
5606
|
+
...segments.map(
|
|
5607
|
+
(segment, i) => [`text[${i}].color`, segment.color]
|
|
5608
|
+
)
|
|
5609
|
+
],
|
|
5610
|
+
at,
|
|
5611
|
+
deps
|
|
5612
|
+
);
|
|
5613
|
+
if (!colors) return void 0;
|
|
5614
|
+
const frame = resolveFrame(props, canvas, { defaultHeightInches: 0 });
|
|
5615
|
+
if (props.flipH) frame.flipHorizontal = true;
|
|
5616
|
+
if (props.flipV) frame.flipVertical = true;
|
|
5617
|
+
const dash = props.line?.dashType;
|
|
5618
|
+
if (dash !== void 0 && !DASH_VALUES.has(dash)) {
|
|
5619
|
+
deps.reject(`${at}.line.dashType "${dash}"`);
|
|
5620
|
+
return void 0;
|
|
5621
|
+
}
|
|
5622
|
+
const outline = props.line ? {
|
|
5623
|
+
...colors.get("line.color") ? { color: colors.get("line.color") } : {},
|
|
5624
|
+
...props.line.width !== void 0 ? { widthEmu: pointsToEmu(props.line.width) } : {},
|
|
5625
|
+
...dash && dash !== "solid" ? { dash } : {}
|
|
5626
|
+
} : void 0;
|
|
5627
|
+
const text = segments.length ? {
|
|
5628
|
+
paragraphs: shapeParagraphs(segments, props, colors, at),
|
|
5629
|
+
anchor: ANCHOR[props.valign ?? "middle"] ?? "middle",
|
|
5630
|
+
// Unstated insets are left unstated so OOXML's own shape defaults
|
|
5631
|
+
// apply, which is what the raster path draws.
|
|
5632
|
+
...marginInsets(props.margin) ? { insetsEmu: marginInsets(props.margin) } : {}
|
|
5633
|
+
} : void 0;
|
|
5634
|
+
return {
|
|
5635
|
+
kind: "shape",
|
|
5636
|
+
frame,
|
|
5637
|
+
geometry: GEOMETRY_ALIASES[props.type] ?? props.type,
|
|
5638
|
+
...props.fill ? {
|
|
5639
|
+
fill: props.fill.color ? solidFill(colors.get("fill.color"), props.fill.transparency) : { kind: "none" }
|
|
5640
|
+
} : {},
|
|
5641
|
+
...outline && Object.keys(outline).length > 0 ? { outline } : {},
|
|
5642
|
+
...text ? { text } : {},
|
|
5643
|
+
name: `Shape ${at}`
|
|
5644
|
+
};
|
|
5645
|
+
}
|
|
5646
|
+
function shapeSegments(props) {
|
|
5647
|
+
if (props.text === void 0) return [];
|
|
5648
|
+
if (typeof props.text === "string") {
|
|
5649
|
+
return props.text ? [{ text: props.text }] : [];
|
|
5650
|
+
}
|
|
5651
|
+
return props.text;
|
|
5652
|
+
}
|
|
5653
|
+
function shapeParagraphs(segments, props, colors, at) {
|
|
5654
|
+
const paragraphs = [];
|
|
5655
|
+
let current = [];
|
|
5656
|
+
const flush = () => {
|
|
5657
|
+
paragraphs.push({
|
|
5658
|
+
kind: "paragraph",
|
|
5659
|
+
id: `${at}.p${paragraphs.length}`,
|
|
5660
|
+
path: `${at}.paragraphs[${paragraphs.length}]`,
|
|
5661
|
+
children: current,
|
|
5662
|
+
formatting: {
|
|
5663
|
+
// A shape centres its text unless told otherwise, which is what the
|
|
5664
|
+
// rasterized shape draws.
|
|
5665
|
+
alignment: props.align ? ALIGNMENT[props.align] : "center",
|
|
5666
|
+
spacing: { beforeTwips: 0, afterTwips: 0 }
|
|
5667
|
+
}
|
|
5668
|
+
});
|
|
5669
|
+
current = [];
|
|
5670
|
+
};
|
|
5671
|
+
segments.forEach((segment, index) => {
|
|
5672
|
+
const fontFamily = segment.fontFace ?? props.fontFace;
|
|
5673
|
+
const sizePoints = segment.fontSize ?? props.fontSize;
|
|
5674
|
+
const color = colors.get(`text[${index}].color`) ?? (segment.color === void 0 ? colors.get("fontColor") : void 0);
|
|
5675
|
+
const bold = segment.bold ?? props.bold;
|
|
5676
|
+
const italic = segment.italic ?? props.italic;
|
|
5677
|
+
const formatting = {
|
|
5678
|
+
...fontFamily ? { fontFamily } : {},
|
|
5679
|
+
...sizePoints !== void 0 ? { sizeHalfPoints: pointsToHalfPoints(sizePoints) } : {},
|
|
5680
|
+
...color ? { color } : {},
|
|
5681
|
+
...bold !== void 0 ? { bold } : {},
|
|
5682
|
+
...italic !== void 0 ? { italic } : {}
|
|
5683
|
+
};
|
|
5684
|
+
const wrapped = Object.keys(formatting).length > 0 ? { formatting } : void 0;
|
|
5685
|
+
segment.text.split("\n").forEach((line, lineIndex) => {
|
|
5686
|
+
if (lineIndex > 0) current.push({ kind: "lineBreak" });
|
|
5687
|
+
if (line) current.push({ kind: "text", text: line, ...wrapped });
|
|
5688
|
+
});
|
|
5689
|
+
if (segment.breakLine && index < segments.length - 1) flush();
|
|
5690
|
+
});
|
|
5691
|
+
flush();
|
|
5692
|
+
return paragraphs;
|
|
5693
|
+
}
|
|
5694
|
+
function compileImage(props, at, canvas, deps) {
|
|
5695
|
+
const source = resolveImageSource(props);
|
|
5696
|
+
if (!source) {
|
|
5697
|
+
deps.reject(`${at} has none of "path", "base64" or "svg"`);
|
|
5698
|
+
return void 0;
|
|
5699
|
+
}
|
|
5700
|
+
const resource = deps.picture(source);
|
|
5701
|
+
if (!resource) {
|
|
5702
|
+
deps.reject(`${at} image could not be loaded`);
|
|
5703
|
+
return void 0;
|
|
5704
|
+
}
|
|
5705
|
+
const aspect = resource.intrinsic ? resource.intrinsic.width / resource.intrinsic.height : void 0;
|
|
5706
|
+
const intrinsicInches = resource.intrinsic ? {
|
|
5707
|
+
width: resource.intrinsic.width / PIXELS_PER_INCH2,
|
|
5708
|
+
height: resource.intrinsic.height / PIXELS_PER_INCH2
|
|
5709
|
+
} : void 0;
|
|
5710
|
+
const box = resolveImageBox(props, canvas, aspect, intrinsicInches);
|
|
5711
|
+
const sizing = props.sizing;
|
|
5712
|
+
if (!sizing || !aspect) {
|
|
5713
|
+
if (sizing && !aspect) {
|
|
5714
|
+
deps.warn(
|
|
5715
|
+
`[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.`
|
|
5716
|
+
);
|
|
5717
|
+
}
|
|
5718
|
+
return picture(box, props, resource, at);
|
|
5719
|
+
}
|
|
5720
|
+
const boxAspect = box.widthEmu / box.heightEmu;
|
|
5721
|
+
if (sizing.type === "contain") {
|
|
5722
|
+
const fitted = boxAspect > aspect ? {
|
|
5723
|
+
widthEmu: Math.round(box.heightEmu * aspect),
|
|
5724
|
+
heightEmu: box.heightEmu
|
|
5725
|
+
} : {
|
|
5726
|
+
widthEmu: box.widthEmu,
|
|
5727
|
+
heightEmu: Math.round(box.widthEmu / aspect)
|
|
5728
|
+
};
|
|
5729
|
+
return picture(
|
|
5730
|
+
{
|
|
5731
|
+
...box,
|
|
5732
|
+
xEmu: box.xEmu + Math.round((box.widthEmu - fitted.widthEmu) / 2),
|
|
5733
|
+
yEmu: box.yEmu + Math.round((box.heightEmu - fitted.heightEmu) / 2),
|
|
5734
|
+
...fitted
|
|
5735
|
+
},
|
|
5736
|
+
props,
|
|
5737
|
+
resource,
|
|
5738
|
+
at
|
|
5739
|
+
);
|
|
5740
|
+
}
|
|
5741
|
+
const visible = boxAspect > aspect ? { widthFraction: 1, heightFraction: aspect / boxAspect } : { widthFraction: boxAspect / aspect, heightFraction: 1 };
|
|
5742
|
+
const crop = sizing.type === "cover" ? {
|
|
5743
|
+
...visible.widthFraction < 1 ? {
|
|
5744
|
+
left: (1 - visible.widthFraction) / 2,
|
|
5745
|
+
right: (1 - visible.widthFraction) / 2
|
|
5746
|
+
} : {},
|
|
5747
|
+
...visible.heightFraction < 1 ? {
|
|
5748
|
+
top: (1 - visible.heightFraction) / 2,
|
|
5749
|
+
bottom: (1 - visible.heightFraction) / 2
|
|
5750
|
+
} : {}
|
|
5751
|
+
} : {
|
|
5752
|
+
...visible.widthFraction < 1 ? { right: 1 - visible.widthFraction } : {},
|
|
5753
|
+
...visible.heightFraction < 1 ? { bottom: 1 - visible.heightFraction } : {}
|
|
5754
|
+
};
|
|
5755
|
+
return picture(box, props, resource, at, crop);
|
|
5756
|
+
}
|
|
5757
|
+
function picture(frame, props, resource, at, crop) {
|
|
5758
|
+
return {
|
|
5759
|
+
kind: "picture",
|
|
5760
|
+
frame,
|
|
5761
|
+
resourceId: resource.resourceId,
|
|
5762
|
+
...props.alt ? { altText: props.alt } : {},
|
|
5763
|
+
...crop && Object.keys(crop).length > 0 ? { crop } : {},
|
|
5764
|
+
name: `Image ${at}`
|
|
5765
|
+
};
|
|
5766
|
+
}
|
|
5767
|
+
function resolveImageBox(props, canvas, aspect, intrinsicInches) {
|
|
5768
|
+
const width = props.sizing?.w ?? props.w;
|
|
5769
|
+
const height = props.sizing?.h ?? props.h;
|
|
5770
|
+
const widthEmu = width !== void 0 ? resolveExtent(width, canvas.widthEmu) : void 0;
|
|
5771
|
+
const heightEmu = height !== void 0 ? resolveExtent(height, canvas.heightEmu) : void 0;
|
|
5772
|
+
const resolved = (() => {
|
|
5773
|
+
if (widthEmu !== void 0 && heightEmu !== void 0) {
|
|
5774
|
+
return { widthEmu, heightEmu };
|
|
5775
|
+
}
|
|
5776
|
+
if (widthEmu !== void 0) {
|
|
5777
|
+
return {
|
|
5778
|
+
widthEmu,
|
|
5779
|
+
heightEmu: aspect ? Math.round(widthEmu / aspect) : Math.round(widthEmu * (3 / 4))
|
|
5780
|
+
};
|
|
5781
|
+
}
|
|
5782
|
+
if (heightEmu !== void 0) {
|
|
5783
|
+
return {
|
|
5784
|
+
widthEmu: aspect ? Math.round(heightEmu * aspect) : Math.round(heightEmu * (4 / 3)),
|
|
5785
|
+
heightEmu
|
|
5786
|
+
};
|
|
5787
|
+
}
|
|
5788
|
+
if (intrinsicInches) {
|
|
5789
|
+
return {
|
|
5790
|
+
widthEmu: inchesToEmu(intrinsicInches.width),
|
|
5791
|
+
heightEmu: inchesToEmu(intrinsicInches.height)
|
|
5792
|
+
};
|
|
5793
|
+
}
|
|
5794
|
+
const fallbackWidth = Math.round(canvas.widthEmu * DEFAULT_WIDTH_FRACTION);
|
|
5795
|
+
return {
|
|
5796
|
+
widthEmu: fallbackWidth,
|
|
5797
|
+
heightEmu: Math.round(fallbackWidth * (3 / 4))
|
|
5798
|
+
};
|
|
5799
|
+
})();
|
|
5800
|
+
return {
|
|
5801
|
+
xEmu: props.x !== void 0 ? resolveOffset(props.x, canvas.widthEmu) : 0,
|
|
5802
|
+
yEmu: props.y !== void 0 ? resolveOffset(props.y, canvas.heightEmu) : 0,
|
|
5803
|
+
...resolved,
|
|
5804
|
+
...rotation(props.rotate)
|
|
5805
|
+
};
|
|
5806
|
+
}
|
|
5807
|
+
function fullBleed(canvas) {
|
|
5808
|
+
return {
|
|
5809
|
+
xEmu: 0,
|
|
5810
|
+
yEmu: 0,
|
|
5811
|
+
widthEmu: canvas.widthEmu,
|
|
5812
|
+
heightEmu: canvas.heightEmu
|
|
5813
|
+
};
|
|
5814
|
+
}
|
|
5815
|
+
function resolveFrame(props, canvas, options) {
|
|
5816
|
+
return {
|
|
5817
|
+
xEmu: props.x !== void 0 ? resolveOffset(props.x, canvas.widthEmu) : 0,
|
|
5818
|
+
yEmu: props.y !== void 0 ? resolveOffset(props.y, canvas.heightEmu) : 0,
|
|
5819
|
+
widthEmu: props.w !== void 0 ? resolveExtent(props.w, canvas.widthEmu) : Math.round(canvas.widthEmu * DEFAULT_WIDTH_FRACTION),
|
|
5820
|
+
heightEmu: props.h !== void 0 ? resolveExtent(props.h, canvas.heightEmu) : inchesToEmu(options.defaultHeightInches),
|
|
5821
|
+
...rotation(props.rotate)
|
|
5822
|
+
};
|
|
5823
|
+
}
|
|
5824
|
+
function rotation(degrees) {
|
|
5825
|
+
if (degrees === void 0 || degrees % 360 === 0) return {};
|
|
5826
|
+
return { rotationDegrees: degrees };
|
|
5827
|
+
}
|
|
5828
|
+
function resolveOffset(value, axisEmu) {
|
|
5829
|
+
return resolveLength(value, axisEmu);
|
|
5830
|
+
}
|
|
5831
|
+
function resolveExtent(value, axisEmu) {
|
|
5832
|
+
return Math.max(0, resolveLength(value, axisEmu));
|
|
5833
|
+
}
|
|
5834
|
+
function resolveLength(value, axisEmu) {
|
|
5835
|
+
if (typeof value === "number") return inchesToEmu(value);
|
|
5836
|
+
const percent = Number.parseFloat(value);
|
|
5837
|
+
if (!Number.isFinite(percent)) return 0;
|
|
5838
|
+
return Math.round(percent / 100 * axisEmu);
|
|
5839
|
+
}
|
|
5840
|
+
function marginInsets(margin) {
|
|
5841
|
+
if (margin === void 0) return void 0;
|
|
5842
|
+
const [top, right, bottom, left] = Array.isArray(margin) ? margin : [margin, margin, margin, margin];
|
|
5843
|
+
return {
|
|
5844
|
+
top: pointsToEmu(top ?? 0),
|
|
5845
|
+
right: pointsToEmu(right ?? 0),
|
|
5846
|
+
bottom: pointsToEmu(bottom ?? 0),
|
|
5847
|
+
left: pointsToEmu(left ?? 0)
|
|
5848
|
+
};
|
|
5849
|
+
}
|
|
5850
|
+
function solidFill(color, transparency) {
|
|
5851
|
+
return {
|
|
5852
|
+
kind: "solid",
|
|
5853
|
+
color,
|
|
5854
|
+
...transparency !== void 0 && transparency > 0 ? { transparencyPercent: transparency } : {}
|
|
5855
|
+
};
|
|
5856
|
+
}
|
|
5857
|
+
function resolveColors(entries, at, deps) {
|
|
5858
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
5859
|
+
let failed = false;
|
|
5860
|
+
for (const [key, value] of entries) {
|
|
5861
|
+
if (value === void 0) continue;
|
|
5862
|
+
const color = deps.color(value);
|
|
5863
|
+
if (!color) {
|
|
5864
|
+
deps.reject(`${at}.${key} "${value}"`);
|
|
5865
|
+
failed = true;
|
|
5866
|
+
continue;
|
|
5867
|
+
}
|
|
5868
|
+
resolved.set(key, color);
|
|
5869
|
+
}
|
|
5870
|
+
return failed ? void 0 : resolved;
|
|
5871
|
+
}
|
|
5151
5872
|
|
|
5152
5873
|
// src/styles/themeToStyles.ts
|
|
5153
5874
|
init_themes();
|
|
@@ -6990,7 +7711,7 @@ function decoratedStyle(match, base) {
|
|
|
6990
7711
|
function pushSegment(out, text, options, override = {}) {
|
|
6991
7712
|
const bold = override.bold ?? options.base.bold;
|
|
6992
7713
|
const italic = override.italic ?? options.base.italic;
|
|
6993
|
-
const formatting =
|
|
7714
|
+
const formatting = runFormatting2(options, { bold, italic });
|
|
6994
7715
|
const lines = text.split("\n");
|
|
6995
7716
|
for (const [lineIndex, line] of lines.entries()) {
|
|
6996
7717
|
if (!line && lineIndex === 0) continue;
|
|
@@ -7068,7 +7789,7 @@ function pushNoProofWords(out, text, formatting, options) {
|
|
|
7068
7789
|
out.push({ kind: "text", text: text.slice(lastIndex), formatting });
|
|
7069
7790
|
}
|
|
7070
7791
|
}
|
|
7071
|
-
function
|
|
7792
|
+
function runFormatting2(options, state) {
|
|
7072
7793
|
const formatting = { ...options.base };
|
|
7073
7794
|
if (state.bold !== void 0) formatting.bold = state.bold;
|
|
7074
7795
|
if (state.italic !== void 0) formatting.italic = state.italic;
|
|
@@ -7440,7 +8161,9 @@ function compileComponent(component, scope) {
|
|
|
7440
8161
|
case "columns":
|
|
7441
8162
|
return compileColumns(component, scope);
|
|
7442
8163
|
case "image":
|
|
7443
|
-
return
|
|
8164
|
+
return compileImage2(component, scope);
|
|
8165
|
+
case "visual":
|
|
8166
|
+
return compileVisual(component, scope);
|
|
7444
8167
|
case "table":
|
|
7445
8168
|
return compileTable(component, scope);
|
|
7446
8169
|
default:
|
|
@@ -7684,7 +8407,7 @@ function compileTextBox(component, scope) {
|
|
|
7684
8407
|
const style = props.style;
|
|
7685
8408
|
const padding = style?.padding;
|
|
7686
8409
|
if (props.renderAs === "shape") {
|
|
7687
|
-
const shape =
|
|
8410
|
+
const shape = compileShape2(props, children, scope);
|
|
7688
8411
|
if (shape) return shape;
|
|
7689
8412
|
}
|
|
7690
8413
|
ctx.features.require("tables", path4);
|
|
@@ -7738,7 +8461,7 @@ function compileTextBox(component, scope) {
|
|
|
7738
8461
|
];
|
|
7739
8462
|
}
|
|
7740
8463
|
var EMU_PER_PIXEL = 9525;
|
|
7741
|
-
function
|
|
8464
|
+
function compileShape2(props, children, scope) {
|
|
7742
8465
|
const { ctx, path: path4 } = scope;
|
|
7743
8466
|
const width = shapeSize(props.width, "width", ctx);
|
|
7744
8467
|
const height = shapeSize(props.height, "height", ctx);
|
|
@@ -8232,7 +8955,7 @@ function reportUnlowered(component, props, text, scope) {
|
|
|
8232
8955
|
}
|
|
8233
8956
|
function compileRuns(props, text, ctx, path4, mode = "inline") {
|
|
8234
8957
|
const font = props.font ?? {};
|
|
8235
|
-
const base =
|
|
8958
|
+
const base = runFormatting3(font, props, ctx);
|
|
8236
8959
|
if (base.language) ctx.features.require("proofing-language", path4);
|
|
8237
8960
|
if (base.noProof) ctx.features.require("proofing-language", path4);
|
|
8238
8961
|
const words = mergeNoProofWords(noProofWords(ctx.theme), props.noProofWords);
|
|
@@ -8502,7 +9225,7 @@ function isoDate(date) {
|
|
|
8502
9225
|
function isoDateTime(date) {
|
|
8503
9226
|
return date.toISOString().replace("T", " ").replace(/\.\d{3}Z$/, "Z");
|
|
8504
9227
|
}
|
|
8505
|
-
function
|
|
9228
|
+
function runFormatting3(font, props, ctx) {
|
|
8506
9229
|
const hasWeightRequest = font.fontWeight != null || font.bold === true;
|
|
8507
9230
|
const effectiveFamily = font.family ?? (hasWeightRequest ? resolveBodyFamily(ctx.theme) : void 0);
|
|
8508
9231
|
const weighted = applyFontWeightAlias({
|
|
@@ -8665,8 +9388,107 @@ function headingLevel(level) {
|
|
|
8665
9388
|
const value = typeof level === "number" ? level : 1;
|
|
8666
9389
|
return value >= 1 && value <= 6 ? value : 1;
|
|
8667
9390
|
}
|
|
9391
|
+
function compileVisual(component, scope) {
|
|
9392
|
+
const { ctx, path: path4 } = scope;
|
|
9393
|
+
const props = component.props ?? {};
|
|
9394
|
+
if (!isNativeVisualProps4(props)) {
|
|
9395
|
+
ctx.unsupported.push({
|
|
9396
|
+
name: "visual",
|
|
9397
|
+
path: path4,
|
|
9398
|
+
detail: "a raster visual reached the compiler; it should have been rasterized during desugaring"
|
|
9399
|
+
});
|
|
9400
|
+
return [];
|
|
9401
|
+
}
|
|
9402
|
+
const group = compileNativeVisualGroup(
|
|
9403
|
+
props,
|
|
9404
|
+
nativeVisualDeps(ctx, path4)
|
|
9405
|
+
);
|
|
9406
|
+
if (!group) return [];
|
|
9407
|
+
const size = visualPlacementPixels(props, group.canvas, {
|
|
9408
|
+
widthPx: Math.round(twipsToPixels(getAvailableWidthTwips(ctx.theme))),
|
|
9409
|
+
heightPx: Math.round(twipsToPixels(getAvailableHeightTwips(ctx.theme)))
|
|
9410
|
+
});
|
|
9411
|
+
const drawing = {
|
|
9412
|
+
kind: "drawingGroup",
|
|
9413
|
+
widthEmu: pixelsToEmu(size.width),
|
|
9414
|
+
heightEmu: pixelsToEmu(size.height),
|
|
9415
|
+
canvasWidthEmu: group.canvas.widthEmu,
|
|
9416
|
+
canvasHeightEmu: group.canvas.heightEmu,
|
|
9417
|
+
children: group.children,
|
|
9418
|
+
...typeof props.alt === "string" && props.alt ? { altText: props.alt } : {},
|
|
9419
|
+
...props.floating ? { floating: compileFloating(props.floating, ctx, path4) } : {}
|
|
9420
|
+
};
|
|
9421
|
+
ctx.features.require("drawing-groups", path4);
|
|
9422
|
+
if (props.floating) ctx.features.require("floating-images", path4);
|
|
9423
|
+
const spacing = {};
|
|
9424
|
+
if (props.spacing?.before !== void 0) {
|
|
9425
|
+
spacing.beforeTwips = pointsToTwips2(props.spacing.before);
|
|
9426
|
+
}
|
|
9427
|
+
if (props.spacing?.after !== void 0) {
|
|
9428
|
+
spacing.afterTwips = pointsToTwips2(props.spacing.after);
|
|
9429
|
+
}
|
|
9430
|
+
const blocks = [
|
|
9431
|
+
{
|
|
9432
|
+
kind: "paragraph",
|
|
9433
|
+
id: scope.id,
|
|
9434
|
+
path: path4,
|
|
9435
|
+
children: [drawing],
|
|
9436
|
+
formatting: {
|
|
9437
|
+
// A floating drawing is anchored, so aligning its paragraph would move
|
|
9438
|
+
// the anchor rather than the graphic — the same rule an image follows.
|
|
9439
|
+
...props.floating ? {} : { alignment: compileAlignment(props.alignment) ?? "center" },
|
|
9440
|
+
...Object.keys(spacing).length > 0 ? { spacing } : {},
|
|
9441
|
+
...props.keepNext !== void 0 ? { keepNext: props.keepNext } : {},
|
|
9442
|
+
...props.keepLines !== void 0 ? { keepLines: props.keepLines } : {}
|
|
9443
|
+
}
|
|
9444
|
+
}
|
|
9445
|
+
];
|
|
9446
|
+
const caption = captionBlock(props.caption, scope, "visual");
|
|
9447
|
+
if (caption === void 0) return [];
|
|
9448
|
+
if (caption) blocks.push(caption);
|
|
9449
|
+
return blocks;
|
|
9450
|
+
}
|
|
9451
|
+
function nativeVisualDeps(ctx, path4) {
|
|
9452
|
+
return {
|
|
9453
|
+
color: (value) => {
|
|
9454
|
+
try {
|
|
9455
|
+
return irColor(resolveColor(value, ctx.theme));
|
|
9456
|
+
} catch {
|
|
9457
|
+
return void 0;
|
|
9458
|
+
}
|
|
9459
|
+
},
|
|
9460
|
+
picture: (source) => {
|
|
9461
|
+
const loaded = ctx.images.get(source);
|
|
9462
|
+
if (!loaded) return void 0;
|
|
9463
|
+
const mediaType = detectImageType(source, loaded.contentType);
|
|
9464
|
+
if (mediaType === "svg") ctx.features.require("svg-images", path4);
|
|
9465
|
+
return {
|
|
9466
|
+
resourceId: declareResource(loaded, mediaType, ctx),
|
|
9467
|
+
mediaType,
|
|
9468
|
+
...loaded.intrinsic ? { intrinsic: loaded.intrinsic } : {}
|
|
9469
|
+
};
|
|
9470
|
+
},
|
|
9471
|
+
reject: (detail) => ctx.unsupported.push({ name: "visual", path: path4, detail }),
|
|
9472
|
+
warn: (message) => warnOnce(ctx, `visual:${message}`, message)
|
|
9473
|
+
};
|
|
9474
|
+
}
|
|
9475
|
+
function visualPlacementPixels(props, canvas, reference) {
|
|
9476
|
+
const canvasWidthPx = Math.round(emuToPixels(canvas.widthEmu));
|
|
9477
|
+
const canvasHeightPx = Math.round(emuToPixels(canvas.heightEmu));
|
|
9478
|
+
const targetWidth = props.width !== void 0 ? parseWidthValue(props.width, reference.widthPx) : void 0;
|
|
9479
|
+
const targetHeight = props.height !== void 0 ? parseDimensionValue(props.height, reference.heightPx) : void 0;
|
|
9480
|
+
if (targetWidth === void 0 && targetHeight === void 0) {
|
|
9481
|
+
return { width: canvasWidthPx, height: canvasHeightPx };
|
|
9482
|
+
}
|
|
9483
|
+
return calculateMissingDimension(
|
|
9484
|
+
canvasWidthPx,
|
|
9485
|
+
canvasHeightPx,
|
|
9486
|
+
targetWidth,
|
|
9487
|
+
targetHeight
|
|
9488
|
+
);
|
|
9489
|
+
}
|
|
8668
9490
|
var UNLOWERED_IMAGE_PROPS = ["comment"];
|
|
8669
|
-
function
|
|
9491
|
+
function compileImage2(component, scope) {
|
|
8670
9492
|
const { ctx, path: path4 } = scope;
|
|
8671
9493
|
const props = component.props ?? {};
|
|
8672
9494
|
for (const prop of UNLOWERED_IMAGE_PROPS) {
|
|
@@ -8727,32 +9549,37 @@ function compileImage(component, scope) {
|
|
|
8727
9549
|
}
|
|
8728
9550
|
}
|
|
8729
9551
|
];
|
|
8730
|
-
|
|
8731
|
-
|
|
8732
|
-
|
|
8733
|
-
if (syntax) {
|
|
8734
|
-
ctx.unsupported.push({ name: "image", path: path4, detail: syntax });
|
|
8735
|
-
return [];
|
|
8736
|
-
}
|
|
8737
|
-
blocks.push({
|
|
8738
|
-
kind: "paragraph",
|
|
8739
|
-
id: `${scope.id}:caption`,
|
|
8740
|
-
path: `${path4}.caption`,
|
|
8741
|
-
styleId: "Normal",
|
|
8742
|
-
// Captions default to left alignment, whatever the figure did.
|
|
8743
|
-
formatting: { alignment: "left" },
|
|
8744
|
-
// A caption reaches the parser only when it carries a decorator, which
|
|
8745
|
-
// is why a link in an otherwise plain caption stays literal.
|
|
8746
|
-
children: DECORATED.test(caption) ? parseInline(caption, {
|
|
8747
|
-
base: {},
|
|
8748
|
-
hyperlinks: true,
|
|
8749
|
-
resolvePlaceholder: placeholderResolver(ctx),
|
|
8750
|
-
resolveCrossReference: crossReferenceResolver(ctx, path4)
|
|
8751
|
-
}) : parseLiteral(caption, { base: {} })
|
|
8752
|
-
});
|
|
8753
|
-
}
|
|
9552
|
+
const caption = captionBlock(props.caption, scope, "image");
|
|
9553
|
+
if (caption === void 0) return [];
|
|
9554
|
+
if (caption) blocks.push(caption);
|
|
8754
9555
|
return blocks;
|
|
8755
9556
|
}
|
|
9557
|
+
function captionBlock(value, scope, componentName) {
|
|
9558
|
+
if (!value) return null;
|
|
9559
|
+
const { ctx, path: path4 } = scope;
|
|
9560
|
+
const caption = String(value);
|
|
9561
|
+
const syntax = containsUnsupportedSyntax(caption);
|
|
9562
|
+
if (syntax) {
|
|
9563
|
+
ctx.unsupported.push({ name: componentName, path: path4, detail: syntax });
|
|
9564
|
+
return void 0;
|
|
9565
|
+
}
|
|
9566
|
+
return {
|
|
9567
|
+
kind: "paragraph",
|
|
9568
|
+
id: `${scope.id}:caption`,
|
|
9569
|
+
path: `${path4}.caption`,
|
|
9570
|
+
styleId: "Normal",
|
|
9571
|
+
// Captions default to left alignment, whatever the figure did.
|
|
9572
|
+
formatting: { alignment: "left" },
|
|
9573
|
+
// A caption reaches the parser only when it carries a decorator, which
|
|
9574
|
+
// is why a link in an otherwise plain caption stays literal.
|
|
9575
|
+
children: DECORATED.test(caption) ? parseInline(caption, {
|
|
9576
|
+
base: {},
|
|
9577
|
+
hyperlinks: true,
|
|
9578
|
+
resolvePlaceholder: placeholderResolver(ctx),
|
|
9579
|
+
resolveCrossReference: crossReferenceResolver(ctx, path4)
|
|
9580
|
+
}) : parseLiteral(caption, { base: {} })
|
|
9581
|
+
};
|
|
9582
|
+
}
|
|
8756
9583
|
function relativeFrom(value, axis) {
|
|
8757
9584
|
const allowed = axis === "horizontal" ? ["character", "column", "margin", "page"] : ["margin", "page", "paragraph", "line"];
|
|
8758
9585
|
return typeof value === "string" && allowed.includes(value) ? value : void 0;
|
|
@@ -9176,6 +10003,9 @@ function cellChildren(cell, baseStyle, scope, rowRevision) {
|
|
|
9176
10003
|
if (content.name === "image") {
|
|
9177
10004
|
return wrap(cellImage(content, base, ctx));
|
|
9178
10005
|
}
|
|
10006
|
+
if (content.name === "visual") {
|
|
10007
|
+
return wrap(cellVisual(content, scope));
|
|
10008
|
+
}
|
|
9179
10009
|
if (content.name !== "paragraph") {
|
|
9180
10010
|
return wrap([
|
|
9181
10011
|
{
|
|
@@ -9215,6 +10045,40 @@ function cellChildren(cell, baseStyle, scope, rowRevision) {
|
|
|
9215
10045
|
})
|
|
9216
10046
|
);
|
|
9217
10047
|
}
|
|
10048
|
+
function cellVisual(component, scope) {
|
|
10049
|
+
const { ctx, path: path4 } = scope;
|
|
10050
|
+
const props = component.props ?? {};
|
|
10051
|
+
if (!isNativeVisualProps4(props)) {
|
|
10052
|
+
ctx.unsupported.push({
|
|
10053
|
+
name: "visual",
|
|
10054
|
+
path: path4,
|
|
10055
|
+
detail: "a raster visual reached the compiler; it should have been rasterized during desugaring"
|
|
10056
|
+
});
|
|
10057
|
+
return [];
|
|
10058
|
+
}
|
|
10059
|
+
const group = compileNativeVisualGroup(
|
|
10060
|
+
props,
|
|
10061
|
+
nativeVisualDeps(ctx, path4)
|
|
10062
|
+
);
|
|
10063
|
+
if (!group) return [];
|
|
10064
|
+
const size = visualPlacementPixels(props, group.canvas, {
|
|
10065
|
+
widthPx: CELL_DRAWING_REFERENCE.width,
|
|
10066
|
+
heightPx: CELL_DRAWING_REFERENCE.height
|
|
10067
|
+
});
|
|
10068
|
+
ctx.features.require("drawing-groups", path4);
|
|
10069
|
+
return [
|
|
10070
|
+
{
|
|
10071
|
+
kind: "drawingGroup",
|
|
10072
|
+
widthEmu: pixelsToEmu(size.width),
|
|
10073
|
+
heightEmu: pixelsToEmu(size.height),
|
|
10074
|
+
canvasWidthEmu: group.canvas.widthEmu,
|
|
10075
|
+
canvasHeightEmu: group.canvas.heightEmu,
|
|
10076
|
+
children: group.children,
|
|
10077
|
+
...typeof props.alt === "string" && props.alt ? { altText: props.alt } : {}
|
|
10078
|
+
}
|
|
10079
|
+
];
|
|
10080
|
+
}
|
|
10081
|
+
var CELL_DRAWING_REFERENCE = { width: 300, height: 200 };
|
|
9218
10082
|
function cellImage(component, base, ctx) {
|
|
9219
10083
|
const props = component.props ?? {};
|
|
9220
10084
|
const source = resolveImageSource(props);
|
|
@@ -10330,6 +11194,9 @@ import {
|
|
|
10330
11194
|
clampVisualDpi as clampVisualDpi3,
|
|
10331
11195
|
DEFAULT_VISUAL_DPI as DEFAULT_VISUAL_DPI3
|
|
10332
11196
|
} from "@json-to-office/shared";
|
|
11197
|
+
import {
|
|
11198
|
+
isNativeVisualProps as isNativeVisualProps5
|
|
11199
|
+
} from "@json-to-office/shared-docx";
|
|
10333
11200
|
var DEFAULT_CONCURRENCY = 4;
|
|
10334
11201
|
async function flattenVisuals(doc, options) {
|
|
10335
11202
|
let rasterize = options.rasterize;
|
|
@@ -10375,8 +11242,10 @@ async function flattenVisuals(doc, options) {
|
|
|
10375
11242
|
doc,
|
|
10376
11243
|
async (node) => (
|
|
10377
11244
|
// A disabled visual is left as-is: it is filtered out at render anyway, so
|
|
10378
|
-
// rasterizing it would be wasted work.
|
|
10379
|
-
|
|
11245
|
+
// rasterizing it would be wasted work. A native one is left as-is for a
|
|
11246
|
+
// stronger reason — it is already service-free, and flattening exists to
|
|
11247
|
+
// remove the service dependency, not to trade editable objects for pixels.
|
|
11248
|
+
node.name === "visual" && node.enabled !== false && !isNativeVisualProps5(node.props) ? rasterizeVisual(node, ctx) : void 0
|
|
10380
11249
|
)
|
|
10381
11250
|
);
|
|
10382
11251
|
}
|