@anaralabs/lector 3.12.8 → 3.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_tsup-dts-rollup.d.ts +124 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +653 -55
- package/package.json +1 -1
|
@@ -132,6 +132,23 @@ declare interface AnnotationTooltipProps {
|
|
|
132
132
|
}) => React.ReactNode;
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
/**
|
|
136
|
+
* Recolors a 2D context at draw time: every fill/stroke whose style is a
|
|
137
|
+
* string is painted with `map(style)` and the original style is restored
|
|
138
|
+
* right after, so pdf.js readbacks (`ctx.fillStyle`, `copyCtxState`,
|
|
139
|
+
* save/restore) always observe original-document colors and nothing is ever
|
|
140
|
+
* mapped twice. Gradients are recolored per stop at creation. `drawImage`
|
|
141
|
+
* and `putImageData` are deliberately untouched — photos keep their pixels —
|
|
142
|
+
* with one exception: a drawImage that composes a luminosity soft mask
|
|
143
|
+
* (destination-in through a pdf.js `*_luminosity_map_*` filter) draws a
|
|
144
|
+
* luma-corrected copy of the mask so the recoloring doesn't invert the
|
|
145
|
+
* mask's alpha.
|
|
146
|
+
*
|
|
147
|
+
* Wrapping installs own properties on the context instance (the prototype is
|
|
148
|
+
* never modified). Returns a cleanup that restores the pristine context.
|
|
149
|
+
*/
|
|
150
|
+
export declare function applyContextRecolor(ctx: CanvasRenderingContext2D, map: RenderColorMap): () => void;
|
|
151
|
+
|
|
135
152
|
/**
|
|
136
153
|
* Calculates the highlight rectangles for a given text match.
|
|
137
154
|
*
|
|
@@ -151,6 +168,11 @@ export declare const cancellable: <T extends Promise<unknown>>(promise: T) => {
|
|
|
151
168
|
cancel: () => void;
|
|
152
169
|
};
|
|
153
170
|
|
|
171
|
+
declare interface CanvasAndContext {
|
|
172
|
+
canvas: HTMLCanvasElement | null;
|
|
173
|
+
context: CanvasRenderingContext2D | null;
|
|
174
|
+
}
|
|
175
|
+
|
|
154
176
|
export declare const CanvasLayer: MemoExoticComponent<({ style, background, ...props }: HTMLProps<HTMLCanvasElement> & {
|
|
155
177
|
background?: string;
|
|
156
178
|
}) => JSX.Element>;
|
|
@@ -182,6 +204,49 @@ declare type ColoredHighlightLayerProps = {
|
|
|
182
204
|
onHighlight?: (highlight: ColoredHighlight) => void;
|
|
183
205
|
};
|
|
184
206
|
|
|
207
|
+
declare type ColorScheme = "light" | "dark";
|
|
208
|
+
export { ColorScheme }
|
|
209
|
+
export { ColorScheme as ColorScheme_alias_1 }
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Builds a memoized color map that flips perceived lightness onto the
|
|
213
|
+
* background<->foreground ramp while preserving hue and chroma (OKLab).
|
|
214
|
+
* Position on the ramp follows gamma-encoded BT.709 luma — the same measure
|
|
215
|
+
* "luminance inversion" night modes use — so saturated accents move the way
|
|
216
|
+
* readers expect (red becomes pink, not cyan; blue links become light blue).
|
|
217
|
+
* Neutrals land exactly on the ramp: white maps to `background`, black to
|
|
218
|
+
* `foreground`, including the palette's tint.
|
|
219
|
+
*/
|
|
220
|
+
declare function createDarkModeColorMap(colors?: DarkModeColors): RenderColorMap;
|
|
221
|
+
export { createDarkModeColorMap }
|
|
222
|
+
export { createDarkModeColorMap as createDarkModeColorMap_alias_1 }
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* pdf.js paints transparency groups, soft masks, tiling/shading patterns and
|
|
226
|
+
* image-mask fills on internal scratch canvases obtained from the document's
|
|
227
|
+
* CanvasFactory — that content never touches the context we hand to
|
|
228
|
+
* `page.render()`. Passing this factory via getDocument's `CanvasFactory`
|
|
229
|
+
* option extends dark-mode recoloring to those canvases, which is what makes
|
|
230
|
+
* the scheme complete (pages with a page-level transparency group otherwise
|
|
231
|
+
* render entirely on a scratch canvas).
|
|
232
|
+
*
|
|
233
|
+
* The map is captured per canvas at creation time from `mapRef`; scratch
|
|
234
|
+
* canvases only live for a single render task, so toggling the scheme simply
|
|
235
|
+
* takes effect on the next render.
|
|
236
|
+
*/
|
|
237
|
+
export declare function createRecolorCanvasFactory(mapRef: RenderColorMapRef): {
|
|
238
|
+
new ({ ownerDocument, enableHWA, }?: {
|
|
239
|
+
ownerDocument?: Document;
|
|
240
|
+
enableHWA?: boolean;
|
|
241
|
+
}): {
|
|
242
|
+
"__#44@#document": Document;
|
|
243
|
+
"__#44@#enableHWA": boolean;
|
|
244
|
+
create(width: number, height: number): CanvasAndContext;
|
|
245
|
+
reset(canvasAndContext: CanvasAndContext, width: number, height: number): void;
|
|
246
|
+
destroy(canvasAndContext: CanvasAndContext): void;
|
|
247
|
+
};
|
|
248
|
+
};
|
|
249
|
+
|
|
185
250
|
export declare const CurrentPage: ({ ...props }: HTMLProps<HTMLInputElement>) => JSX.Element;
|
|
186
251
|
|
|
187
252
|
export declare const CurrentZoom: ({ ...props }: HTMLProps<HTMLInputElement>) => JSX.Element;
|
|
@@ -190,6 +255,19 @@ export declare const CustomLayer: MemoExoticComponent<({ children }: {
|
|
|
190
255
|
children: (pageNumber: number) => JSX_2.Element;
|
|
191
256
|
}) => JSX_2.Element>;
|
|
192
257
|
|
|
258
|
+
declare interface DarkModeColors {
|
|
259
|
+
/** Replaces the white paper background. Any CSS hex/rgb color. */
|
|
260
|
+
background?: string;
|
|
261
|
+
/** Replaces black text and line art. Any CSS hex/rgb color. */
|
|
262
|
+
foreground?: string;
|
|
263
|
+
}
|
|
264
|
+
export { DarkModeColors }
|
|
265
|
+
export { DarkModeColors as DarkModeColors_alias_1 }
|
|
266
|
+
|
|
267
|
+
declare const DEFAULT_DARK_MODE_COLORS: Required<DarkModeColors>;
|
|
268
|
+
export { DEFAULT_DARK_MODE_COLORS }
|
|
269
|
+
export { DEFAULT_DARK_MODE_COLORS as DEFAULT_DARK_MODE_COLORS_alias_1 }
|
|
270
|
+
|
|
193
271
|
export declare const ensureAnnotationLayerStyles: () => void;
|
|
194
272
|
|
|
195
273
|
export declare const firstMemo: <T>(first: boolean, memo: unknown, initializer: () => T) => T;
|
|
@@ -238,6 +316,9 @@ export declare interface InitialPDFState {
|
|
|
238
316
|
zoom: number;
|
|
239
317
|
isZoomFitWidth?: boolean;
|
|
240
318
|
zoomOptions?: ZoomOptions;
|
|
319
|
+
colorScheme?: ColorScheme;
|
|
320
|
+
darkModeColors?: DarkModeColors;
|
|
321
|
+
renderColorMapRef?: RenderColorMapRef;
|
|
241
322
|
}
|
|
242
323
|
|
|
243
324
|
export declare class LinkService {
|
|
@@ -349,6 +430,14 @@ declare interface PDFState {
|
|
|
349
430
|
renderedPages: Record<number, true>;
|
|
350
431
|
markPageRendered: (pageNumber: number) => void;
|
|
351
432
|
unmarkPageRendered: (pageNumber: number) => void;
|
|
433
|
+
colorScheme: ColorScheme;
|
|
434
|
+
darkModeColors: Required<DarkModeColors>;
|
|
435
|
+
setColorScheme: (colorScheme: ColorScheme, colors?: DarkModeColors) => void;
|
|
436
|
+
/**
|
|
437
|
+
* Shared with the document's pdf.js CanvasFactory so internal scratch
|
|
438
|
+
* canvases (transparency groups, soft masks, patterns) follow the scheme.
|
|
439
|
+
*/
|
|
440
|
+
renderColorMapRef: RenderColorMapRef;
|
|
352
441
|
}
|
|
353
442
|
|
|
354
443
|
export declare const PDFStore: {
|
|
@@ -364,6 +453,27 @@ export declare type PDFVirtualizer = Virtualizer<any, any>;
|
|
|
364
453
|
|
|
365
454
|
export declare const PreviousPage: () => void;
|
|
366
455
|
|
|
456
|
+
/**
|
|
457
|
+
* Removes any recolor wrapper installed on the context. For long-lived
|
|
458
|
+
* contexts (the visible canvas in the no-buffer fallback, thumbnails) that
|
|
459
|
+
* are about to render in the light scheme: a still-pending dark render's
|
|
460
|
+
* deferred restore could otherwise leave its wrapper active across the
|
|
461
|
+
* scheme switch.
|
|
462
|
+
*/
|
|
463
|
+
export declare function removeContextRecolor(ctx: CanvasRenderingContext2D): void;
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Maps a color from the original document to its dark-scheme equivalent.
|
|
467
|
+
* Unparseable and fully-transparent colors are returned unchanged.
|
|
468
|
+
*/
|
|
469
|
+
declare type RenderColorMap = (color: string) => string;
|
|
470
|
+
export { RenderColorMap }
|
|
471
|
+
export { RenderColorMap as RenderColorMap_alias_1 }
|
|
472
|
+
|
|
473
|
+
export declare interface RenderColorMapRef {
|
|
474
|
+
current: RenderColorMap | null;
|
|
475
|
+
}
|
|
476
|
+
|
|
367
477
|
export declare const Root: ForwardRefExoticComponent<Omit<Omit<HTMLProps<HTMLDivElement>, "onError"> & usePDFDocumentParams & {
|
|
368
478
|
loader?: ReactNode;
|
|
369
479
|
}, "ref"> & RefAttributes<unknown>>;
|
|
@@ -462,7 +572,7 @@ declare const usePdf: <T>(selector: (state: PDFState) => T) => T;
|
|
|
462
572
|
export { usePdf }
|
|
463
573
|
export { usePdf as usePdf_alias_1 }
|
|
464
574
|
|
|
465
|
-
export declare const usePDFDocumentContext: ({ onDocumentLoad, onError, source, initialRotation, isZoomFitWidth, zoom, zoomOptions, documentOptions, }: usePDFDocumentParams) => {
|
|
575
|
+
export declare const usePDFDocumentContext: ({ onDocumentLoad, onError, source, initialRotation, isZoomFitWidth, zoom, zoomOptions, documentOptions, colorScheme, darkModeColors, }: usePDFDocumentParams) => {
|
|
466
576
|
initialState: InitialPDFState | null | undefined;
|
|
467
577
|
};
|
|
468
578
|
|
|
@@ -501,6 +611,19 @@ export declare interface usePDFDocumentParams {
|
|
|
501
611
|
* Must be a stable reference (module-level constant or useMemo) to avoid reloading the document.
|
|
502
612
|
*/
|
|
503
613
|
documentOptions?: Partial<DocumentInitParameters>;
|
|
614
|
+
/**
|
|
615
|
+
* Initial color scheme for page rendering. "dark" recolors the document at
|
|
616
|
+
* render time (native dark mode): perceived lightness is flipped onto the
|
|
617
|
+
* darkModeColors ramp while hue is preserved, and images keep their
|
|
618
|
+
* original pixels. Changing the prop after mount updates the scheme; the
|
|
619
|
+
* runtime equivalent is `usePdf((s) => s.setColorScheme)`.
|
|
620
|
+
*/
|
|
621
|
+
colorScheme?: ColorScheme;
|
|
622
|
+
/**
|
|
623
|
+
* Palette for the dark scheme: `background` replaces white paper,
|
|
624
|
+
* `foreground` replaces black text. Defaults to #141210 / #eae6e0.
|
|
625
|
+
*/
|
|
626
|
+
darkModeColors?: DarkModeColors;
|
|
504
627
|
}
|
|
505
628
|
|
|
506
629
|
export declare const usePdfJump: () => {
|
package/dist/index.d.ts
CHANGED
|
@@ -42,3 +42,8 @@ export { useSelectionDimensions } from './_tsup-dts-rollup.js';
|
|
|
42
42
|
export { ColoredHighlight } from './_tsup-dts-rollup.js';
|
|
43
43
|
export { HighlightRect } from './_tsup-dts-rollup.js';
|
|
44
44
|
export { usePdf } from './_tsup-dts-rollup.js';
|
|
45
|
+
export { ColorScheme } from './_tsup-dts-rollup.js';
|
|
46
|
+
export { createDarkModeColorMap } from './_tsup-dts-rollup.js';
|
|
47
|
+
export { DarkModeColors } from './_tsup-dts-rollup.js';
|
|
48
|
+
export { DEFAULT_DARK_MODE_COLORS } from './_tsup-dts-rollup.js';
|
|
49
|
+
export { RenderColorMap } from './_tsup-dts-rollup.js';
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,209 @@ var clamp = (value, minimum, maximum) => {
|
|
|
15
15
|
return Math.min(Math.max(value, minimum), maximum);
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
+
// src/lib/dark-mode.ts
|
|
19
|
+
var DEFAULT_DARK_MODE_COLORS = {
|
|
20
|
+
background: "#141210",
|
|
21
|
+
foreground: "#eae6e0"
|
|
22
|
+
};
|
|
23
|
+
var NAMED_COLORS = {
|
|
24
|
+
white: [1, 1, 1, 1],
|
|
25
|
+
black: [0, 0, 0, 1],
|
|
26
|
+
transparent: [0, 0, 0, 0]
|
|
27
|
+
};
|
|
28
|
+
function clamp01(value) {
|
|
29
|
+
return value < 0 ? 0 : value > 1 ? 1 : value;
|
|
30
|
+
}
|
|
31
|
+
function parseColor(input) {
|
|
32
|
+
const str = input.trim().toLowerCase();
|
|
33
|
+
const named = Object.hasOwn(NAMED_COLORS, str) ? NAMED_COLORS[str] : void 0;
|
|
34
|
+
if (named) return named;
|
|
35
|
+
if (str.startsWith("#")) {
|
|
36
|
+
const hex = str.slice(1);
|
|
37
|
+
if (!/^[0-9a-f]{3,8}$/.test(hex)) return null;
|
|
38
|
+
if (hex.length === 3 || hex.length === 4) {
|
|
39
|
+
const r2 = Number.parseInt(hex[0], 16) / 15;
|
|
40
|
+
const g = Number.parseInt(hex[1], 16) / 15;
|
|
41
|
+
const b = Number.parseInt(hex[2], 16) / 15;
|
|
42
|
+
const a = hex.length === 4 ? Number.parseInt(hex[3], 16) / 15 : 1;
|
|
43
|
+
return [r2, g, b, a];
|
|
44
|
+
}
|
|
45
|
+
if (hex.length === 6 || hex.length === 8) {
|
|
46
|
+
const r2 = Number.parseInt(hex.slice(0, 2), 16) / 255;
|
|
47
|
+
const g = Number.parseInt(hex.slice(2, 4), 16) / 255;
|
|
48
|
+
const b = Number.parseInt(hex.slice(4, 6), 16) / 255;
|
|
49
|
+
const a = hex.length === 8 ? Number.parseInt(hex.slice(6, 8), 16) / 255 : 1;
|
|
50
|
+
return [r2, g, b, a];
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const fn = /^rgba?\(([^)]+)\)$/.exec(str);
|
|
55
|
+
if (fn) {
|
|
56
|
+
const parts = fn[1].split(/[,\s/]+/).filter(Boolean);
|
|
57
|
+
if (parts.length < 3) return null;
|
|
58
|
+
const channel = (part) => part.endsWith("%") ? Number.parseFloat(part) / 100 : Number.parseFloat(part) / 255;
|
|
59
|
+
const alpha = (part) => part.endsWith("%") ? Number.parseFloat(part) / 100 : Number.parseFloat(part);
|
|
60
|
+
const r2 = channel(parts[0]);
|
|
61
|
+
const g = channel(parts[1]);
|
|
62
|
+
const b = channel(parts[2]);
|
|
63
|
+
const a = parts[3] !== void 0 ? alpha(parts[3]) : 1;
|
|
64
|
+
if (Number.isNaN(r2) || Number.isNaN(g) || Number.isNaN(b) || Number.isNaN(a))
|
|
65
|
+
return null;
|
|
66
|
+
return [clamp01(r2), clamp01(g), clamp01(b), clamp01(a)];
|
|
67
|
+
}
|
|
68
|
+
const normalized = normalizeCssColor(str);
|
|
69
|
+
if (normalized !== null && normalized !== str) return parseColor(normalized);
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
var normalizeCtx;
|
|
73
|
+
function normalizeCssColor(input) {
|
|
74
|
+
if (normalizeCtx === void 0) {
|
|
75
|
+
normalizeCtx = typeof document === "undefined" ? null : document.createElement("canvas").getContext("2d", {
|
|
76
|
+
willReadFrequently: true
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
const ctx = normalizeCtx;
|
|
80
|
+
if (!ctx) return null;
|
|
81
|
+
ctx.fillStyle = "#000000";
|
|
82
|
+
ctx.fillStyle = input;
|
|
83
|
+
const first = ctx.fillStyle;
|
|
84
|
+
ctx.fillStyle = "#ffffff";
|
|
85
|
+
ctx.fillStyle = input;
|
|
86
|
+
return first === ctx.fillStyle ? first : null;
|
|
87
|
+
}
|
|
88
|
+
function srgbToLinear(c) {
|
|
89
|
+
return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
|
|
90
|
+
}
|
|
91
|
+
function linearToSrgb(c) {
|
|
92
|
+
return c <= 31308e-7 ? 12.92 * c : 1.055 * c ** (1 / 2.4) - 0.055;
|
|
93
|
+
}
|
|
94
|
+
function rgbToOklab(r2, g, b) {
|
|
95
|
+
const lr = srgbToLinear(r2);
|
|
96
|
+
const lg = srgbToLinear(g);
|
|
97
|
+
const lb = srgbToLinear(b);
|
|
98
|
+
const l = Math.cbrt(
|
|
99
|
+
0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb
|
|
100
|
+
);
|
|
101
|
+
const m = Math.cbrt(
|
|
102
|
+
0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb
|
|
103
|
+
);
|
|
104
|
+
const s = Math.cbrt(
|
|
105
|
+
0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb
|
|
106
|
+
);
|
|
107
|
+
return [
|
|
108
|
+
0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,
|
|
109
|
+
1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,
|
|
110
|
+
0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s
|
|
111
|
+
];
|
|
112
|
+
}
|
|
113
|
+
function oklabToLinearRgb(L, a, b) {
|
|
114
|
+
const l = (L + 0.3963377774 * a + 0.2158037573 * b) ** 3;
|
|
115
|
+
const m = (L - 0.1055613458 * a - 0.0638541728 * b) ** 3;
|
|
116
|
+
const s = (L - 0.0894841775 * a - 1.291485548 * b) ** 3;
|
|
117
|
+
return [
|
|
118
|
+
4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,
|
|
119
|
+
-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,
|
|
120
|
+
-0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s
|
|
121
|
+
];
|
|
122
|
+
}
|
|
123
|
+
var GAMUT_EPSILON = 5e-4;
|
|
124
|
+
function oklabToSrgbExact(L, a, b) {
|
|
125
|
+
const [lr, lg, lb] = oklabToLinearRgb(L, a, b);
|
|
126
|
+
if (lr < -GAMUT_EPSILON || lr > 1 + GAMUT_EPSILON || lg < -GAMUT_EPSILON || lg > 1 + GAMUT_EPSILON || lb < -GAMUT_EPSILON || lb > 1 + GAMUT_EPSILON) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
return [
|
|
130
|
+
linearToSrgb(clamp01(lr)),
|
|
131
|
+
linearToSrgb(clamp01(lg)),
|
|
132
|
+
linearToSrgb(clamp01(lb))
|
|
133
|
+
];
|
|
134
|
+
}
|
|
135
|
+
function oklabToSrgbGamutMapped(L, a, b) {
|
|
136
|
+
const exact = oklabToSrgbExact(L, a, b);
|
|
137
|
+
if (exact) return exact;
|
|
138
|
+
let lo = 0;
|
|
139
|
+
let hi = 1;
|
|
140
|
+
for (let i = 0; i < 12; i++) {
|
|
141
|
+
const mid = (lo + hi) / 2;
|
|
142
|
+
if (oklabToSrgbExact(L, a * mid, b * mid)) lo = mid;
|
|
143
|
+
else hi = mid;
|
|
144
|
+
}
|
|
145
|
+
const [lr, lg, lb] = oklabToLinearRgb(L, a * lo, b * lo);
|
|
146
|
+
return [
|
|
147
|
+
linearToSrgb(clamp01(lr)),
|
|
148
|
+
linearToSrgb(clamp01(lg)),
|
|
149
|
+
linearToSrgb(clamp01(lb))
|
|
150
|
+
];
|
|
151
|
+
}
|
|
152
|
+
function toHexByte(channel) {
|
|
153
|
+
return Math.round(clamp01(channel) * 255).toString(16).padStart(2, "0");
|
|
154
|
+
}
|
|
155
|
+
function toCssColor(r2, g, b, a) {
|
|
156
|
+
const rgb = `#${toHexByte(r2)}${toHexByte(g)}${toHexByte(b)}`;
|
|
157
|
+
return a >= 1 ? rgb : `${rgb}${toHexByte(a)}`;
|
|
158
|
+
}
|
|
159
|
+
var NEUTRAL_CHROMA_THRESHOLD = 0.04;
|
|
160
|
+
var COLOR_CACHE_MAX = 4096;
|
|
161
|
+
var mapInstances = /* @__PURE__ */ new Map();
|
|
162
|
+
function createDarkModeColorMap(colors) {
|
|
163
|
+
const background = colors?.background ?? DEFAULT_DARK_MODE_COLORS.background;
|
|
164
|
+
const foreground = colors?.foreground ?? DEFAULT_DARK_MODE_COLORS.foreground;
|
|
165
|
+
const instanceKey = `${background}|${foreground}`;
|
|
166
|
+
const existing = mapInstances.get(instanceKey);
|
|
167
|
+
if (existing) {
|
|
168
|
+
mapInstances.delete(instanceKey);
|
|
169
|
+
mapInstances.set(instanceKey, existing);
|
|
170
|
+
return existing;
|
|
171
|
+
}
|
|
172
|
+
const bgRgba = parseColor(background) ?? parseColor(DEFAULT_DARK_MODE_COLORS.background);
|
|
173
|
+
const fgRgba = parseColor(foreground) ?? parseColor(DEFAULT_DARK_MODE_COLORS.foreground);
|
|
174
|
+
const bgLab = rgbToOklab(bgRgba[0], bgRgba[1], bgRgba[2]);
|
|
175
|
+
const fgLab = rgbToOklab(fgRgba[0], fgRgba[1], fgRgba[2]);
|
|
176
|
+
const cache = /* @__PURE__ */ new Map();
|
|
177
|
+
const backgroundCss = toCssColor(bgRgba[0], bgRgba[1], bgRgba[2], 1);
|
|
178
|
+
const foregroundCss = toCssColor(fgRgba[0], fgRgba[1], fgRgba[2], 1);
|
|
179
|
+
const seedPoles = () => {
|
|
180
|
+
for (const white of ["#ffffff", "#fff", "white"])
|
|
181
|
+
cache.set(white, backgroundCss);
|
|
182
|
+
for (const black of ["#000000", "#000", "black"])
|
|
183
|
+
cache.set(black, foregroundCss);
|
|
184
|
+
};
|
|
185
|
+
seedPoles();
|
|
186
|
+
const transform = (input) => {
|
|
187
|
+
const parsed = parseColor(input);
|
|
188
|
+
if (!parsed || parsed[3] === 0) return input;
|
|
189
|
+
const [r2, g, b, alpha] = parsed;
|
|
190
|
+
const [, labA, labB] = rgbToOklab(r2, g, b);
|
|
191
|
+
const luma = 0.2126 * r2 + 0.7152 * g + 0.0722 * b;
|
|
192
|
+
const rampL = fgLab[0] + (bgLab[0] - fgLab[0]) * luma;
|
|
193
|
+
const rampA = fgLab[1] + (bgLab[1] - fgLab[1]) * luma;
|
|
194
|
+
const rampB = fgLab[2] + (bgLab[2] - fgLab[2]) * luma;
|
|
195
|
+
const chroma = Math.hypot(labA, labB);
|
|
196
|
+
const hueWeight = Math.min(1, chroma / NEUTRAL_CHROMA_THRESHOLD);
|
|
197
|
+
const outA = rampA + (labA - rampA) * hueWeight;
|
|
198
|
+
const outB = rampB + (labB - rampB) * hueWeight;
|
|
199
|
+
const [sr, sg, sb] = oklabToSrgbGamutMapped(rampL, outA, outB);
|
|
200
|
+
return toCssColor(sr, sg, sb, alpha);
|
|
201
|
+
};
|
|
202
|
+
const map = (input) => {
|
|
203
|
+
const hit = cache.get(input);
|
|
204
|
+
if (hit !== void 0) return hit;
|
|
205
|
+
const out = transform(input);
|
|
206
|
+
if (cache.size >= COLOR_CACHE_MAX) {
|
|
207
|
+
cache.clear();
|
|
208
|
+
seedPoles();
|
|
209
|
+
}
|
|
210
|
+
cache.set(input, out);
|
|
211
|
+
return out;
|
|
212
|
+
};
|
|
213
|
+
if (mapInstances.size >= 16) {
|
|
214
|
+
const oldest = mapInstances.keys().next().value;
|
|
215
|
+
if (oldest !== void 0) mapInstances.delete(oldest);
|
|
216
|
+
}
|
|
217
|
+
mapInstances.set(instanceKey, map);
|
|
218
|
+
return map;
|
|
219
|
+
}
|
|
220
|
+
|
|
18
221
|
// src/lib/zoom.ts
|
|
19
222
|
var getFitWidthZoom = (containerWidth, viewports, zoomOptions) => {
|
|
20
223
|
const { minZoom, maxZoom } = zoomOptions;
|
|
@@ -39,6 +242,15 @@ var createZustandContext = (getStore) => {
|
|
|
39
242
|
// src/internal.ts
|
|
40
243
|
var PDFStore = createZustandContext(
|
|
41
244
|
(initialState) => {
|
|
245
|
+
const initialColorScheme = initialState.colorScheme ?? "light";
|
|
246
|
+
const initialDarkModeColors = {
|
|
247
|
+
...DEFAULT_DARK_MODE_COLORS,
|
|
248
|
+
...initialState.darkModeColors
|
|
249
|
+
};
|
|
250
|
+
const renderColorMapRef = initialState.renderColorMapRef ?? {
|
|
251
|
+
current: null
|
|
252
|
+
};
|
|
253
|
+
renderColorMapRef.current = initialColorScheme === "dark" ? createDarkModeColorMap(initialDarkModeColors) : null;
|
|
42
254
|
return createStore((set, get) => ({
|
|
43
255
|
pdfDocumentProxy: initialState.pdfDocumentProxy,
|
|
44
256
|
zoom: initialState.zoom,
|
|
@@ -131,6 +343,21 @@ var PDFStore = createZustandContext(
|
|
|
131
343
|
(rect) => rect.uuid !== uuid
|
|
132
344
|
)
|
|
133
345
|
})),
|
|
346
|
+
colorScheme: initialColorScheme,
|
|
347
|
+
darkModeColors: initialDarkModeColors,
|
|
348
|
+
renderColorMapRef,
|
|
349
|
+
setColorScheme: (colorScheme, colors) => {
|
|
350
|
+
const prev = get();
|
|
351
|
+
const darkModeColors = {
|
|
352
|
+
background: colors?.background ?? prev.darkModeColors.background,
|
|
353
|
+
foreground: colors?.foreground ?? prev.darkModeColors.foreground
|
|
354
|
+
};
|
|
355
|
+
if (prev.colorScheme === colorScheme && prev.darkModeColors.background === darkModeColors.background && prev.darkModeColors.foreground === darkModeColors.foreground) {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
prev.renderColorMapRef.current = colorScheme === "dark" ? createDarkModeColorMap(darkModeColors) : null;
|
|
359
|
+
set({ colorScheme, darkModeColors });
|
|
360
|
+
},
|
|
134
361
|
renderedPages: {},
|
|
135
362
|
markPageRendered: (pageNumber) => {
|
|
136
363
|
const current = get().renderedPages;
|
|
@@ -1165,6 +1392,129 @@ function clampScaleForPage(targetScale, pageWidth, pageHeight) {
|
|
|
1165
1392
|
);
|
|
1166
1393
|
return Math.max(safeScale, 0);
|
|
1167
1394
|
}
|
|
1395
|
+
|
|
1396
|
+
// src/lib/recolor-context.ts
|
|
1397
|
+
var RECOLOR_CLEANUP = Symbol("lectorRecolorCleanup");
|
|
1398
|
+
var RECOLOR_PAINTED = Symbol("lectorRecolorPainted");
|
|
1399
|
+
var FILL_METHODS = ["fill", "fillRect", "fillText"];
|
|
1400
|
+
var STROKE_METHODS = ["stroke", "strokeRect", "strokeText"];
|
|
1401
|
+
var GRADIENT_METHODS = [
|
|
1402
|
+
"createLinearGradient",
|
|
1403
|
+
"createRadialGradient"
|
|
1404
|
+
];
|
|
1405
|
+
function ntscLuma(r2, g, b) {
|
|
1406
|
+
return 0.3 * r2 + 0.59 * g + 0.11 * b;
|
|
1407
|
+
}
|
|
1408
|
+
function parseHexLuma(color) {
|
|
1409
|
+
if (!/^#[0-9a-f]{6}$/i.test(color)) return null;
|
|
1410
|
+
return ntscLuma(
|
|
1411
|
+
Number.parseInt(color.slice(1, 3), 16),
|
|
1412
|
+
Number.parseInt(color.slice(3, 5), 16),
|
|
1413
|
+
Number.parseInt(color.slice(5, 7), 16)
|
|
1414
|
+
);
|
|
1415
|
+
}
|
|
1416
|
+
function correctLuminosityMask(source, mappedWhiteLuma, mappedBlackLuma) {
|
|
1417
|
+
if (typeof document === "undefined") return null;
|
|
1418
|
+
if (!(typeof HTMLCanvasElement !== "undefined" && source instanceof HTMLCanvasElement) && !(typeof OffscreenCanvas !== "undefined" && source instanceof OffscreenCanvas)) {
|
|
1419
|
+
return null;
|
|
1420
|
+
}
|
|
1421
|
+
const width = source.width;
|
|
1422
|
+
const height = source.height;
|
|
1423
|
+
if (!width || !height) return null;
|
|
1424
|
+
const span = mappedWhiteLuma - mappedBlackLuma;
|
|
1425
|
+
if (Math.abs(span) < 1) return null;
|
|
1426
|
+
const tmp = document.createElement("canvas");
|
|
1427
|
+
tmp.width = width;
|
|
1428
|
+
tmp.height = height;
|
|
1429
|
+
const tmpCtx = tmp.getContext("2d", { willReadFrequently: true });
|
|
1430
|
+
if (!tmpCtx) return null;
|
|
1431
|
+
tmpCtx.drawImage(source, 0, 0);
|
|
1432
|
+
let image;
|
|
1433
|
+
try {
|
|
1434
|
+
image = tmpCtx.getImageData(0, 0, width, height);
|
|
1435
|
+
} catch {
|
|
1436
|
+
return null;
|
|
1437
|
+
}
|
|
1438
|
+
const data = image.data;
|
|
1439
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
1440
|
+
const luma = ntscLuma(data[i], data[i + 1], data[i + 2]);
|
|
1441
|
+
const t = (luma - mappedBlackLuma) / span;
|
|
1442
|
+
const gray = t <= 0 ? 0 : t >= 1 ? 255 : Math.round(t * 255);
|
|
1443
|
+
data[i] = data[i + 1] = data[i + 2] = gray;
|
|
1444
|
+
}
|
|
1445
|
+
tmpCtx.putImageData(image, 0, 0);
|
|
1446
|
+
return tmp;
|
|
1447
|
+
}
|
|
1448
|
+
function applyContextRecolor(ctx, map) {
|
|
1449
|
+
const target = ctx;
|
|
1450
|
+
target[RECOLOR_CLEANUP]?.();
|
|
1451
|
+
const mappedWhiteLuma = parseHexLuma(map("#ffffff"));
|
|
1452
|
+
const mappedBlackLuma = parseHexLuma(map("#000000"));
|
|
1453
|
+
const withMaskCorrection = (original) => function(...args) {
|
|
1454
|
+
if (mappedWhiteLuma !== null && mappedBlackLuma !== null && this.globalCompositeOperation === "destination-in" && typeof this.filter === "string" && this.filter.includes("luminosity")) {
|
|
1455
|
+
const source = args[0];
|
|
1456
|
+
const sourcePainted = (typeof HTMLCanvasElement !== "undefined" && source instanceof HTMLCanvasElement || typeof OffscreenCanvas !== "undefined" && source instanceof OffscreenCanvas) && source.getContext("2d")?.[RECOLOR_PAINTED] === true;
|
|
1457
|
+
const corrected = sourcePainted ? correctLuminosityMask(source, mappedWhiteLuma, mappedBlackLuma) : null;
|
|
1458
|
+
if (corrected) {
|
|
1459
|
+
const next = [corrected, ...args.slice(1)];
|
|
1460
|
+
return original.apply(this, next);
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
return original.apply(this, args);
|
|
1464
|
+
};
|
|
1465
|
+
const withMappedStyle = (original, styleProp) => function(...args) {
|
|
1466
|
+
const style = this[styleProp];
|
|
1467
|
+
if (typeof style === "string") {
|
|
1468
|
+
const mapped = map(style);
|
|
1469
|
+
if (mapped !== style) {
|
|
1470
|
+
this[RECOLOR_PAINTED] = true;
|
|
1471
|
+
this[styleProp] = mapped;
|
|
1472
|
+
try {
|
|
1473
|
+
return original.apply(this, args);
|
|
1474
|
+
} finally {
|
|
1475
|
+
this[styleProp] = style;
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
return original.apply(this, args);
|
|
1480
|
+
};
|
|
1481
|
+
const withMappedStops = (original) => function(...args) {
|
|
1482
|
+
const gradient = original.apply(this, args);
|
|
1483
|
+
const addColorStop = gradient.addColorStop.bind(gradient);
|
|
1484
|
+
const context = this;
|
|
1485
|
+
gradient.addColorStop = (offset3, color) => {
|
|
1486
|
+
const mapped = map(color);
|
|
1487
|
+
if (mapped !== color) context[RECOLOR_PAINTED] = true;
|
|
1488
|
+
addColorStop(offset3, mapped);
|
|
1489
|
+
};
|
|
1490
|
+
return gradient;
|
|
1491
|
+
};
|
|
1492
|
+
const record = target;
|
|
1493
|
+
for (const name of FILL_METHODS)
|
|
1494
|
+
record[name] = withMappedStyle(target[name], "fillStyle");
|
|
1495
|
+
for (const name of STROKE_METHODS)
|
|
1496
|
+
record[name] = withMappedStyle(target[name], "strokeStyle");
|
|
1497
|
+
for (const name of GRADIENT_METHODS)
|
|
1498
|
+
record[name] = withMappedStops(target[name]);
|
|
1499
|
+
record.drawImage = withMaskCorrection(target.drawImage);
|
|
1500
|
+
const cleanup = () => {
|
|
1501
|
+
if (target[RECOLOR_CLEANUP] !== cleanup) return;
|
|
1502
|
+
for (const name of [
|
|
1503
|
+
...FILL_METHODS,
|
|
1504
|
+
...STROKE_METHODS,
|
|
1505
|
+
...GRADIENT_METHODS,
|
|
1506
|
+
"drawImage"
|
|
1507
|
+
])
|
|
1508
|
+
delete record[name];
|
|
1509
|
+
delete target[RECOLOR_CLEANUP];
|
|
1510
|
+
delete target[RECOLOR_PAINTED];
|
|
1511
|
+
};
|
|
1512
|
+
target[RECOLOR_CLEANUP] = cleanup;
|
|
1513
|
+
return cleanup;
|
|
1514
|
+
}
|
|
1515
|
+
function removeContextRecolor(ctx) {
|
|
1516
|
+
ctx[RECOLOR_CLEANUP]?.();
|
|
1517
|
+
}
|
|
1168
1518
|
var useDpr = () => {
|
|
1169
1519
|
const [dpr, setDPR] = useState(
|
|
1170
1520
|
typeof window === "undefined" ? 1 : Math.min(window.devicePixelRatio, 2)
|
|
@@ -1208,16 +1558,16 @@ function clearBitmapCache(documentId) {
|
|
|
1208
1558
|
cacheEntries.splice(i, 1);
|
|
1209
1559
|
}
|
|
1210
1560
|
}
|
|
1211
|
-
function cacheKey(scale, background) {
|
|
1212
|
-
const bg = background ?? "white"
|
|
1561
|
+
function cacheKey(scale, background, recolorKey) {
|
|
1562
|
+
const bg = `${background ?? "white"}|${recolorKey ?? ""}`;
|
|
1213
1563
|
let hash = Math.round(scale * 1e4);
|
|
1214
1564
|
for (let i = 0; i < bg.length; i++) {
|
|
1215
1565
|
hash = hash * 31 + bg.charCodeAt(i) | 0;
|
|
1216
1566
|
}
|
|
1217
1567
|
return hash;
|
|
1218
1568
|
}
|
|
1219
|
-
function getCachedBitmap(proxy, scale, background) {
|
|
1220
|
-
const key = cacheKey(scale, background);
|
|
1569
|
+
function getCachedBitmap(proxy, scale, background, recolorKey) {
|
|
1570
|
+
const key = cacheKey(scale, background, recolorKey);
|
|
1221
1571
|
const bitmap = canvasBitmapCache.get(proxy)?.get(key);
|
|
1222
1572
|
if (!bitmap) return null;
|
|
1223
1573
|
const idx = cacheEntries.findIndex((e) => e.proxy === proxy && e.key === key);
|
|
@@ -1227,8 +1577,8 @@ function getCachedBitmap(proxy, scale, background) {
|
|
|
1227
1577
|
}
|
|
1228
1578
|
return bitmap;
|
|
1229
1579
|
}
|
|
1230
|
-
function setCachedBitmap(docId, proxy, scale, background, bitmap) {
|
|
1231
|
-
const key = cacheKey(scale, background);
|
|
1580
|
+
function setCachedBitmap(docId, proxy, scale, background, recolorKey, bitmap) {
|
|
1581
|
+
const key = cacheKey(scale, background, recolorKey);
|
|
1232
1582
|
let map = canvasBitmapCache.get(proxy);
|
|
1233
1583
|
if (!map) {
|
|
1234
1584
|
map = /* @__PURE__ */ new Map();
|
|
@@ -1255,8 +1605,9 @@ function setCachedBitmap(docId, proxy, scale, background, bitmap) {
|
|
|
1255
1605
|
}
|
|
1256
1606
|
var useCanvasLayer = ({ background }) => {
|
|
1257
1607
|
const canvasRef = useRef(null);
|
|
1258
|
-
const
|
|
1608
|
+
const paintedKeyRef = useRef(null);
|
|
1259
1609
|
const pageNumber = usePDFPageNumber();
|
|
1610
|
+
const store = PDFStore.useContext();
|
|
1260
1611
|
const dpr = useDpr();
|
|
1261
1612
|
const bouncyZoom = usePdf((state) => state.zoom);
|
|
1262
1613
|
const isResizing = usePdf((state) => state.isResizing);
|
|
@@ -1264,6 +1615,10 @@ var useCanvasLayer = ({ background }) => {
|
|
|
1264
1615
|
const pdfPageProxy = usePdf((state) => state.getPdfPageProxy(pageNumber));
|
|
1265
1616
|
const markPageRendered = usePdf((state) => state.markPageRendered);
|
|
1266
1617
|
const unmarkPageRendered = usePdf((state) => state.unmarkPageRendered);
|
|
1618
|
+
const colorScheme = usePdf((state) => state.colorScheme);
|
|
1619
|
+
const darkModeColors = usePdf((state) => state.darkModeColors);
|
|
1620
|
+
const recolor = colorScheme === "dark" ? createDarkModeColorMap(darkModeColors) : null;
|
|
1621
|
+
const recolorKey = recolor ? `dark:${darkModeColors.background},${darkModeColors.foreground}` : void 0;
|
|
1267
1622
|
const [zoom] = useDebounce(bouncyZoom, 100);
|
|
1268
1623
|
useLayoutEffect(() => {
|
|
1269
1624
|
if (!canvasRef.current) return;
|
|
@@ -1271,13 +1626,14 @@ var useCanvasLayer = ({ background }) => {
|
|
|
1271
1626
|
const baseViewport = pdfPageProxy.getViewport({ scale: 1 });
|
|
1272
1627
|
const pageWidth = baseViewport.width;
|
|
1273
1628
|
const pageHeight = baseViewport.height;
|
|
1274
|
-
|
|
1629
|
+
const contentKey = `${background ?? "white"}|${recolorKey ?? ""}`;
|
|
1630
|
+
if (isResizing && paintedKeyRef.current === contentKey) {
|
|
1275
1631
|
baseCanvas.style.visibility = "";
|
|
1276
1632
|
baseCanvas.style.width = `${pageWidth}px`;
|
|
1277
1633
|
baseCanvas.style.height = `${pageHeight}px`;
|
|
1278
1634
|
return;
|
|
1279
1635
|
}
|
|
1280
|
-
|
|
1636
|
+
paintedKeyRef.current = null;
|
|
1281
1637
|
const targetBaseScale = dpr * Math.min(zoom, 1);
|
|
1282
1638
|
const baseScale = clampScaleForPage(targetBaseScale, pageWidth, pageHeight);
|
|
1283
1639
|
baseCanvas.width = Math.floor(pageWidth * baseScale);
|
|
@@ -1290,15 +1646,20 @@ var useCanvasLayer = ({ background }) => {
|
|
|
1290
1646
|
baseCanvas.style.transform = "translate(0px, 0px)";
|
|
1291
1647
|
baseCanvas.style.zIndex = "0";
|
|
1292
1648
|
baseCanvas.style.pointerEvents = "none";
|
|
1293
|
-
baseCanvas.style.backgroundColor = background ?? "white";
|
|
1649
|
+
baseCanvas.style.backgroundColor = recolor ? recolor(background ?? "#ffffff") : background ?? "white";
|
|
1294
1650
|
baseCanvas.style.visibility = "";
|
|
1295
1651
|
const context = baseCanvas.getContext("2d");
|
|
1296
1652
|
if (!context) return;
|
|
1297
|
-
const cached = getCachedBitmap(
|
|
1653
|
+
const cached = getCachedBitmap(
|
|
1654
|
+
pdfPageProxy,
|
|
1655
|
+
baseScale,
|
|
1656
|
+
background,
|
|
1657
|
+
recolorKey
|
|
1658
|
+
);
|
|
1298
1659
|
if (cached) {
|
|
1299
1660
|
context.drawImage(cached, 0, 0);
|
|
1300
1661
|
markPageRendered(pageNumber);
|
|
1301
|
-
|
|
1662
|
+
paintedKeyRef.current = contentKey;
|
|
1302
1663
|
return;
|
|
1303
1664
|
}
|
|
1304
1665
|
context.setTransform(1, 0, 0, 1, 0, 0);
|
|
@@ -1313,28 +1674,48 @@ var useCanvasLayer = ({ background }) => {
|
|
|
1313
1674
|
const useBuffer = bufferCtx !== null;
|
|
1314
1675
|
const renderCanvas = useBuffer ? buffer : baseCanvas;
|
|
1315
1676
|
const renderCtx = bufferCtx ?? context;
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
}
|
|
1677
|
+
let restoreRecolor = null;
|
|
1678
|
+
if (recolor) {
|
|
1679
|
+
restoreRecolor = applyContextRecolor(renderCtx, recolor);
|
|
1680
|
+
} else {
|
|
1681
|
+
removeContextRecolor(renderCtx);
|
|
1682
|
+
}
|
|
1683
|
+
let renderingTask;
|
|
1684
|
+
try {
|
|
1685
|
+
renderingTask = pdfPageProxy.render({
|
|
1686
|
+
canvas: renderCanvas,
|
|
1687
|
+
canvasContext: renderCtx,
|
|
1688
|
+
viewport,
|
|
1689
|
+
background
|
|
1690
|
+
});
|
|
1691
|
+
} catch (error) {
|
|
1692
|
+
restoreRecolor?.();
|
|
1693
|
+
throw error;
|
|
1694
|
+
}
|
|
1322
1695
|
const releaseBuffer = () => {
|
|
1323
1696
|
buffer.width = 0;
|
|
1324
1697
|
buffer.height = 0;
|
|
1325
1698
|
};
|
|
1326
|
-
renderingTask.promise.
|
|
1699
|
+
renderingTask.promise.finally(() => {
|
|
1700
|
+
restoreRecolor?.();
|
|
1701
|
+
}).then(() => {
|
|
1327
1702
|
if (cancelled) {
|
|
1328
1703
|
releaseBuffer();
|
|
1329
1704
|
return;
|
|
1330
1705
|
}
|
|
1706
|
+
const state = store.getState();
|
|
1707
|
+
const currentRecolorKey = state.colorScheme === "dark" ? `dark:${state.darkModeColors.background},${state.darkModeColors.foreground}` : void 0;
|
|
1708
|
+
if (currentRecolorKey !== recolorKey) {
|
|
1709
|
+
releaseBuffer();
|
|
1710
|
+
return;
|
|
1711
|
+
}
|
|
1331
1712
|
if (useBuffer) {
|
|
1332
1713
|
context.clearRect(0, 0, baseCanvas.width, baseCanvas.height);
|
|
1333
1714
|
context.drawImage(buffer, 0, 0);
|
|
1334
1715
|
}
|
|
1335
1716
|
baseCanvas.style.visibility = "";
|
|
1336
1717
|
markPageRendered(pageNumber);
|
|
1337
|
-
|
|
1718
|
+
paintedKeyRef.current = contentKey;
|
|
1338
1719
|
if (typeof createImageBitmap !== "undefined") {
|
|
1339
1720
|
createImageBitmap(renderCanvas).then((bitmap) => {
|
|
1340
1721
|
if (cancelled) {
|
|
@@ -1346,6 +1727,7 @@ var useCanvasLayer = ({ background }) => {
|
|
|
1346
1727
|
pdfPageProxy,
|
|
1347
1728
|
baseScale,
|
|
1348
1729
|
background,
|
|
1730
|
+
recolorKey,
|
|
1349
1731
|
bitmap
|
|
1350
1732
|
);
|
|
1351
1733
|
}).catch(() => {
|
|
@@ -1372,7 +1754,10 @@ var useCanvasLayer = ({ background }) => {
|
|
|
1372
1754
|
isResizing,
|
|
1373
1755
|
pageNumber,
|
|
1374
1756
|
markPageRendered,
|
|
1375
|
-
docId
|
|
1757
|
+
docId,
|
|
1758
|
+
recolor,
|
|
1759
|
+
recolorKey,
|
|
1760
|
+
store
|
|
1376
1761
|
]);
|
|
1377
1762
|
useEffect(() => {
|
|
1378
1763
|
const canvas = canvasRef.current;
|
|
@@ -1467,6 +1852,11 @@ var useDetailCanvasLayer = ({
|
|
|
1467
1852
|
const isPinching = usePdf((state) => state.isPinching);
|
|
1468
1853
|
const pdfPageProxy = usePdf((state) => state.getPdfPageProxy(pageNumber));
|
|
1469
1854
|
const viewportRef = usePdf((state) => state.viewportRef);
|
|
1855
|
+
const colorScheme = usePdf((state) => state.colorScheme);
|
|
1856
|
+
const darkModeColors = usePdf((state) => state.darkModeColors);
|
|
1857
|
+
const recolor = colorScheme === "dark" ? createDarkModeColorMap(darkModeColors) : null;
|
|
1858
|
+
const recolorKey = recolor ? `dark:${darkModeColors.background},${darkModeColors.foreground}` : "light";
|
|
1859
|
+
const paintedKeyRef = useRef(null);
|
|
1470
1860
|
const [zoom] = useDebounce(bouncyZoom, 200);
|
|
1471
1861
|
const pageDimsRef = useRef(null);
|
|
1472
1862
|
if (!pageDimsRef.current || pageDimsRef.current.proxy !== pdfPageProxy) {
|
|
@@ -1489,7 +1879,8 @@ var useDetailCanvasLayer = ({
|
|
|
1489
1879
|
}
|
|
1490
1880
|
let renderingTask = null;
|
|
1491
1881
|
let renderTimeoutId = null;
|
|
1492
|
-
const bgColor = background ?? "white";
|
|
1882
|
+
const bgColor = recolor ? recolor(background ?? "#ffffff") : background ?? "white";
|
|
1883
|
+
const contentKey = `${background ?? "white"}|${recolorKey}`;
|
|
1493
1884
|
const detailBaseStyle = `position:absolute;top:0;left:0;pointer-events:none;z-index:1;background-color:${bgColor}`;
|
|
1494
1885
|
const hideDetailCanvas = () => {
|
|
1495
1886
|
renderingTask?.cancel();
|
|
@@ -1569,6 +1960,7 @@ var useDetailCanvasLayer = ({
|
|
|
1569
1960
|
const detailViewport = pdfPageProxy.getViewport({
|
|
1570
1961
|
scale: effectiveScale
|
|
1571
1962
|
});
|
|
1963
|
+
if (recolor) applyContextRecolor(bufferCtx, recolor);
|
|
1572
1964
|
const currentRenderingTask = pdfPageProxy.render({
|
|
1573
1965
|
canvas: buffer,
|
|
1574
1966
|
canvasContext: bufferCtx,
|
|
@@ -1579,6 +1971,9 @@ var useDetailCanvasLayer = ({
|
|
|
1579
1971
|
renderingTask = currentRenderingTask;
|
|
1580
1972
|
currentRenderingTask.promise.then(() => {
|
|
1581
1973
|
if (renderingTask !== currentRenderingTask) return;
|
|
1974
|
+
const state = store.getState();
|
|
1975
|
+
const currentRecolorKey = state.colorScheme === "dark" ? `dark:${state.darkModeColors.background},${state.darkModeColors.foreground}` : "light";
|
|
1976
|
+
if (currentRecolorKey !== recolorKey) return;
|
|
1582
1977
|
detailCanvas.width = actualWidth;
|
|
1583
1978
|
detailCanvas.height = actualHeight;
|
|
1584
1979
|
detailCanvas.style.cssText = `${detailBaseStyle};display:block;opacity:1;width:${visibleWidth * zoom}px;height:${visibleHeight * zoom}px;transform-origin:center center;transform:translate(${visibleLeft * zoom}px,${visibleTop * zoom}px)`;
|
|
@@ -1587,6 +1982,7 @@ var useDetailCanvasLayer = ({
|
|
|
1587
1982
|
if (ctx) {
|
|
1588
1983
|
ctx.drawImage(buffer, 0, 0);
|
|
1589
1984
|
}
|
|
1985
|
+
paintedKeyRef.current = contentKey;
|
|
1590
1986
|
}).catch((error) => {
|
|
1591
1987
|
if (error.name === "RenderingCancelledException") {
|
|
1592
1988
|
return;
|
|
@@ -1617,6 +2013,10 @@ var useDetailCanvasLayer = ({
|
|
|
1617
2013
|
if (zoom <= 1 || isPinching) {
|
|
1618
2014
|
hideDetailCanvas();
|
|
1619
2015
|
} else {
|
|
2016
|
+
if (paintedKeyRef.current !== null && paintedKeyRef.current !== contentKey) {
|
|
2017
|
+
hideDetailCanvas();
|
|
2018
|
+
paintedKeyRef.current = null;
|
|
2019
|
+
}
|
|
1620
2020
|
scheduleRender(0);
|
|
1621
2021
|
}
|
|
1622
2022
|
return () => {
|
|
@@ -1634,7 +2034,9 @@ var useDetailCanvasLayer = ({
|
|
|
1634
2034
|
dpr,
|
|
1635
2035
|
viewportRef,
|
|
1636
2036
|
baseCanvasRef,
|
|
1637
|
-
store
|
|
2037
|
+
store,
|
|
2038
|
+
recolor,
|
|
2039
|
+
recolorKey
|
|
1638
2040
|
]);
|
|
1639
2041
|
useEffect(() => {
|
|
1640
2042
|
const canvas = detailCanvasRef.current;
|
|
@@ -2353,8 +2755,11 @@ var ColoredHighlightComponent = ({
|
|
|
2353
2755
|
const deleteColoredHighlight = usePdf(
|
|
2354
2756
|
(state) => state.deleteColoredHighlight
|
|
2355
2757
|
);
|
|
2758
|
+
const colorScheme = usePdf((state) => state.colorScheme);
|
|
2759
|
+
const darkModeColors = usePdf((state) => state.darkModeColors);
|
|
2356
2760
|
const [showButton, setShowButton] = useState(false);
|
|
2357
2761
|
const pageNumber = usePDFPageNumber();
|
|
2762
|
+
const highlightColor = colorScheme === "dark" ? createDarkModeColorMap(darkModeColors)(selection.color) : selection.color;
|
|
2358
2763
|
const pageRectangles = useMemo(
|
|
2359
2764
|
() => selection.rectangles.filter((r2) => r2.pageNumber === pageNumber),
|
|
2360
2765
|
[selection.rectangles, pageNumber]
|
|
@@ -2377,11 +2782,10 @@ var ColoredHighlightComponent = ({
|
|
|
2377
2782
|
width: rect.width,
|
|
2378
2783
|
cursor: "pointer",
|
|
2379
2784
|
zIndex: 30,
|
|
2380
|
-
backgroundColor:
|
|
2381
|
-
//
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
// mixBlendMode: "multiply", // works but coloring has some inconsistencies
|
|
2785
|
+
backgroundColor: highlightColor,
|
|
2786
|
+
// "darken" keeps dark text legible on a light page; on a dark
|
|
2787
|
+
// page the equivalent is "lighten" (keeps light text legible).
|
|
2788
|
+
mixBlendMode: colorScheme === "dark" ? "lighten" : "darken",
|
|
2385
2789
|
borderRadius: "0.2rem"
|
|
2386
2790
|
}
|
|
2387
2791
|
},
|
|
@@ -2669,6 +3073,7 @@ var createTextSelectionManager = () => {
|
|
|
2669
3073
|
var bindMouseEvents = createTextSelectionManager();
|
|
2670
3074
|
var TEXT_BUILD_IDLE_MS = 300;
|
|
2671
3075
|
var TEXT_SCROLL_SETTLE_MS = 160;
|
|
3076
|
+
var VIEWPORT_ATTACH_MAX_FRAMES = 300;
|
|
2672
3077
|
var useTextLayer = () => {
|
|
2673
3078
|
const textContainerRef = useRef(null);
|
|
2674
3079
|
const textLayerRef = useRef(null);
|
|
@@ -2681,15 +3086,22 @@ var useTextLayer = () => {
|
|
|
2681
3086
|
return;
|
|
2682
3087
|
}
|
|
2683
3088
|
let isCancelled = false;
|
|
2684
|
-
|
|
2685
|
-
|
|
3089
|
+
let buildTimer = null;
|
|
3090
|
+
let unsubscribe = null;
|
|
3091
|
+
let built = false;
|
|
3092
|
+
let building = false;
|
|
3093
|
+
let buildGen = 0;
|
|
3094
|
+
const build = () => {
|
|
3095
|
+
building = true;
|
|
3096
|
+
const gen = ++buildGen;
|
|
2686
3097
|
textContainer.innerHTML = "";
|
|
2687
3098
|
if (textLayerRef.current) {
|
|
2688
3099
|
textLayerRef.current.cancel();
|
|
2689
3100
|
textLayerRef.current = null;
|
|
2690
3101
|
}
|
|
2691
3102
|
void import('pdfjs-dist/legacy/build/pdf.mjs').then(({ TextLayer: TextLayer2 }) => {
|
|
2692
|
-
if (isCancelled || textContainerRef.current !== textContainer)
|
|
3103
|
+
if (isCancelled || gen !== buildGen || textContainerRef.current !== textContainer)
|
|
3104
|
+
return;
|
|
2693
3105
|
const textLayer = new TextLayer2({
|
|
2694
3106
|
textContentSource: pdfPageProxy.streamTextContent(),
|
|
2695
3107
|
container: textContainer,
|
|
@@ -2698,22 +3110,61 @@ var useTextLayer = () => {
|
|
|
2698
3110
|
textLayerRef.current = textLayer;
|
|
2699
3111
|
return textLayer.render();
|
|
2700
3112
|
}).then(() => {
|
|
2701
|
-
if (isCancelled || textContainerRef.current !== textContainer) {
|
|
3113
|
+
if (isCancelled || gen !== buildGen || textContainerRef.current !== textContainer) {
|
|
2702
3114
|
return;
|
|
2703
3115
|
}
|
|
2704
3116
|
const endOfContent = document.createElement("div");
|
|
2705
3117
|
endOfContent.className = "endOfContent";
|
|
2706
3118
|
textContainer.appendChild(endOfContent);
|
|
2707
3119
|
bindMouseEvents(textContainer, endOfContent);
|
|
3120
|
+
built = true;
|
|
3121
|
+
building = false;
|
|
3122
|
+
unsubscribe?.();
|
|
3123
|
+
unsubscribe = null;
|
|
2708
3124
|
}).catch((error) => {
|
|
3125
|
+
if (gen === buildGen) building = false;
|
|
2709
3126
|
if (error instanceof Error && error.name !== "AbortException") {
|
|
2710
3127
|
console.error("TextLayer rendering error:", error);
|
|
2711
3128
|
}
|
|
2712
3129
|
});
|
|
2713
|
-
}
|
|
3130
|
+
};
|
|
3131
|
+
const schedule = () => {
|
|
3132
|
+
if (built) return;
|
|
3133
|
+
if (building) {
|
|
3134
|
+
buildGen++;
|
|
3135
|
+
building = false;
|
|
3136
|
+
if (textLayerRef.current) {
|
|
3137
|
+
textLayerRef.current.cancel();
|
|
3138
|
+
textLayerRef.current = null;
|
|
3139
|
+
}
|
|
3140
|
+
textContainer.innerHTML = "";
|
|
3141
|
+
}
|
|
3142
|
+
if (buildTimer) clearTimeout(buildTimer);
|
|
3143
|
+
buildTimer = setTimeout(() => {
|
|
3144
|
+
buildTimer = null;
|
|
3145
|
+
if (isCancelled || textContainerRef.current !== textContainer) return;
|
|
3146
|
+
build();
|
|
3147
|
+
}, TEXT_BUILD_IDLE_MS);
|
|
3148
|
+
};
|
|
3149
|
+
let attachRaf = null;
|
|
3150
|
+
let attachAttempts = 0;
|
|
3151
|
+
const attachScrollSubscription = () => {
|
|
3152
|
+
attachRaf = null;
|
|
3153
|
+
const scrollViewport = viewportRef?.current;
|
|
3154
|
+
if (!scrollViewport) {
|
|
3155
|
+
if (++attachAttempts > VIEWPORT_ATTACH_MAX_FRAMES) return;
|
|
3156
|
+
attachRaf = requestAnimationFrame(attachScrollSubscription);
|
|
3157
|
+
return;
|
|
3158
|
+
}
|
|
3159
|
+
unsubscribe = subscribeToViewportInvalidation(scrollViewport, schedule);
|
|
3160
|
+
};
|
|
3161
|
+
attachScrollSubscription();
|
|
3162
|
+
schedule();
|
|
2714
3163
|
return () => {
|
|
2715
3164
|
isCancelled = true;
|
|
2716
|
-
clearTimeout(buildTimer);
|
|
3165
|
+
if (buildTimer) clearTimeout(buildTimer);
|
|
3166
|
+
if (attachRaf !== null) cancelAnimationFrame(attachRaf);
|
|
3167
|
+
unsubscribe?.();
|
|
2717
3168
|
if (textLayerRef.current) {
|
|
2718
3169
|
textLayerRef.current.cancel();
|
|
2719
3170
|
textLayerRef.current = null;
|
|
@@ -2723,11 +3174,10 @@ var useTextLayer = () => {
|
|
|
2723
3174
|
delete textContainer._cleanupTextSelection;
|
|
2724
3175
|
}
|
|
2725
3176
|
};
|
|
2726
|
-
}, [pdfPageProxy.streamTextContent, pdfPageProxy.getViewport]);
|
|
3177
|
+
}, [pdfPageProxy.streamTextContent, pdfPageProxy.getViewport, viewportRef]);
|
|
2727
3178
|
useEffect(() => {
|
|
2728
3179
|
const textContainer = textContainerRef.current;
|
|
2729
|
-
|
|
2730
|
-
if (!textContainer || !viewport) return;
|
|
3180
|
+
if (!textContainer) return;
|
|
2731
3181
|
let settleTimer = null;
|
|
2732
3182
|
let pointerDown = false;
|
|
2733
3183
|
let selecting = false;
|
|
@@ -2756,14 +3206,28 @@ var useTextLayer = () => {
|
|
|
2756
3206
|
pointerDown = false;
|
|
2757
3207
|
selecting = false;
|
|
2758
3208
|
};
|
|
2759
|
-
|
|
3209
|
+
let unsubscribe = null;
|
|
3210
|
+
let attachRaf = null;
|
|
3211
|
+
let attachAttempts = 0;
|
|
3212
|
+
const attachScrollSubscription = () => {
|
|
3213
|
+
attachRaf = null;
|
|
3214
|
+
const scrollViewport = viewportRef?.current;
|
|
3215
|
+
if (!scrollViewport) {
|
|
3216
|
+
if (++attachAttempts > VIEWPORT_ATTACH_MAX_FRAMES) return;
|
|
3217
|
+
attachRaf = requestAnimationFrame(attachScrollSubscription);
|
|
3218
|
+
return;
|
|
3219
|
+
}
|
|
3220
|
+
unsubscribe = subscribeToViewportInvalidation(scrollViewport, onScroll);
|
|
3221
|
+
};
|
|
3222
|
+
attachScrollSubscription();
|
|
2760
3223
|
document.addEventListener("pointerdown", onPointerDown, true);
|
|
2761
3224
|
document.addEventListener("pointerup", clearPointer, true);
|
|
2762
3225
|
document.addEventListener("pointercancel", clearPointer, true);
|
|
2763
3226
|
document.addEventListener("selectionchange", onSelectionChange);
|
|
2764
3227
|
window.addEventListener("blur", clearPointer);
|
|
2765
3228
|
return () => {
|
|
2766
|
-
|
|
3229
|
+
if (attachRaf !== null) cancelAnimationFrame(attachRaf);
|
|
3230
|
+
unsubscribe?.();
|
|
2767
3231
|
document.removeEventListener("pointerdown", onPointerDown, true);
|
|
2768
3232
|
document.removeEventListener("pointerup", clearPointer, true);
|
|
2769
3233
|
document.removeEventListener("pointercancel", clearPointer, true);
|
|
@@ -2978,6 +3442,9 @@ var Page = memo(
|
|
|
2978
3442
|
}) => {
|
|
2979
3443
|
const pdfPageProxy = usePdf((state) => state.getPdfPageProxy(pageNumber));
|
|
2980
3444
|
const viewport = usePdf((state) => state.viewports[pageNumber - 1]);
|
|
3445
|
+
const colorScheme = usePdf((state) => state.colorScheme);
|
|
3446
|
+
const darkModeColors = usePdf((state) => state.darkModeColors);
|
|
3447
|
+
const pageBackground = colorScheme === "dark" ? createDarkModeColorMap(darkModeColors)("#ffffff") : "white";
|
|
2981
3448
|
const width = viewport?.width ?? (pdfPageProxy.view[2] ?? 0) - (pdfPageProxy.view[0] ?? 0);
|
|
2982
3449
|
const height = viewport?.height ?? (pdfPageProxy.view[3] ?? 0) - (pdfPageProxy.view[1] ?? 0);
|
|
2983
3450
|
return /* @__PURE__ */ jsx(PDFPageNumberContext.Provider, { value: pdfPageProxy.pageNumber, children: /* @__PURE__ */ jsx(
|
|
@@ -2992,7 +3459,7 @@ var Page = memo(
|
|
|
2992
3459
|
style: {
|
|
2993
3460
|
"--scale-factor": 1,
|
|
2994
3461
|
"--total-scale-factor": 1,
|
|
2995
|
-
backgroundColor:
|
|
3462
|
+
backgroundColor: pageBackground,
|
|
2996
3463
|
position: "relative",
|
|
2997
3464
|
width,
|
|
2998
3465
|
height,
|
|
@@ -5535,8 +6002,63 @@ var getDefaultPdfJsAssetUrls = (version) => {
|
|
|
5535
6002
|
};
|
|
5536
6003
|
};
|
|
5537
6004
|
|
|
6005
|
+
// src/lib/recolor-canvas-factory.ts
|
|
6006
|
+
function createRecolorCanvasFactory(mapRef) {
|
|
6007
|
+
return class RecolorCanvasFactory {
|
|
6008
|
+
#document;
|
|
6009
|
+
#enableHWA;
|
|
6010
|
+
constructor({
|
|
6011
|
+
ownerDocument = globalThis.document,
|
|
6012
|
+
enableHWA = false
|
|
6013
|
+
} = {}) {
|
|
6014
|
+
this.#document = ownerDocument;
|
|
6015
|
+
this.#enableHWA = enableHWA;
|
|
6016
|
+
}
|
|
6017
|
+
create(width, height) {
|
|
6018
|
+
if (width <= 0 || height <= 0) {
|
|
6019
|
+
throw new Error("Invalid canvas size");
|
|
6020
|
+
}
|
|
6021
|
+
const canvas = this.#document.createElement("canvas");
|
|
6022
|
+
canvas.width = width;
|
|
6023
|
+
canvas.height = height;
|
|
6024
|
+
const context = canvas.getContext("2d", {
|
|
6025
|
+
willReadFrequently: !this.#enableHWA
|
|
6026
|
+
});
|
|
6027
|
+
const map = mapRef.current;
|
|
6028
|
+
if (context && map) {
|
|
6029
|
+
applyContextRecolor(context, map);
|
|
6030
|
+
}
|
|
6031
|
+
return { canvas, context };
|
|
6032
|
+
}
|
|
6033
|
+
reset(canvasAndContext, width, height) {
|
|
6034
|
+
if (!canvasAndContext.canvas) {
|
|
6035
|
+
throw new Error("Canvas is not specified");
|
|
6036
|
+
}
|
|
6037
|
+
if (width <= 0 || height <= 0) {
|
|
6038
|
+
throw new Error("Invalid canvas size");
|
|
6039
|
+
}
|
|
6040
|
+
canvasAndContext.canvas.width = width;
|
|
6041
|
+
canvasAndContext.canvas.height = height;
|
|
6042
|
+
if (canvasAndContext.context) {
|
|
6043
|
+
const map = mapRef.current;
|
|
6044
|
+
if (map) applyContextRecolor(canvasAndContext.context, map);
|
|
6045
|
+
else removeContextRecolor(canvasAndContext.context);
|
|
6046
|
+
}
|
|
6047
|
+
}
|
|
6048
|
+
destroy(canvasAndContext) {
|
|
6049
|
+
if (!canvasAndContext.canvas) {
|
|
6050
|
+
throw new Error("Canvas is not specified");
|
|
6051
|
+
}
|
|
6052
|
+
canvasAndContext.canvas.width = 0;
|
|
6053
|
+
canvasAndContext.canvas.height = 0;
|
|
6054
|
+
canvasAndContext.canvas = null;
|
|
6055
|
+
canvasAndContext.context = null;
|
|
6056
|
+
}
|
|
6057
|
+
};
|
|
6058
|
+
}
|
|
6059
|
+
|
|
5538
6060
|
// src/hooks/document/document.ts
|
|
5539
|
-
function buildDocumentInitParams(source, pdfJsVersion, overrides) {
|
|
6061
|
+
function buildDocumentInitParams(source, pdfJsVersion, renderColorMapRef, overrides) {
|
|
5540
6062
|
let params;
|
|
5541
6063
|
if (typeof source === "string" || source instanceof URL) {
|
|
5542
6064
|
params = { url: source };
|
|
@@ -5572,7 +6094,12 @@ function buildDocumentInitParams(source, pdfJsVersion, overrides) {
|
|
|
5572
6094
|
cMapUrl: assetUrls.cMapUrl,
|
|
5573
6095
|
cMapPacked: true,
|
|
5574
6096
|
standardFontDataUrl: assetUrls.standardFontDataUrl,
|
|
5575
|
-
iccUrl: assetUrls.iccUrl
|
|
6097
|
+
iccUrl: assetUrls.iccUrl,
|
|
6098
|
+
// Internal pdf.js scratch canvases (transparency groups, soft masks,
|
|
6099
|
+
// patterns) go through this factory so native dark mode can recolor
|
|
6100
|
+
// content that never touches the context passed to render(). A no-op
|
|
6101
|
+
// while the light scheme is active.
|
|
6102
|
+
CanvasFactory: createRecolorCanvasFactory(renderColorMapRef)
|
|
5576
6103
|
};
|
|
5577
6104
|
return { ...defaults, ...params, ...overrides };
|
|
5578
6105
|
}
|
|
@@ -5584,15 +6111,24 @@ var usePDFDocumentContext = ({
|
|
|
5584
6111
|
isZoomFitWidth,
|
|
5585
6112
|
zoom = 1,
|
|
5586
6113
|
zoomOptions,
|
|
5587
|
-
documentOptions
|
|
6114
|
+
documentOptions,
|
|
6115
|
+
colorScheme,
|
|
6116
|
+
darkModeColors
|
|
5588
6117
|
}) => {
|
|
5589
6118
|
const [_, setProgress] = useState(0);
|
|
5590
6119
|
const [initialState, setInitialState] = useState();
|
|
5591
6120
|
const [rotation] = useState(initialRotation);
|
|
6121
|
+
const [renderColorMapRef] = useState(() => ({
|
|
6122
|
+
current: null
|
|
6123
|
+
}));
|
|
5592
6124
|
const documentOptionsRef = useRef(documentOptions);
|
|
5593
6125
|
documentOptionsRef.current = documentOptions;
|
|
5594
6126
|
const onErrorRef = useRef(onError);
|
|
5595
6127
|
onErrorRef.current = onError;
|
|
6128
|
+
const colorSchemeRef = useRef(colorScheme);
|
|
6129
|
+
colorSchemeRef.current = colorScheme;
|
|
6130
|
+
const darkModeColorsRef = useRef(darkModeColors);
|
|
6131
|
+
darkModeColorsRef.current = darkModeColors;
|
|
5596
6132
|
useEffect(() => {
|
|
5597
6133
|
const generateViewports = async (pdf) => {
|
|
5598
6134
|
const pageProxies = [];
|
|
@@ -5620,7 +6156,10 @@ var usePDFDocumentContext = ({
|
|
|
5620
6156
|
pageProxies: sortedPageProxies,
|
|
5621
6157
|
pdfDocumentProxy: pdf,
|
|
5622
6158
|
zoom,
|
|
5623
|
-
zoomOptions
|
|
6159
|
+
zoomOptions,
|
|
6160
|
+
colorScheme: colorSchemeRef.current,
|
|
6161
|
+
darkModeColors: darkModeColorsRef.current,
|
|
6162
|
+
renderColorMapRef
|
|
5624
6163
|
}));
|
|
5625
6164
|
};
|
|
5626
6165
|
const loadDocument = () => {
|
|
@@ -5636,6 +6175,7 @@ var usePDFDocumentContext = ({
|
|
|
5636
6175
|
buildDocumentInitParams(
|
|
5637
6176
|
source,
|
|
5638
6177
|
version,
|
|
6178
|
+
renderColorMapRef,
|
|
5639
6179
|
documentOptionsRef.current
|
|
5640
6180
|
)
|
|
5641
6181
|
);
|
|
@@ -5697,6 +6237,38 @@ var usePDFDocumentContext = ({
|
|
|
5697
6237
|
initialState
|
|
5698
6238
|
};
|
|
5699
6239
|
};
|
|
6240
|
+
var ColorSchemeSync = ({
|
|
6241
|
+
colorScheme,
|
|
6242
|
+
darkModeColors
|
|
6243
|
+
}) => {
|
|
6244
|
+
const setColorScheme = usePdf((state) => state.setColorScheme);
|
|
6245
|
+
const storeColorScheme = usePdf((state) => state.colorScheme);
|
|
6246
|
+
const storeBackground = usePdf((state) => state.darkModeColors.background);
|
|
6247
|
+
const storeForeground = usePdf((state) => state.darkModeColors.foreground);
|
|
6248
|
+
const hasPalette = darkModeColors !== void 0;
|
|
6249
|
+
const background = darkModeColors?.background;
|
|
6250
|
+
const foreground = darkModeColors?.foreground;
|
|
6251
|
+
useEffect(() => {
|
|
6252
|
+
if (!colorScheme && !hasPalette) return;
|
|
6253
|
+
setColorScheme(
|
|
6254
|
+
colorScheme ?? storeColorScheme,
|
|
6255
|
+
hasPalette ? {
|
|
6256
|
+
background: background ?? DEFAULT_DARK_MODE_COLORS.background,
|
|
6257
|
+
foreground: foreground ?? DEFAULT_DARK_MODE_COLORS.foreground
|
|
6258
|
+
} : void 0
|
|
6259
|
+
);
|
|
6260
|
+
}, [
|
|
6261
|
+
colorScheme,
|
|
6262
|
+
storeColorScheme,
|
|
6263
|
+
storeBackground,
|
|
6264
|
+
storeForeground,
|
|
6265
|
+
hasPalette,
|
|
6266
|
+
background,
|
|
6267
|
+
foreground,
|
|
6268
|
+
setColorScheme
|
|
6269
|
+
]);
|
|
6270
|
+
return null;
|
|
6271
|
+
};
|
|
5700
6272
|
var Root = forwardRef(
|
|
5701
6273
|
({
|
|
5702
6274
|
children,
|
|
@@ -5708,6 +6280,8 @@ var Root = forwardRef(
|
|
|
5708
6280
|
zoom,
|
|
5709
6281
|
zoomOptions,
|
|
5710
6282
|
documentOptions,
|
|
6283
|
+
colorScheme,
|
|
6284
|
+
darkModeColors,
|
|
5711
6285
|
...props
|
|
5712
6286
|
}, ref) => {
|
|
5713
6287
|
const { initialState } = usePDFDocumentContext({
|
|
@@ -5717,7 +6291,9 @@ var Root = forwardRef(
|
|
|
5717
6291
|
isZoomFitWidth,
|
|
5718
6292
|
zoom,
|
|
5719
6293
|
zoomOptions,
|
|
5720
|
-
documentOptions
|
|
6294
|
+
documentOptions,
|
|
6295
|
+
colorScheme,
|
|
6296
|
+
darkModeColors
|
|
5721
6297
|
});
|
|
5722
6298
|
const linkService = useCreatePDFLinkService(
|
|
5723
6299
|
initialState?.pdfDocumentProxy ?? null
|
|
@@ -5729,7 +6305,16 @@ var Root = forwardRef(
|
|
|
5729
6305
|
clearBitmapCache(documentId);
|
|
5730
6306
|
};
|
|
5731
6307
|
}, [documentId]);
|
|
5732
|
-
return /* @__PURE__ */ jsx(Primitive.div, { ref, ...props, children: initialState ? /* @__PURE__ */
|
|
6308
|
+
return /* @__PURE__ */ jsx(Primitive.div, { ref, ...props, children: initialState ? /* @__PURE__ */ jsxs(PDFStore.Provider, { initialValue: initialState, children: [
|
|
6309
|
+
/* @__PURE__ */ jsx(
|
|
6310
|
+
ColorSchemeSync,
|
|
6311
|
+
{
|
|
6312
|
+
colorScheme,
|
|
6313
|
+
darkModeColors
|
|
6314
|
+
}
|
|
6315
|
+
),
|
|
6316
|
+
/* @__PURE__ */ jsx(PDFLinkServiceContext.Provider, { value: linkService, children })
|
|
6317
|
+
] }) : loader ?? "Loading..." });
|
|
5733
6318
|
}
|
|
5734
6319
|
);
|
|
5735
6320
|
Root.displayName = "Root";
|
|
@@ -5776,6 +6361,9 @@ var useThumbnail = (pageNumber, config = {}) => {
|
|
|
5776
6361
|
const { visible } = useVisibility({ elementRef: containerRef });
|
|
5777
6362
|
const [debouncedVisible] = useDebounce(visible, 50);
|
|
5778
6363
|
const dpr = useDpr();
|
|
6364
|
+
const colorScheme = usePdf((state) => state.colorScheme);
|
|
6365
|
+
const darkModeColors = usePdf((state) => state.darkModeColors);
|
|
6366
|
+
const recolor = colorScheme === "dark" ? createDarkModeColorMap(darkModeColors) : null;
|
|
5779
6367
|
const isVisible = isFirstPage || debouncedVisible;
|
|
5780
6368
|
useEffect(() => {
|
|
5781
6369
|
const renderThumbnail = async () => {
|
|
@@ -5792,13 +6380,23 @@ var useThumbnail = (pageNumber, config = {}) => {
|
|
|
5792
6380
|
canvas2.height = scaledViewport.height;
|
|
5793
6381
|
const context = canvas2.getContext("2d");
|
|
5794
6382
|
if (!context) return;
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
6383
|
+
let restoreRecolor = null;
|
|
6384
|
+
if (recolor) {
|
|
6385
|
+
restoreRecolor = applyContextRecolor(context, recolor);
|
|
6386
|
+
} else {
|
|
6387
|
+
removeContextRecolor(context);
|
|
6388
|
+
}
|
|
6389
|
+
try {
|
|
6390
|
+
const renderTask = pageProxy.render({
|
|
6391
|
+
canvas: canvas2,
|
|
6392
|
+
canvasContext: context,
|
|
6393
|
+
viewport: scaledViewport
|
|
6394
|
+
});
|
|
6395
|
+
renderTaskRef.current = renderTask;
|
|
6396
|
+
await renderTask.promise;
|
|
6397
|
+
} finally {
|
|
6398
|
+
restoreRecolor?.();
|
|
6399
|
+
}
|
|
5802
6400
|
} catch (error) {
|
|
5803
6401
|
if (error instanceof Error && error.name === "RenderingCancelledException") {
|
|
5804
6402
|
console.log("Rendering cancelled");
|
|
@@ -5816,7 +6414,7 @@ var useThumbnail = (pageNumber, config = {}) => {
|
|
|
5816
6414
|
canvas.height = 1;
|
|
5817
6415
|
}
|
|
5818
6416
|
};
|
|
5819
|
-
}, [pageProxy, isVisible, dpr, maxHeight, maxWidth]);
|
|
6417
|
+
}, [pageProxy, isVisible, dpr, maxHeight, maxWidth, recolor]);
|
|
5820
6418
|
return {
|
|
5821
6419
|
canvasRef,
|
|
5822
6420
|
containerRef,
|
|
@@ -6152,4 +6750,4 @@ function mergeAdjacentRects(rects) {
|
|
|
6152
6750
|
// src/hooks/usePageRendered.ts
|
|
6153
6751
|
var usePageRendered = (pageNumber) => usePdf((state) => !!state.renderedPages[pageNumber]);
|
|
6154
6752
|
|
|
6155
|
-
export { AnnotationHighlightLayer, AnnotationLayer, AnnotationTooltip, AnnotationsStoreProvider, CanvasLayer, ColoredHighlightLayer, CurrentPage, CurrentZoom, CustomLayer, HighlightLayer, LinkService, NextPage, Outline, OutlineChildItems, OutlineItem, PDFLinkServiceContext, Page, Pages, PreviousPage, Root, Search, SelectionTooltip, TextLayer, Thumbnail, Thumbnails, TotalPages, ZoomIn, ZoomOut, calculateHighlightRects, useAnnotations, useCreatePDFLinkService, usePDFLinkService, usePDFPageNumber, usePageRendered, usePdf, usePdfJump, useSearch, useSelectionDimensions };
|
|
6753
|
+
export { AnnotationHighlightLayer, AnnotationLayer, AnnotationTooltip, AnnotationsStoreProvider, CanvasLayer, ColoredHighlightLayer, CurrentPage, CurrentZoom, CustomLayer, DEFAULT_DARK_MODE_COLORS, HighlightLayer, LinkService, NextPage, Outline, OutlineChildItems, OutlineItem, PDFLinkServiceContext, Page, Pages, PreviousPage, Root, Search, SelectionTooltip, TextLayer, Thumbnail, Thumbnails, TotalPages, ZoomIn, ZoomOut, calculateHighlightRects, createDarkModeColorMap, useAnnotations, useCreatePDFLinkService, usePDFLinkService, usePDFPageNumber, usePageRendered, usePdf, usePdfJump, useSearch, useSelectionDimensions };
|