reamkit 1.31.0 → 1.32.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/esm/core/bidi/index.d.ts +10 -0
- package/dist/esm/core/bidi/index.js +18 -2
- package/dist/esm/core/converter/facade.js +10 -5
- package/dist/esm/core/converter/project.js +1 -0
- package/dist/esm/core/converter/ream.js +17 -6
- package/dist/esm/core/converter/sheet-images.d.ts +17 -0
- package/dist/esm/core/converter/sheet-images.js +33 -0
- package/dist/esm/core/crypto/offcrypto.js +56 -12
- package/dist/esm/core/document-model/index.d.ts +1 -1
- package/dist/esm/core/document-model/types.d.ts +112 -0
- package/dist/esm/core/drawingml/chart-geometry.d.ts +36 -13
- package/dist/esm/core/drawingml/chart-geometry.js +372 -146
- package/dist/esm/core/drawingml/chart-parser.d.ts +19 -0
- package/dist/esm/core/drawingml/chart-parser.js +126 -18
- package/dist/esm/core/drawingml/colors.d.ts +6 -0
- package/dist/esm/core/drawingml/colors.js +23 -3
- package/dist/esm/core/drawingml/shape-render.d.ts +3 -1
- package/dist/esm/core/drawingml/shape-render.js +4 -2
- package/dist/esm/core/font/measure.d.ts +9 -0
- package/dist/esm/core/font/measure.js +9 -1
- package/dist/esm/core/fonts/families.js +4 -2
- package/dist/esm/core/fonts/scripts.js +4 -2
- package/dist/esm/core/ir/flow.d.ts +6 -0
- package/dist/esm/core/ir/sheet.d.ts +43 -0
- package/dist/esm/core/number-format.d.ts +40 -15
- package/dist/esm/core/number-format.js +295 -83
- package/dist/esm/core/opc/tag-scan.d.ts +15 -0
- package/dist/esm/core/opc/tag-scan.js +41 -0
- package/dist/esm/core/opc/xml-entities.js +39 -2
- package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
- package/dist/esm/core/spreadsheet-model/types.d.ts +96 -0
- package/dist/esm/core/style-cascade/resolver.js +4 -2
- package/dist/esm/excel/column-bands.d.ts +39 -6
- package/dist/esm/excel/column-bands.js +41 -17
- package/dist/esm/excel/comments-parser.js +19 -0
- package/dist/esm/excel/conditional-format.d.ts +7 -3
- package/dist/esm/excel/conditional-format.js +77 -39
- package/dist/esm/excel/formula/context.d.ts +18 -0
- package/dist/esm/excel/formula/eval.js +51 -0
- package/dist/esm/excel/formula/functions.js +37 -14
- package/dist/esm/excel/formula/index.d.ts +2 -2
- package/dist/esm/excel/formula/lexer.d.ts +1 -1
- package/dist/esm/excel/formula/lexer.js +41 -0
- package/dist/esm/excel/formula/parser.d.ts +25 -0
- package/dist/esm/excel/formula/parser.js +95 -0
- package/dist/esm/excel/print-model.d.ts +38 -1
- package/dist/esm/excel/print-model.js +536 -83
- package/dist/esm/excel/shared-strings-parser.d.ts +9 -0
- package/dist/esm/excel/shared-strings-parser.js +11 -1
- package/dist/esm/excel/sheet-drawing.d.ts +38 -2
- package/dist/esm/excel/sheet-drawing.js +47 -13
- package/dist/esm/excel/sheet-shape-parser.d.ts +6 -2
- package/dist/esm/excel/sheet-shape-parser.js +18 -6
- package/dist/esm/excel/sheet-to-flow.d.ts +17 -0
- package/dist/esm/excel/sheet-to-flow.js +434 -47
- package/dist/esm/excel/styles-parser.d.ts +9 -0
- package/dist/esm/excel/styles-parser.js +80 -26
- package/dist/esm/excel/table-parser.js +5 -0
- package/dist/esm/excel/table-style-presets.d.ts +26 -0
- package/dist/esm/excel/table-style-presets.js +1555 -0
- package/dist/esm/excel/table-style.d.ts +13 -0
- package/dist/esm/excel/table-style.js +75 -0
- package/dist/esm/excel/tint.d.ts +10 -0
- package/dist/esm/excel/tint.js +75 -0
- package/dist/esm/excel/vml-drawing.d.ts +41 -3
- package/dist/esm/excel/vml-drawing.js +144 -11
- package/dist/esm/excel/worksheet-parser.js +89 -15
- package/dist/esm/excel/xls/biff-reader.js +10 -2
- package/dist/esm/excel/xlsx-reader.js +234 -19
- package/dist/esm/excel/xlsx-writer.js +8 -7
- package/dist/esm/html/html-writer.js +202 -20
- package/dist/esm/layout/fit-pages.d.ts +11 -0
- package/dist/esm/layout/fit-pages.js +86 -0
- package/dist/esm/layout/line-paint.d.ts +58 -0
- package/dist/esm/layout/line-paint.js +107 -0
- package/dist/esm/layout/page-doc.d.ts +51 -0
- package/dist/esm/layout/page-doc.js +141 -1
- package/dist/esm/layout/styled-layout.d.ts +7 -0
- package/dist/esm/layout/styled-layout.js +275 -52
- package/dist/esm/layout/turned-section.js +14 -2
- package/dist/esm/markdown/markdown-writer.js +75 -13
- package/dist/esm/pdf/styled-page-emitter.js +49 -71
- package/dist/esm/pdf/styled-page-renderer.js +1 -1
- package/dist/esm/pdf-reader/annot-draw.js +2 -2
- package/dist/esm/pdf-reader/annots.js +3 -1
- package/dist/esm/pdf-reader/font.js +4 -4
- package/dist/esm/pdf-reader/layout.js +1 -1
- package/dist/esm/svg/svg-text.d.ts +58 -0
- package/dist/esm/svg/svg-text.js +322 -0
- package/dist/esm/svg/svg-writer.js +135 -71
- package/dist/esm/word/document-parser.js +1 -1
- package/dist/esm/word/docx-reader.js +13 -8
- package/dist/esm/word/docx-to-pdf.js +3 -3
- package/dist/esm/word/docx-writer.js +11 -2
- package/dist/esm/word/font-table.js +89 -10
- package/dist/esm/word/numbering-parser.js +1 -1
- package/package.json +7 -3
|
@@ -32,6 +32,7 @@ function writeMarkdown(flow, options = {}) {
|
|
|
32
32
|
anchors: referencedAnchors(flow.body),
|
|
33
33
|
notes: noteNumbers(flow),
|
|
34
34
|
mediaNames: /* @__PURE__ */ new Map(),
|
|
35
|
+
drawn: /* @__PURE__ */ new Set(),
|
|
35
36
|
...flow.numbering ? { numbering: flow.numbering } : {}
|
|
36
37
|
};
|
|
37
38
|
if (flow.headersFooters && flow.headersFooters.size > 0) lose(ctx, "dropped", FEATURES.headersFooters, "markdown has no pages to band headers onto");
|
|
@@ -81,9 +82,29 @@ function lose(ctx, severity, feature, detail) {
|
|
|
81
82
|
*/
|
|
82
83
|
function joinBlocks(blocks) {
|
|
83
84
|
const body = blocks.map(trimHardBreaks).filter((b) => b.length > 0).join("\n\n");
|
|
84
|
-
return body.length > 0 ? `${body
|
|
85
|
+
return body.length > 0 ? `${trimLineEnds(body)}\n` : "";
|
|
85
86
|
}
|
|
86
87
|
/**
|
|
88
|
+
* The text without the spaces and tabs that end its lines — what
|
|
89
|
+
* `/[ \t]+$/gm` leaves, a line ending wherever `$` does under `m`: at LF, CR,
|
|
90
|
+
* U+2028, U+2029 and the end of the text. The expression retries every blank
|
|
91
|
+
* of a long run that something else follows, and is quadratic on it; this is
|
|
92
|
+
* one pass.
|
|
93
|
+
*/
|
|
94
|
+
function trimLineEnds(text) {
|
|
95
|
+
const out = [];
|
|
96
|
+
let start = 0;
|
|
97
|
+
for (let i = 0; i <= text.length; i++) {
|
|
98
|
+
if (i < text.length && !isLineEnd(text.charCodeAt(i))) continue;
|
|
99
|
+
let end = i;
|
|
100
|
+
while (end > start && (text[end - 1] === " " || text[end - 1] === " ")) end--;
|
|
101
|
+
out.push(text.slice(start, end), text.slice(i, i + 1));
|
|
102
|
+
start = i + 1;
|
|
103
|
+
}
|
|
104
|
+
return out.join("");
|
|
105
|
+
}
|
|
106
|
+
var isLineEnd = (c) => c === 10 || c === 13 || c === 8232 || c === 8233;
|
|
107
|
+
/**
|
|
87
108
|
* Drop a hard break sitting at either end of a block: at the end there is no
|
|
88
109
|
* next line to break to, at the start no previous one, and either way the
|
|
89
110
|
* backslash is left standing in the text as itself. Only a break is taken —
|
|
@@ -98,8 +119,14 @@ function emitBlock(out, el, ctx) {
|
|
|
98
119
|
return;
|
|
99
120
|
}
|
|
100
121
|
ctx.list.length = 0;
|
|
101
|
-
if (el.kind === "table")
|
|
102
|
-
|
|
122
|
+
if (el.kind === "table") {
|
|
123
|
+
emitTable(out, el.table, ctx);
|
|
124
|
+
for (const drawing of el.table.overlay?.drawings ?? []) {
|
|
125
|
+
if (ctx.drawn.has(drawing)) continue;
|
|
126
|
+
ctx.drawn.add(drawing);
|
|
127
|
+
emitBlock(out, drawing, ctx);
|
|
128
|
+
}
|
|
129
|
+
} else if (el.kind === "image") {
|
|
103
130
|
const img = pictureMarkdown(el.image.resource, el.image.altText, ctx);
|
|
104
131
|
if (img.length > 0) out.push(img);
|
|
105
132
|
} else if (el.kind === "chart") lose(ctx, "dropped", FEATURES.charts, "markdown cannot draw a chart");
|
|
@@ -211,7 +238,7 @@ function emitListItem(out, resolved, marker, inline, ctx) {
|
|
|
211
238
|
top.markerWidth = bullet.length + 1;
|
|
212
239
|
const pad = " ".repeat(top.indent);
|
|
213
240
|
const cont = " ".repeat(top.indent + top.markerWidth);
|
|
214
|
-
const line = `${pad}${bullet} ${inline.replaceAll("\\\n", `\\\n${cont}`)}`.
|
|
241
|
+
const line = `${pad}${bullet} ${inline.replaceAll("\\\n", `\\\n${cont}`)}`.trimEnd();
|
|
215
242
|
if (wasOpen && out.length > 0) out[out.length - 1] += `\n${line}`;
|
|
216
243
|
else out.push(line);
|
|
217
244
|
}
|
|
@@ -225,9 +252,24 @@ function listBullet(marker, numId, ilvl, counter, ctx) {
|
|
|
225
252
|
const level = numberingLevel(numId, ilvl, ctx.numbering);
|
|
226
253
|
if (!(level ? level.format !== "bullet" && level.format !== "none" : /\d/.test(marker))) return "-";
|
|
227
254
|
if (level && level.format !== "decimal" && level.format !== "decimalZero") lose(ctx, "degraded", FEATURES.lists, `${level.format} list markers render as decimal`);
|
|
228
|
-
const digits =
|
|
229
|
-
if (marker.replace(/\D+/g, "").length > (digits?.
|
|
230
|
-
return `${digits ? Number(digits
|
|
255
|
+
const digits = lastNumber(marker);
|
|
256
|
+
if (marker.replace(/\D+/g, "").length > (digits?.length ?? 0)) lose(ctx, "degraded", FEATURES.lists, "multi-level list marker flattened to one number");
|
|
257
|
+
return `${digits !== void 0 ? Number(digits) : counter}.`;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* The last run of digits in a marker — what `/(\d+)\D*$/` matched, read back
|
|
261
|
+
* from the end: that expression tried every digit of a long run as its start.
|
|
262
|
+
* The template the marker comes from is the file's, so the run can be any
|
|
263
|
+
* length, and every item of the list reads it.
|
|
264
|
+
*/
|
|
265
|
+
function lastNumber(marker) {
|
|
266
|
+
const isDigit = (i) => /\d/.test(marker[i] ?? "");
|
|
267
|
+
let end = marker.length;
|
|
268
|
+
while (end > 0 && !isDigit(end - 1)) end--;
|
|
269
|
+
if (end === 0) return void 0;
|
|
270
|
+
let start = end - 1;
|
|
271
|
+
while (start > 0 && isDigit(start - 1)) start--;
|
|
272
|
+
return marker.slice(start, end);
|
|
231
273
|
}
|
|
232
274
|
function numberingLevel(numId, ilvl, numbering) {
|
|
233
275
|
if (!numbering) return void 0;
|
|
@@ -275,8 +317,10 @@ function noteNumbers(flow) {
|
|
|
275
317
|
add(r.endnoteRef, endnotes, flow.endnotes);
|
|
276
318
|
add(r.commentRef, comments, flow.comments);
|
|
277
319
|
}
|
|
278
|
-
else if (el.kind === "table")
|
|
279
|
-
|
|
320
|
+
else if (el.kind === "table") {
|
|
321
|
+
for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
|
|
322
|
+
visit(el.table.overlay?.drawings ?? []);
|
|
323
|
+
} else if (el.kind === "shape") visitShape(el.shape);
|
|
280
324
|
};
|
|
281
325
|
visit(flow.body);
|
|
282
326
|
return {
|
|
@@ -357,7 +401,7 @@ function destination(url) {
|
|
|
357
401
|
* paragraph it belongs to — the same shape GFM's own heading anchors take.
|
|
358
402
|
*/
|
|
359
403
|
function bookmarkSlug(name) {
|
|
360
|
-
const slug = name.toLowerCase().
|
|
404
|
+
const slug = name.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((word) => word.length > 0).join("-");
|
|
361
405
|
return slug.length > 0 ? slug : "bookmark";
|
|
362
406
|
}
|
|
363
407
|
/** The bookmark names some run actually links to — the only ones worth an anchor. */
|
|
@@ -370,8 +414,10 @@ function referencedAnchors(body) {
|
|
|
370
414
|
const visit = (els) => {
|
|
371
415
|
for (const el of els) if (el.kind === "paragraph") {
|
|
372
416
|
for (const r of el.paragraph.runs) if (r.anchor !== void 0) out.add(r.anchor);
|
|
373
|
-
} else if (el.kind === "table")
|
|
374
|
-
|
|
417
|
+
} else if (el.kind === "table") {
|
|
418
|
+
for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
|
|
419
|
+
visit(el.table.overlay?.drawings ?? []);
|
|
420
|
+
} else if (el.kind === "shape") visitShape(el.shape);
|
|
375
421
|
};
|
|
376
422
|
visit(body);
|
|
377
423
|
return out;
|
|
@@ -673,7 +719,7 @@ var SPAN_EDGE = String.raw`(?:\\\n|\s)`;
|
|
|
673
719
|
function applyMarks(text, marks, ctx, before, after) {
|
|
674
720
|
const escaped = escapeInline(text);
|
|
675
721
|
const lead = new RegExp(`^${SPAN_EDGE}*`).exec(escaped)?.[0] ?? "";
|
|
676
|
-
const trail =
|
|
722
|
+
const trail = trailingEdge(escaped);
|
|
677
723
|
const core = escaped.slice(lead.length, escaped.length - trail.length);
|
|
678
724
|
if (core.length === 0) return escaped;
|
|
679
725
|
const outerBefore = lead.length > 0 ? lead[lead.length - 1] : before;
|
|
@@ -707,6 +753,22 @@ function applyMarks(text, marks, ctx, before, after) {
|
|
|
707
753
|
}
|
|
708
754
|
return `${lead}${linkify(out, marks, ctx)}${trail}`;
|
|
709
755
|
}
|
|
756
|
+
/**
|
|
757
|
+
* The whitespace and hard breaks the escaped text ends in — what
|
|
758
|
+
* `${SPAN_EDGE}*$` matches, read back from the end instead. Tried from the
|
|
759
|
+
* front, that expression rescans a long run of whitespace from each of its
|
|
760
|
+
* characters whenever something other than the end follows the run, and is
|
|
761
|
+
* quadratic on it.
|
|
762
|
+
*/
|
|
763
|
+
function trailingEdge(escaped) {
|
|
764
|
+
let from = escaped.length;
|
|
765
|
+
while (from > 0) {
|
|
766
|
+
const c = escaped[from - 1];
|
|
767
|
+
if (c === "\\" ? escaped[from] !== "\n" : !/\s/u.test(c)) break;
|
|
768
|
+
from--;
|
|
769
|
+
}
|
|
770
|
+
return escaped.slice(from);
|
|
771
|
+
}
|
|
710
772
|
var isSpace = (c) => c === "" || /\s/u.test(c);
|
|
711
773
|
var isPunct = (c) => /[\p{P}\p{S}]/u.test(c);
|
|
712
774
|
/** §6.2 — the delimiter run is left-flanking, so it can open emphasis. */
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { dict, name, ref, stream, unicodeString } from "./objects.js";
|
|
2
2
|
import { embedTtfFont } from "./cid-font.js";
|
|
3
3
|
import { preparePdfEncryption } from "./encryption.js";
|
|
4
|
-
import {
|
|
4
|
+
import { pageLayers, paintPlan } from "../layout/page-doc.js";
|
|
5
5
|
import { reverseByCodePoint } from "../core/bidi/index.js";
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
6
|
+
import "../layout/styled-layout.js";
|
|
7
|
+
import { BORDER_DASHES, FAUX_ITALIC_SHEAR, computeJustifyExtra, lineVisualOrder, settableInOneMatrix, washVeil } from "../layout/line-paint.js";
|
|
8
8
|
import { emitClipPath, emitVectorShape, shadowBlurLayers } from "./vector-graphics.js";
|
|
9
9
|
import { buildGradientAlphaMask, buildGradientPattern, shapeBbox } from "./shading.js";
|
|
10
10
|
import { warpGlyphMatrix } from "../core/drawingml/text-warp.js";
|
|
@@ -16,16 +16,6 @@ import { addSignaturePlaceholder } from "./signature.js";
|
|
|
16
16
|
import { buildXmpPacket } from "./xmp.js";
|
|
17
17
|
//#region src/pdf/styled-page-emitter.ts
|
|
18
18
|
var encoder = new TextEncoder();
|
|
19
|
-
function washVeil(wash) {
|
|
20
|
-
if (!wash) return void 0;
|
|
21
|
-
const alpha = 1 - wash.gain;
|
|
22
|
-
if (!(alpha > .004)) return void 0;
|
|
23
|
-
const offset = .5 * (1 - wash.gain) + wash.black;
|
|
24
|
-
return {
|
|
25
|
-
alpha: Math.min(1, alpha),
|
|
26
|
-
grey: Math.max(0, Math.min(1, offset / alpha))
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
19
|
/**
|
|
30
20
|
* The veil operators for a washed picture, to follow its own `/Im Do`: the
|
|
31
21
|
* placement matrix maps the unit square onto the picture as drawn, so the wash
|
|
@@ -268,7 +258,7 @@ function assembleStyledPdf(laid, options, doc) {
|
|
|
268
258
|
tagFor: (structId) => builder.node(structId).type
|
|
269
259
|
};
|
|
270
260
|
}
|
|
271
|
-
const { content: contentBytes, links } =
|
|
261
|
+
const { content: contentBytes, links } = emitPageLayers(page, pageTagging, gradientNames, alphaStateNames, wantDuotone, gradientMaskNames);
|
|
272
262
|
const contentsRef = doc.add(stream({}, contentBytes));
|
|
273
263
|
const pageEntries = {
|
|
274
264
|
Type: name("Page"),
|
|
@@ -611,31 +601,56 @@ function emitTaggedRuns(out, cmds, tagging, body) {
|
|
|
611
601
|
* 24. Scaling them by the width instead drew page-borders-export-case-2.docx's
|
|
612
602
|
* half-point frame as a row of specks a quarter the length it should be.
|
|
613
603
|
*/
|
|
614
|
-
var BORDER_DASHES = new Map([
|
|
615
|
-
["dashed", [8, 2.5]],
|
|
616
|
-
["dashSmallGap", [3, 1]],
|
|
617
|
-
["dotted", [.5, 1]],
|
|
618
|
-
["dashDot", [
|
|
619
|
-
8,
|
|
620
|
-
2.5,
|
|
621
|
-
2.5,
|
|
622
|
-
2.5
|
|
623
|
-
]],
|
|
624
|
-
["dashDotDot", [
|
|
625
|
-
8,
|
|
626
|
-
2.5,
|
|
627
|
-
2.5,
|
|
628
|
-
2.5,
|
|
629
|
-
2.5,
|
|
630
|
-
2.5
|
|
631
|
-
]]
|
|
632
|
-
]);
|
|
633
|
-
var FAUX_ITALIC_SHEAR = .2126;
|
|
634
604
|
/** The PDF dash array for a border style; an empty string means a solid rule. */
|
|
635
605
|
function dashPatternFor(style) {
|
|
636
606
|
const pattern = style !== void 0 ? BORDER_DASHES.get(style) : void 0;
|
|
637
607
|
return pattern ? `[${pattern.map(formatNumber).join(" ")}]` : "";
|
|
638
608
|
}
|
|
609
|
+
/**
|
|
610
|
+
* A page's content stream in its layers (pageLayers): its own items, then —
|
|
611
|
+
* when some of them stand IN FRONT of its text (PageItemBase.over) — those,
|
|
612
|
+
* painted again pass by pass over what the first layer left, their own text
|
|
613
|
+
* last; and each group seen through a window of its own (PageItemBase.window)
|
|
614
|
+
* inside a clip to it. Each layer is a balanced stream of its own, so they
|
|
615
|
+
* simply follow one another.
|
|
616
|
+
*/
|
|
617
|
+
function emitPageLayers(page, ...rest) {
|
|
618
|
+
const layers = pageLayers(page.commands);
|
|
619
|
+
if (layers.length === 1 && !layers[0].window) return emitPageContent(page, ...rest);
|
|
620
|
+
const parts = [];
|
|
621
|
+
const links = [];
|
|
622
|
+
for (const layer of layers) {
|
|
623
|
+
const emitted = emitPageContent({
|
|
624
|
+
...page,
|
|
625
|
+
commands: layer.items
|
|
626
|
+
}, ...rest);
|
|
627
|
+
links.push(...emitted.links);
|
|
628
|
+
const w = layer.window;
|
|
629
|
+
if (!w) {
|
|
630
|
+
parts.push(emitted.content);
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
if (emitted.content.length === 0) continue;
|
|
634
|
+
const rect = [
|
|
635
|
+
w.x,
|
|
636
|
+
page.height - w.y - w.height,
|
|
637
|
+
w.width,
|
|
638
|
+
w.height
|
|
639
|
+
].map(formatNumber);
|
|
640
|
+
parts.push(encoder.encode(`q ${rect.join(" ")} re W n`), emitted.content, encoder.encode("Q"));
|
|
641
|
+
}
|
|
642
|
+
const content = new Uint8Array(parts.reduce((sum, p) => sum + p.length, 0) + Math.max(0, parts.length - 1));
|
|
643
|
+
let at = 0;
|
|
644
|
+
parts.forEach((p, i) => {
|
|
645
|
+
if (i > 0) content[at++] = 10;
|
|
646
|
+
content.set(p, at);
|
|
647
|
+
at += p.length;
|
|
648
|
+
});
|
|
649
|
+
return {
|
|
650
|
+
content,
|
|
651
|
+
links
|
|
652
|
+
};
|
|
653
|
+
}
|
|
639
654
|
function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneStateFor, gradientMaskNames) {
|
|
640
655
|
const commands = page.commands;
|
|
641
656
|
const links = [];
|
|
@@ -1414,9 +1429,6 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
|
|
|
1414
1429
|
links
|
|
1415
1430
|
};
|
|
1416
1431
|
}
|
|
1417
|
-
function lineVisualOrder(line) {
|
|
1418
|
-
return reorderVisual(line.tokens.map((t) => t.bidiLevel));
|
|
1419
|
-
}
|
|
1420
1432
|
/**
|
|
1421
1433
|
* The operators that put an image XObject in a box — ISO 32000-1 §8.9.5.1: the
|
|
1422
1434
|
* unit square scaled to the box and moved to `(x, y)`, its bottom-left corner.
|
|
@@ -1460,40 +1472,6 @@ function rotateAboutCm(cx, cy, deg) {
|
|
|
1460
1472
|
const s = Math.sin(rad);
|
|
1461
1473
|
return `${formatNumber(c)} ${formatNumber(s)} ${formatNumber(-s)} ${formatNumber(c)} ${formatNumber(cx - cx * c + cy * s)} ${formatNumber(cy - cx * s - cy * c)} cm`;
|
|
1462
1474
|
}
|
|
1463
|
-
/**
|
|
1464
|
-
* Whether a line is ONE text matrix and a run of glyphs: nothing in it placed
|
|
1465
|
-
* on its own — a tab, a stretched space, a picture, a formula, a right-to-left
|
|
1466
|
-
* run, a rise, a faked italic — and nothing drawn beside its glyphs, a run's
|
|
1467
|
-
* shading, a highlight or a rule. Only such a line can be turned by its matrix
|
|
1468
|
-
* alone; any other is placed token by token in page space.
|
|
1469
|
-
*
|
|
1470
|
-
* @param line The laid-out line.
|
|
1471
|
-
* @returns True when a single `Tm` sets the whole of it.
|
|
1472
|
-
*/
|
|
1473
|
-
function settableInOneMatrix(line) {
|
|
1474
|
-
return computeJustifyExtra(line) === 0 && line.tokens.every((t) => t.kind === "text" && t.tab !== true && t.bidiLevel % 2 === 0 && t.risePt === void 0 && t.synthetic?.italic !== true && t.highlight !== true && t.resolvedRun.shadingColorHex === void 0 && t.resolvedRun.underline === "none" && !t.resolvedRun.strike);
|
|
1475
|
-
}
|
|
1476
|
-
function computeJustifyExtra(line) {
|
|
1477
|
-
const alignment = line.resolved.alignment;
|
|
1478
|
-
if (alignment !== "both" && alignment !== "distribute") return 0;
|
|
1479
|
-
if (line.noJustify === true) return 0;
|
|
1480
|
-
if (line.isLastInParagraph && alignment !== "distribute") return 0;
|
|
1481
|
-
let last = line.tokens.length - 1;
|
|
1482
|
-
while (last >= 0 && line.tokens[last].kind === "text" && line.tokens[last].isSpace) last--;
|
|
1483
|
-
let spaces = 0;
|
|
1484
|
-
let narrowest = Infinity;
|
|
1485
|
-
let contentWidthPt = 0;
|
|
1486
|
-
for (let i = 0; i <= last; i++) {
|
|
1487
|
-
const tok = line.tokens[i];
|
|
1488
|
-
contentWidthPt += tok.widthPt;
|
|
1489
|
-
if (!tok.isSpace) continue;
|
|
1490
|
-
spaces++;
|
|
1491
|
-
if (tok.widthPt < narrowest) narrowest = tok.widthPt;
|
|
1492
|
-
}
|
|
1493
|
-
if (spaces === 0) return 0;
|
|
1494
|
-
const extra = (line.availableWidthPt - contentWidthPt) / spaces;
|
|
1495
|
-
return extra < 0 ? Math.max(extra, -narrowest * GLUE_SHRINK_RATIO) : extra;
|
|
1496
|
-
}
|
|
1497
1475
|
function hexToRgb01(hex) {
|
|
1498
1476
|
return [
|
|
1499
1477
|
parseInt(hex.slice(0, 2), 16) / 255,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PdfDocument } from "./writer.js";
|
|
2
|
-
import { layoutStyledDocument } from "../layout/styled-layout.js";
|
|
3
2
|
import "../layout/page-doc.js";
|
|
3
|
+
import { layoutStyledDocument } from "../layout/styled-layout.js";
|
|
4
4
|
import { emitStyledPdf, emitStyledPdfEncrypted } from "./styled-page-emitter.js";
|
|
5
5
|
//#region src/pdf/styled-page-renderer.ts
|
|
6
6
|
/**
|
|
@@ -260,7 +260,7 @@ function freeTextContents(file, annot) {
|
|
|
260
260
|
const found = file.get(annot, "Contents");
|
|
261
261
|
const raw = found instanceof PdfHexString ? String.fromCharCode(...found.bytes) : typeof found === "string" ? found : void 0;
|
|
262
262
|
if (raw === void 0) return void 0;
|
|
263
|
-
const text = textString(raw).
|
|
263
|
+
const text = textString(raw).trimEnd();
|
|
264
264
|
return text.length > 0 ? text : void 0;
|
|
265
265
|
}
|
|
266
266
|
/**
|
|
@@ -331,7 +331,7 @@ function defaultAppearance(file, annot) {
|
|
|
331
331
|
const form = acro instanceof Map ? file.get(acro, "DA") : void 0;
|
|
332
332
|
const da = own ?? (typeof form === "string" ? form : "");
|
|
333
333
|
const tf = /\/([^\s/]+)\s+([\d.]+)\s+Tf/u.exec(da);
|
|
334
|
-
const rgb = /([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+rg/u.exec(da);
|
|
334
|
+
const rgb = /(?<![\d.])([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+rg/u.exec(da);
|
|
335
335
|
const gray = /(^|\s)([\d.]+)\s+g(\s|$)/u.exec(da);
|
|
336
336
|
const color = rgb ? `${num(Number(rgb[1]))} ${num(Number(rgb[2]))} ${num(Number(rgb[3]))}` : gray ? `${num(Number(gray[2]))} ${num(Number(gray[2]))} ${num(Number(gray[2]))}` : "0 0 0";
|
|
337
337
|
return {
|
|
@@ -137,7 +137,9 @@ function typedField(file, annot) {
|
|
|
137
137
|
* field whose value is "Hello World".
|
|
138
138
|
*/
|
|
139
139
|
function withoutVariableText(file, stream) {
|
|
140
|
-
const
|
|
140
|
+
const text = new TextDecoder("latin1").decode(file.streamData(stream));
|
|
141
|
+
const end = text.lastIndexOf("EMC") + 3;
|
|
142
|
+
const stripped = text.slice(0, end).replace(/\/Tx\s+BMC[\s\S]*?EMC/gu, "") + text.slice(end);
|
|
141
143
|
const dict = new Map(stream.dict);
|
|
142
144
|
dict.delete("Filter");
|
|
143
145
|
dict.delete("DecodeParms");
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { knowsFamily, resolveFamilyStyle } from "../core/fonts/remote-fonts.js";
|
|
2
2
|
import { parseTtf } from "../core/font/ttf-parser.js";
|
|
3
3
|
import { PDF_NULL, PdfName, PdfStream } from "../pdf/objects.js";
|
|
4
|
+
import { baseEncodingTable, isStandardLatinFace, macGlyphName, standardEncodingTable, winAnsiLatinName } from "./encodings.js";
|
|
5
|
+
import { cffCidToGid, cffFsType, cffNameToGid, cffOutlineSource, cffSpaceAdvance, openTypeCff } from "./cff-outline.js";
|
|
6
|
+
import { cmapSubtables, outlineSource, postGlyphNames, sfntFsType, sfntSpaceAdvance } from "./glyf-outline.js";
|
|
4
7
|
import { parseToUnicodeCMap } from "./cmap.js";
|
|
5
8
|
import { decodePredefined, predefinedCMap, splitPredefined } from "./predefined-cmap.js";
|
|
6
9
|
import { isZapfDingbats, zapfDingbatsChar } from "./dingbats.js";
|
|
7
10
|
import { textForGlyphName } from "./glyph-names.js";
|
|
8
|
-
import { baseEncodingTable, isStandardLatinFace, macGlyphName, standardEncodingTable, winAnsiLatinName } from "./encodings.js";
|
|
9
|
-
import { cffCidToGid, cffFsType, cffNameToGid, cffOutlineSource, cffSpaceAdvance, openTypeCff } from "./cff-outline.js";
|
|
10
11
|
import { type1Font } from "./type1-outline.js";
|
|
11
|
-
import { cmapSubtables, outlineSource, postGlyphNames, sfntFsType, sfntSpaceAdvance } from "./glyf-outline.js";
|
|
12
12
|
import { standardFace, standardWidth } from "./standard-widths.js";
|
|
13
13
|
import { eachPageFont, embeddedFontName, hasLiftableProgram, programStyle } from "./embedded-fonts.js";
|
|
14
14
|
//#region src/pdf-reader/font.ts
|
|
@@ -1047,7 +1047,7 @@ function familyOfFace(baseFont) {
|
|
|
1047
1047
|
const name = plainFace(baseFont);
|
|
1048
1048
|
const cut = /^(.+?)[-,]([^-,]+)$/u.exec(name);
|
|
1049
1049
|
const whole = cut && STYLE_WORDS.test(cut[2]) ? cut[1] : cut ? name.replace(/-/gu, " ") : GLUED_STYLE.exec(name)?.[1] ?? name;
|
|
1050
|
-
return (whole.replace(/(?:PSMT|PS|MT)$/u, "") || whole).replace(/([a-z])([A-Z])/gu, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/gu, "$1 $2").replace(/\bDeja Vu\b/u, "DejaVu").replace(/\s+/gu, " ").trim();
|
|
1050
|
+
return (whole.replace(/(?:PSMT|PS|MT)$/u, "") || whole).replace(/([a-z])([A-Z])/gu, "$1 $2").replace(/(?<![A-Z])([A-Z]+)([A-Z][a-z])/gu, "$1 $2").replace(/\bDeja Vu\b/u, "DejaVu").replace(/\s+/gu, " ").trim();
|
|
1051
1051
|
}
|
|
1052
1052
|
/**
|
|
1053
1053
|
* A face name with what producers write around it taken off: the subset tag
|
|
@@ -1585,7 +1585,7 @@ function inkSpan(runs) {
|
|
|
1585
1585
|
const last = marked[marked.length - 1];
|
|
1586
1586
|
const space = (r) => r.spaceWidthPt !== void 0 && r.spaceWidthPt > 0 ? r.spaceWidthPt : (r.fontSizePt || 10) * .25;
|
|
1587
1587
|
const lead = (/^\s*/u.exec(first.text)?.[0].length ?? 0) * space(first);
|
|
1588
|
-
const trail = (
|
|
1588
|
+
const trail = (last.text.length - last.text.trimEnd().length) * space(last);
|
|
1589
1589
|
const x = first.x + lead;
|
|
1590
1590
|
return {
|
|
1591
1591
|
x,
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { FontResource, LaidOutDocument, TextLineItem } from '../layout/page-doc.js';
|
|
2
|
+
import { ImageCrop } from '../core/document-model/index.js';
|
|
3
|
+
import { VectorShape } from '../core/vector.js';
|
|
4
|
+
/** The glyph outlines of a page's faces, each defined once and used by id. */
|
|
5
|
+
export declare class SvgGlyphs {
|
|
6
|
+
private readonly sources;
|
|
7
|
+
private readonly ids;
|
|
8
|
+
private readonly defs;
|
|
9
|
+
private readonly faceIds;
|
|
10
|
+
/**
|
|
11
|
+
* The id of the path that draws glyph `gid` of `font`, defining it on first
|
|
12
|
+
* use: `null` for a glyph with no ink (a space), `undefined` where the face
|
|
13
|
+
* carries no outlines this reads — the caller then sets the token as text.
|
|
14
|
+
*
|
|
15
|
+
* @param font The face.
|
|
16
|
+
* @param gid The glyph.
|
|
17
|
+
* @returns The `<path>` id, `null`, or `undefined`.
|
|
18
|
+
*/
|
|
19
|
+
use(font: FontResource, gid: number): string | null | undefined;
|
|
20
|
+
/** The `<defs>` holding every glyph used, or nothing when none was. */
|
|
21
|
+
defsXml(): string;
|
|
22
|
+
private sourceOf;
|
|
23
|
+
}
|
|
24
|
+
/** What drawing a line needs from the writer around it. */
|
|
25
|
+
export interface SvgLineCtx {
|
|
26
|
+
readonly glyphs: SvgGlyphs;
|
|
27
|
+
/** The document-wide counter generated ids are numbered from. */
|
|
28
|
+
readonly ids: {
|
|
29
|
+
n: number;
|
|
30
|
+
};
|
|
31
|
+
readonly laid: LaidOutDocument;
|
|
32
|
+
/** A picture in a box, as the writer draws one. */
|
|
33
|
+
readonly image: (out: Array<string>, placed: PlacedImage) => void;
|
|
34
|
+
/** A vector shape, as the writer draws one. */
|
|
35
|
+
readonly shape: (out: Array<string>, shape: VectorShape) => void;
|
|
36
|
+
}
|
|
37
|
+
/** A picture placed in a box on the page — its top-left corner and size, in points. */
|
|
38
|
+
export interface PlacedImage {
|
|
39
|
+
readonly resourceName: string;
|
|
40
|
+
readonly x: number;
|
|
41
|
+
readonly y: number;
|
|
42
|
+
readonly width: number;
|
|
43
|
+
readonly height: number;
|
|
44
|
+
readonly crop?: ImageCrop;
|
|
45
|
+
readonly rotationDeg?: number;
|
|
46
|
+
readonly flipH?: boolean;
|
|
47
|
+
readonly flipV?: boolean;
|
|
48
|
+
readonly alpha?: number;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Draw one laid-out line: what stands behind its glyphs, then the glyphs, a
|
|
52
|
+
* picture or formula wherever one stands in it.
|
|
53
|
+
*
|
|
54
|
+
* @param out The SVG fragment sink.
|
|
55
|
+
* @param item The line, as the layout placed it.
|
|
56
|
+
* @param ctx The glyph definitions and the writer's own drawing.
|
|
57
|
+
*/
|
|
58
|
+
export declare function emitSvgLine(out: Array<string>, item: TextLineItem, ctx: SvgLineCtx): void;
|