@rightkit/document-core 0.1.1 → 0.2.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/CHANGELOG.md +29 -0
- package/README.md +31 -7
- package/dist/app/chrome.js +1 -1
- package/dist/app/documentPrinting.d.ts +0 -6
- package/dist/app/documentPrinting.js +0 -18
- package/dist/app/shell/nativeMenu.d.ts +3 -2
- package/dist/app/shell/nativeMenu.js +8 -5
- package/dist/documents/perfEventCounters.d.ts +0 -8
- package/dist/documents/perfEventCounters.js +2 -1
- package/dist/documents/preview/previewFrame.d.ts +2 -1
- package/dist/documents/preview/previewFrame.js +9 -7
- package/dist/documents/searchUtils.js +1 -1
- package/dist/host.d.ts +36 -0
- package/dist/host.js +54 -0
- package/dist/index.d.ts +1 -50
- package/dist/index.js +1 -50
- package/dist/settings/themes.js +1 -1
- package/dist/state/bridgeEvents.d.ts +1 -1
- package/dist/state/bridgeEvents.js +7 -4
- package/dist/state/recentUrls.js +4 -3
- package/dist/state/tauriBridge.d.ts +3 -61
- package/dist/state/tauriBridge.js +8 -297
- package/dist/styles.css +2 -2
- package/package.json +25 -98
- package/dist/app/shortcutChords.d.ts +0 -6
- package/dist/app/shortcutChords.js +0 -14
- package/dist/app/useCloseFindOnSurfaceChange.d.ts +0 -14
- package/dist/app/useCloseFindOnSurfaceChange.js +0 -23
- package/dist/app/useEditText.d.ts +0 -6
- package/dist/app/useEditText.js +0 -10
- package/dist/appName.d.ts +0 -2
- package/dist/appName.js +0 -8
- package/dist/documents/FullscreenIcon.d.ts +0 -4
- package/dist/documents/FullscreenIcon.js +0 -7
- package/dist/documents/mdx/mdxRuntimeScript.d.ts +0 -2
- package/dist/documents/mdx/mdxRuntimeScript.js +0 -3
- package/dist/documents/mdx/mdxRuntimeScript.txt +0 -584
- package/dist/documents/pdf/MacPdfReadPane.d.ts +0 -23
- package/dist/documents/pdf/MacPdfReadPane.js +0 -708
- package/dist/documents/pdf/NativePdfDocument.d.ts +0 -9
- package/dist/documents/pdf/NativePdfDocument.js +0 -41
- package/dist/documents/pdf/PdfInlineTextEditor.d.ts +0 -53
- package/dist/documents/pdf/PdfInlineTextEditor.js +0 -225
- package/dist/documents/pdf/PdfOcrLayer.d.ts +0 -4
- package/dist/documents/pdf/PdfOcrLayer.js +0 -30
- package/dist/documents/pdf/PdfOutline.d.ts +0 -6
- package/dist/documents/pdf/PdfOutline.js +0 -12
- package/dist/documents/pdf/PdfPasswordPrompt.d.ts +0 -5
- package/dist/documents/pdf/PdfPasswordPrompt.js +0 -24
- package/dist/documents/pdf/PdfThumbnails.d.ts +0 -8
- package/dist/documents/pdf/PdfThumbnails.js +0 -201
- package/dist/documents/pdf/PdfToolbar.d.ts +0 -47
- package/dist/documents/pdf/PdfToolbar.js +0 -76
- package/dist/documents/pdf/PdfViewer.d.ts +0 -36
- package/dist/documents/pdf/PdfViewer.js +0 -1522
- package/dist/documents/pdf/PdfViewerBody.d.ts +0 -22
- package/dist/documents/pdf/PdfViewerBody.js +0 -42
- package/dist/documents/pdf/macPdfNativeBridge.d.ts +0 -118
- package/dist/documents/pdf/macPdfNativeBridge.js +0 -134
- package/dist/documents/pdf/nearestVisiblePdfPage.d.ts +0 -2
- package/dist/documents/pdf/nearestVisiblePdfPage.js +0 -22
- package/dist/documents/pdf/pdfBench.d.ts +0 -88
- package/dist/documents/pdf/pdfBench.js +0 -535
- package/dist/documents/pdf/pdfBenchAutopilot.d.ts +0 -48
- package/dist/documents/pdf/pdfBenchAutopilot.js +0 -154
- package/dist/documents/pdf/pdfBenchTransport.d.ts +0 -21
- package/dist/documents/pdf/pdfBenchTransport.js +0 -233
- package/dist/documents/pdf/pdfEditModel.d.ts +0 -294
- package/dist/documents/pdf/pdfEditModel.js +0 -105
- package/dist/documents/pdf/pdfEditor.d.ts +0 -100
- package/dist/documents/pdf/pdfEditor.js +0 -68
- package/dist/documents/pdf/pdfOcr.d.ts +0 -36
- package/dist/documents/pdf/pdfOcr.js +0 -88
- package/dist/documents/pdf/pdfOcrEdit.d.ts +0 -32
- package/dist/documents/pdf/pdfOcrEdit.js +0 -138
- package/dist/documents/pdf/pdfPrinting.d.ts +0 -28
- package/dist/documents/pdf/pdfPrinting.js +0 -110
- package/dist/documents/pdf/pdfSave.d.ts +0 -87
- package/dist/documents/pdf/pdfSave.js +0 -250
- package/dist/documents/pdf/pdfShared.d.ts +0 -16
- package/dist/documents/pdf/pdfShared.js +0 -20
- package/dist/documents/pdf/pdfToMarkdown.d.ts +0 -11
- package/dist/documents/pdf/pdfToMarkdown.js +0 -67
- package/dist/documents/pdf/pdfjsLibGlobal.d.ts +0 -1
- package/dist/documents/pdf/pdfjsLibGlobal.js +0 -12
- package/dist/documents/pdf/toolbarIcons.d.ts +0 -18
- package/dist/documents/pdf/toolbarIcons.js +0 -31
- package/dist/documents/pdf/usePdfDocumentLoader.d.ts +0 -40
- package/dist/documents/pdf/usePdfDocumentLoader.js +0 -243
- package/dist/documents/pdf/usePdfEditSession.d.ts +0 -88
- package/dist/documents/pdf/usePdfEditSession.js +0 -341
- package/dist/documents/pdf/usePdfEditor.d.ts +0 -59
- package/dist/documents/pdf/usePdfEditor.js +0 -317
- package/dist/documents/pdf/usePdfFit.d.ts +0 -19
- package/dist/documents/pdf/usePdfFit.js +0 -55
- package/dist/documents/pdf/usePdfOcr.d.ts +0 -38
- package/dist/documents/pdf/usePdfOcr.js +0 -337
- package/dist/documents/pdf/usePdfPreviewRecompose.d.ts +0 -28
- package/dist/documents/pdf/usePdfPreviewRecompose.js +0 -256
- package/dist/documents/pdf/usePdfSavePlan.d.ts +0 -61
- package/dist/documents/pdf/usePdfSavePlan.js +0 -303
- package/dist/documents/pdf/usePdfSearch.d.ts +0 -31
- package/dist/documents/pdf/usePdfSearch.js +0 -367
- package/dist/documents/pdf/usePdfViewControls.d.ts +0 -72
- package/dist/documents/pdf/usePdfViewControls.js +0 -152
- package/dist/documents/scopedSelectAll.d.ts +0 -5
- package/dist/documents/scopedSelectAll.js +0 -77
- package/dist/documents/txt/cmScroll.d.ts +0 -11
- package/dist/documents/txt/cmScroll.js +0 -52
- package/dist/documents/useMirroredEditText.d.ts +0 -15
- package/dist/documents/useMirroredEditText.js +0 -29
- package/dist/documents/useSplitResize.d.ts +0 -7
- package/dist/documents/useSplitResize.js +0 -37
- package/dist/settings/rk-settings.css +0 -38
- package/dist/settings/sections/EditorSection.d.ts +0 -1
- package/dist/settings/sections/EditorSection.js +0 -18
- package/dist/settings/sections/FileAssociationsSection.d.ts +0 -1
- package/dist/settings/sections/FileAssociationsSection.js +0 -5
- package/dist/settings/sections/MacFileAssociationsBlock.d.ts +0 -33
- package/dist/settings/sections/MacFileAssociationsBlock.js +0 -151
- package/dist/settings/sections/ThemeSection.d.ts +0 -1
- package/dist/settings/sections/ThemeSection.js +0 -31
- package/dist/settings/sections/TypographySection.d.ts +0 -1
- package/dist/settings/sections/TypographySection.js +0 -19
- package/dist/settings/sections/onOff.d.ts +0 -11
- package/dist/settings/sections/onOff.js +0 -8
- package/dist/state/browserQa.d.ts +0 -21
- package/dist/state/browserQa.js +0 -303
- package/dist/state/browserQaDocx.d.ts +0 -3
- package/dist/state/browserQaDocx.js +0 -35
- package/dist/state/browserQaLicense.d.ts +0 -8
- package/dist/state/browserQaLicense.js +0 -7
- package/dist/state/browserQaPdf.d.ts +0 -60
- package/dist/state/browserQaPdf.js +0 -302
- package/dist/state/browserQaText.d.ts +0 -6
- package/dist/state/browserQaText.js +0 -48
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* VR-P0-1 benchmark autopilot (dev-only, temporary instrumentation).
|
|
3
|
-
*
|
|
4
|
-
* The PDF loading benchmark has to run in the real WKWebView/WebView2 webview, whose DevTools
|
|
5
|
-
* console is GUI-only — so driving it by hand does not scale to "3 repeats x 3 modes x N fixtures,
|
|
6
|
-
* on two operating systems". This module runs the whole matrix unattended:
|
|
7
|
-
*
|
|
8
|
-
* 1. a plan (modes x repeats) is stored in localStorage;
|
|
9
|
-
* 2. each app load runs whichever mode the plan currently points at;
|
|
10
|
-
* 3. on completion the full result is appended to native PDF benchmark JSONL;
|
|
11
|
-
* a content-free diagnostics breadcrumb remains for quick status checks;
|
|
12
|
-
* 4. the plan advances and the app reloads, until the matrix is done.
|
|
13
|
-
*
|
|
14
|
-
* Repeats are INTERLEAVED (round-robin across modes) rather than grouped, so a thermal or
|
|
15
|
-
* page-cache drift over the run cannot systematically favour whichever mode ran first.
|
|
16
|
-
*
|
|
17
|
-
* Delete this file together with the rest of the benchmark harness once the study is decided.
|
|
18
|
-
*/
|
|
19
|
-
import { logEvent } from "../../state/diagnostics.js";
|
|
20
|
-
const PLAN_KEY = "vr.pdf.bench.autopilot.v1";
|
|
21
|
-
const ENABLED_KEY = "vr.pdf.bench.enabled";
|
|
22
|
-
const MODE_KEY = "vr.pdf.bench.mode";
|
|
23
|
-
const DEFAULT_MODES = ["buffer", "url-native", "custom-range"];
|
|
24
|
-
const DEFAULT_REPEATS = 3;
|
|
25
|
-
function dev() {
|
|
26
|
-
// Vitest runs with MODE=test; retaining this explicit test-only gate keeps benchmark controls
|
|
27
|
-
// unavailable in production builds while allowing orchestration behavior to stay covered.
|
|
28
|
-
return (typeof window !== "undefined" &&
|
|
29
|
-
(import.meta.env?.DEV ||
|
|
30
|
-
import.meta.env?.MODE === "test" ||
|
|
31
|
-
(typeof process !== "undefined" && process.env.NODE_ENV === "test")));
|
|
32
|
-
}
|
|
33
|
-
function loadPlan() {
|
|
34
|
-
if (!dev())
|
|
35
|
-
return null;
|
|
36
|
-
try {
|
|
37
|
-
const raw = localStorage.getItem(PLAN_KEY);
|
|
38
|
-
if (!raw)
|
|
39
|
-
return null;
|
|
40
|
-
const parsed = JSON.parse(raw);
|
|
41
|
-
if (!Array.isArray(parsed.modes) || parsed.modes.length === 0)
|
|
42
|
-
return null;
|
|
43
|
-
return parsed;
|
|
44
|
-
}
|
|
45
|
-
catch {
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
function savePlan(plan) {
|
|
50
|
-
try {
|
|
51
|
-
localStorage.setItem(PLAN_KEY, JSON.stringify(plan));
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
// Dev-only; never an application correctness dependency.
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
function totalRuns(plan) {
|
|
58
|
-
return plan.modes.length * plan.repeats;
|
|
59
|
-
}
|
|
60
|
-
function modeForIndex(plan, index) {
|
|
61
|
-
return plan.modes[index % plan.modes.length];
|
|
62
|
-
}
|
|
63
|
-
/** Point the benchmark at the mode this run should use, and make sure it is armed. */
|
|
64
|
-
function armMode(mode) {
|
|
65
|
-
try {
|
|
66
|
-
localStorage.setItem(ENABLED_KEY, "1");
|
|
67
|
-
localStorage.setItem(MODE_KEY, mode);
|
|
68
|
-
}
|
|
69
|
-
catch {
|
|
70
|
-
// Dev-only.
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
/** The mode the plan wants for the current load, or null when no plan is active. */
|
|
74
|
-
export function autopilotCurrentMode() {
|
|
75
|
-
const plan = loadPlan();
|
|
76
|
-
if (!plan || plan.done)
|
|
77
|
-
return null;
|
|
78
|
-
return modeForIndex(plan, plan.index);
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Called from benchmark `finalize()`. Records a content-free diagnostics breadcrumb, advances plan,
|
|
82
|
-
* & either reloads for next mode or reports matrix complete. Full result is native JSONL.
|
|
83
|
-
*/
|
|
84
|
-
export function autopilotOnComplete() {
|
|
85
|
-
const plan = loadPlan();
|
|
86
|
-
if (!plan || plan.done)
|
|
87
|
-
return;
|
|
88
|
-
const of = totalRuns(plan);
|
|
89
|
-
logEvent("pdf-bench-autopilot-result");
|
|
90
|
-
plan.index += 1;
|
|
91
|
-
if (plan.index >= of) {
|
|
92
|
-
plan.done = true;
|
|
93
|
-
savePlan(plan);
|
|
94
|
-
logEvent("pdf-bench-autopilot-done");
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
savePlan(plan);
|
|
98
|
-
armMode(modeForIndex(plan, plan.index));
|
|
99
|
-
// finalize() has already awaited both settle sampling & durable native write. Reload now: WKWebView
|
|
100
|
-
// can indefinitely throttle a timer once its benchmark window becomes occluded, stranding a
|
|
101
|
-
// multi-mode plan after its first valid sample.
|
|
102
|
-
if (import.meta.env?.MODE !== "test")
|
|
103
|
-
location.reload();
|
|
104
|
-
}
|
|
105
|
-
/** Begin a matrix. Arms the first mode and reloads so the run starts from a clean document load. */
|
|
106
|
-
export function startAutopilot(label = "unlabelled", modes = DEFAULT_MODES, repeats = DEFAULT_REPEATS, reload = true, runId = String(import.meta.env?.VITE_PDF_BENCH_RUN_ID ?? label)) {
|
|
107
|
-
if (!dev())
|
|
108
|
-
return;
|
|
109
|
-
const plan = { runId, modes, repeats, index: 0, label, done: false };
|
|
110
|
-
savePlan(plan);
|
|
111
|
-
armMode(modeForIndex(plan, 0));
|
|
112
|
-
logEvent("pdf-bench-autopilot-start");
|
|
113
|
-
if (reload)
|
|
114
|
-
location.reload();
|
|
115
|
-
}
|
|
116
|
-
export function stopAutopilot() {
|
|
117
|
-
try {
|
|
118
|
-
localStorage.removeItem(PLAN_KEY);
|
|
119
|
-
localStorage.removeItem(ENABLED_KEY);
|
|
120
|
-
}
|
|
121
|
-
catch {
|
|
122
|
-
// Dev-only.
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
export function autopilotStatus() {
|
|
126
|
-
return loadPlan();
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Auto-start from the environment so the matrix needs no console at all:
|
|
130
|
-
* VITE_PDF_BENCH_AUTOPILOT=1 pnpm dev:tauri <file.pdf>
|
|
131
|
-
*
|
|
132
|
-
* Only fires when no plan exists yet, so the per-run reloads do not restart the matrix.
|
|
133
|
-
*/
|
|
134
|
-
export function maybeAutoStartFromEnv() {
|
|
135
|
-
if (!dev())
|
|
136
|
-
return;
|
|
137
|
-
if (import.meta.env?.VITE_PDF_BENCH_AUTOPILOT !== "1")
|
|
138
|
-
return;
|
|
139
|
-
const label = String(import.meta.env?.VITE_PDF_BENCH_LABEL ?? "auto");
|
|
140
|
-
const runId = String(import.meta.env?.VITE_PDF_BENCH_RUN_ID ?? label);
|
|
141
|
-
const modes = String(import.meta.env?.VITE_PDF_BENCH_MODES ?? "")
|
|
142
|
-
.split(",")
|
|
143
|
-
.map((mode) => mode.trim())
|
|
144
|
-
.filter((mode) => DEFAULT_MODES.includes(mode));
|
|
145
|
-
const existing = loadPlan();
|
|
146
|
-
// A plan for THIS label that is still mid-matrix must never be restarted — that's the
|
|
147
|
-
// per-run reload case. But localStorage survives across app launches (same webview profile),
|
|
148
|
-
// so a plan left over from an earlier session/label (done, or for a different fixture) must
|
|
149
|
-
// NOT silently block every future auto-start; start fresh for a new/changed label.
|
|
150
|
-
if (existing && !existing.done && existing.runId === runId)
|
|
151
|
-
return;
|
|
152
|
-
const repeats = Number(import.meta.env?.VITE_PDF_BENCH_REPEATS ?? DEFAULT_REPEATS);
|
|
153
|
-
startAutopilot(label, modes.length > 0 ? modes : DEFAULT_MODES, Number.isFinite(repeats) && repeats > 0 ? repeats : DEFAULT_REPEATS, false, runId);
|
|
154
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import * as pdfjs from "pdfjs-dist";
|
|
2
|
-
import type { PDFDocumentProxy } from "pdfjs-dist";
|
|
3
|
-
import { type PdfBenchLoadMode } from "./pdfBench.js";
|
|
4
|
-
/** Above this size the whole-file buffer fallback is skipped in favour of range loading. */
|
|
5
|
-
export declare const MAX_BUFFERED_PDF_BYTES: number;
|
|
6
|
-
export declare function shouldRangeLoadInsteadOfBuffer(contentLength: string | null): boolean;
|
|
7
|
-
export interface PreparedPdfLoading {
|
|
8
|
-
readonly task: ReturnType<typeof pdfjs.getDocument>;
|
|
9
|
-
readonly promise: Promise<PDFDocumentProxy>;
|
|
10
|
-
dispose(): void;
|
|
11
|
-
}
|
|
12
|
-
interface PreparePdfLoadingArgs {
|
|
13
|
-
readonly url: string;
|
|
14
|
-
readonly mode: PdfBenchLoadMode;
|
|
15
|
-
readonly signal: AbortSignal;
|
|
16
|
-
readonly wasmUrl: string;
|
|
17
|
-
}
|
|
18
|
-
export declare function formatPdfRangeHeader(begin: number, end: number): string;
|
|
19
|
-
export declare function parseContentRangeTotal(value: string | null): number | null;
|
|
20
|
-
export declare function preparePdfLoading({ url, mode, signal, wasmUrl, }: PreparePdfLoadingArgs): Promise<PreparedPdfLoading>;
|
|
21
|
-
export {};
|
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
import * as pdfjs from "pdfjs-dist";
|
|
2
|
-
import { pdfDocumentInit } from "@rightkit/pdf-ui";
|
|
3
|
-
import { isPdfBenchEnabled } from "./pdfBench.js";
|
|
4
|
-
const RANGE_CHUNK_SIZE = 64 * 1024;
|
|
5
|
-
/** Above this size the whole-file buffer fallback is skipped in favour of range loading. */
|
|
6
|
-
export const MAX_BUFFERED_PDF_BYTES = 150 * 1024 * 1024;
|
|
7
|
-
export function shouldRangeLoadInsteadOfBuffer(contentLength) {
|
|
8
|
-
const n = Number(contentLength);
|
|
9
|
-
return Number.isFinite(n) && n > MAX_BUFFERED_PDF_BYTES;
|
|
10
|
-
}
|
|
11
|
-
export function formatPdfRangeHeader(begin, end) {
|
|
12
|
-
if (!Number.isInteger(begin) || !Number.isInteger(end) || begin < 0 || end <= begin) {
|
|
13
|
-
throw new Error(`Invalid PDF byte range [${begin}, ${end})`);
|
|
14
|
-
}
|
|
15
|
-
return `bytes=${begin}-${end - 1}`;
|
|
16
|
-
}
|
|
17
|
-
export function parseContentRangeTotal(value) {
|
|
18
|
-
return parseContentRange(value)?.total ?? null;
|
|
19
|
-
}
|
|
20
|
-
function parseContentRange(value) {
|
|
21
|
-
if (!value)
|
|
22
|
-
return null;
|
|
23
|
-
const match = value.match(/^bytes\s+(\d+)-(\d+)\/(\d+)$/i);
|
|
24
|
-
if (!match)
|
|
25
|
-
return null;
|
|
26
|
-
const begin = Number(match[1]);
|
|
27
|
-
const end = Number(match[2]);
|
|
28
|
-
const total = Number(match[3]);
|
|
29
|
-
if (!Number.isSafeInteger(begin) ||
|
|
30
|
-
!Number.isSafeInteger(end) ||
|
|
31
|
-
!Number.isSafeInteger(total) ||
|
|
32
|
-
begin < 0 ||
|
|
33
|
-
end < begin ||
|
|
34
|
-
total <= end) {
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
return { begin, end, total };
|
|
38
|
-
}
|
|
39
|
-
function parseContentLength(value) {
|
|
40
|
-
if (!value)
|
|
41
|
-
return null;
|
|
42
|
-
const length = Number(value);
|
|
43
|
-
return Number.isSafeInteger(length) && length > 0 ? length : null;
|
|
44
|
-
}
|
|
45
|
-
async function probePdfLength(url, signal) {
|
|
46
|
-
// HEAD is cheapest when the webview/custom protocol handles it correctly.
|
|
47
|
-
try {
|
|
48
|
-
const head = await fetch(url, { method: "HEAD", signal, cache: "no-store" });
|
|
49
|
-
const length = head.ok ? parseContentLength(head.headers.get("content-length")) : null;
|
|
50
|
-
if (length !== null)
|
|
51
|
-
return { length, initialData: null };
|
|
52
|
-
}
|
|
53
|
-
catch (error) {
|
|
54
|
-
if (error?.name === "AbortError")
|
|
55
|
-
throw error;
|
|
56
|
-
// Fall through to a 1-byte range probe. Benchmarking exists specifically because custom-scheme
|
|
57
|
-
// behavior differs across webviews; HEAD support must not be assumed.
|
|
58
|
-
}
|
|
59
|
-
const probe = await fetch(url, {
|
|
60
|
-
signal,
|
|
61
|
-
cache: "no-store",
|
|
62
|
-
headers: { Range: "bytes=0-0" },
|
|
63
|
-
});
|
|
64
|
-
if (probe.status !== 206) {
|
|
65
|
-
throw new Error(`PDF range probe failed: expected 206, got ${probe.status}`);
|
|
66
|
-
}
|
|
67
|
-
const length = parseContentRangeTotal(probe.headers.get("content-range"));
|
|
68
|
-
if (length === null)
|
|
69
|
-
throw new Error("PDF range probe did not expose a valid total length");
|
|
70
|
-
const initialData = new Uint8Array(await probe.arrayBuffer());
|
|
71
|
-
const range = parseContentRange(probe.headers.get("content-range"));
|
|
72
|
-
if (!range || range.begin !== 0 || range.end !== initialData.byteLength - 1) {
|
|
73
|
-
throw new Error("PDF range probe returned bytes that do not match Content-Range");
|
|
74
|
-
}
|
|
75
|
-
return { length, initialData };
|
|
76
|
-
}
|
|
77
|
-
class HostPdfRangeTransport extends pdfjs.PDFDataRangeTransport {
|
|
78
|
-
#url;
|
|
79
|
-
#length;
|
|
80
|
-
#controllers = new Set();
|
|
81
|
-
#aborted = false;
|
|
82
|
-
#failure = null;
|
|
83
|
-
#onFailure = null;
|
|
84
|
-
constructor(url, length, initialData) {
|
|
85
|
-
// progressiveDone=true: this transport intentionally supplies explicit ranges only.
|
|
86
|
-
super(length, initialData, true);
|
|
87
|
-
this.#url = url;
|
|
88
|
-
this.#length = length;
|
|
89
|
-
}
|
|
90
|
-
get failure() {
|
|
91
|
-
return this.#failure;
|
|
92
|
-
}
|
|
93
|
-
setFailureHandler(handler) {
|
|
94
|
-
this.#onFailure = handler;
|
|
95
|
-
}
|
|
96
|
-
requestDataRange(begin, end) {
|
|
97
|
-
if (this.#aborted)
|
|
98
|
-
return;
|
|
99
|
-
const controller = new AbortController();
|
|
100
|
-
this.#controllers.add(controller);
|
|
101
|
-
void fetch(this.#url, {
|
|
102
|
-
signal: controller.signal,
|
|
103
|
-
cache: "no-store",
|
|
104
|
-
headers: { Range: formatPdfRangeHeader(begin, end) },
|
|
105
|
-
})
|
|
106
|
-
.then(async (response) => {
|
|
107
|
-
if (response.status !== 206) {
|
|
108
|
-
throw new Error(`PDF custom range failed for [${begin}, ${end}): expected 206, got ${response.status}`);
|
|
109
|
-
}
|
|
110
|
-
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
111
|
-
const expected = end - begin;
|
|
112
|
-
const range = parseContentRange(response.headers.get("content-range"));
|
|
113
|
-
if (!range ||
|
|
114
|
-
range.begin !== begin ||
|
|
115
|
-
range.end !== end - 1 ||
|
|
116
|
-
range.total !== this.#length) {
|
|
117
|
-
throw new Error(`PDF custom range returned invalid Content-Range for [${begin}, ${end})`);
|
|
118
|
-
}
|
|
119
|
-
if (bytes.byteLength !== expected) {
|
|
120
|
-
throw new Error(`PDF custom range returned ${bytes.byteLength} bytes for [${begin}, ${end}); expected ${expected}`);
|
|
121
|
-
}
|
|
122
|
-
if (!this.#aborted)
|
|
123
|
-
this.onDataRange(begin, bytes);
|
|
124
|
-
})
|
|
125
|
-
.catch((error) => {
|
|
126
|
-
if (this.#aborted || error?.name === "AbortError")
|
|
127
|
-
return;
|
|
128
|
-
const failure = error instanceof Error
|
|
129
|
-
? error
|
|
130
|
-
: new Error(String(error?.message ?? error));
|
|
131
|
-
this.#failure = failure;
|
|
132
|
-
this.#onFailure?.(failure);
|
|
133
|
-
})
|
|
134
|
-
.finally(() => {
|
|
135
|
-
this.#controllers.delete(controller);
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
abort() {
|
|
139
|
-
if (this.#aborted)
|
|
140
|
-
return;
|
|
141
|
-
this.#aborted = true;
|
|
142
|
-
for (const controller of this.#controllers)
|
|
143
|
-
controller.abort();
|
|
144
|
-
this.#controllers.clear();
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
function commonOptions(wasmUrl) {
|
|
148
|
-
return {
|
|
149
|
-
wasmUrl,
|
|
150
|
-
useWorkerFetch: false,
|
|
151
|
-
// Only the deterministic benchmark pins OffscreenCanvas off. This transport also serves every
|
|
152
|
-
// ordinary PDF load, and pdf.js's save path REQUIRES OffscreenCanvas to encode Stamp (image)
|
|
153
|
-
// annotations: with it forced off, `saveDocument()` hands the stamp writer a null image and
|
|
154
|
-
// throws ("Cannot destructure property 'imageRef'"), so every image-stamp save failed with
|
|
155
|
-
// "annotations could not be serialized". Let pdf.js detect support in the real app.
|
|
156
|
-
isOffscreenCanvasSupported: isPdfBenchEnabled()
|
|
157
|
-
? false
|
|
158
|
-
: typeof OffscreenCanvas !== "undefined",
|
|
159
|
-
isImageDecoderSupported: false,
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
export async function preparePdfLoading({ url, mode, signal, wasmUrl, }) {
|
|
163
|
-
let bufferResponse = null;
|
|
164
|
-
if (mode === "buffer") {
|
|
165
|
-
const response = await fetch(url, { signal });
|
|
166
|
-
if (!response.ok) {
|
|
167
|
-
throw new Error(`PDF fetch failed: ${response.status} ${response.statusText}`.trim());
|
|
168
|
-
}
|
|
169
|
-
if (shouldRangeLoadInsteadOfBuffer(response.headers.get("content-length"))) {
|
|
170
|
-
void response.body?.cancel().catch(() => { });
|
|
171
|
-
}
|
|
172
|
-
else {
|
|
173
|
-
bufferResponse = response;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
if (bufferResponse) {
|
|
177
|
-
const response = bufferResponse;
|
|
178
|
-
const data = new Uint8Array(await response.arrayBuffer());
|
|
179
|
-
const task = pdfjs.getDocument(pdfDocumentInit(data, commonOptions(wasmUrl)));
|
|
180
|
-
let disposed = false;
|
|
181
|
-
const dispose = () => {
|
|
182
|
-
if (disposed)
|
|
183
|
-
return;
|
|
184
|
-
disposed = true;
|
|
185
|
-
void task.destroy();
|
|
186
|
-
};
|
|
187
|
-
signal.addEventListener("abort", dispose, { once: true });
|
|
188
|
-
return { task, promise: task.promise, dispose };
|
|
189
|
-
}
|
|
190
|
-
if (mode === "url-native") {
|
|
191
|
-
// Deliberately use PDF.js's own Fetch/XHR network stream with default stream/range behavior.
|
|
192
|
-
const task = pdfjs.getDocument(pdfDocumentInit(url, { rangeChunkSize: RANGE_CHUNK_SIZE, ...commonOptions(wasmUrl) }));
|
|
193
|
-
let disposed = false;
|
|
194
|
-
const dispose = () => {
|
|
195
|
-
if (disposed)
|
|
196
|
-
return;
|
|
197
|
-
disposed = true;
|
|
198
|
-
void task.destroy();
|
|
199
|
-
};
|
|
200
|
-
signal.addEventListener("abort", dispose, { once: true });
|
|
201
|
-
return { task, promise: task.promise, dispose };
|
|
202
|
-
}
|
|
203
|
-
const probe = await probePdfLength(url, signal);
|
|
204
|
-
const transport = new HostPdfRangeTransport(url, probe.length, probe.initialData);
|
|
205
|
-
const task = pdfjs.getDocument({
|
|
206
|
-
range: transport,
|
|
207
|
-
rangeChunkSize: RANGE_CHUNK_SIZE,
|
|
208
|
-
disableRange: false,
|
|
209
|
-
disableStream: true,
|
|
210
|
-
...commonOptions(wasmUrl),
|
|
211
|
-
});
|
|
212
|
-
transport.setFailureHandler((error) => {
|
|
213
|
-
console.error("[pdf-bench] custom range transport failed", error);
|
|
214
|
-
void task.destroy();
|
|
215
|
-
});
|
|
216
|
-
const promise = task.promise.catch((error) => {
|
|
217
|
-
throw transport.failure ?? error;
|
|
218
|
-
});
|
|
219
|
-
let disposed = false;
|
|
220
|
-
const dispose = () => {
|
|
221
|
-
if (disposed)
|
|
222
|
-
return;
|
|
223
|
-
disposed = true;
|
|
224
|
-
transport.abort();
|
|
225
|
-
void task.destroy();
|
|
226
|
-
};
|
|
227
|
-
signal.addEventListener("abort", dispose, { once: true });
|
|
228
|
-
return {
|
|
229
|
-
task,
|
|
230
|
-
promise,
|
|
231
|
-
dispose,
|
|
232
|
-
};
|
|
233
|
-
}
|