reamkit 1.15.4 → 1.17.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/esm/core/bidi/segments.js +1 -2
- package/dist/esm/core/converter/facade.d.ts +5 -0
- package/dist/esm/core/converter/project.js +5 -1
- package/dist/esm/core/converter/ream.d.ts +6 -0
- package/dist/esm/core/converter/ream.js +34 -8
- package/dist/esm/core/document-model/index.d.ts +1 -1
- package/dist/esm/core/document-model/types.d.ts +659 -7
- package/dist/esm/core/drawingml/chart-geometry.d.ts +34 -10
- package/dist/esm/core/drawingml/chart-geometry.js +424 -92
- package/dist/esm/core/drawingml/chart-parser.js +301 -11
- package/dist/esm/core/drawingml/colors.d.ts +7 -7
- package/dist/esm/core/drawingml/colors.js +84 -18
- package/dist/esm/core/drawingml/preset-geometry.js +240 -6
- package/dist/esm/core/drawingml/shape-render.d.ts +12 -0
- package/dist/esm/core/drawingml/shape-render.js +116 -3
- package/dist/esm/core/drawingml/sparkline-geometry.js +6 -4
- package/dist/esm/core/drawingml/theme-parser.d.ts +51 -0
- package/dist/esm/core/drawingml/theme-parser.js +92 -1
- package/dist/esm/core/font/measure.d.ts +5 -0
- package/dist/esm/core/font/measure.js +21 -1
- package/dist/esm/core/font/ttf-subset.js +26 -1
- package/dist/esm/core/images.d.ts +2 -2
- package/dist/esm/core/images.js +353 -43
- package/dist/esm/core/indexed-colors.d.ts +11 -0
- package/dist/esm/core/indexed-colors.js +81 -0
- package/dist/esm/core/ir/flow.d.ts +23 -1
- package/dist/esm/core/ir/sheet.d.ts +61 -0
- package/dist/esm/{excel → core}/number-format.d.ts +37 -0
- package/dist/esm/core/number-format.js +800 -0
- package/dist/esm/core/numbering/apply.d.ts +5 -3
- package/dist/esm/core/numbering/apply.js +42 -11
- package/dist/esm/core/numbering/index.js +2 -0
- package/dist/esm/core/numbering/state.d.ts +13 -1
- package/dist/esm/core/numbering/state.js +256 -15
- package/dist/esm/core/opc/alternate-content.d.ts +13 -0
- package/dist/esm/core/opc/alternate-content.js +72 -0
- package/dist/esm/core/opc/core-properties.js +1 -0
- package/dist/esm/core/opc/package.d.ts +14 -1
- package/dist/esm/core/opc/package.js +43 -4
- package/dist/esm/core/opc/relationships.js +1 -0
- package/dist/esm/core/opc/xml-entities.d.ts +12 -0
- package/dist/esm/core/opc/xml-entities.js +91 -0
- package/dist/esm/core/po-helpers.js +16 -1
- package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
- package/dist/esm/core/spreadsheet-model/types.d.ts +137 -0
- package/dist/esm/core/style-cascade/resolver.js +66 -11
- package/dist/esm/core/style-cascade/table.js +35 -11
- package/dist/esm/core/style-cascade/types.d.ts +42 -1
- package/dist/esm/core/style-cascade/types.js +4 -0
- package/dist/esm/core/vector.d.ts +30 -0
- package/dist/esm/excel/activex-parser.js +1 -0
- package/dist/esm/excel/column-bands.d.ts +26 -1
- package/dist/esm/excel/column-bands.js +156 -20
- package/dist/esm/excel/comments-parser.js +5 -3
- package/dist/esm/excel/conditional-format.d.ts +25 -2
- package/dist/esm/excel/conditional-format.js +63 -19
- package/dist/esm/excel/escaped-text.d.ts +9 -0
- package/dist/esm/excel/escaped-text.js +20 -0
- package/dist/esm/excel/form-control-parser.js +1 -0
- package/dist/esm/excel/formula/dates.js +1 -1
- package/dist/esm/excel/header-footer.d.ts +9 -1
- package/dist/esm/excel/header-footer.js +162 -14
- package/dist/esm/excel/index.d.ts +1 -1
- package/dist/esm/excel/index.js +2 -2
- package/dist/esm/excel/pivot-table-parser.js +1 -0
- package/dist/esm/excel/print-model.d.ts +75 -0
- package/dist/esm/excel/print-model.js +899 -77
- package/dist/esm/excel/printer-settings.d.ts +16 -0
- package/dist/esm/excel/printer-settings.js +36 -0
- package/dist/esm/excel/rich-value.d.ts +13 -0
- package/dist/esm/excel/rich-value.js +120 -0
- package/dist/esm/excel/shared-strings-parser.js +11 -7
- package/dist/esm/excel/sheet-drawing.d.ts +11 -0
- package/dist/esm/excel/sheet-drawing.js +26 -3
- package/dist/esm/excel/sheet-shape-parser.d.ts +53 -1
- package/dist/esm/excel/sheet-shape-parser.js +461 -32
- package/dist/esm/excel/sheet-to-flow.d.ts +23 -2
- package/dist/esm/excel/sheet-to-flow.js +553 -36
- package/dist/esm/excel/slicer-parser.js +1 -0
- package/dist/esm/excel/styles-parser.d.ts +30 -2
- package/dist/esm/excel/styles-parser.js +177 -40
- package/dist/esm/excel/table-parser.js +1 -0
- package/dist/esm/excel/vml-drawing.d.ts +68 -0
- package/dist/esm/excel/vml-drawing.js +158 -0
- package/dist/esm/excel/workbook-parser.d.ts +5 -0
- package/dist/esm/excel/workbook-parser.js +5 -1
- package/dist/esm/excel/worksheet-parser.d.ts +2 -1
- package/dist/esm/excel/worksheet-parser.js +150 -61
- package/dist/esm/excel/xlsx-reader.js +242 -11
- package/dist/esm/excel/xlsx-to-pdf.js +12 -1
- package/dist/esm/html/html-writer.js +29 -6
- package/dist/esm/layout/page-doc.d.ts +87 -1
- package/dist/esm/layout/styled-layout.d.ts +46 -2
- package/dist/esm/layout/styled-layout.js +2345 -274
- package/dist/esm/pdf/cid-font.js +20 -9
- package/dist/esm/pdf/objects.d.ts +13 -0
- package/dist/esm/pdf/objects.js +18 -1
- package/dist/esm/pdf/shading.js +76 -6
- package/dist/esm/pdf/styled-page-emitter.js +355 -49
- package/dist/esm/pdf/text-page-renderer.js +1 -0
- package/dist/esm/pdf/vector-graphics.d.ts +31 -2
- package/dist/esm/pdf/vector-graphics.js +103 -3
- package/dist/esm/pptx/pptx-reader.js +2 -0
- package/dist/esm/pptx/slide-parser.d.ts +21 -4
- package/dist/esm/pptx/slide-parser.js +98 -24
- package/dist/esm/svg/svg-writer.js +23 -4
- package/dist/esm/word/document-parser.d.ts +96 -7
- package/dist/esm/word/document-parser.js +538 -116
- package/dist/esm/word/docx-reader.js +135 -43
- package/dist/esm/word/docx-writer.js +6 -1
- package/dist/esm/word/drawing-parser.d.ts +86 -5
- package/dist/esm/word/drawing-parser.js +1590 -54
- package/dist/esm/word/index.d.ts +4 -4
- package/dist/esm/word/numbering-parser.d.ts +2 -1
- package/dist/esm/word/numbering-parser.js +108 -21
- package/dist/esm/word/paragraph-properties.d.ts +11 -1
- package/dist/esm/word/paragraph-properties.js +219 -10
- package/dist/esm/word/run-properties.js +45 -1
- package/dist/esm/word/settings-parser.d.ts +24 -0
- package/dist/esm/word/settings-parser.js +32 -5
- package/dist/esm/word/shading.d.ts +11 -0
- package/dist/esm/word/shading.js +64 -0
- package/dist/esm/word/styles-parser.js +9 -2
- package/dist/esm/word/table-parser.d.ts +9 -1
- package/dist/esm/word/table-parser.js +182 -30
- package/package.json +7 -3
- package/dist/esm/excel/number-format.js +0 -476
|
@@ -3,9 +3,9 @@ import { embedTtfFont } from "./cid-font.js";
|
|
|
3
3
|
import { preparePdfEncryption } from "./encryption.js";
|
|
4
4
|
import { reorderVisual } from "../core/bidi/algorithm.js";
|
|
5
5
|
import { reverseByCodePoint } from "../core/bidi/index.js";
|
|
6
|
-
import "../layout/styled-layout.js";
|
|
6
|
+
import { GLUE_SHRINK_RATIO } from "../layout/styled-layout.js";
|
|
7
7
|
import { paintPlan } from "../layout/page-doc.js";
|
|
8
|
-
import { emitVectorShape } from "./vector-graphics.js";
|
|
8
|
+
import { emitClipPath, emitVectorShape, shadowBlurLayers } from "./vector-graphics.js";
|
|
9
9
|
import { buildGradientPattern, shapeBbox } from "./shading.js";
|
|
10
10
|
import { sanitizeHref } from "../core/links.js";
|
|
11
11
|
import { embedAssociatedFile } from "./embedded-file.js";
|
|
@@ -95,10 +95,32 @@ function assembleStyledPdf(laid, options, doc) {
|
|
|
95
95
|
gradientNames.set(item.shape, nm);
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
|
+
const alphaStateNames = /* @__PURE__ */ new Map();
|
|
99
|
+
const extGStateEntries = {};
|
|
100
|
+
const wantAlpha = (alpha) => {
|
|
101
|
+
if (alpha === void 0 || alpha >= 1) return;
|
|
102
|
+
const key = Math.round(alpha * 1e3) / 1e3;
|
|
103
|
+
if (alphaStateNames.has(key)) return;
|
|
104
|
+
const nm = `GSa${alphaStateNames.size}`;
|
|
105
|
+
alphaStateNames.set(key, nm);
|
|
106
|
+
extGStateEntries[nm] = dict({
|
|
107
|
+
ca: key,
|
|
108
|
+
CA: key
|
|
109
|
+
});
|
|
110
|
+
};
|
|
111
|
+
for (const page of renderedPages) {
|
|
112
|
+
const plan = paintPlan(page.commands);
|
|
113
|
+
for (const item of plan.shapes) {
|
|
114
|
+
const s = item.shape.shadow;
|
|
115
|
+
if (s) wantAlpha(shadowBlurLayers(s).alpha);
|
|
116
|
+
}
|
|
117
|
+
for (const line of plan.lines) for (const tok of line.line.tokens) if (tok.kind === "image" && tok.shadow) wantAlpha(shadowBlurLayers(tok.shadow).alpha);
|
|
118
|
+
}
|
|
98
119
|
const resourcesDict = dict({
|
|
99
120
|
Font: fontResourceDict,
|
|
100
121
|
...xobjectResourceDict ? { XObject: xobjectResourceDict } : {},
|
|
101
|
-
...Object.keys(patternEntries).length > 0 ? { Pattern: dict(patternEntries) } : {}
|
|
122
|
+
...Object.keys(patternEntries).length > 0 ? { Pattern: dict(patternEntries) } : {},
|
|
123
|
+
...Object.keys(extGStateEntries).length > 0 ? { ExtGState: dict(extGStateEntries) } : {}
|
|
102
124
|
});
|
|
103
125
|
let pdfaIccRef;
|
|
104
126
|
let transparencyGroup;
|
|
@@ -125,7 +147,7 @@ function assembleStyledPdf(laid, options, doc) {
|
|
|
125
147
|
tagFor: (structId) => builder.node(structId).type
|
|
126
148
|
};
|
|
127
149
|
}
|
|
128
|
-
const { content: contentBytes, links } = emitPageContent(page, pageTagging, gradientNames);
|
|
150
|
+
const { content: contentBytes, links } = emitPageContent(page, pageTagging, gradientNames, alphaStateNames);
|
|
129
151
|
const contentsRef = doc.add(stream({}, contentBytes));
|
|
130
152
|
const pageEntries = {
|
|
131
153
|
Type: name("Page"),
|
|
@@ -363,7 +385,8 @@ function defaultPageCtx() {
|
|
|
363
385
|
even: { commands: [] }
|
|
364
386
|
},
|
|
365
387
|
titlePg: false,
|
|
366
|
-
evenAndOddHeaders: false
|
|
388
|
+
evenAndOddHeaders: false,
|
|
389
|
+
continuous: false
|
|
367
390
|
};
|
|
368
391
|
}
|
|
369
392
|
function embedImageResources(doc, laid) {
|
|
@@ -374,10 +397,13 @@ function embedImageResources(doc, laid) {
|
|
|
374
397
|
function embedFontResources(doc, laid, pdfaProfile) {
|
|
375
398
|
const cidSet = pdfaProfile?.part === 1;
|
|
376
399
|
const out = /* @__PURE__ */ new Map();
|
|
377
|
-
for (const [variant, res] of laid.fontResources)
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
400
|
+
for (const [variant, res] of laid.fontResources) {
|
|
401
|
+
if (res.gids.size === 0) continue;
|
|
402
|
+
out.set(variant, embedTtfFont(doc, res.parsed, {
|
|
403
|
+
usedGids: res.gids,
|
|
404
|
+
cidSet
|
|
405
|
+
}));
|
|
406
|
+
}
|
|
381
407
|
return out;
|
|
382
408
|
}
|
|
383
409
|
function buildFontResourceDict(resources, embedded) {
|
|
@@ -426,7 +452,40 @@ function emitTaggedRuns(out, cmds, tagging, body) {
|
|
|
426
452
|
}
|
|
427
453
|
close();
|
|
428
454
|
}
|
|
429
|
-
|
|
455
|
+
/**
|
|
456
|
+
* The dash array for a §18.18.3 / §17.18.2 border style, in points.
|
|
457
|
+
*
|
|
458
|
+
* A pattern is a pattern, not a weight: Word and Excel both draw the same
|
|
459
|
+
* dashes on a hairline and on a 3pt rule, and both references agree on the
|
|
460
|
+
* lengths — a `dashed` page border strokes `8 2.5` whether its `w:sz` is 4 or
|
|
461
|
+
* 24. Scaling them by the width instead drew page-borders-export-case-2.docx's
|
|
462
|
+
* half-point frame as a row of specks a quarter the length it should be.
|
|
463
|
+
*/
|
|
464
|
+
var BORDER_DASHES = new Map([
|
|
465
|
+
["dashed", [8, 2.5]],
|
|
466
|
+
["dashSmallGap", [3, 1]],
|
|
467
|
+
["dotted", [.5, 1]],
|
|
468
|
+
["dashDot", [
|
|
469
|
+
8,
|
|
470
|
+
2.5,
|
|
471
|
+
2.5,
|
|
472
|
+
2.5
|
|
473
|
+
]],
|
|
474
|
+
["dashDotDot", [
|
|
475
|
+
8,
|
|
476
|
+
2.5,
|
|
477
|
+
2.5,
|
|
478
|
+
2.5,
|
|
479
|
+
2.5,
|
|
480
|
+
2.5
|
|
481
|
+
]]
|
|
482
|
+
]);
|
|
483
|
+
/** The PDF dash array for a border style; an empty string means a solid rule. */
|
|
484
|
+
function dashPatternFor(style) {
|
|
485
|
+
const pattern = style !== void 0 ? BORDER_DASHES.get(style) : void 0;
|
|
486
|
+
return pattern ? `[${pattern.map(formatNumber).join(" ")}]` : "";
|
|
487
|
+
}
|
|
488
|
+
function emitPageContent(page, tagging, gradientNames, alphaStateNames) {
|
|
430
489
|
const commands = page.commands;
|
|
431
490
|
const links = [];
|
|
432
491
|
if (commands.length === 0) return {
|
|
@@ -456,7 +515,18 @@ function emitPageContent(page, tagging, gradientNames) {
|
|
|
456
515
|
}
|
|
457
516
|
emitTaggedRuns(out, plan.images.filter((c) => c.imageResourceName !== ""), tagging, (img) => {
|
|
458
517
|
out.push("q");
|
|
459
|
-
|
|
518
|
+
if (img.clip) {
|
|
519
|
+
const t = img.clip.transform;
|
|
520
|
+
out.push(...emitClipPath(img.clip.paths, [
|
|
521
|
+
t[0],
|
|
522
|
+
-t[1],
|
|
523
|
+
t[2],
|
|
524
|
+
-t[3],
|
|
525
|
+
t[4],
|
|
526
|
+
H - t[5]
|
|
527
|
+
]));
|
|
528
|
+
}
|
|
529
|
+
out.push(...placeImage(img.x, H - img.y - img.height, img.width, img.height, img.crop, img.rotationDeg, img.flipH, img.flipV));
|
|
460
530
|
out.push(`/${img.imageResourceName} Do`);
|
|
461
531
|
out.push("Q");
|
|
462
532
|
});
|
|
@@ -464,14 +534,22 @@ function emitPageContent(page, tagging, gradientNames) {
|
|
|
464
534
|
if (borders.length > 0) {
|
|
465
535
|
if (tagging) out.push("/Artifact BMC");
|
|
466
536
|
out.push("q");
|
|
537
|
+
out.push("2 J");
|
|
467
538
|
let lastWidth = -1;
|
|
468
539
|
let lastColor = "";
|
|
540
|
+
let lastDash = "";
|
|
469
541
|
for (const b of borders) {
|
|
470
542
|
const width = b.borderSizePt;
|
|
471
543
|
const color = b.borderColorHex;
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
544
|
+
const dash = dashPatternFor(b.borderStyle);
|
|
545
|
+
if (dash !== lastDash) {
|
|
546
|
+
out.push(dash === "" ? "[] 0 d 2 J" : `${dash} 0 d 0 J`);
|
|
547
|
+
lastDash = dash;
|
|
548
|
+
}
|
|
549
|
+
const strokeW = b.borderStyle === "double" ? Math.max(.25, width / 3) : width;
|
|
550
|
+
if (strokeW !== lastWidth) {
|
|
551
|
+
out.push(`${formatNumber(strokeW)} w`);
|
|
552
|
+
lastWidth = strokeW;
|
|
475
553
|
}
|
|
476
554
|
if (color !== lastColor) {
|
|
477
555
|
const [r, g, bl] = hexToRgb01(color);
|
|
@@ -482,23 +560,39 @@ function emitPageContent(page, tagging, gradientNames) {
|
|
|
482
560
|
const y = H - b.y - b.height;
|
|
483
561
|
const w = b.width;
|
|
484
562
|
const h = b.height;
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
563
|
+
const [x1, y1, x2, y2, nx, ny] = b.side === "top" ? [
|
|
564
|
+
x,
|
|
565
|
+
y + h,
|
|
566
|
+
x + w,
|
|
567
|
+
y + h,
|
|
568
|
+
0,
|
|
569
|
+
1
|
|
570
|
+
] : b.side === "bottom" ? [
|
|
571
|
+
x,
|
|
572
|
+
y,
|
|
573
|
+
x + w,
|
|
574
|
+
y,
|
|
575
|
+
0,
|
|
576
|
+
1
|
|
577
|
+
] : b.side === "left" ? [
|
|
578
|
+
x,
|
|
579
|
+
y,
|
|
580
|
+
x,
|
|
581
|
+
y + h,
|
|
582
|
+
1,
|
|
583
|
+
0
|
|
584
|
+
] : [
|
|
585
|
+
x + w,
|
|
586
|
+
y,
|
|
587
|
+
x + w,
|
|
588
|
+
y + h,
|
|
589
|
+
1,
|
|
590
|
+
0
|
|
591
|
+
];
|
|
592
|
+
const offsets = b.borderStyle === "double" ? [-width / 3, width / 3] : [0];
|
|
593
|
+
for (const d of offsets) {
|
|
594
|
+
out.push(`${formatNumber(x1 + nx * d)} ${formatNumber(y1 + ny * d)} m`);
|
|
595
|
+
out.push(`${formatNumber(x2 + nx * d)} ${formatNumber(y2 + ny * d)} l`);
|
|
502
596
|
}
|
|
503
597
|
out.push("S");
|
|
504
598
|
}
|
|
@@ -519,12 +613,15 @@ function emitPageContent(page, tagging, gradientNames) {
|
|
|
519
613
|
H - t[5]
|
|
520
614
|
]
|
|
521
615
|
};
|
|
522
|
-
|
|
616
|
+
const shadowLayer = sh.shape.shadow ? shadowBlurLayers(sh.shape.shadow) : void 0;
|
|
617
|
+
const alphaState = shadowLayer && shadowLayer.alpha < 1 ? alphaStateNames?.get(Math.round(shadowLayer.alpha * 1e3) / 1e3) : void 0;
|
|
618
|
+
for (const op of emitVectorShape(shape, gradientNames?.get(sh.shape), alphaState)) out.push(op);
|
|
523
619
|
});
|
|
524
620
|
const lines = plan.lines;
|
|
525
621
|
if (lines.length > 0) {
|
|
526
622
|
let inBT = false;
|
|
527
623
|
let lastFont = "";
|
|
624
|
+
let lastTc = 0;
|
|
528
625
|
let lastSize = -1;
|
|
529
626
|
let lastColor = "";
|
|
530
627
|
const switchFontIfNeeded = (tok) => {
|
|
@@ -534,6 +631,11 @@ function emitPageContent(page, tagging, gradientNames) {
|
|
|
534
631
|
lastFont = fontKey;
|
|
535
632
|
lastSize = tok.fontSizePt;
|
|
536
633
|
}
|
|
634
|
+
const tc = tok.resolvedRun.letterSpacingPt ?? 0;
|
|
635
|
+
if (tc !== lastTc) {
|
|
636
|
+
out.push(`${formatNumber(tc)} Tc`);
|
|
637
|
+
lastTc = tc;
|
|
638
|
+
}
|
|
537
639
|
if (tok.resolvedRun.colorHex !== lastColor) {
|
|
538
640
|
const [r, g, b] = hexToRgb01(tok.resolvedRun.colorHex);
|
|
539
641
|
out.push(`${formatNumber(r)} ${formatNumber(g)} ${formatNumber(b)} rg`);
|
|
@@ -546,13 +648,45 @@ function emitPageContent(page, tagging, gradientNames) {
|
|
|
546
648
|
out.push("ET");
|
|
547
649
|
inBT = false;
|
|
548
650
|
}
|
|
651
|
+
const shdw = tok.shadow;
|
|
652
|
+
if (shdw) {
|
|
653
|
+
const bx = tok.drawBox;
|
|
654
|
+
const [shr, shg, shb] = hexToRgb01(shdw.colorHex);
|
|
655
|
+
const layers = shadowBlurLayers(shdw);
|
|
656
|
+
const state = layers.alpha < 1 ? alphaStateNames?.get(Math.round(layers.alpha * 1e3) / 1e3) : void 0;
|
|
657
|
+
const sx = x + (bx?.dxPt ?? 0) + shdw.dxPt;
|
|
658
|
+
const sy = baselineY + (bx?.dyPt ?? 0) - shdw.dyPt;
|
|
659
|
+
const sw = bx?.widthPt ?? tok.widthPt;
|
|
660
|
+
const sh = bx?.heightPt ?? tok.heightPt;
|
|
661
|
+
out.push("q");
|
|
662
|
+
if (state) out.push(`/${state} gs`);
|
|
663
|
+
out.push(`${formatNumber(shr)} ${formatNumber(shg)} ${formatNumber(shb)} rg`);
|
|
664
|
+
for (let i = 0; i < layers.count; i++) {
|
|
665
|
+
const grow = layers.count > 1 ? -shdw.blurPt / 2 + (i + .5) * shdw.blurPt / layers.count : 0;
|
|
666
|
+
out.push(`${formatNumber(sx - grow)} ${formatNumber(sy - grow)} ${formatNumber(Math.max(sw + 2 * grow, 0))} ${formatNumber(Math.max(sh + 2 * grow, 0))} re`);
|
|
667
|
+
out.push("f");
|
|
668
|
+
}
|
|
669
|
+
out.push("Q");
|
|
670
|
+
}
|
|
549
671
|
out.push("q");
|
|
550
|
-
|
|
672
|
+
const b = tok.drawBox;
|
|
673
|
+
out.push(...b ? placeImage(x + b.dxPt, baselineY + b.dyPt, b.widthPt, b.heightPt, tok.crop, tok.rotationDeg, tok.flipH, tok.flipV) : placeImage(x, baselineY, tok.widthPt, tok.heightPt, tok.crop, tok.rotationDeg, tok.flipH, tok.flipV));
|
|
551
674
|
out.push(`/${tok.imageResourceName} Do`);
|
|
552
675
|
out.push("Q");
|
|
676
|
+
const frame = tok.outline;
|
|
677
|
+
if (frame) {
|
|
678
|
+
const [fr, fg, fb] = hexToRgb01(frame.colorHex);
|
|
679
|
+
out.push("q");
|
|
680
|
+
out.push(`${formatNumber(frame.widthPt)} w`);
|
|
681
|
+
out.push(`${formatNumber(fr)} ${formatNumber(fg)} ${formatNumber(fb)} RG`);
|
|
682
|
+
out.push(`${formatNumber(x + (b?.dxPt ?? 0))} ${formatNumber(baselineY + (b?.dyPt ?? 0))} ${formatNumber(b?.widthPt ?? tok.widthPt)} ${formatNumber(b?.heightPt ?? tok.heightPt)} re`);
|
|
683
|
+
out.push("S");
|
|
684
|
+
out.push("Q");
|
|
685
|
+
}
|
|
553
686
|
lastFont = "";
|
|
554
687
|
lastSize = -1;
|
|
555
688
|
lastColor = "";
|
|
689
|
+
lastTc = 0;
|
|
556
690
|
};
|
|
557
691
|
const emitMathToken = (tok, originX, baselineY) => {
|
|
558
692
|
for (const it of tok.items) if (it.kind === "glyph") {
|
|
@@ -570,7 +704,7 @@ function emitPageContent(page, tagging, gradientNames) {
|
|
|
570
704
|
lastColor = "000000";
|
|
571
705
|
}
|
|
572
706
|
out.push(`1 0 0 1 ${formatNumber(originX + it.x)} ${formatNumber(baselineY + it.y)} Tm`);
|
|
573
|
-
out.push(
|
|
707
|
+
out.push(it.font.measure.showText(it.text));
|
|
574
708
|
} else if (it.kind === "rule") {
|
|
575
709
|
if (inBT) {
|
|
576
710
|
out.push("ET");
|
|
@@ -611,10 +745,40 @@ function emitPageContent(page, tagging, gradientNames) {
|
|
|
611
745
|
lastColor = "";
|
|
612
746
|
}
|
|
613
747
|
};
|
|
748
|
+
const emitClipped = (cmd, body) => {
|
|
749
|
+
const clip = cmd.clip;
|
|
750
|
+
if (!clip) {
|
|
751
|
+
body(cmd);
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
if (inBT) {
|
|
755
|
+
out.push("ET");
|
|
756
|
+
inBT = false;
|
|
757
|
+
}
|
|
758
|
+
out.push("q");
|
|
759
|
+
out.push(`${formatNumber(clip.x)} ${formatNumber(H - clip.y - clip.height)} ${formatNumber(clip.width)} ${formatNumber(clip.height)} re W n`);
|
|
760
|
+
lastFont = "";
|
|
761
|
+
lastSize = -1;
|
|
762
|
+
lastColor = "";
|
|
763
|
+
lastTc = 0;
|
|
764
|
+
out.push("BT");
|
|
765
|
+
inBT = true;
|
|
766
|
+
body(cmd);
|
|
767
|
+
if (inBT) {
|
|
768
|
+
out.push("ET");
|
|
769
|
+
inBT = false;
|
|
770
|
+
}
|
|
771
|
+
out.push("Q");
|
|
772
|
+
lastFont = "";
|
|
773
|
+
lastSize = -1;
|
|
774
|
+
lastColor = "";
|
|
775
|
+
lastTc = 0;
|
|
776
|
+
};
|
|
614
777
|
const emitOneLine = (cmd) => {
|
|
615
778
|
const line = cmd.line;
|
|
616
779
|
const originX = cmd.originX;
|
|
617
780
|
const baselineY = H - cmd.baselineY;
|
|
781
|
+
const rotationDeg = cmd.rotationDeg ?? 0;
|
|
618
782
|
const lineFs = line.maxFontSizePt || 12;
|
|
619
783
|
const linkAscent = Math.max(lineFs, line.mathAscentPt ?? 0);
|
|
620
784
|
const linkDescent = Math.max(lineFs * .2, line.mathDescentPt ?? 0);
|
|
@@ -660,10 +824,40 @@ function emitPageContent(page, tagging, gradientNames) {
|
|
|
660
824
|
const hasImageToken = line.tokens.some((t) => t.kind === "image");
|
|
661
825
|
const hasMathToken = line.tokens.some((t) => t.kind === "math");
|
|
662
826
|
const hasRtl = line.tokens.some((t) => t.bidiLevel % 2 === 1);
|
|
663
|
-
const
|
|
827
|
+
const hasTab = line.tokens.some((t) => t.kind === "text" && t.tab === true);
|
|
828
|
+
/** A text token carrying underline or strikethrough. */
|
|
829
|
+
const isDecorated = (tok) => tok.kind === "text" && (tok.resolvedRun.underline !== "none" || tok.resolvedRun.strike);
|
|
830
|
+
const showToken = (tok) => {
|
|
664
831
|
const text = tok.bidiLevel % 2 === 1 ? reverseByCodePoint(tok.text) : tok.text;
|
|
665
|
-
return tok.font.measure.
|
|
832
|
+
return tok.font.measure.showText(text);
|
|
666
833
|
};
|
|
834
|
+
if (line.tokens.some((t) => t.kind === "text" && t.resolvedRun.shadingColorHex)) {
|
|
835
|
+
if (inBT) {
|
|
836
|
+
out.push("ET");
|
|
837
|
+
inBT = false;
|
|
838
|
+
}
|
|
839
|
+
const shAscent = Math.max(lineFs, line.mathAscentPt ?? 0);
|
|
840
|
+
const shDescent = Math.max(lineFs * .2, line.mathDescentPt ?? 0);
|
|
841
|
+
out.push("q");
|
|
842
|
+
let sx = originX;
|
|
843
|
+
let painted = "";
|
|
844
|
+
for (const tok of line.tokens) {
|
|
845
|
+
const w = tok.widthPt + (tok.kind === "text" && tok.isSpace ? extraPerSpace : 0);
|
|
846
|
+
const hex = tok.kind === "text" ? tok.resolvedRun.shadingColorHex : void 0;
|
|
847
|
+
if (hex !== void 0) {
|
|
848
|
+
if (hex !== painted) {
|
|
849
|
+
if (painted !== "") out.push("f");
|
|
850
|
+
const [sr, sg, sb] = hexToRgb01(hex);
|
|
851
|
+
out.push(`${formatNumber(sr)} ${formatNumber(sg)} ${formatNumber(sb)} rg`);
|
|
852
|
+
painted = hex;
|
|
853
|
+
}
|
|
854
|
+
out.push(`${formatNumber(sx)} ${formatNumber(baselineY - shDescent)} ${formatNumber(w)} ${formatNumber(shAscent + shDescent)} re`);
|
|
855
|
+
}
|
|
856
|
+
sx += w;
|
|
857
|
+
}
|
|
858
|
+
if (painted !== "") out.push("f");
|
|
859
|
+
out.push("Q");
|
|
860
|
+
}
|
|
667
861
|
if (line.tokens.some((t) => t.kind === "text" && t.highlight === true)) {
|
|
668
862
|
if (inBT) {
|
|
669
863
|
out.push("ET");
|
|
@@ -683,7 +877,66 @@ function emitPageContent(page, tagging, gradientNames) {
|
|
|
683
877
|
out.push("f");
|
|
684
878
|
out.push("Q");
|
|
685
879
|
}
|
|
686
|
-
if (
|
|
880
|
+
if (line.tokens.some(isDecorated)) {
|
|
881
|
+
if (inBT) {
|
|
882
|
+
out.push("ET");
|
|
883
|
+
inBT = false;
|
|
884
|
+
}
|
|
885
|
+
out.push("q");
|
|
886
|
+
let dx = originX;
|
|
887
|
+
for (const tok of line.tokens) {
|
|
888
|
+
const w = tok.widthPt + (tok.kind === "text" && tok.isSpace ? extraPerSpace : 0);
|
|
889
|
+
if (isDecorated(tok)) {
|
|
890
|
+
const thickness = Math.max(.4, tok.fontSizePt * .06);
|
|
891
|
+
const paint = (hex) => {
|
|
892
|
+
const [r, g, b] = hexToRgb01(hex);
|
|
893
|
+
out.push(`${formatNumber(r)} ${formatNumber(g)} ${formatNumber(b)} rg`);
|
|
894
|
+
};
|
|
895
|
+
paint(tok.resolvedRun.colorHex);
|
|
896
|
+
if (tok.resolvedRun.underline !== "none") {
|
|
897
|
+
if (tok.resolvedRun.underlineColorHex) paint(tok.resolvedRun.underlineColorHex);
|
|
898
|
+
const style = tok.resolvedRun.underline;
|
|
899
|
+
const t = style === "thick" || style === "dottedHeavy" || style === "dashHeavy" ? thickness * 2 : thickness;
|
|
900
|
+
const y = baselineY - tok.fontSizePt * .12 - t;
|
|
901
|
+
const dashed = style === "dotted" || style === "dottedHeavy" ? [t, t * 2] : style === "dash" || style === "dashHeavy" ? [t * 4, t * 3] : void 0;
|
|
902
|
+
if (dashed) {
|
|
903
|
+
out.push(`${formatNumber(t)} w`);
|
|
904
|
+
out.push(`[${dashed.map((n) => formatNumber(n)).join(" ")}] 0 d`);
|
|
905
|
+
const [r, g, b] = hexToRgb01(tok.resolvedRun.underlineColorHex ?? tok.resolvedRun.colorHex);
|
|
906
|
+
out.push(`${formatNumber(r)} ${formatNumber(g)} ${formatNumber(b)} RG`);
|
|
907
|
+
out.push(`${formatNumber(dx)} ${formatNumber(y + t / 2)} m ${formatNumber(dx + w)} ${formatNumber(y + t / 2)} l S`);
|
|
908
|
+
out.push("[] 0 d");
|
|
909
|
+
} else {
|
|
910
|
+
out.push(`${formatNumber(dx)} ${formatNumber(y)} ${formatNumber(w)} ${formatNumber(t)} re f`);
|
|
911
|
+
if (style === "double") out.push(`${formatNumber(dx)} ${formatNumber(y - t * 2)} ${formatNumber(w)} ${formatNumber(t)} re f`);
|
|
912
|
+
}
|
|
913
|
+
if (tok.resolvedRun.underlineColorHex) paint(tok.resolvedRun.colorHex);
|
|
914
|
+
}
|
|
915
|
+
if (tok.resolvedRun.strike) {
|
|
916
|
+
const y = baselineY + tok.fontSizePt * .26;
|
|
917
|
+
out.push(`${formatNumber(dx)} ${formatNumber(y)} ${formatNumber(w)} ${formatNumber(thickness)} re f`);
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
dx += w;
|
|
921
|
+
}
|
|
922
|
+
out.push("Q");
|
|
923
|
+
}
|
|
924
|
+
const hasRise = line.tokens.some((t) => t.kind === "text" && t.risePt !== void 0);
|
|
925
|
+
if (rotationDeg !== 0 && !hasImageToken && !hasMathToken) {
|
|
926
|
+
if (!inBT) {
|
|
927
|
+
out.push("BT");
|
|
928
|
+
inBT = true;
|
|
929
|
+
}
|
|
930
|
+
const rad = rotationDeg * Math.PI / 180;
|
|
931
|
+
const cos = formatNumber(Math.cos(rad));
|
|
932
|
+
const sin = formatNumber(Math.sin(rad));
|
|
933
|
+
out.push(`${cos} ${sin} ${formatNumber(-Math.sin(rad))} ${cos} ${formatNumber(originX)} ${formatNumber(baselineY)} Tm`);
|
|
934
|
+
for (const tok of line.tokens) {
|
|
935
|
+
if (tok.kind !== "text") continue;
|
|
936
|
+
switchFontIfNeeded(tok);
|
|
937
|
+
out.push(tok.font.measure.showText(tok.text));
|
|
938
|
+
}
|
|
939
|
+
} else if (extraPerSpace !== 0 || hasImageToken || hasMathToken || hasRtl || hasRise || hasTab) {
|
|
687
940
|
const order = hasRtl ? lineVisualOrder(line) : line.tokens.map((_, i) => i);
|
|
688
941
|
let x = originX;
|
|
689
942
|
for (const ti of order) {
|
|
@@ -705,8 +958,8 @@ function emitPageContent(page, tagging, gradientNames) {
|
|
|
705
958
|
inBT = true;
|
|
706
959
|
}
|
|
707
960
|
switchFontIfNeeded(tok);
|
|
708
|
-
out.push(`1 0 0 1 ${formatNumber(x)} ${formatNumber(baselineY)} Tm`);
|
|
709
|
-
out.push(
|
|
961
|
+
out.push(`1 0 0 1 ${formatNumber(x)} ${formatNumber(baselineY + (tok.risePt ?? 0))} Tm`);
|
|
962
|
+
out.push(showToken(tok));
|
|
710
963
|
const tokenX0 = x;
|
|
711
964
|
x += tok.widthPt;
|
|
712
965
|
if (tok.isSpace) x += extraPerSpace;
|
|
@@ -723,8 +976,7 @@ function emitPageContent(page, tagging, gradientNames) {
|
|
|
723
976
|
for (const tok of line.tokens) {
|
|
724
977
|
if (tok.kind !== "text") continue;
|
|
725
978
|
switchFontIfNeeded(tok);
|
|
726
|
-
|
|
727
|
-
out.push(`<${hex}> Tj`);
|
|
979
|
+
out.push(tok.font.measure.showText(tok.text));
|
|
728
980
|
trackLink(tok.href, tok.anchor, tok.text, x, x + tok.widthPt);
|
|
729
981
|
x += tok.widthPt;
|
|
730
982
|
}
|
|
@@ -744,7 +996,7 @@ function emitPageContent(page, tagging, gradientNames) {
|
|
|
744
996
|
out.push(`/${tagging.tagFor(sid)} <</MCID ${mcid}>> BDC`);
|
|
745
997
|
} else if (cmd.artifact === "pagination") out.push("/Artifact <</Type /Pagination>> BDC");
|
|
746
998
|
else out.push("/Artifact BMC");
|
|
747
|
-
|
|
999
|
+
emitClipped(cmd, emitOneLine);
|
|
748
1000
|
if (inBT) {
|
|
749
1001
|
out.push("ET");
|
|
750
1002
|
inBT = false;
|
|
@@ -754,7 +1006,7 @@ function emitPageContent(page, tagging, gradientNames) {
|
|
|
754
1006
|
else {
|
|
755
1007
|
out.push("BT");
|
|
756
1008
|
inBT = true;
|
|
757
|
-
for (const cmd of lines)
|
|
1009
|
+
for (const cmd of lines) emitClipped(cmd, emitOneLine);
|
|
758
1010
|
if (inBT) out.push("ET");
|
|
759
1011
|
}
|
|
760
1012
|
}
|
|
@@ -766,15 +1018,69 @@ function emitPageContent(page, tagging, gradientNames) {
|
|
|
766
1018
|
function lineVisualOrder(line) {
|
|
767
1019
|
return reorderVisual(line.tokens.map((t) => t.bidiLevel));
|
|
768
1020
|
}
|
|
1021
|
+
/**
|
|
1022
|
+
* The operators that put an image XObject in a box — ISO 32000-1 §8.9.5.1: the
|
|
1023
|
+
* unit square scaled to the box and moved to `(x, y)`, its bottom-left corner.
|
|
1024
|
+
*
|
|
1025
|
+
* A `a:srcRect` crop (§20.1.8.55) cuts edges off the SOURCE and fits what is
|
|
1026
|
+
* left to the same box, so the picture is drawn larger than the box and the box
|
|
1027
|
+
* is made a clipping path. Ignored, ImageCrop.docx drew the whole mountain
|
|
1028
|
+
* where LibreOffice shows the peak.
|
|
1029
|
+
*
|
|
1030
|
+
* @returns The content-stream lines, to be pushed between `q` and the `Do`.
|
|
1031
|
+
*/
|
|
1032
|
+
function placeImage(x, y, width, height, crop, rotationDeg, flipH, flipV) {
|
|
1033
|
+
const cm = (w, h, ox, oy) => `${formatNumber(w)} 0 0 ${formatNumber(h)} ${formatNumber(ox)} ${formatNumber(oy)} cm`;
|
|
1034
|
+
const spin = rotationDeg ? [rotateAboutCm(x + width / 2, y + height / 2, -rotationDeg)] : [];
|
|
1035
|
+
if (flipH === true || flipV === true) {
|
|
1036
|
+
const sx = flipH === true ? -1 : 1;
|
|
1037
|
+
const sy = flipV === true ? -1 : 1;
|
|
1038
|
+
spin.push(`${formatNumber(sx)} 0 0 ${formatNumber(sy)} ${formatNumber(sx === -1 ? 2 * x + width : 0)} ${formatNumber(sy === -1 ? 2 * y + height : 0)} cm`);
|
|
1039
|
+
}
|
|
1040
|
+
if (!crop) return [...spin, cm(width, height, x, y)];
|
|
1041
|
+
const kept = {
|
|
1042
|
+
w: 1 - crop.left - crop.right,
|
|
1043
|
+
h: 1 - crop.top - crop.bottom
|
|
1044
|
+
};
|
|
1045
|
+
const full = {
|
|
1046
|
+
w: width / kept.w,
|
|
1047
|
+
h: height / kept.h
|
|
1048
|
+
};
|
|
1049
|
+
return [
|
|
1050
|
+
...spin,
|
|
1051
|
+
`${formatNumber(x)} ${formatNumber(y)} ${formatNumber(width)} ${formatNumber(height)} re`,
|
|
1052
|
+
"W",
|
|
1053
|
+
"n",
|
|
1054
|
+
cm(full.w, full.h, x - crop.left * full.w, y - crop.bottom * full.h)
|
|
1055
|
+
];
|
|
1056
|
+
}
|
|
1057
|
+
/** A `cm` that turns the frame `deg` counter-clockwise about `(cx, cy)`. */
|
|
1058
|
+
function rotateAboutCm(cx, cy, deg) {
|
|
1059
|
+
const rad = deg * Math.PI / 180;
|
|
1060
|
+
const c = Math.cos(rad);
|
|
1061
|
+
const s = Math.sin(rad);
|
|
1062
|
+
return `${formatNumber(c)} ${formatNumber(s)} ${formatNumber(-s)} ${formatNumber(c)} ${formatNumber(cx - cx * c + cy * s)} ${formatNumber(cy - cx * s - cy * c)} cm`;
|
|
1063
|
+
}
|
|
769
1064
|
function computeJustifyExtra(line) {
|
|
770
|
-
|
|
771
|
-
if (
|
|
1065
|
+
const alignment = line.resolved.alignment;
|
|
1066
|
+
if (alignment !== "both" && alignment !== "distribute") return 0;
|
|
1067
|
+
if (line.noJustify === true) return 0;
|
|
1068
|
+
if (line.isLastInParagraph && alignment !== "distribute") return 0;
|
|
1069
|
+
let last = line.tokens.length - 1;
|
|
1070
|
+
while (last >= 0 && line.tokens[last].kind === "text" && line.tokens[last].isSpace) last--;
|
|
772
1071
|
let spaces = 0;
|
|
773
|
-
|
|
1072
|
+
let narrowest = Infinity;
|
|
1073
|
+
let contentWidthPt = 0;
|
|
1074
|
+
for (let i = 0; i <= last; i++) {
|
|
1075
|
+
const tok = line.tokens[i];
|
|
1076
|
+
contentWidthPt += tok.widthPt;
|
|
1077
|
+
if (!tok.isSpace) continue;
|
|
1078
|
+
spaces++;
|
|
1079
|
+
if (tok.widthPt < narrowest) narrowest = tok.widthPt;
|
|
1080
|
+
}
|
|
774
1081
|
if (spaces === 0) return 0;
|
|
775
|
-
const extra = line.availableWidthPt -
|
|
776
|
-
|
|
777
|
-
return extra / spaces;
|
|
1082
|
+
const extra = (line.availableWidthPt - contentWidthPt) / spaces;
|
|
1083
|
+
return extra < 0 ? Math.max(extra, -narrowest * GLUE_SHRINK_RATIO) : extra;
|
|
778
1084
|
}
|
|
779
1085
|
function hexToRgb01(hex) {
|
|
780
1086
|
return [
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { VectorShape } from '../core/vector.js';
|
|
1
|
+
import { VectorPath, VectorShape } from '../core/vector.js';
|
|
2
2
|
export type { PathSegment, StrokeStyle, VectorPath, VectorShape } from '../core/vector.js';
|
|
3
3
|
export { PathBuilder } from '../core/vector.js';
|
|
4
4
|
/**
|
|
@@ -11,6 +11,35 @@ export { PathBuilder } from '../core/vector.js';
|
|
|
11
11
|
* @param patternName When set, the fill is this named shading pattern from the
|
|
12
12
|
* page's `/Pattern` resources (a gradient, EP16b) rather than a solid colour;
|
|
13
13
|
* only used if the shape actually carries a `fillGradient`.
|
|
14
|
+
* @param alphaStateName When set, the shape's shadow is drawn first, under this
|
|
15
|
+
* named `/ExtGState` (its constant alpha) from the page's resources.
|
|
14
16
|
* @returns The content-stream operator lines.
|
|
15
17
|
*/
|
|
16
|
-
|
|
18
|
+
/**
|
|
19
|
+
* §20.1.8.40 `blurRad` — how a blurred shadow is drawn: as `count` copies of
|
|
20
|
+
* the silhouette, each at `alpha` transparency, growing through the blur's
|
|
21
|
+
* width. Shared with the emitter that registers the /ExtGState for it, so the
|
|
22
|
+
* alpha it registers is the one the layers are drawn at.
|
|
23
|
+
*
|
|
24
|
+
* @param shadow The shadow (its `blurPt` and `alpha` are read).
|
|
25
|
+
* @returns The number of layers and the transparency of each.
|
|
26
|
+
*/
|
|
27
|
+
export declare function shadowBlurLayers(shadow: {
|
|
28
|
+
readonly blurPt: number;
|
|
29
|
+
readonly alpha: number;
|
|
30
|
+
}): {
|
|
31
|
+
count: number;
|
|
32
|
+
alpha: number;
|
|
33
|
+
};
|
|
34
|
+
export declare function emitVectorShape(shape: VectorShape, patternName?: string, alphaStateName?: string): Array<string>;
|
|
35
|
+
/**
|
|
36
|
+
* The operators that make `paths` the current CLIP, under `transform` — the
|
|
37
|
+
* same local→y-up-page matrix {@link emitVectorShape} takes. Emitted inside the
|
|
38
|
+
* caller's own `q`…`Q`, and followed by the inverse matrix so what comes after
|
|
39
|
+
* is back in page coordinates.
|
|
40
|
+
*
|
|
41
|
+
* @param paths The clip outline, in the shape's local frame.
|
|
42
|
+
* @param transform The local→page matrix.
|
|
43
|
+
* @returns The operators: `cm`, the path, `W n`, and the inverse `cm`.
|
|
44
|
+
*/
|
|
45
|
+
export declare function emitClipPath(paths: ReadonlyArray<VectorPath>, transform: readonly [number, number, number, number, number, number]): Array<string>;
|