gutterpress 0.11.20-alpha.2 → 0.11.20-alpha.4
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/api/index.js +3 -2
- package/dist/{build-qmjg9r2d.js → build-epdcrm2c.js} +5 -4
- package/dist/{cli-zacevwcy.js → cli-0ey902sx.js} +267 -455
- package/dist/{cli-j802s31j.js → cli-61vd4ews.js} +83 -41
- package/dist/{cli-7z29z4wz.js → cli-fmksazej.js} +1 -1
- package/dist/{cli-gh0ym80r.js → cli-h4tfmpg3.js} +121 -20
- package/dist/cli-sth219v1.js +418 -0
- package/dist/cli.js +12 -12
- package/dist/{doctor-fe7tcs4v.js → doctor-78es5scm.js} +2 -2
- package/dist/engine/compiler/build.d.ts +1 -1
- package/dist/engine/shared/margin-box-support.d.ts +2 -2
- package/dist/engine/shared/pin-edges.d.ts +127 -0
- package/dist/engine/shared/print-area.d.ts +68 -0
- package/dist/{engine-9xb1ky8d.js → engine-67f68bxs.js} +1 -1
- package/dist/{engine-yax79k9m.js → engine-7ha27n2b.js} +2 -2
- package/dist/{ext-ahwxt6gv.js → ext-9mc7sr0d.js} +5 -4
- package/dist/{gutterpress-agent-cazqstr1.js → gutterpress-agent-mrdnwktc.js} +78 -13
- package/dist/{gutterpress-viewer-retj6ym2.js → gutterpress-viewer-jr30yw8t.js} +155 -56
- package/dist/{index-6xwtb7cw.js → index-ad95fws8.js} +82 -40
- package/dist/{index-3nr5rzzp.js → index-gm5xbx5w.js} +3 -3
- package/dist/index-sth219v1.js +418 -0
- package/dist/{index-razpty47.js → index-t13p8y08.js} +493 -582
- package/dist/index.js +4 -3
- package/dist/lib/build-fingerprint.d.ts +8 -0
- package/dist/lib/extension-manifest.d.ts +13 -0
- package/dist/lib/ghostscript.d.ts +70 -16
- package/dist/lib/markdown/assemble.d.ts +12 -2
- package/dist/lib/markdown/gutterpress-css.d.ts +46 -30
- package/dist/lib/markdown/index.d.ts +5 -0
- package/dist/lib/pdf-inspect.d.ts +45 -0
- package/dist/{manifest.schema-74s1ykyv.json → manifest.schema-13dfax80.json} +16 -4
- package/dist/{new-ftd4fec4.js → new-s4f0tnya.js} +5 -4
- package/dist/pdf-inspect-zdmy7qca.js +35 -0
- package/dist/{preflight-8sqarcsr.js → preflight-7ck4rbsq.js} +5 -4
- package/dist/preview/file-watcher.d.ts +4 -10
- package/dist/{preview-q76xn95h.js → preview-j91sxadd.js} +5 -4
- package/dist/{publish-7gdy6gbf.js → publish-f12xaenm.js} +5 -4
- package/dist/render.js +57 -6
- package/dist/schema/manifest.types.d.ts +28 -8
- package/dist/test-helpers/testkit.d.ts +2 -2
- package/dist/{validate-e3ryegce.js → validate-pb5gysvr.js} +5 -4
- package/package.json +1 -1
- package/dist/engine/shared/flush.d.ts +0 -59
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
// src/lib/pdf-inspect.ts
|
|
2
|
+
import { stat, readFile } from "node:fs/promises";
|
|
3
|
+
import { getDocumentProxy } from "unpdf";
|
|
4
|
+
import * as pdfjs from "unpdf/pdfjs";
|
|
5
|
+
function getOps(mod) {
|
|
6
|
+
if ("OPS" in mod && mod.OPS && typeof mod.OPS === "object") {
|
|
7
|
+
return mod.OPS;
|
|
8
|
+
}
|
|
9
|
+
throw new Error("unpdf/pdfjs missing OPS export");
|
|
10
|
+
}
|
|
11
|
+
var OPS = getOps(pdfjs);
|
|
12
|
+
var docCache = new Map;
|
|
13
|
+
var DOC_CACHE_MAX = 8;
|
|
14
|
+
function destroyEntry(entry) {
|
|
15
|
+
entry.doc.then((d) => d.destroy()).catch(() => {});
|
|
16
|
+
}
|
|
17
|
+
var EVICT_DESTROY_GRACE_MS = 60000;
|
|
18
|
+
var gracePending = new Map;
|
|
19
|
+
function destroyEntryAfterGrace(entry) {
|
|
20
|
+
const t = setTimeout(() => {
|
|
21
|
+
gracePending.delete(t);
|
|
22
|
+
destroyEntry(entry);
|
|
23
|
+
}, EVICT_DESTROY_GRACE_MS);
|
|
24
|
+
t.unref?.();
|
|
25
|
+
gracePending.set(t, entry);
|
|
26
|
+
}
|
|
27
|
+
function evictLru() {
|
|
28
|
+
while (docCache.size > DOC_CACHE_MAX) {
|
|
29
|
+
const oldestKey = docCache.keys().next().value;
|
|
30
|
+
if (oldestKey === undefined)
|
|
31
|
+
break;
|
|
32
|
+
const evicted = docCache.get(oldestKey);
|
|
33
|
+
docCache.delete(oldestKey);
|
|
34
|
+
if (evicted)
|
|
35
|
+
destroyEntryAfterGrace(evicted);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async function loadPdf(path) {
|
|
39
|
+
let st;
|
|
40
|
+
try {
|
|
41
|
+
st = await stat(path);
|
|
42
|
+
} catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
const hit = docCache.get(path);
|
|
46
|
+
if (hit && hit.mtimeMs === st.mtimeMs && hit.size === st.size) {
|
|
47
|
+
docCache.delete(path);
|
|
48
|
+
docCache.set(path, hit);
|
|
49
|
+
try {
|
|
50
|
+
return await hit.doc;
|
|
51
|
+
} catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (hit)
|
|
56
|
+
destroyEntry(hit);
|
|
57
|
+
const docPromise = readFile(path).then((buf) => getDocumentProxy(new Uint8Array(buf)));
|
|
58
|
+
docCache.delete(path);
|
|
59
|
+
docCache.set(path, { mtimeMs: st.mtimeMs, size: st.size, doc: docPromise });
|
|
60
|
+
evictLru();
|
|
61
|
+
try {
|
|
62
|
+
return await docPromise;
|
|
63
|
+
} catch {
|
|
64
|
+
if (docCache.get(path)?.doc === docPromise)
|
|
65
|
+
docCache.delete(path);
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
var activeRetains = 0;
|
|
70
|
+
function retainPdfCache() {
|
|
71
|
+
activeRetains++;
|
|
72
|
+
let released = false;
|
|
73
|
+
return () => {
|
|
74
|
+
if (released)
|
|
75
|
+
return;
|
|
76
|
+
released = true;
|
|
77
|
+
activeRetains--;
|
|
78
|
+
if (activeRetains === 0)
|
|
79
|
+
clearPdfCache();
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function clearPdfCache() {
|
|
83
|
+
for (const [t, entry] of gracePending) {
|
|
84
|
+
clearTimeout(t);
|
|
85
|
+
destroyEntry(entry);
|
|
86
|
+
}
|
|
87
|
+
gracePending.clear();
|
|
88
|
+
for (const entry of docCache.values())
|
|
89
|
+
destroyEntry(entry);
|
|
90
|
+
docCache.clear();
|
|
91
|
+
}
|
|
92
|
+
function pendingGraceDestroyCount() {
|
|
93
|
+
return gracePending.size;
|
|
94
|
+
}
|
|
95
|
+
function getPageSize(page) {
|
|
96
|
+
const vp = page.getViewport({ scale: 1 });
|
|
97
|
+
return { w: vp.width, h: vp.height };
|
|
98
|
+
}
|
|
99
|
+
async function getOutlineCount(doc) {
|
|
100
|
+
try {
|
|
101
|
+
const outline = await doc.getOutline();
|
|
102
|
+
return outline ? outline.length : 0;
|
|
103
|
+
} catch {
|
|
104
|
+
return 0;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async function getPageLabels(doc) {
|
|
108
|
+
try {
|
|
109
|
+
return await doc.getPageLabels();
|
|
110
|
+
} catch {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async function countLinkAnnotations(doc, stopAtFirst = false) {
|
|
115
|
+
let count = 0;
|
|
116
|
+
for (let i = 1;i <= doc.numPages; i++) {
|
|
117
|
+
try {
|
|
118
|
+
const page = await doc.getPage(i);
|
|
119
|
+
const annots = await page.getAnnotations();
|
|
120
|
+
for (const a of annots) {
|
|
121
|
+
if (a.subtype === "Link") {
|
|
122
|
+
count++;
|
|
123
|
+
if (stopAtFirst)
|
|
124
|
+
return count;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
} catch {}
|
|
128
|
+
}
|
|
129
|
+
return count;
|
|
130
|
+
}
|
|
131
|
+
var textCache = new WeakMap;
|
|
132
|
+
function getTextPass(doc) {
|
|
133
|
+
let p = textCache.get(doc);
|
|
134
|
+
if (!p) {
|
|
135
|
+
p = (async () => {
|
|
136
|
+
const textByPage = [];
|
|
137
|
+
const positions = [];
|
|
138
|
+
const runsByPage = [];
|
|
139
|
+
for (let i = 1;i <= doc.numPages; i++) {
|
|
140
|
+
try {
|
|
141
|
+
const page = await doc.getPage(i);
|
|
142
|
+
const tc = await page.getTextContent();
|
|
143
|
+
let pageText = "";
|
|
144
|
+
const runs = [];
|
|
145
|
+
for (const item of tc.items) {
|
|
146
|
+
const it = item;
|
|
147
|
+
if (typeof it.str !== "string")
|
|
148
|
+
continue;
|
|
149
|
+
pageText += it.str;
|
|
150
|
+
if (it.transform && it.str.trim().length > 0) {
|
|
151
|
+
const x = it.transform[4];
|
|
152
|
+
const y = it.transform[5];
|
|
153
|
+
positions.push({ x, y });
|
|
154
|
+
runs.push({ s: it.str, x, y, w: it.width ?? 0, h: it.height ?? 0 });
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
textByPage.push(pageText);
|
|
158
|
+
runsByPage.push(runs);
|
|
159
|
+
} catch {
|
|
160
|
+
textByPage.push("");
|
|
161
|
+
runsByPage.push([]);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return { textByPage, positions, runsByPage };
|
|
165
|
+
})();
|
|
166
|
+
textCache.set(doc, p);
|
|
167
|
+
}
|
|
168
|
+
return p;
|
|
169
|
+
}
|
|
170
|
+
var opCache = new WeakMap;
|
|
171
|
+
function multiply(m1, m2) {
|
|
172
|
+
return [
|
|
173
|
+
m1[0] * m2[0] + m1[2] * m2[1],
|
|
174
|
+
m1[1] * m2[0] + m1[3] * m2[1],
|
|
175
|
+
m1[0] * m2[2] + m1[2] * m2[3],
|
|
176
|
+
m1[1] * m2[2] + m1[3] * m2[3],
|
|
177
|
+
m1[0] * m2[4] + m1[2] * m2[5] + m1[4],
|
|
178
|
+
m1[1] * m2[4] + m1[3] * m2[5] + m1[5]
|
|
179
|
+
];
|
|
180
|
+
}
|
|
181
|
+
function fontIsEmbedded(f) {
|
|
182
|
+
if (f.isType3Font)
|
|
183
|
+
return true;
|
|
184
|
+
if (f.missingFile === true)
|
|
185
|
+
return false;
|
|
186
|
+
if (f.isStandardFont === true)
|
|
187
|
+
return false;
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
function getOpPass(doc) {
|
|
191
|
+
let p = opCache.get(doc);
|
|
192
|
+
if (!p) {
|
|
193
|
+
p = (async () => {
|
|
194
|
+
const imagesByPage = new Map;
|
|
195
|
+
const fonts = [];
|
|
196
|
+
const seenFonts = new Set;
|
|
197
|
+
for (let i = 1;i <= doc.numPages; i++) {
|
|
198
|
+
let page;
|
|
199
|
+
try {
|
|
200
|
+
page = await doc.getPage(i);
|
|
201
|
+
} catch {
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
let ops;
|
|
205
|
+
try {
|
|
206
|
+
ops = await page.getOperatorList();
|
|
207
|
+
} catch {
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
let ctm = [1, 0, 0, 1, 0, 0];
|
|
211
|
+
const stack = [];
|
|
212
|
+
const pageImages = [];
|
|
213
|
+
for (let j = 0;j < ops.fnArray.length; j++) {
|
|
214
|
+
const fn = ops.fnArray[j];
|
|
215
|
+
const args = ops.argsArray[j];
|
|
216
|
+
if (fn === OPS.save) {
|
|
217
|
+
stack.push(ctm.slice());
|
|
218
|
+
} else if (fn === OPS.restore) {
|
|
219
|
+
ctm = stack.pop() ?? ctm;
|
|
220
|
+
} else if (fn === OPS.transform) {
|
|
221
|
+
ctm = multiply(ctm, args);
|
|
222
|
+
} else if (fn === OPS.paintImageXObject || fn === OPS.paintImageXObjectRepeat || fn === OPS.paintInlineImageXObject) {
|
|
223
|
+
const name = typeof args[0] === "string" ? args[0] : "(inline)";
|
|
224
|
+
pageImages.push({
|
|
225
|
+
name,
|
|
226
|
+
placedW: Math.hypot(ctm[0], ctm[1]),
|
|
227
|
+
placedH: Math.hypot(ctm[2], ctm[3]),
|
|
228
|
+
x: ctm[4],
|
|
229
|
+
y: ctm[5],
|
|
230
|
+
page: i
|
|
231
|
+
});
|
|
232
|
+
} else if (fn === OPS.setFont) {
|
|
233
|
+
const ref = args[0];
|
|
234
|
+
if (typeof ref === "string" && !seenFonts.has(ref)) {
|
|
235
|
+
seenFonts.add(ref);
|
|
236
|
+
try {
|
|
237
|
+
if (page.commonObjs.has(ref)) {
|
|
238
|
+
const f = page.commonObjs.get(ref);
|
|
239
|
+
fonts.push({
|
|
240
|
+
name: f?.name ?? ref,
|
|
241
|
+
embedded: fontIsEmbedded(f ?? {})
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
} catch {}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (pageImages.length > 0)
|
|
249
|
+
imagesByPage.set(i, pageImages);
|
|
250
|
+
}
|
|
251
|
+
return { imagesByPage, fonts };
|
|
252
|
+
})();
|
|
253
|
+
opCache.set(doc, p);
|
|
254
|
+
}
|
|
255
|
+
return p;
|
|
256
|
+
}
|
|
257
|
+
var opSet = (names) => new Set(names.map((name) => OPS[name]).filter((op) => op !== undefined));
|
|
258
|
+
var FILL_COLOR_OPS = opSet(["setFillGray", "setFillRGBColor", "setFillCMYKColor", "setFillColor"]);
|
|
259
|
+
var STROKE_COLOR_OPS = opSet(["setStrokeGray", "setStrokeRGBColor", "setStrokeCMYKColor", "setStrokeColor"]);
|
|
260
|
+
var SHOW_TEXT_OPS = opSet(["showText", "showSpacedText", "nextLineShowText", "nextLineSetSpacingShowText"]);
|
|
261
|
+
async function unsplittableTextReasons(pdfBytes, pages) {
|
|
262
|
+
const GSTATE_BIT = { ca: 1, CA: 2, BM: 4, SMask: 8 };
|
|
263
|
+
const COMPOSITING = 15;
|
|
264
|
+
const FILL_PATTERN = 16;
|
|
265
|
+
const STROKE_PATTERN = 32;
|
|
266
|
+
const CLIP_TEXT = 64;
|
|
267
|
+
const doc = await getDocumentProxy(new Uint8Array(pdfBytes));
|
|
268
|
+
const reasons = new Map;
|
|
269
|
+
try {
|
|
270
|
+
const scan = pages ?? Array.from({ length: doc.numPages }, (_, i) => i + 1);
|
|
271
|
+
for (const i of scan) {
|
|
272
|
+
const page = await doc.getPage(i);
|
|
273
|
+
const ops = await page.getOperatorList();
|
|
274
|
+
let state = 0;
|
|
275
|
+
const groups = [];
|
|
276
|
+
const stack = [];
|
|
277
|
+
let showsText = false;
|
|
278
|
+
for (let j = 0;j < ops.fnArray.length; j++) {
|
|
279
|
+
const fn = ops.fnArray[j];
|
|
280
|
+
if (fn === OPS.save || fn === OPS.paintFormXObjectBegin)
|
|
281
|
+
stack.push(state);
|
|
282
|
+
else if (fn === OPS.restore || fn === OPS.paintFormXObjectEnd)
|
|
283
|
+
state = stack.pop() ?? 0;
|
|
284
|
+
else if (fn === OPS.beginGroup) {
|
|
285
|
+
const group = ops.argsArray[j][0];
|
|
286
|
+
groups.push((state & COMPOSITING) !== 0 || !!(group.smask || group.knockout));
|
|
287
|
+
} else if (fn === OPS.endGroup)
|
|
288
|
+
groups.pop();
|
|
289
|
+
else if (fn === OPS.setFont) {
|
|
290
|
+
const ref = ops.argsArray[j][0];
|
|
291
|
+
const font = typeof ref === "string" && page.commonObjs.has(ref) ? page.commonObjs.get(ref) : undefined;
|
|
292
|
+
if (font?.isType3Font) {
|
|
293
|
+
reasons.set(i, "type3-font");
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
} else if (fn === OPS.setFillColorN || fn === OPS.setStrokeColorN) {
|
|
297
|
+
const bit = fn === OPS.setFillColorN ? FILL_PATTERN : STROKE_PATTERN;
|
|
298
|
+
const kind = ops.argsArray[j][0];
|
|
299
|
+
state = kind === "TilingPattern" || kind === "Shading" ? state | bit : state & ~bit;
|
|
300
|
+
} else if (FILL_COLOR_OPS.has(fn))
|
|
301
|
+
state &= ~FILL_PATTERN;
|
|
302
|
+
else if (STROKE_COLOR_OPS.has(fn))
|
|
303
|
+
state &= ~STROKE_PATTERN;
|
|
304
|
+
else if (fn === OPS.setTextRenderingMode) {
|
|
305
|
+
state = ops.argsArray[j][0] >= 4 ? state | CLIP_TEXT : state & ~CLIP_TEXT;
|
|
306
|
+
} else if (fn === OPS.setGState) {
|
|
307
|
+
for (const [key, value] of ops.argsArray[j][0]) {
|
|
308
|
+
const bit = GSTATE_BIT[key];
|
|
309
|
+
if (!bit)
|
|
310
|
+
continue;
|
|
311
|
+
const on = key === "BM" ? value !== "source-over" : key === "SMask" ? !!value : value < 1;
|
|
312
|
+
state = on ? state | bit : state & ~bit;
|
|
313
|
+
}
|
|
314
|
+
} else if (SHOW_TEXT_OPS.has(fn)) {
|
|
315
|
+
showsText = true;
|
|
316
|
+
if (state || groups.includes(true)) {
|
|
317
|
+
const paint = state & (FILL_PATTERN | STROKE_PATTERN | CLIP_TEXT);
|
|
318
|
+
reasons.set(i, paint ? "text-pattern-or-clip" : "text-transparency");
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
if (!showsText && !reasons.has(i))
|
|
324
|
+
reasons.set(i, "no-text");
|
|
325
|
+
}
|
|
326
|
+
} finally {
|
|
327
|
+
await doc.destroy();
|
|
328
|
+
}
|
|
329
|
+
return reasons;
|
|
330
|
+
}
|
|
331
|
+
function getImagePixelSize(page, name, timeoutMs = 2000) {
|
|
332
|
+
return new Promise((resolve) => {
|
|
333
|
+
let done = false;
|
|
334
|
+
const finish = (v) => {
|
|
335
|
+
if (!done) {
|
|
336
|
+
done = true;
|
|
337
|
+
resolve(v);
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
const t = setTimeout(() => finish(null), timeoutMs);
|
|
341
|
+
try {
|
|
342
|
+
page.objs.get(name, (obj) => {
|
|
343
|
+
clearTimeout(t);
|
|
344
|
+
finish(obj && obj.width && obj.height ? { width: obj.width, height: obj.height } : null);
|
|
345
|
+
});
|
|
346
|
+
} catch {
|
|
347
|
+
clearTimeout(t);
|
|
348
|
+
finish(null);
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
var PAINT_OPS = new Set([
|
|
353
|
+
"stroke",
|
|
354
|
+
"closeStroke",
|
|
355
|
+
"fill",
|
|
356
|
+
"eoFill",
|
|
357
|
+
"fillStroke",
|
|
358
|
+
"eoFillStroke",
|
|
359
|
+
"closeFillStroke",
|
|
360
|
+
"closeEOFillStroke",
|
|
361
|
+
"shadingFill",
|
|
362
|
+
"paintImageXObject",
|
|
363
|
+
"paintInlineImageXObject",
|
|
364
|
+
"paintImageXObjectRepeat",
|
|
365
|
+
"paintImageMaskXObject",
|
|
366
|
+
"paintImageMaskXObjectGroup",
|
|
367
|
+
"paintImageMaskXObjectRepeat",
|
|
368
|
+
"paintSolidColorImageMask",
|
|
369
|
+
"showText",
|
|
370
|
+
"showSpacedText",
|
|
371
|
+
"nextLineShowText",
|
|
372
|
+
"nextLineSetSpacingShowText"
|
|
373
|
+
].map((name) => OPS[name]).filter((op) => op !== undefined));
|
|
374
|
+
async function pageHasMarks(doc, pageNum) {
|
|
375
|
+
const ops = await (await doc.getPage(pageNum)).getOperatorList();
|
|
376
|
+
return ops.fnArray.some((fn, i) => fn === OPS.constructPath ? PAINT_OPS.has(ops.argsArray[i][0]) : PAINT_OPS.has(fn));
|
|
377
|
+
}
|
|
378
|
+
async function getImageResolutions(doc) {
|
|
379
|
+
const { imagesByPage } = await getOpPass(doc);
|
|
380
|
+
const out = [];
|
|
381
|
+
for (const [pageNum, imgs] of imagesByPage) {
|
|
382
|
+
let page;
|
|
383
|
+
try {
|
|
384
|
+
page = await doc.getPage(pageNum);
|
|
385
|
+
} catch {
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
for (const img of imgs) {
|
|
389
|
+
if (img.name === "(inline)")
|
|
390
|
+
continue;
|
|
391
|
+
const px = await getImagePixelSize(page, img.name);
|
|
392
|
+
if (!px)
|
|
393
|
+
continue;
|
|
394
|
+
const placedWin = img.placedW / 72;
|
|
395
|
+
const placedHin = img.placedH / 72;
|
|
396
|
+
if (placedWin <= 0 || placedHin <= 0)
|
|
397
|
+
continue;
|
|
398
|
+
out.push({
|
|
399
|
+
page: pageNum,
|
|
400
|
+
xDpi: px.width / placedWin,
|
|
401
|
+
yDpi: px.height / placedHin
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return out;
|
|
406
|
+
}
|
|
407
|
+
async function isLoadable(doc) {
|
|
408
|
+
try {
|
|
409
|
+
for (let i = 1;i <= doc.numPages; i++) {
|
|
410
|
+
await doc.getPage(i);
|
|
411
|
+
}
|
|
412
|
+
return true;
|
|
413
|
+
} catch {
|
|
414
|
+
return false;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
export { DOC_CACHE_MAX, loadPdf, retainPdfCache, clearPdfCache, pendingGraceDestroyCount, getPageSize, getOutlineCount, getPageLabels, countLinkAnnotations, getTextPass, getOpPass, unsplittableTextReasons, pageHasMarks, getImageResolutions, isLoadable };
|
package/dist/cli.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
UsageError,
|
|
7
7
|
package_default,
|
|
8
8
|
rejectUnknownFlags
|
|
9
|
-
} from "./cli-
|
|
9
|
+
} from "./cli-fmksazej.js";
|
|
10
10
|
import {
|
|
11
11
|
EXIT_CODES
|
|
12
12
|
} from "./cli-46ycxe6r.js";
|
|
@@ -18,14 +18,14 @@ import {
|
|
|
18
18
|
import { defineCommand, parseArgs, runMain } from "citty";
|
|
19
19
|
import { statSync } from "node:fs";
|
|
20
20
|
var SUBCOMMANDS = {
|
|
21
|
-
new: () => import("./new-
|
|
22
|
-
preview: () => import("./preview-
|
|
23
|
-
build: () => import("./build-
|
|
24
|
-
publish: () => import("./publish-
|
|
25
|
-
validate: () => import("./validate-
|
|
26
|
-
preflight: () => import("./preflight-
|
|
27
|
-
doctor: () => import("./doctor-
|
|
28
|
-
ext: () => import("./ext-
|
|
21
|
+
new: () => import("./new-s4f0tnya.js").then((m) => m.default),
|
|
22
|
+
preview: () => import("./preview-j91sxadd.js").then((m) => m.default),
|
|
23
|
+
build: () => import("./build-epdcrm2c.js").then((m) => m.default),
|
|
24
|
+
publish: () => import("./publish-f12xaenm.js").then((m) => m.default),
|
|
25
|
+
validate: () => import("./validate-pb5gysvr.js").then((m) => m.default),
|
|
26
|
+
preflight: () => import("./preflight-7ck4rbsq.js").then((m) => m.default),
|
|
27
|
+
doctor: () => import("./doctor-78es5scm.js").then((m) => m.default),
|
|
28
|
+
ext: () => import("./ext-9mc7sr0d.js").then((m) => m.default)
|
|
29
29
|
};
|
|
30
30
|
var VERSION = package_default.version;
|
|
31
31
|
var main = defineCommand({
|
|
@@ -50,7 +50,7 @@ async function preflightRequiredInvocations(rawArgs) {
|
|
|
50
50
|
const [command, ...commandArgs] = rawArgs;
|
|
51
51
|
try {
|
|
52
52
|
if (command === "new") {
|
|
53
|
-
const { newArgs } = await import("./new-
|
|
53
|
+
const { newArgs } = await import("./new-s4f0tnya.js");
|
|
54
54
|
rejectUnknownFlags(commandArgs, newArgs, "new");
|
|
55
55
|
const parsed = parseArgs(commandArgs, {
|
|
56
56
|
...newArgs,
|
|
@@ -62,7 +62,7 @@ async function preflightRequiredInvocations(rawArgs) {
|
|
|
62
62
|
return;
|
|
63
63
|
}
|
|
64
64
|
if (command === "preflight") {
|
|
65
|
-
const { preflightArgs } = await import("./preflight-
|
|
65
|
+
const { preflightArgs } = await import("./preflight-7ck4rbsq.js");
|
|
66
66
|
rejectUnknownFlags(commandArgs, preflightArgs, "preflight");
|
|
67
67
|
const parsed = parseArgs(commandArgs, {
|
|
68
68
|
...preflightArgs,
|
|
@@ -82,7 +82,7 @@ async function preflightRequiredInvocations(rawArgs) {
|
|
|
82
82
|
}
|
|
83
83
|
if (subcommand.startsWith("-"))
|
|
84
84
|
return;
|
|
85
|
-
const { EXT_SUBCOMMANDS, extAddArgs, extRemoveArgs, extEnableArgs, extDisableArgs } = await import("./ext-
|
|
85
|
+
const { EXT_SUBCOMMANDS, extAddArgs, extRemoveArgs, extEnableArgs, extDisableArgs } = await import("./ext-9mc7sr0d.js");
|
|
86
86
|
if (!EXT_SUBCOMMANDS.includes(subcommand)) {
|
|
87
87
|
throw new UsageError(`gutterpress ext: unknown command "${subcommand}"`);
|
|
88
88
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getSystemDiagnostics,
|
|
3
3
|
log
|
|
4
|
-
} from "./cli-
|
|
4
|
+
} from "./cli-h4tfmpg3.js";
|
|
5
5
|
import {
|
|
6
6
|
UsageError,
|
|
7
7
|
rejectExtraPositionals,
|
|
8
8
|
rejectUnknownFlags
|
|
9
|
-
} from "./cli-
|
|
9
|
+
} from "./cli-fmksazej.js";
|
|
10
10
|
import"./cli-46ycxe6r.js";
|
|
11
11
|
import"./cli-asap4t1z.js";
|
|
12
12
|
import"./cli-37x76zdn.js";
|
|
@@ -8,7 +8,7 @@ import { type PostprocessResult } from "./postprocess.ts";
|
|
|
8
8
|
* set is closed here so a surface's label table can be asserted complete
|
|
9
9
|
* against it rather than drifting silently as checks are added.
|
|
10
10
|
*/
|
|
11
|
-
export type BuildDiagnosticCode = "engine.width.overflow" | "engine.width.intrinsic" | "engine.xref.broken" | "engine.abspos.leak" | "engine.layer.trapped" | "engine.multicol.dead-column" | "engine.content.overheight" | "engine.image.low-dpi" | "engine.
|
|
11
|
+
export type BuildDiagnosticCode = "engine.width.overflow" | "engine.width.intrinsic" | "engine.xref.broken" | "engine.abspos.leak" | "engine.layer.trapped" | "engine.multicol.dead-column" | "engine.content.overheight" | "engine.image.low-dpi" | "engine.pin.margin-box" | "engine.page-background.unreferenced" | "engine.hyphenation.unsupported";
|
|
12
12
|
export declare const BUILD_DIAGNOSTIC_CODES: readonly BuildDiagnosticCode[];
|
|
13
13
|
export interface BuildDiagnostic {
|
|
14
14
|
code: BuildDiagnosticCode;
|
|
@@ -31,8 +31,8 @@ export declare function isIgnoredMarginBoxProperty(property: string): boolean;
|
|
|
31
31
|
* Geometry of each of the 16 margin boxes, per CSS Paged Media §5.3, in pt
|
|
32
32
|
* SHEET coordinates (origin at the sheet's top-left). One implementation for
|
|
33
33
|
* both furniture painters: the viewer's `drawMarginBoxes` overlays and the
|
|
34
|
-
* compiler's
|
|
35
|
-
* spot or preview and print disagree about where the folio sits.
|
|
34
|
+
* compiler's relocation of a pinned edge's furniture must place a box at the
|
|
35
|
+
* same spot or preview and print disagree about where the folio sits.
|
|
36
36
|
*/
|
|
37
37
|
export declare function marginBoxRectPt(name: string, g: {
|
|
38
38
|
width: number;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pinned-art policy — shared by the compiler and the viewer, like every rule
|
|
3
|
+
* that DECIDES something (see synthesis.ts's header).
|
|
4
|
+
*
|
|
5
|
+
* THE MODEL: a pin box the text never shares. A `.gp-pin` with an edge word
|
|
6
|
+
* (`.gp-top`, `.gp-right`, `.gp-bottom`, `.gp-left`) positions itself inside
|
|
7
|
+
* its page root (`.page`/`.spread`, `position: relative` in MARKER_CSS), and
|
|
8
|
+
* where it sits has no effect on the text:
|
|
9
|
+
*
|
|
10
|
+
* 1. The page MARGIN on each pinned edge is freed. Chromium's printable area
|
|
11
|
+
* IS the page area — MEASURED, a pinned box pulled into the margin with
|
|
12
|
+
* negative insets fragments onto the NEXT sheet, one moved there with a
|
|
13
|
+
* transform is clipped away entirely, and a margin box in a ~zero margin
|
|
14
|
+
* (1px, with padding/height compensation) simply does not render. So the
|
|
15
|
+
* only way art reaches the paper is page GEOMETRY, per page:
|
|
16
|
+
* - the COMPILER aliases the root's own page context under a generated
|
|
17
|
+
* name (verbatim rule copies — never a resolved flatten, which is the
|
|
18
|
+
* "re-implement the @page cascade" trap tier2's history records),
|
|
19
|
+
* zeroes the pinned margins on the alias, and assigns the root to it;
|
|
20
|
+
* - the VIEWER keeps the author's page context and frees the margins in
|
|
21
|
+
* the run's strip geometry, from these same functions.
|
|
22
|
+
* 2. The root gets that same margin back as PADDING (`box-decoration-break:
|
|
23
|
+
* clone`, so every fragment of a root that spans pages keeps it), added
|
|
24
|
+
* to any padding the root has of its own. Its content box is therefore
|
|
25
|
+
* the author's content box — same width, same height, same place, same
|
|
26
|
+
* line and page breaks — whatever is pinned where. That is the whole
|
|
27
|
+
* point: freeing the margin once let the text grow into it. (What the
|
|
28
|
+
* root paints itself — a background, a border — does grow with it: the
|
|
29
|
+
* root's box is the page area, and that area reaches the paper.)
|
|
30
|
+
* 3. The pin box is the root's padding box — which now reaches the paper on
|
|
31
|
+
* the pinned edges — inset by the printer's ART-SAFE line: `--gp-pin-<edge>`
|
|
32
|
+
* on the root, read by `.gp-pin`'s four insets (GUTTERPRESS_CSS). On the
|
|
33
|
+
* top, bottom and outside edge that is the bleed plus `page.artSafe`; at
|
|
34
|
+
* the binding edge `page.safe` (no bleed there) — the manifest's print
|
|
35
|
+
* area (print-area.ts), so a book with none sets 0, the paper's edge.
|
|
36
|
+
* 4. `.gp-flush` on the pin zeroes the four properties in plain CSS: the
|
|
37
|
+
* opt-in escape hatch to the paper's edge (the markdown pipeline warns).
|
|
38
|
+
* Nothing else here knows about it.
|
|
39
|
+
*
|
|
40
|
+
* Sides. Recto values are the defaults (`@page :right`'s margins, the
|
|
41
|
+
* binding on the left). Mirrored `:left`/`:right` margins agree on the content
|
|
42
|
+
* SIZE, so the viewer paginates identically either way; each engine corrects a
|
|
43
|
+
* verso root once it knows the side — the viewer after it measures its
|
|
44
|
+
* strips, the compiler in its synthesis step (`setPinGeometry`).
|
|
45
|
+
*
|
|
46
|
+
* Margin boxes live IN the margin, so a freed edge's furniture (folio, running
|
|
47
|
+
* head) cannot render natively there — MEASURED, no compensation trick
|
|
48
|
+
* survives. Both renderers therefore keep painting that furniture themselves
|
|
49
|
+
* at its ORIGINAL coordinates: the viewer already synthesizes all furniture as
|
|
50
|
+
* DOM; the compiler injects the freed edge's boxes into the page root as
|
|
51
|
+
* positioned elements with engine-resolved text. Nothing the author declared
|
|
52
|
+
* is lost — that is the contract this module's box map exists to keep.
|
|
53
|
+
*
|
|
54
|
+
* Everything the author's document gets is standard CSS: a padding, a custom
|
|
55
|
+
* property per edge, absolute insets, and (print only) a named page.
|
|
56
|
+
*/
|
|
57
|
+
import type { PageGeometry } from "./gcpm-extract.ts";
|
|
58
|
+
import type { PageSide, PrintArea } from "./print-area.ts";
|
|
59
|
+
export declare const PIN_EDGES: readonly ["top", "right", "bottom", "left"];
|
|
60
|
+
export type PinEdge = (typeof PIN_EDGES)[number];
|
|
61
|
+
/** Every pin that names an edge. */
|
|
62
|
+
export declare const EDGE_PIN_SELECTOR: string;
|
|
63
|
+
/** Minimal DOM shapes `pinEdgesIn` needs — keeps this module node-safe. */
|
|
64
|
+
interface PinElement {
|
|
65
|
+
/** an HTML element's containing block for abspos (`HTMLElement.offsetParent`) */
|
|
66
|
+
readonly offsetParent?: unknown;
|
|
67
|
+
readonly classList: {
|
|
68
|
+
contains(token: string): boolean;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
interface QueryRoot {
|
|
72
|
+
querySelectorAll(selector: string): ArrayLike<PinElement>;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The edges the pins of page root `root` use, in canonical t/r/b/l order — a
|
|
76
|
+
* pin counts when `root` is its containing block (its `offsetParent`): a pin
|
|
77
|
+
* in a nested page root belongs to that root, and one a positioned card
|
|
78
|
+
* frames belongs to the card, so neither frees this root's margins. A pin
|
|
79
|
+
* without an edge word frees nothing and centres in the text block.
|
|
80
|
+
*/
|
|
81
|
+
export declare function pinEdgesIn(root: QueryRoot): PinEdge[];
|
|
82
|
+
/** Canonical short key for an edge set: "b", "rb", "trbl"… (t/r/b/l order). */
|
|
83
|
+
export declare function pinKey(edges: readonly PinEdge[]): string;
|
|
84
|
+
/**
|
|
85
|
+
* Generated page name for (author page context, edge set). The `gp--` double
|
|
86
|
+
* dash marks an engine-generated page (the `gp--blank` convention); these
|
|
87
|
+
* exist only in builds that actually contain an edge pin, which is why the
|
|
88
|
+
* width check may treat them as real author contexts.
|
|
89
|
+
*/
|
|
90
|
+
export declare function pinPageName(authorPage: string | undefined, edges: readonly PinEdge[]): string;
|
|
91
|
+
/** The page's margins with the pinned edges freed. */
|
|
92
|
+
export declare function flushMargins(margin: PageGeometry["margin"], edges: readonly PinEdge[]): PageGeometry["margin"];
|
|
93
|
+
/**
|
|
94
|
+
* The art-safe line's distance from the sheet edge on each pinned edge, in pt:
|
|
95
|
+
* the bleed plus `artSafe` on the top, bottom and outside edge, `safe` on the
|
|
96
|
+
* binding edge (the left of a recto, the right of a verso), 0 on an edge the
|
|
97
|
+
* pins do not use (the pin box's edge is the text block's there). No print
|
|
98
|
+
* area means 0 everywhere: the paper's edge.
|
|
99
|
+
*/
|
|
100
|
+
export declare function pinInsets(area: Pick<PrintArea, "bleed" | "safe" | "artSafe"> | null, side: PageSide, edges: readonly PinEdge[]): Record<PinEdge, number>;
|
|
101
|
+
/** No padding of the root's own. */
|
|
102
|
+
export declare const NO_PADDING: Readonly<Record<PinEdge, number>>;
|
|
103
|
+
/**
|
|
104
|
+
* A pin root's own declarations for one side, in px (pt × 96/72, like the
|
|
105
|
+
* strip variables): the freed margins back as padding, cloned onto every
|
|
106
|
+
* fragment, and the four inset properties. `margin` is the ORIGINAL margin of
|
|
107
|
+
* the page context the root lands on; `padding` is the root's own (author)
|
|
108
|
+
* padding in px, which the freed margin is added to rather than replacing,
|
|
109
|
+
* so a padded root's text stays put too. All four properties are set, so a
|
|
110
|
+
* pin never reads another root's values.
|
|
111
|
+
*/
|
|
112
|
+
export declare function pinRootStyle(margin: PageGeometry["margin"], area: Pick<PrintArea, "bleed" | "safe" | "artSafe"> | null, side: PageSide, edges: readonly PinEdge[], padding?: Readonly<Record<PinEdge, number>>): Record<string, string>;
|
|
113
|
+
/** A computed style's four paddings, in px. */
|
|
114
|
+
export declare function paddingOf(cs: {
|
|
115
|
+
paddingTop: string;
|
|
116
|
+
paddingRight: string;
|
|
117
|
+
paddingBottom: string;
|
|
118
|
+
paddingLeft: string;
|
|
119
|
+
}): Record<PinEdge, number>;
|
|
120
|
+
/**
|
|
121
|
+
* Margin boxes that live (wholly or partly) in a freed edge's margin area —
|
|
122
|
+
* the set both renderers must keep painting themselves. A corner box belongs
|
|
123
|
+
* to BOTH of its edges: `bottom-left-corner` dies when either the bottom or
|
|
124
|
+
* the left margin is freed.
|
|
125
|
+
*/
|
|
126
|
+
export declare function marginBoxesOnEdges(edges: readonly PinEdge[]): string[];
|
|
127
|
+
export {};
|