@bendyline/squisq-formats 2.1.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/NOTICE.md +19 -0
- package/README.md +2 -2
- package/dist/{chunk-NNHKUXKA.js → chunk-26ISNJ7Y.js} +85 -65
- package/dist/{chunk-NKAJPJ4G.js → chunk-2JJ5RFDZ.js} +0 -1
- package/dist/{chunk-KURGXM4I.js → chunk-4V3KCHAP.js} +3 -4
- package/dist/{chunk-MLX2BOJC.js → chunk-6RQOV3B3.js} +1 -2
- package/dist/{chunk-EW54IRRS.js → chunk-6S6GU3ZG.js} +5 -6
- package/dist/{chunk-FE6OJV6O.js → chunk-7AWFHP5U.js} +1 -1
- package/dist/{chunk-RFAPOKHJ.js → chunk-AD2WT564.js} +59 -9
- package/dist/{chunk-O3GVVND4.js → chunk-AONELFLA.js} +0 -1
- package/dist/{chunk-SC67HYQJ.js → chunk-EJTNGKEA.js} +5 -8
- package/dist/{chunk-WQSHGBLN.js → chunk-G6J326JW.js} +193 -42
- package/dist/chunk-GX7RAUME.js +121 -0
- package/dist/{chunk-GVS2XXV6.js → chunk-GYIVES2E.js} +485 -59
- package/dist/{chunk-SSUPBUF5.js → chunk-IIQYS2YH.js} +0 -1
- package/dist/{chunk-RLU7UFYU.js → chunk-IPN56VLW.js} +83 -58
- package/dist/{chunk-DTDF6QDP.js → chunk-JE6LSIHE.js} +81 -20
- package/dist/{chunk-U4MRIFKL.js → chunk-JU2RHXUB.js} +0 -1
- package/dist/{chunk-4VUWTSGM.js → chunk-K6XRMVPW.js} +64 -31
- package/dist/{chunk-ODL3SSPT.js → chunk-KXOZMWBS.js} +0 -1
- package/dist/chunk-OGS5VCGJ.js +446 -0
- package/dist/{chunk-74GO3FVS.js → chunk-PU7REGWV.js} +5 -8
- package/dist/{chunk-PN52A5AA.js → chunk-SBUW7NHR.js} +0 -1
- package/dist/{chunk-QFLDYKCR.js → chunk-TAAENIRB.js} +5 -8
- package/dist/{chunk-7ARKUCQT.js → chunk-X2DEAXNK.js} +54 -2
- package/dist/container/index.js +1 -2
- package/dist/csv/index.d.ts +27 -2
- package/dist/csv/index.js +1 -2
- package/dist/docx/index.d.ts +5 -1
- package/dist/docx/index.js +9 -11
- package/dist/epub/index.d.ts +2 -0
- package/dist/epub/index.js +5 -6
- package/dist/{export-D2NkylDT.d.ts → export-D9msROJS.d.ts} +18 -6
- package/dist/extract-MN7LA3NL.js +13 -0
- package/dist/html/index.d.ts +11 -4
- package/dist/html/index.js +3 -4
- package/dist/images-ESPQKVTW.js +6 -0
- package/dist/{import-K8mfc0fz.d.ts → import-C3htUTss.d.ts} +5 -1
- package/dist/{import-DTkDxHmZ.d.ts → import-C8whCC7_.d.ts} +6 -0
- package/dist/index.d.ts +7 -7
- package/dist/index.js +28 -26
- package/dist/infer/index.d.ts +3 -3
- package/dist/infer/index.js +7 -9
- package/dist/{layouts-BHrgZ5FS.d.ts → layouts-CTdPlB-u.d.ts} +1 -1
- package/dist/layouts-DRWZGSPD.js +10 -0
- package/dist/{mapTheme-IR27S6IV.js → mapTheme-4TWH25FT.js} +1 -2
- package/dist/ooxml/index.d.ts +3 -3
- package/dist/ooxml/index.js +14 -13
- package/dist/pdf/index.d.ts +18 -0
- package/dist/pdf/index.js +2 -3
- package/dist/pptx/index.d.ts +4 -4
- package/dist/pptx/index.js +11 -13
- package/dist/{reader-B9L8Ucbj.d.ts → reader-B_m1aKZC.d.ts} +30 -1
- package/dist/registry/index.d.ts +21 -5
- package/dist/registry/index.js +9 -6
- package/dist/{themeReader-DJKErl_j.d.ts → themeReader-DCtwC83Q.d.ts} +1 -1
- package/dist/xlsx/index.d.ts +3 -3
- package/dist/xlsx/index.js +6 -7
- package/package.json +10 -4
- package/dist/chunk-4VUWTSGM.js.map +0 -1
- package/dist/chunk-6M7Z25LA.js +0 -46
- package/dist/chunk-6M7Z25LA.js.map +0 -1
- package/dist/chunk-74GO3FVS.js.map +0 -1
- package/dist/chunk-7ARKUCQT.js.map +0 -1
- package/dist/chunk-DTDF6QDP.js.map +0 -1
- package/dist/chunk-EW54IRRS.js.map +0 -1
- package/dist/chunk-FE6OJV6O.js.map +0 -1
- package/dist/chunk-GVS2XXV6.js.map +0 -1
- package/dist/chunk-KURGXM4I.js.map +0 -1
- package/dist/chunk-MLX2BOJC.js.map +0 -1
- package/dist/chunk-NKAJPJ4G.js.map +0 -1
- package/dist/chunk-NNHKUXKA.js.map +0 -1
- package/dist/chunk-O3GVVND4.js.map +0 -1
- package/dist/chunk-ODL3SSPT.js.map +0 -1
- package/dist/chunk-PN52A5AA.js.map +0 -1
- package/dist/chunk-QFLDYKCR.js.map +0 -1
- package/dist/chunk-RFAPOKHJ.js.map +0 -1
- package/dist/chunk-RLU7UFYU.js.map +0 -1
- package/dist/chunk-SC67HYQJ.js.map +0 -1
- package/dist/chunk-SSUPBUF5.js.map +0 -1
- package/dist/chunk-U4MRIFKL.js.map +0 -1
- package/dist/chunk-UGYF5AZE.js +0 -275
- package/dist/chunk-UGYF5AZE.js.map +0 -1
- package/dist/chunk-WQSHGBLN.js.map +0 -1
- package/dist/chunk-YRT7GQ5Y.js +0 -28
- package/dist/chunk-YRT7GQ5Y.js.map +0 -1
- package/dist/container/index.js.map +0 -1
- package/dist/csv/index.js.map +0 -1
- package/dist/docx/index.js.map +0 -1
- package/dist/epub/index.js.map +0 -1
- package/dist/extract-OJ7ZQV6P.js +0 -15
- package/dist/extract-OJ7ZQV6P.js.map +0 -1
- package/dist/html/index.js.map +0 -1
- package/dist/images-7FBWPKE3.js +0 -7
- package/dist/images-7FBWPKE3.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/infer/index.js.map +0 -1
- package/dist/layouts-5VDIRPIJ.js +0 -12
- package/dist/layouts-5VDIRPIJ.js.map +0 -1
- package/dist/mapTheme-IR27S6IV.js.map +0 -1
- package/dist/ooxml/index.js.map +0 -1
- package/dist/pdf/index.js.map +0 -1
- package/dist/pptx/index.js.map +0 -1
- package/dist/registry/index.js.map +0 -1
- package/dist/xlsx/index.js.map +0 -1
- package/src/__tests__/container.test.ts +0 -230
- package/src/__tests__/convert.test.ts +0 -495
- package/src/__tests__/csvImport.test.ts +0 -84
- package/src/__tests__/docxExport.test.ts +0 -491
- package/src/__tests__/docxImport.test.ts +0 -531
- package/src/__tests__/epub.test.ts +0 -649
- package/src/__tests__/exportThemeReconciliation.test.ts +0 -87
- package/src/__tests__/formatRegistry.test.ts +0 -174
- package/src/__tests__/html.test.ts +0 -439
- package/src/__tests__/htmlImport.test.ts +0 -57
- package/src/__tests__/inferTheme.test.ts +0 -135
- package/src/__tests__/lossyWarnings.test.ts +0 -146
- package/src/__tests__/ooxml.test.ts +0 -271
- package/src/__tests__/ooxmlCancellation.test.ts +0 -113
- package/src/__tests__/ooxmlThemeReader.test.ts +0 -92
- package/src/__tests__/pdfExport.test.ts +0 -322
- package/src/__tests__/pdfImport.test.ts +0 -384
- package/src/__tests__/plainHtml.test.ts +0 -417
- package/src/__tests__/plainHtmlBundle.test.ts +0 -253
- package/src/__tests__/pptxExport.test.ts +0 -138
- package/src/__tests__/pptxImport.test.ts +0 -145
- package/src/__tests__/pptxInferFixtures.ts +0 -314
- package/src/__tests__/pptxLayoutInfer.test.ts +0 -395
- package/src/__tests__/roundTrip.test.ts +0 -201
- package/src/__tests__/roundTripAssets.test.ts +0 -50
- package/src/__tests__/roundTripMatrix.fixtures.ts +0 -86
- package/src/__tests__/roundTripMatrix.helpers.ts +0 -154
- package/src/__tests__/roundTripMatrix.test.ts +0 -142
- package/src/__tests__/sharedContainer.test.ts +0 -41
- package/src/__tests__/sharedImages.test.ts +0 -61
- package/src/__tests__/xlsxExport.test.ts +0 -164
- package/src/__tests__/xlsxImport.test.ts +0 -80
- package/src/__tests__/zipSafety.test.ts +0 -317
- package/src/container/index.ts +0 -94
- package/src/csv/index.ts +0 -188
- package/src/docx/export.ts +0 -1375
- package/src/docx/import.ts +0 -1250
- package/src/docx/index.ts +0 -26
- package/src/docx/styles.ts +0 -145
- package/src/epub/export.ts +0 -968
- package/src/epub/index.ts +0 -20
- package/src/html/docsHtmlBundle.ts +0 -373
- package/src/html/htmlTemplate.ts +0 -385
- package/src/html/imageUtils.ts +0 -61
- package/src/html/import.ts +0 -297
- package/src/html/index.ts +0 -212
- package/src/html/plainHtml.ts +0 -790
- package/src/html/plainHtmlBundle.ts +0 -421
- package/src/index.ts +0 -109
- package/src/infer/extract.ts +0 -127
- package/src/infer/index.ts +0 -199
- package/src/infer/mapTheme.ts +0 -176
- package/src/infer/types.ts +0 -27
- package/src/ooxml/index.ts +0 -111
- package/src/ooxml/namespaces.ts +0 -217
- package/src/ooxml/readUtils.ts +0 -44
- package/src/ooxml/reader.ts +0 -318
- package/src/ooxml/themeReader.ts +0 -197
- package/src/ooxml/types.ts +0 -103
- package/src/ooxml/writer.ts +0 -339
- package/src/ooxml/xmlUtils.ts +0 -123
- package/src/pdf/export.ts +0 -1084
- package/src/pdf/import.ts +0 -1164
- package/src/pdf/index.ts +0 -29
- package/src/pdf/styles.ts +0 -180
- package/src/pptx/export.ts +0 -1184
- package/src/pptx/import.ts +0 -455
- package/src/pptx/index.ts +0 -52
- package/src/pptx/layouts.ts +0 -1222
- package/src/pptx/styles.ts +0 -96
- package/src/pptx/templates.ts +0 -187
- package/src/registry/convert.ts +0 -433
- package/src/registry/defaultFormats.ts +0 -413
- package/src/registry/errors.ts +0 -46
- package/src/registry/index.ts +0 -43
- package/src/registry/registry.ts +0 -48
- package/src/registry/types.ts +0 -170
- package/src/shared/boundedZipArchive.ts +0 -383
- package/src/shared/container.ts +0 -28
- package/src/shared/fidelity.ts +0 -130
- package/src/shared/images.ts +0 -44
- package/src/shared/inlineRuns.ts +0 -99
- package/src/shared/text.ts +0 -41
- package/src/shared/zipEntryCount.ts +0 -151
- package/src/shared/zipLimits.ts +0 -296
- package/src/shared/zipSafety.ts +0 -19
- package/src/xlsx/export.ts +0 -253
- package/src/xlsx/import.ts +0 -160
- package/src/xlsx/index.ts +0 -35
|
@@ -1,11 +1,314 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildContainer
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-IIQYS2YH.js";
|
|
4
4
|
|
|
5
5
|
// src/pdf/export.ts
|
|
6
|
-
import { PDFDocument, StandardFonts, rgb } from "pdf-lib";
|
|
6
|
+
import { PDFDocument, StandardFonts, rgb, PDFString } from "pdf-lib";
|
|
7
7
|
import { docToMarkdown, resolveThemeForDoc } from "@bendyline/squisq/doc";
|
|
8
|
-
import { readFrontmatterThemeId } from "@bendyline/squisq/markdown";
|
|
8
|
+
import { readFrontmatterThemeId, sanitizeUrl } from "@bendyline/squisq/markdown";
|
|
9
|
+
|
|
10
|
+
// src/pdf/winAnsi.ts
|
|
11
|
+
var CP1252_HIGH_SPECIALS = /* @__PURE__ */ new Set([
|
|
12
|
+
8364,
|
|
13
|
+
// euro sign
|
|
14
|
+
8218,
|
|
15
|
+
// single low-9 quotation mark
|
|
16
|
+
402,
|
|
17
|
+
// latin small letter f with hook
|
|
18
|
+
8222,
|
|
19
|
+
// double low-9 quotation mark
|
|
20
|
+
8230,
|
|
21
|
+
// horizontal ellipsis
|
|
22
|
+
8224,
|
|
23
|
+
// dagger
|
|
24
|
+
8225,
|
|
25
|
+
// double dagger
|
|
26
|
+
710,
|
|
27
|
+
// modifier letter circumflex accent
|
|
28
|
+
8240,
|
|
29
|
+
// per mille sign
|
|
30
|
+
352,
|
|
31
|
+
// latin capital letter S with caron
|
|
32
|
+
8249,
|
|
33
|
+
// single left-pointing angle quotation mark
|
|
34
|
+
338,
|
|
35
|
+
// latin capital ligature OE
|
|
36
|
+
381,
|
|
37
|
+
// latin capital letter Z with caron
|
|
38
|
+
8216,
|
|
39
|
+
// left single quotation mark
|
|
40
|
+
8217,
|
|
41
|
+
// right single quotation mark
|
|
42
|
+
8220,
|
|
43
|
+
// left double quotation mark
|
|
44
|
+
8221,
|
|
45
|
+
// right double quotation mark
|
|
46
|
+
8226,
|
|
47
|
+
// bullet
|
|
48
|
+
8211,
|
|
49
|
+
// en dash
|
|
50
|
+
8212,
|
|
51
|
+
// em dash
|
|
52
|
+
732,
|
|
53
|
+
// small tilde
|
|
54
|
+
8482,
|
|
55
|
+
// trade mark sign
|
|
56
|
+
353,
|
|
57
|
+
// latin small letter s with caron
|
|
58
|
+
8250,
|
|
59
|
+
// single right-pointing angle quotation mark
|
|
60
|
+
339,
|
|
61
|
+
// latin small ligature oe
|
|
62
|
+
382,
|
|
63
|
+
// latin small letter z with caron
|
|
64
|
+
376
|
|
65
|
+
// latin capital letter Y with diaeresis
|
|
66
|
+
]);
|
|
67
|
+
function isWinAnsiEncodable(codePoint) {
|
|
68
|
+
if (codePoint >= 32 && codePoint <= 126) return true;
|
|
69
|
+
if (codePoint >= 160 && codePoint <= 255) return true;
|
|
70
|
+
return CP1252_HIGH_SPECIALS.has(codePoint);
|
|
71
|
+
}
|
|
72
|
+
var WINANSI_REPLACEMENT = "?";
|
|
73
|
+
var TRANSLITERATIONS = /* @__PURE__ */ new Map([
|
|
74
|
+
// ── Whitespace & invisibles ──
|
|
75
|
+
// A tab keeps its visual width (code fences are the common case); every
|
|
76
|
+
// other control/format character collapses to a space or disappears.
|
|
77
|
+
[9, " "],
|
|
78
|
+
// tab
|
|
79
|
+
[10, " "],
|
|
80
|
+
// line feed
|
|
81
|
+
[11, " "],
|
|
82
|
+
// vertical tab
|
|
83
|
+
[12, " "],
|
|
84
|
+
// form feed
|
|
85
|
+
[13, " "],
|
|
86
|
+
// carriage return
|
|
87
|
+
[133, " "],
|
|
88
|
+
// next line
|
|
89
|
+
[8232, " "],
|
|
90
|
+
// line separator
|
|
91
|
+
[8233, " "],
|
|
92
|
+
// paragraph separator
|
|
93
|
+
[8192, " "],
|
|
94
|
+
// en quad
|
|
95
|
+
[8193, " "],
|
|
96
|
+
// em quad
|
|
97
|
+
[8194, " "],
|
|
98
|
+
// en space
|
|
99
|
+
[8195, " "],
|
|
100
|
+
// em space
|
|
101
|
+
[8196, " "],
|
|
102
|
+
// three-per-em space
|
|
103
|
+
[8197, " "],
|
|
104
|
+
// four-per-em space
|
|
105
|
+
[8198, " "],
|
|
106
|
+
// six-per-em space
|
|
107
|
+
[8199, " "],
|
|
108
|
+
// figure space
|
|
109
|
+
[8200, " "],
|
|
110
|
+
// punctuation space
|
|
111
|
+
[8201, " "],
|
|
112
|
+
// thin space
|
|
113
|
+
[8202, " "],
|
|
114
|
+
// hair space
|
|
115
|
+
[8239, " "],
|
|
116
|
+
// narrow no-break space
|
|
117
|
+
[8287, " "],
|
|
118
|
+
// medium mathematical space
|
|
119
|
+
[12288, " "],
|
|
120
|
+
// ideographic space
|
|
121
|
+
[8203, ""],
|
|
122
|
+
// zero-width space
|
|
123
|
+
[8204, ""],
|
|
124
|
+
// zero-width non-joiner
|
|
125
|
+
[8205, ""],
|
|
126
|
+
// zero-width joiner
|
|
127
|
+
[8288, ""],
|
|
128
|
+
// word joiner
|
|
129
|
+
[65279, ""],
|
|
130
|
+
// zero-width no-break space / BOM
|
|
131
|
+
// ── Dashes & hyphens (en/em dash ARE encodable and stay verbatim) ──
|
|
132
|
+
[8208, "-"],
|
|
133
|
+
// hyphen
|
|
134
|
+
[8209, "-"],
|
|
135
|
+
// non-breaking hyphen
|
|
136
|
+
[8210, "-"],
|
|
137
|
+
// figure dash
|
|
138
|
+
[8213, "--"],
|
|
139
|
+
// horizontal bar
|
|
140
|
+
[8722, "-"],
|
|
141
|
+
// minus sign
|
|
142
|
+
// ── Quotes & primes (curly quotes ARE encodable and stay verbatim) ──
|
|
143
|
+
[8242, "'"],
|
|
144
|
+
// prime
|
|
145
|
+
[8243, '"'],
|
|
146
|
+
// double prime
|
|
147
|
+
[8245, "'"],
|
|
148
|
+
// reversed prime
|
|
149
|
+
[8246, '"'],
|
|
150
|
+
// reversed double prime
|
|
151
|
+
// ── Arrows ──
|
|
152
|
+
[8592, "<-"],
|
|
153
|
+
// leftwards arrow
|
|
154
|
+
[8593, "^"],
|
|
155
|
+
// upwards arrow
|
|
156
|
+
[8594, "->"],
|
|
157
|
+
// rightwards arrow
|
|
158
|
+
[8595, "v"],
|
|
159
|
+
// downwards arrow
|
|
160
|
+
[8596, "<->"],
|
|
161
|
+
// left right arrow
|
|
162
|
+
[8656, "<="],
|
|
163
|
+
// leftwards double arrow
|
|
164
|
+
[8658, "=>"],
|
|
165
|
+
// rightwards double arrow
|
|
166
|
+
[8660, "<=>"],
|
|
167
|
+
// left right double arrow
|
|
168
|
+
// ── Math & comparison ──
|
|
169
|
+
[8800, "!="],
|
|
170
|
+
// not equal to
|
|
171
|
+
[8804, "<="],
|
|
172
|
+
// less-than or equal to
|
|
173
|
+
[8805, ">="],
|
|
174
|
+
// greater-than or equal to
|
|
175
|
+
[8776, "~="],
|
|
176
|
+
// almost equal to
|
|
177
|
+
[8801, "=="],
|
|
178
|
+
// identical to
|
|
179
|
+
[8734, "inf"],
|
|
180
|
+
// infinity
|
|
181
|
+
[8730, "sqrt"],
|
|
182
|
+
// square root
|
|
183
|
+
[8721, "sum"],
|
|
184
|
+
// n-ary summation
|
|
185
|
+
[8719, "prod"],
|
|
186
|
+
// n-ary product
|
|
187
|
+
[8260, "/"],
|
|
188
|
+
// fraction slash
|
|
189
|
+
// ── Bullets & marks (U+2022 bullet IS encodable) ──
|
|
190
|
+
[8227, "*"],
|
|
191
|
+
// triangular bullet
|
|
192
|
+
[8259, "-"],
|
|
193
|
+
// hyphen bullet
|
|
194
|
+
[8729, "*"],
|
|
195
|
+
// bullet operator
|
|
196
|
+
[9642, "*"],
|
|
197
|
+
// black small square
|
|
198
|
+
[9643, "*"],
|
|
199
|
+
// white small square
|
|
200
|
+
[9675, "o"],
|
|
201
|
+
// white circle
|
|
202
|
+
[9679, "*"],
|
|
203
|
+
// black circle
|
|
204
|
+
[9702, "o"],
|
|
205
|
+
// white bullet
|
|
206
|
+
[10003, "v"],
|
|
207
|
+
// check mark
|
|
208
|
+
[10004, "v"],
|
|
209
|
+
// heavy check mark
|
|
210
|
+
[10007, "x"],
|
|
211
|
+
// ballot x
|
|
212
|
+
[10008, "x"],
|
|
213
|
+
// heavy ballot x
|
|
214
|
+
// ── Ellipsis variants (U+2026 itself IS encodable) ──
|
|
215
|
+
[8229, ".."],
|
|
216
|
+
// two dot leader
|
|
217
|
+
[8943, "..."],
|
|
218
|
+
// midline horizontal ellipsis
|
|
219
|
+
// ── Ligatures ──
|
|
220
|
+
[64256, "ff"],
|
|
221
|
+
[64257, "fi"],
|
|
222
|
+
[64258, "fl"],
|
|
223
|
+
[64259, "ffi"],
|
|
224
|
+
[64260, "ffl"]
|
|
225
|
+
]);
|
|
226
|
+
var WHITESPACE_LIKE = /* @__PURE__ */ new Set([
|
|
227
|
+
9,
|
|
228
|
+
10,
|
|
229
|
+
11,
|
|
230
|
+
12,
|
|
231
|
+
13,
|
|
232
|
+
133,
|
|
233
|
+
8232,
|
|
234
|
+
8233,
|
|
235
|
+
8192,
|
|
236
|
+
8193,
|
|
237
|
+
8194,
|
|
238
|
+
8195,
|
|
239
|
+
8196,
|
|
240
|
+
8197,
|
|
241
|
+
8198,
|
|
242
|
+
8199,
|
|
243
|
+
8200,
|
|
244
|
+
8201,
|
|
245
|
+
8202,
|
|
246
|
+
8239,
|
|
247
|
+
8287,
|
|
248
|
+
12288,
|
|
249
|
+
8203,
|
|
250
|
+
8204,
|
|
251
|
+
8205,
|
|
252
|
+
8288,
|
|
253
|
+
65279
|
|
254
|
+
]);
|
|
255
|
+
var MAX_SAMPLES = 5;
|
|
256
|
+
var ASCII_ONLY = /^[\x20-\x7e]*$/;
|
|
257
|
+
var COMBINING_MARKS = /[̀-ͯ]/g;
|
|
258
|
+
function stripDiacritics(ch) {
|
|
259
|
+
const decomposed = ch.normalize("NFKD").replace(COMBINING_MARKS, "");
|
|
260
|
+
if (!decomposed) return void 0;
|
|
261
|
+
for (const c of decomposed) {
|
|
262
|
+
if (!isWinAnsiEncodable(c.codePointAt(0))) return void 0;
|
|
263
|
+
}
|
|
264
|
+
return decomposed;
|
|
265
|
+
}
|
|
266
|
+
function sanitizeWinAnsi(input) {
|
|
267
|
+
if (ASCII_ONLY.test(input)) return { text: input, substitutions: 0, samples: [] };
|
|
268
|
+
const text = input.normalize("NFC");
|
|
269
|
+
let out = "";
|
|
270
|
+
let substitutions = 0;
|
|
271
|
+
const samples = [];
|
|
272
|
+
for (const ch of text) {
|
|
273
|
+
const cp = ch.codePointAt(0);
|
|
274
|
+
if (isWinAnsiEncodable(cp)) {
|
|
275
|
+
out += ch;
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
const mapped = TRANSLITERATIONS.get(cp) ?? stripDiacritics(ch) ?? WINANSI_REPLACEMENT;
|
|
279
|
+
out += mapped;
|
|
280
|
+
if (WHITESPACE_LIKE.has(cp)) continue;
|
|
281
|
+
substitutions++;
|
|
282
|
+
const sample = `"${ch}" -> "${mapped}"`;
|
|
283
|
+
if (samples.length < MAX_SAMPLES && !samples.includes(sample)) samples.push(sample);
|
|
284
|
+
}
|
|
285
|
+
return { text: out, substitutions, samples };
|
|
286
|
+
}
|
|
287
|
+
var WinAnsiTracker = class {
|
|
288
|
+
constructor() {
|
|
289
|
+
this.count = 0;
|
|
290
|
+
this.samples = [];
|
|
291
|
+
}
|
|
292
|
+
/** Sanitize `text` for pdf-lib and record any substitutions. */
|
|
293
|
+
clean(text) {
|
|
294
|
+
const result = sanitizeWinAnsi(text);
|
|
295
|
+
this.count += result.substitutions;
|
|
296
|
+
for (const sample of result.samples) {
|
|
297
|
+
if (this.samples.length >= MAX_SAMPLES) break;
|
|
298
|
+
if (!this.samples.includes(sample)) this.samples.push(sample);
|
|
299
|
+
}
|
|
300
|
+
return result.text;
|
|
301
|
+
}
|
|
302
|
+
/** Total substituted characters across the export. */
|
|
303
|
+
get substitutions() {
|
|
304
|
+
return this.count;
|
|
305
|
+
}
|
|
306
|
+
/** One-shot summary for the export, or undefined when nothing was lost. */
|
|
307
|
+
warning() {
|
|
308
|
+
if (this.count === 0) return void 0;
|
|
309
|
+
return `PDF export replaced ${this.count} character(s) that the standard PDF fonts cannot encode (${this.samples.join(", ")}). Scripts such as emoji, CJK and Cyrillic require an embedded Unicode font.`;
|
|
310
|
+
}
|
|
311
|
+
};
|
|
9
312
|
|
|
10
313
|
// src/pdf/styles.ts
|
|
11
314
|
var PAGE_WIDTH_LETTER = 612;
|
|
@@ -72,6 +375,7 @@ var IMPORT_URL_PATTERN = /https?:\/\/[^\s)>\]]+/g;
|
|
|
72
375
|
|
|
73
376
|
// src/pdf/export.ts
|
|
74
377
|
async function markdownDocToPdf(doc, options = {}) {
|
|
378
|
+
options.signal?.throwIfAborted();
|
|
75
379
|
const pdfDoc = await PDFDocument.create();
|
|
76
380
|
if (options.title) pdfDoc.setTitle(options.title);
|
|
77
381
|
if (options.author) pdfDoc.setAuthor(options.author);
|
|
@@ -79,6 +383,11 @@ async function markdownDocToPdf(doc, options = {}) {
|
|
|
79
383
|
pdfDoc.setModificationDate(/* @__PURE__ */ new Date());
|
|
80
384
|
const ctx = await createExportContext(pdfDoc, options, doc);
|
|
81
385
|
renderBlocks(doc.children, ctx, 0);
|
|
386
|
+
const warning = ctx.text.warning();
|
|
387
|
+
if (warning) {
|
|
388
|
+
if (options.onWarning) options.onWarning(warning);
|
|
389
|
+
else console.warn(warning);
|
|
390
|
+
}
|
|
82
391
|
const bytes = await pdfDoc.save();
|
|
83
392
|
return bytes.buffer;
|
|
84
393
|
}
|
|
@@ -124,7 +433,9 @@ async function createExportContext(pdfDoc, options, doc) {
|
|
|
124
433
|
y: pageHeight - margin,
|
|
125
434
|
contentWidth: pageWidth - 2 * margin,
|
|
126
435
|
bottomY: margin,
|
|
127
|
-
colors: { text: colorText, heading: colorHeading, link: colorLink }
|
|
436
|
+
colors: { text: colorText, heading: colorHeading, link: colorLink },
|
|
437
|
+
text: new WinAnsiTracker(),
|
|
438
|
+
signal: options.signal
|
|
128
439
|
};
|
|
129
440
|
}
|
|
130
441
|
function ensureSpace(ctx, needed) {
|
|
@@ -144,7 +455,7 @@ function flattenInlines(nodes, ctx, state) {
|
|
|
144
455
|
case "text": {
|
|
145
456
|
const font = state.code ? state.bold ? ctx.fonts.monoBold : ctx.fonts.mono : pickFont(ctx, state.bold, state.italic);
|
|
146
457
|
spans.push({
|
|
147
|
-
text: node.value,
|
|
458
|
+
text: ctx.text.clean(node.value),
|
|
148
459
|
font,
|
|
149
460
|
fontSize: state.code ? CODE_FONT_SIZE : ctx.fontSize,
|
|
150
461
|
color: state.code ? COLOR_CODE_TEXT : state.color ?? ctx.colors.text,
|
|
@@ -173,7 +484,7 @@ function flattenInlines(nodes, ctx, state) {
|
|
|
173
484
|
break;
|
|
174
485
|
case "inlineCode": {
|
|
175
486
|
spans.push({
|
|
176
|
-
text: node.value,
|
|
487
|
+
text: ctx.text.clean(node.value),
|
|
177
488
|
font: ctx.fonts.mono,
|
|
178
489
|
fontSize: CODE_FONT_SIZE,
|
|
179
490
|
color: COLOR_CODE_TEXT,
|
|
@@ -183,11 +494,12 @@ function flattenInlines(nodes, ctx, state) {
|
|
|
183
494
|
}
|
|
184
495
|
case "link": {
|
|
185
496
|
const linkNode = node;
|
|
497
|
+
const safeLink = sanitizeUrl(linkNode.url, "link") ?? void 0;
|
|
186
498
|
spans.push(
|
|
187
499
|
...flattenInlines(linkNode.children, ctx, {
|
|
188
500
|
...state,
|
|
189
|
-
link:
|
|
190
|
-
color: ctx.colors.link
|
|
501
|
+
link: safeLink,
|
|
502
|
+
color: safeLink ? ctx.colors.link : state.color
|
|
191
503
|
})
|
|
192
504
|
);
|
|
193
505
|
break;
|
|
@@ -195,7 +507,7 @@ function flattenInlines(nodes, ctx, state) {
|
|
|
195
507
|
case "image": {
|
|
196
508
|
const imgNode = node;
|
|
197
509
|
spans.push({
|
|
198
|
-
text: imgNode.alt ? `[Image: ${imgNode.alt}]` : "[Image]",
|
|
510
|
+
text: ctx.text.clean(imgNode.alt ? `[Image: ${imgNode.alt}]` : "[Image]"),
|
|
199
511
|
font: ctx.fonts.italic,
|
|
200
512
|
fontSize: ctx.fontSize,
|
|
201
513
|
color: COLOR_BLOCKQUOTE_TEXT
|
|
@@ -214,7 +526,7 @@ function flattenInlines(nodes, ctx, state) {
|
|
|
214
526
|
const html = node.rawHtml;
|
|
215
527
|
if (html) {
|
|
216
528
|
spans.push({
|
|
217
|
-
text: html,
|
|
529
|
+
text: ctx.text.clean(html),
|
|
218
530
|
font: ctx.fonts.mono,
|
|
219
531
|
fontSize: CODE_FONT_SIZE,
|
|
220
532
|
color: COLOR_CODE_TEXT
|
|
@@ -224,7 +536,7 @@ function flattenInlines(nodes, ctx, state) {
|
|
|
224
536
|
}
|
|
225
537
|
case "inlineMath":
|
|
226
538
|
spans.push({
|
|
227
|
-
text: node.value,
|
|
539
|
+
text: ctx.text.clean(node.value),
|
|
228
540
|
font: ctx.fonts.mono,
|
|
229
541
|
fontSize: CODE_FONT_SIZE,
|
|
230
542
|
color: COLOR_CODE_TEXT
|
|
@@ -233,7 +545,7 @@ function flattenInlines(nodes, ctx, state) {
|
|
|
233
545
|
case "footnoteReference": {
|
|
234
546
|
const ref = node;
|
|
235
547
|
spans.push({
|
|
236
|
-
text: `[${ref.identifier}]
|
|
548
|
+
text: ctx.text.clean(`[${ref.identifier}]`),
|
|
237
549
|
font: ctx.fonts.regular,
|
|
238
550
|
fontSize: ctx.fontSize * 0.75,
|
|
239
551
|
color: ctx.colors.link
|
|
@@ -247,7 +559,7 @@ function flattenInlines(nodes, ctx, state) {
|
|
|
247
559
|
spans.push(...flattenInlines(fallback.children, ctx, state));
|
|
248
560
|
} else if (fallback.value) {
|
|
249
561
|
spans.push({
|
|
250
|
-
text: fallback.value,
|
|
562
|
+
text: ctx.text.clean(fallback.value),
|
|
251
563
|
font: pickFont(ctx, state.bold, state.italic),
|
|
252
564
|
fontSize: ctx.fontSize,
|
|
253
565
|
color: state.color ?? ctx.colors.text
|
|
@@ -283,12 +595,27 @@ function drawSpans(spans, ctx, availableWidth, x0) {
|
|
|
283
595
|
});
|
|
284
596
|
const textWidth = span.font.widthOfTextAtSize(span.text, span.fontSize);
|
|
285
597
|
if (span.link) {
|
|
598
|
+
const baseline = ctx.y - span.fontSize;
|
|
286
599
|
ctx.page.drawLine({
|
|
287
|
-
start: { x, y:
|
|
288
|
-
end: { x: x + textWidth, y:
|
|
600
|
+
start: { x, y: baseline - 1 },
|
|
601
|
+
end: { x: x + textWidth, y: baseline - 1 },
|
|
289
602
|
thickness: 0.5,
|
|
290
603
|
color: rgb(span.color.r, span.color.g, span.color.b)
|
|
291
604
|
});
|
|
605
|
+
const annotation = ctx.pdfDoc.context.register(
|
|
606
|
+
ctx.pdfDoc.context.obj({
|
|
607
|
+
Type: "Annot",
|
|
608
|
+
Subtype: "Link",
|
|
609
|
+
Rect: [x, baseline - 2, x + textWidth, baseline + span.fontSize + 2],
|
|
610
|
+
Border: [0, 0, 0],
|
|
611
|
+
A: {
|
|
612
|
+
Type: "Action",
|
|
613
|
+
S: "URI",
|
|
614
|
+
URI: PDFString.of(span.link)
|
|
615
|
+
}
|
|
616
|
+
})
|
|
617
|
+
);
|
|
618
|
+
ctx.page.node.addAnnot(annotation);
|
|
292
619
|
}
|
|
293
620
|
if (span.strikethrough) {
|
|
294
621
|
const midY = ctx.y - span.fontSize * 0.6;
|
|
@@ -360,7 +687,9 @@ function getLineHeight(line) {
|
|
|
360
687
|
return (maxSize || DEFAULT_FONT_SIZE) * LINE_HEIGHT_FACTOR;
|
|
361
688
|
}
|
|
362
689
|
function renderBlocks(nodes, ctx, extraIndent) {
|
|
363
|
-
for (
|
|
690
|
+
for (let index = 0; index < nodes.length; index++) {
|
|
691
|
+
if ((index & 255) === 0) ctx.signal?.throwIfAborted();
|
|
692
|
+
const node = nodes[index];
|
|
364
693
|
renderBlock(node, ctx, extraIndent);
|
|
365
694
|
}
|
|
366
695
|
}
|
|
@@ -499,7 +828,7 @@ function renderListItem(item, ctx, extraIndent, depth, ordered, counter) {
|
|
|
499
828
|
function renderCodeBlock(node, ctx, extraIndent) {
|
|
500
829
|
const x0 = ctx.margin + extraIndent;
|
|
501
830
|
const _w = ctx.contentWidth - extraIndent;
|
|
502
|
-
const lines = node.value.split("\n");
|
|
831
|
+
const lines = node.value.split("\n").map((line) => ctx.text.clean(line));
|
|
503
832
|
const lineH = CODE_FONT_SIZE * LINE_HEIGHT_FACTOR;
|
|
504
833
|
const totalHeight = lines.length * lineH + 12;
|
|
505
834
|
ensureSpace(ctx, Math.min(totalHeight, ctx.y - ctx.bottomY));
|
|
@@ -615,7 +944,7 @@ function renderThematicBreak(ctx, extraIndent) {
|
|
|
615
944
|
}
|
|
616
945
|
function renderHtmlBlock(node, ctx, extraIndent) {
|
|
617
946
|
if (!node.rawHtml) return;
|
|
618
|
-
const lines = node.rawHtml.split("\n");
|
|
947
|
+
const lines = node.rawHtml.split("\n").map((line) => ctx.text.clean(line));
|
|
619
948
|
const x0 = ctx.margin + extraIndent;
|
|
620
949
|
for (const line of lines) {
|
|
621
950
|
if (line.trim().length === 0) continue;
|
|
@@ -633,7 +962,7 @@ function renderHtmlBlock(node, ctx, extraIndent) {
|
|
|
633
962
|
ctx.y -= PARAGRAPH_SPACING;
|
|
634
963
|
}
|
|
635
964
|
function renderMathBlock(node, ctx, extraIndent) {
|
|
636
|
-
const lines = node.value.split("\n");
|
|
965
|
+
const lines = node.value.split("\n").map((line) => ctx.text.clean(line));
|
|
637
966
|
const x0 = ctx.margin + extraIndent;
|
|
638
967
|
for (const line of lines) {
|
|
639
968
|
const lineH = CODE_FONT_SIZE * LINE_HEIGHT_FACTOR;
|
|
@@ -652,7 +981,7 @@ function renderMathBlock(node, ctx, extraIndent) {
|
|
|
652
981
|
ctx.y -= PARAGRAPH_SPACING;
|
|
653
982
|
}
|
|
654
983
|
function renderFootnoteDefinition(node, ctx, extraIndent) {
|
|
655
|
-
const label = `[${node.identifier}]
|
|
984
|
+
const label = ctx.text.clean(`[${node.identifier}]`);
|
|
656
985
|
const lineH = ctx.fontSize * LINE_HEIGHT_FACTOR;
|
|
657
986
|
ensureSpace(ctx, lineH);
|
|
658
987
|
ctx.page.drawText(label, {
|
|
@@ -694,7 +1023,7 @@ function renderFallbackBlock(node, ctx, extraIndent) {
|
|
|
694
1023
|
} else if (fallback.value && typeof fallback.value === "string") {
|
|
695
1024
|
const lineH = ctx.fontSize * LINE_HEIGHT_FACTOR;
|
|
696
1025
|
ensureSpace(ctx, lineH);
|
|
697
|
-
ctx.page.drawText(fallback.value, {
|
|
1026
|
+
ctx.page.drawText(ctx.text.clean(fallback.value), {
|
|
698
1027
|
x: ctx.margin + extraIndent,
|
|
699
1028
|
y: ctx.y - ctx.fontSize,
|
|
700
1029
|
size: ctx.fontSize,
|
|
@@ -717,11 +1046,26 @@ function hexToRgb(hex) {
|
|
|
717
1046
|
// src/pdf/import.ts
|
|
718
1047
|
import { markdownToDoc } from "@bendyline/squisq/doc";
|
|
719
1048
|
import { stringifyMarkdown } from "@bendyline/squisq/markdown";
|
|
1049
|
+
|
|
1050
|
+
// src/pdf/upng.ts
|
|
1051
|
+
import UPNG from "@pdf-lib/upng";
|
|
1052
|
+
function isUpngEncoder(value) {
|
|
1053
|
+
return typeof value === "object" && value !== null && "encode" in value && typeof value.encode === "function";
|
|
1054
|
+
}
|
|
1055
|
+
function resolveUpngEncoder(value) {
|
|
1056
|
+
if (isUpngEncoder(value)) return value;
|
|
1057
|
+
if (typeof value !== "object" || value === null || !("default" in value)) return null;
|
|
1058
|
+
return isUpngEncoder(value.default) ? value.default : null;
|
|
1059
|
+
}
|
|
1060
|
+
var upngEncoder = resolveUpngEncoder(UPNG);
|
|
1061
|
+
|
|
1062
|
+
// src/pdf/import.ts
|
|
720
1063
|
async function pdfToMarkdownDoc(data, options = {}) {
|
|
1064
|
+
options.signal?.throwIfAborted();
|
|
721
1065
|
const bytes = data instanceof Blob ? new Uint8Array(await data.arrayBuffer()) : data instanceof ArrayBuffer ? new Uint8Array(data) : data;
|
|
722
|
-
const loaded = await loadPdfDocument(bytes);
|
|
1066
|
+
const loaded = await loadPdfDocument(bytes, options.signal);
|
|
723
1067
|
try {
|
|
724
|
-
const textLines = await extractTextLines(loaded.pdf);
|
|
1068
|
+
const textLines = await extractTextLines(loaded.pdf, options);
|
|
725
1069
|
if (textLines.length === 0) {
|
|
726
1070
|
return { type: "document", children: [] };
|
|
727
1071
|
}
|
|
@@ -737,12 +1081,16 @@ async function pdfToDoc(data, options = {}) {
|
|
|
737
1081
|
return markdownToDoc(markdownDoc);
|
|
738
1082
|
}
|
|
739
1083
|
async function pdfToContainer(data, options = {}) {
|
|
1084
|
+
options.signal?.throwIfAborted();
|
|
740
1085
|
const bytes = data instanceof Blob ? new Uint8Array(await data.arrayBuffer()) : data instanceof ArrayBuffer ? new Uint8Array(data) : data;
|
|
741
|
-
const loaded = await loadPdfDocument(bytes);
|
|
1086
|
+
const loaded = await loadPdfDocument(bytes, options.signal);
|
|
742
1087
|
let textLines;
|
|
743
1088
|
let images;
|
|
744
1089
|
try {
|
|
745
|
-
[textLines, images] = await Promise.all([
|
|
1090
|
+
[textLines, images] = await Promise.all([
|
|
1091
|
+
extractTextLines(loaded.pdf, options),
|
|
1092
|
+
extractImages(loaded, options)
|
|
1093
|
+
]);
|
|
746
1094
|
} finally {
|
|
747
1095
|
await loaded.pdf.destroy?.();
|
|
748
1096
|
}
|
|
@@ -758,25 +1106,48 @@ async function pdfToContainer(data, options = {}) {
|
|
|
758
1106
|
images.map((img) => [img.path, { data: img.data, mimeType: "image/png" }])
|
|
759
1107
|
);
|
|
760
1108
|
}
|
|
761
|
-
async function extractImages(loaded) {
|
|
762
|
-
if (typeof document === "undefined") return [];
|
|
1109
|
+
async function extractImages(loaded, options) {
|
|
763
1110
|
const { pdfjsLib, pdf } = loaded;
|
|
764
1111
|
const OPS_paintImageXObject = pdfjsLib.OPS?.paintImageXObject ?? 85;
|
|
1112
|
+
const OPS_paintInlineImageXObject = pdfjsLib.OPS?.paintInlineImageXObject ?? 86;
|
|
765
1113
|
const images = [];
|
|
766
1114
|
let counter = 0;
|
|
1115
|
+
let totalPixels = 0;
|
|
1116
|
+
const maxPages = safetyLimit("maxPages", options.maxPages ?? 5e3);
|
|
1117
|
+
const maxImagePixels = safetyLimit("maxImagePixels", options.maxImagePixels ?? 1e8);
|
|
1118
|
+
if (pdf.numPages > maxPages)
|
|
1119
|
+
throw new RangeError(`PDF exceeds the ${maxPages}-page safety limit`);
|
|
767
1120
|
for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
|
|
1121
|
+
options.signal?.throwIfAborted();
|
|
768
1122
|
const page = await pdf.getPage(pageNum);
|
|
769
1123
|
if (!page.getOperatorList) continue;
|
|
770
1124
|
const opList = await page.getOperatorList();
|
|
771
1125
|
const seen = /* @__PURE__ */ new Set();
|
|
772
1126
|
for (let i = 0; i < opList.fnArray.length; i++) {
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
1127
|
+
const operation = opList.fnArray[i];
|
|
1128
|
+
if (operation !== OPS_paintImageXObject && operation !== OPS_paintInlineImageXObject)
|
|
1129
|
+
continue;
|
|
1130
|
+
const operand = opList.argsArray[i]?.[0];
|
|
1131
|
+
let imgData = null;
|
|
1132
|
+
if (operation === OPS_paintImageXObject) {
|
|
1133
|
+
if (!operand || typeof operand !== "string" || seen.has(operand)) continue;
|
|
1134
|
+
seen.add(operand);
|
|
1135
|
+
try {
|
|
1136
|
+
imgData = await getPdfImageObject(page.objs, operand, options.signal);
|
|
1137
|
+
} catch {
|
|
1138
|
+
continue;
|
|
1139
|
+
}
|
|
1140
|
+
} else if (operand && typeof operand === "object") {
|
|
1141
|
+
imgData = operand;
|
|
1142
|
+
}
|
|
777
1143
|
try {
|
|
778
|
-
const imgData = page.objs?.get(imgName);
|
|
779
1144
|
if (!imgData?.data || !imgData.width || !imgData.height) continue;
|
|
1145
|
+
totalPixels += imgData.width * imgData.height;
|
|
1146
|
+
if (totalPixels > maxImagePixels) {
|
|
1147
|
+
throw new RangeError(
|
|
1148
|
+
`PDF decoded images exceed the ${maxImagePixels}-pixel safety limit`
|
|
1149
|
+
);
|
|
1150
|
+
}
|
|
780
1151
|
const pngData = imageDataToPng(imgData);
|
|
781
1152
|
if (!pngData) continue;
|
|
782
1153
|
counter++;
|
|
@@ -786,52 +1157,70 @@ async function extractImages(loaded) {
|
|
|
786
1157
|
page: pageNum - 1,
|
|
787
1158
|
y: 0
|
|
788
1159
|
});
|
|
789
|
-
} catch {
|
|
1160
|
+
} catch (error) {
|
|
1161
|
+
if (error instanceof RangeError) throw error;
|
|
790
1162
|
}
|
|
791
1163
|
}
|
|
792
1164
|
}
|
|
793
1165
|
return images;
|
|
794
1166
|
}
|
|
1167
|
+
function getPdfImageObject(objects, name, signal) {
|
|
1168
|
+
if (!objects) return Promise.resolve(null);
|
|
1169
|
+
return new Promise((resolve, reject) => {
|
|
1170
|
+
let settled = false;
|
|
1171
|
+
const finish = (value) => {
|
|
1172
|
+
if (settled) return;
|
|
1173
|
+
settled = true;
|
|
1174
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1175
|
+
resolve(value);
|
|
1176
|
+
};
|
|
1177
|
+
const onAbort = () => {
|
|
1178
|
+
if (settled) return;
|
|
1179
|
+
settled = true;
|
|
1180
|
+
reject(signal?.reason ?? new DOMException("PDF image extraction cancelled", "AbortError"));
|
|
1181
|
+
};
|
|
1182
|
+
if (signal?.aborted) {
|
|
1183
|
+
onAbort();
|
|
1184
|
+
return;
|
|
1185
|
+
}
|
|
1186
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1187
|
+
try {
|
|
1188
|
+
const immediate = objects.get(name, finish);
|
|
1189
|
+
if (immediate !== void 0 && immediate !== null) finish(immediate);
|
|
1190
|
+
} catch (error) {
|
|
1191
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1192
|
+
reject(error);
|
|
1193
|
+
}
|
|
1194
|
+
});
|
|
1195
|
+
}
|
|
795
1196
|
function imageDataToPng(img) {
|
|
796
1197
|
try {
|
|
797
|
-
|
|
798
|
-
canvas.width = img.width;
|
|
799
|
-
canvas.height = img.height;
|
|
800
|
-
const ctx = canvas.getContext("2d");
|
|
801
|
-
if (!ctx) return null;
|
|
802
|
-
let imageData;
|
|
1198
|
+
let rgba;
|
|
803
1199
|
if (img.kind === 3 || img.data.length === img.width * img.height * 4) {
|
|
804
|
-
|
|
1200
|
+
rgba = new Uint8ClampedArray(img.data);
|
|
805
1201
|
} else if (img.kind === 2 || img.data.length === img.width * img.height * 3) {
|
|
806
|
-
|
|
1202
|
+
rgba = new Uint8ClampedArray(img.width * img.height * 4);
|
|
807
1203
|
for (let j = 0, k = 0; j < img.data.length; j += 3, k += 4) {
|
|
808
1204
|
rgba[k] = img.data[j];
|
|
809
1205
|
rgba[k + 1] = img.data[j + 1];
|
|
810
1206
|
rgba[k + 2] = img.data[j + 2];
|
|
811
1207
|
rgba[k + 3] = 255;
|
|
812
1208
|
}
|
|
813
|
-
imageData = new ImageData(rgba, img.width, img.height);
|
|
814
1209
|
} else if (img.kind === 1 || img.data.length === img.width * img.height) {
|
|
815
|
-
|
|
1210
|
+
rgba = new Uint8ClampedArray(img.width * img.height * 4);
|
|
816
1211
|
for (let j = 0, k = 0; j < img.data.length; j++, k += 4) {
|
|
817
1212
|
rgba[k] = img.data[j];
|
|
818
1213
|
rgba[k + 1] = img.data[j];
|
|
819
1214
|
rgba[k + 2] = img.data[j];
|
|
820
1215
|
rgba[k + 3] = 255;
|
|
821
1216
|
}
|
|
822
|
-
imageData = new ImageData(rgba, img.width, img.height);
|
|
823
1217
|
} else {
|
|
824
1218
|
return null;
|
|
825
1219
|
}
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
const bytes = new Uint8Array(binaryStr.length);
|
|
831
|
-
for (let i = 0; i < binaryStr.length; i++) {
|
|
832
|
-
bytes[i] = binaryStr.charCodeAt(i);
|
|
833
|
-
}
|
|
834
|
-
return bytes.buffer;
|
|
1220
|
+
const owned = new Uint8Array(rgba.byteLength);
|
|
1221
|
+
owned.set(rgba);
|
|
1222
|
+
if (!upngEncoder) return null;
|
|
1223
|
+
return upngEncoder.encode([owned.buffer], img.width, img.height, 0);
|
|
835
1224
|
} catch {
|
|
836
1225
|
return null;
|
|
837
1226
|
}
|
|
@@ -880,6 +1269,22 @@ function insertImageBlocks(blocks, blockPages, images) {
|
|
|
880
1269
|
}
|
|
881
1270
|
return result;
|
|
882
1271
|
}
|
|
1272
|
+
function hasInterItemSpace(previous, current) {
|
|
1273
|
+
if (!previous) return false;
|
|
1274
|
+
if (/\s$/.test(previous.str) || /^\s/.test(current.str)) return false;
|
|
1275
|
+
const gap = current.x - (previous.x + previous.width);
|
|
1276
|
+
const referenceHeight = Math.min(previous.height, current.height);
|
|
1277
|
+
return gap > Math.max(0.5, referenceHeight * 0.08);
|
|
1278
|
+
}
|
|
1279
|
+
function joinTextItems(items) {
|
|
1280
|
+
let text = "";
|
|
1281
|
+
for (let index = 0; index < items.length; index++) {
|
|
1282
|
+
const item = items[index];
|
|
1283
|
+
if (hasInterItemSpace(items[index - 1], item)) text += " ";
|
|
1284
|
+
text += item.str;
|
|
1285
|
+
}
|
|
1286
|
+
return text;
|
|
1287
|
+
}
|
|
883
1288
|
function configurePdfWorker(workerSrc) {
|
|
884
1289
|
_workerSrc = workerSrc;
|
|
885
1290
|
}
|
|
@@ -891,7 +1296,8 @@ async function applyWorkerConfig(pdfjsLib) {
|
|
|
891
1296
|
pdfjsLib.GlobalWorkerOptions.workerSrc = _workerSrc;
|
|
892
1297
|
}
|
|
893
1298
|
}
|
|
894
|
-
async function loadPdfDocument(data) {
|
|
1299
|
+
async function loadPdfDocument(data, signal) {
|
|
1300
|
+
signal?.throwIfAborted();
|
|
895
1301
|
let pdfjsLib;
|
|
896
1302
|
try {
|
|
897
1303
|
pdfjsLib = await import("pdfjs-dist/legacy/build/pdf.mjs");
|
|
@@ -904,16 +1310,27 @@ async function loadPdfDocument(data) {
|
|
|
904
1310
|
isEvalSupported: false,
|
|
905
1311
|
useSystemFonts: true
|
|
906
1312
|
});
|
|
907
|
-
|
|
1313
|
+
const pdf = await loadingTask.promise;
|
|
1314
|
+
signal?.throwIfAborted();
|
|
1315
|
+
return { pdfjsLib, pdf };
|
|
908
1316
|
}
|
|
909
|
-
async function extractTextLines(pdf) {
|
|
1317
|
+
async function extractTextLines(pdf, options) {
|
|
910
1318
|
const allLines = [];
|
|
1319
|
+
const maxPages = safetyLimit("maxPages", options.maxPages ?? 5e3);
|
|
1320
|
+
const maxTextItems = safetyLimit("maxTextItems", options.maxTextItems ?? 1e6);
|
|
1321
|
+
let textItemCount = 0;
|
|
1322
|
+
if (pdf.numPages > maxPages)
|
|
1323
|
+
throw new RangeError(`PDF exceeds the ${maxPages}-page safety limit`);
|
|
911
1324
|
for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
|
|
1325
|
+
options.signal?.throwIfAborted();
|
|
912
1326
|
const page = await pdf.getPage(pageNum);
|
|
913
1327
|
const content = await page.getTextContent();
|
|
914
1328
|
const styleMap = content.styles || {};
|
|
915
1329
|
const items = [];
|
|
916
1330
|
for (const item of content.items) {
|
|
1331
|
+
if (++textItemCount > maxTextItems) {
|
|
1332
|
+
throw new RangeError(`PDF exceeds the ${maxTextItems}-text-item safety limit`);
|
|
1333
|
+
}
|
|
917
1334
|
if (!item.str || item.str.trim().length === 0) continue;
|
|
918
1335
|
const transform = item.transform || [1, 0, 0, 1, 0, 0];
|
|
919
1336
|
const x = transform[4];
|
|
@@ -950,7 +1367,7 @@ async function extractTextLines(pdf) {
|
|
|
950
1367
|
const fontNames = lineItems.map((i) => i.fontName);
|
|
951
1368
|
const fontName = modeStr(fontNames) || "";
|
|
952
1369
|
const minX = Math.min(...lineItems.map((i) => i.x));
|
|
953
|
-
const text = lineItems
|
|
1370
|
+
const text = joinTextItems(lineItems);
|
|
954
1371
|
allLines.push({
|
|
955
1372
|
items: lineItems,
|
|
956
1373
|
y: key,
|
|
@@ -965,6 +1382,12 @@ async function extractTextLines(pdf) {
|
|
|
965
1382
|
}
|
|
966
1383
|
return allLines;
|
|
967
1384
|
}
|
|
1385
|
+
function safetyLimit(name, value) {
|
|
1386
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
1387
|
+
throw new RangeError(`${name} must be a non-negative safe integer`);
|
|
1388
|
+
}
|
|
1389
|
+
return value;
|
|
1390
|
+
}
|
|
968
1391
|
function detectBodyFontSize(lines) {
|
|
969
1392
|
const sizes = lines.map((l) => Math.round(l.fontSize * 2) / 2);
|
|
970
1393
|
return mode(sizes) || DEFAULT_FONT_SIZE;
|
|
@@ -1145,9 +1568,13 @@ function isItalicFont(fontName) {
|
|
|
1145
1568
|
function buildInlineNodes(line, options) {
|
|
1146
1569
|
const nodes = [];
|
|
1147
1570
|
const detectLinksOpt = options.detectLinks !== false;
|
|
1148
|
-
for (
|
|
1571
|
+
for (let itemIndex = 0; itemIndex < line.items.length; itemIndex++) {
|
|
1572
|
+
const item = line.items[itemIndex];
|
|
1149
1573
|
const text = item.str;
|
|
1150
1574
|
if (!text || text.trim().length === 0) continue;
|
|
1575
|
+
if (hasInterItemSpace(line.items[itemIndex - 1], item)) {
|
|
1576
|
+
nodes.push({ type: "text", value: " " });
|
|
1577
|
+
}
|
|
1151
1578
|
const bold = isBoldFont(item.fontName);
|
|
1152
1579
|
const italic = isItalicFont(item.fontName);
|
|
1153
1580
|
const mono = isMonospaceItem(item);
|
|
@@ -1329,4 +1756,3 @@ export {
|
|
|
1329
1756
|
pdfToContainer,
|
|
1330
1757
|
configurePdfWorker
|
|
1331
1758
|
};
|
|
1332
|
-
//# sourceMappingURL=chunk-GVS2XXV6.js.map
|