@anaralabs/lector 3.13.4 → 3.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_tsup-dts-rollup.d.ts +91 -5
- package/dist/index.js +461 -12
- package/package.json +102 -101
|
@@ -138,16 +138,20 @@ declare interface AnnotationTooltipProps {
|
|
|
138
138
|
* right after, so pdf.js readbacks (`ctx.fillStyle`, `copyCtxState`,
|
|
139
139
|
* save/restore) always observe original-document colors and nothing is ever
|
|
140
140
|
* mapped twice. Gradients are recolored per stop at creation. `drawImage`
|
|
141
|
-
* and `putImageData`
|
|
142
|
-
*
|
|
141
|
+
* and `putImageData` keep image pixels untouched — photos stay photos — with
|
|
142
|
+
* two exceptions: a drawImage that composes a luminosity soft mask
|
|
143
143
|
* (destination-in through a pdf.js `*_luminosity_map_*` filter) draws a
|
|
144
144
|
* luma-corrected copy of the mask so the recoloring doesn't invert the
|
|
145
|
-
* mask's alpha
|
|
145
|
+
* mask's alpha, and a drawImage that papers the page with near-white pixels
|
|
146
|
+
* (a scanned page) is inverted toward the palette poles right after painting
|
|
147
|
+
* (policy in ./scan-invert.ts).
|
|
146
148
|
*
|
|
147
149
|
* Wrapping installs own properties on the context instance (the prototype is
|
|
148
|
-
* never modified). Returns a cleanup that restores the pristine context
|
|
150
|
+
* never modified). Returns a cleanup that restores the pristine context; pass
|
|
151
|
+
* finalizeRender=true only when the render completed naturally, which lets a
|
|
152
|
+
* papered-but-inkless page finalize as a genuinely blank scanned page.
|
|
149
153
|
*/
|
|
150
|
-
export declare function applyContextRecolor(ctx: CanvasRenderingContext2D, map: RenderColorMap): () => void;
|
|
154
|
+
export declare function applyContextRecolor(ctx: CanvasRenderingContext2D, map: RenderColorMap, options?: RecolorContextOptions): (finalizeRender?: boolean) => void;
|
|
151
155
|
|
|
152
156
|
/**
|
|
153
157
|
* Calculates the highlight rectangles for a given text match.
|
|
@@ -183,6 +187,8 @@ export declare const clamp: (value: number, minimum: number, maximum: number) =>
|
|
|
183
187
|
|
|
184
188
|
export declare function clampScaleForPage(targetScale: number, pageWidth: number, pageHeight: number, maxPixels?: number): number;
|
|
185
189
|
|
|
190
|
+
export declare function classifyScanPaper(source: CanvasImageSource, crop?: SourceCrop): ScanPaperClass | null;
|
|
191
|
+
|
|
186
192
|
declare type CollapsibleSelection = {
|
|
187
193
|
highlights: HighlightRect[];
|
|
188
194
|
underlines?: HighlightRect[];
|
|
@@ -331,6 +337,8 @@ export declare interface InitialPDFState {
|
|
|
331
337
|
|
|
332
338
|
export declare const IS_MOBILE_DEVICE: boolean;
|
|
333
339
|
|
|
340
|
+
export declare function isScanPaperSource(source: CanvasImageSource, crop?: SourceCrop): boolean;
|
|
341
|
+
|
|
334
342
|
export declare class LinkService {
|
|
335
343
|
_pdfDocumentProxy?: PDFDocumentProxy;
|
|
336
344
|
externalLinkEnabled: boolean;
|
|
@@ -463,6 +471,18 @@ export declare type PDFVirtualizer = Virtualizer<any, any>;
|
|
|
463
471
|
|
|
464
472
|
export declare const PreviousPage: () => void;
|
|
465
473
|
|
|
474
|
+
export declare interface RecolorContextOptions {
|
|
475
|
+
/**
|
|
476
|
+
* Full-page area in the context's device space (viewport.width × height).
|
|
477
|
+
* When provided, image draws that paper the page (≥ SCAN_COVERAGE_MIN of
|
|
478
|
+
* this area) with near-white pixels — scanned pages — are inverted toward
|
|
479
|
+
* the palette poles. Omit on pdf.js scratch canvases, whose device space
|
|
480
|
+
* is not the page; a page-covering scan painted through one is still
|
|
481
|
+
* caught when the scratch canvas is composed back onto a page context.
|
|
482
|
+
*/
|
|
483
|
+
pageArea?: number;
|
|
484
|
+
}
|
|
485
|
+
|
|
466
486
|
/**
|
|
467
487
|
* Removes any recolor wrapper installed on the context. For long-lived
|
|
468
488
|
* contexts (the visible canvas in the no-buffer fallback, thumbnails) that
|
|
@@ -488,6 +508,60 @@ export declare const Root: ForwardRefExoticComponent<Omit<Omit<HTMLProps<HTMLDiv
|
|
|
488
508
|
loader?: ReactNode;
|
|
489
509
|
}, "ref"> & RefAttributes<unknown>>;
|
|
490
510
|
|
|
511
|
+
/** Painted area relative to the page above which a draw "papers the page". */
|
|
512
|
+
export declare const SCAN_COVERAGE_MIN = 0.7;
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Minimum count of dark "ink" pixels in the downsample. A pure-white raster
|
|
516
|
+
* is an MRC background layer (its ink lives in a separate dark layer that
|
|
517
|
+
* must stay un-inverted to remain readable) or a blank page — skip both.
|
|
518
|
+
* A single genuine dark sample suffices so sparse pages (a signature, a few
|
|
519
|
+
* lines) still qualify; downsample averaging already erases isolated noise.
|
|
520
|
+
*/
|
|
521
|
+
export declare const SCAN_INK_MIN_PIXELS = 1;
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Minimum opaque fraction: scans are fully opaque; a cut-out PNG would let
|
|
525
|
+
* the inversion fill bleed into its transparent holes.
|
|
526
|
+
*/
|
|
527
|
+
export declare const SCAN_OPAQUE_MIN_FRACTION = 0.99;
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Maximum saturated fraction. Stamps and seals on real scans measure a few
|
|
531
|
+
* percent saturated; a rasterized slide with a colored chart measures far
|
|
532
|
+
* more — inverting it would render the chart as a negative.
|
|
533
|
+
*/
|
|
534
|
+
export declare const SCAN_SATURATED_MAX_FRACTION = 0.15;
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Minimum density (summed tile area / union bounding box area) for pending
|
|
538
|
+
* tiles to read as one contiguous scan: strips partitioning a page are dense,
|
|
539
|
+
* scattered white screenshots on a text page are not.
|
|
540
|
+
*/
|
|
541
|
+
export declare const SCAN_TILE_DENSITY_MIN = 0.85;
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Minimum painted fraction for a draw to count as a scan tile: some raster
|
|
545
|
+
* PDFs encode a page as strips or grids (8×8 grids put single tiles under
|
|
546
|
+
* 2%) whose areas only paper the page in sum. The floor only excludes
|
|
547
|
+
* degenerate sliver spam — the density, saturation, page-level ink and
|
|
548
|
+
* paint-serial guards do the real false-positive filtering.
|
|
549
|
+
*/
|
|
550
|
+
export declare const SCAN_TILE_MIN_FRACTION = 0.005;
|
|
551
|
+
|
|
552
|
+
/** Fraction of near-white opaque pixels above which a source is scan paper. */
|
|
553
|
+
export declare const SCAN_WHITE_MIN_FRACTION = 0.6;
|
|
554
|
+
|
|
555
|
+
export declare interface ScanPaperClass {
|
|
556
|
+
/**
|
|
557
|
+
* Whether the paper carries dark ink pixels. A pure-white raster is an
|
|
558
|
+
* MRC background layer (its ink lives in a separate dark layer that must
|
|
559
|
+
* stay readable) or a blank margin tile — pages only invert when ink is
|
|
560
|
+
* present somewhere, but blank tiles still count toward tiled coverage.
|
|
561
|
+
*/
|
|
562
|
+
inked: boolean;
|
|
563
|
+
}
|
|
564
|
+
|
|
491
565
|
export declare const Search: ({ children, loading }: SearchProps) => ReactNode;
|
|
492
566
|
|
|
493
567
|
declare interface SearchOptions {
|
|
@@ -524,6 +598,18 @@ declare interface SelectionTooltipProps {
|
|
|
524
598
|
|
|
525
599
|
export declare type Source = string | URL | TypedArray | ArrayBuffer | DocumentInitParameters;
|
|
526
600
|
|
|
601
|
+
export declare interface SourceCrop {
|
|
602
|
+
sx: number;
|
|
603
|
+
sy: number;
|
|
604
|
+
sw: number;
|
|
605
|
+
sh: number;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
export declare function sourceSize(source: CanvasImageSource): {
|
|
609
|
+
width: number;
|
|
610
|
+
height: number;
|
|
611
|
+
} | null;
|
|
612
|
+
|
|
527
613
|
export declare const subscribeToViewportInvalidation: (viewport: HTMLDivElement, callback: ViewportCallback) => () => void;
|
|
528
614
|
|
|
529
615
|
declare type TextContent = {
|
package/dist/index.js
CHANGED
|
@@ -1417,6 +1417,96 @@ function computeBaseScale(dpr, zoom, pageWidth, pageHeight) {
|
|
|
1417
1417
|
);
|
|
1418
1418
|
}
|
|
1419
1419
|
|
|
1420
|
+
// src/lib/scan-invert.ts
|
|
1421
|
+
var SCAN_COVERAGE_MIN = 0.7;
|
|
1422
|
+
var SCAN_TILE_MIN_FRACTION = 5e-3;
|
|
1423
|
+
var SCAN_TILE_DENSITY_MIN = 0.85;
|
|
1424
|
+
var SCAN_WHITE_MIN_FRACTION = 0.6;
|
|
1425
|
+
var SCAN_INK_MIN_PIXELS = 1;
|
|
1426
|
+
var SCAN_OPAQUE_MIN_FRACTION = 0.99;
|
|
1427
|
+
var SCAN_SATURATED_MAX_FRACTION = 0.15;
|
|
1428
|
+
var SAMPLE_TARGET_PX = 64;
|
|
1429
|
+
var bitmapVerdicts = /* @__PURE__ */ new WeakMap();
|
|
1430
|
+
var scratchCanvas = null;
|
|
1431
|
+
function getScratchContext(width, height) {
|
|
1432
|
+
if (typeof document === "undefined") return null;
|
|
1433
|
+
scratchCanvas ??= document.createElement("canvas");
|
|
1434
|
+
scratchCanvas.width = width;
|
|
1435
|
+
scratchCanvas.height = height;
|
|
1436
|
+
return scratchCanvas.getContext("2d", { willReadFrequently: true });
|
|
1437
|
+
}
|
|
1438
|
+
function sourceSize(source) {
|
|
1439
|
+
if (typeof ImageBitmap !== "undefined" && source instanceof ImageBitmap) {
|
|
1440
|
+
return { width: source.width, height: source.height };
|
|
1441
|
+
}
|
|
1442
|
+
if (typeof HTMLCanvasElement !== "undefined" && source instanceof HTMLCanvasElement) {
|
|
1443
|
+
return { width: source.width, height: source.height };
|
|
1444
|
+
}
|
|
1445
|
+
if (typeof OffscreenCanvas !== "undefined" && source instanceof OffscreenCanvas) {
|
|
1446
|
+
return { width: source.width, height: source.height };
|
|
1447
|
+
}
|
|
1448
|
+
if (typeof HTMLImageElement !== "undefined" && source instanceof HTMLImageElement) {
|
|
1449
|
+
return { width: source.naturalWidth, height: source.naturalHeight };
|
|
1450
|
+
}
|
|
1451
|
+
return null;
|
|
1452
|
+
}
|
|
1453
|
+
function sampleScanPaper(source, crop) {
|
|
1454
|
+
const scale = Math.min(1, SAMPLE_TARGET_PX / Math.max(crop.sw, crop.sh));
|
|
1455
|
+
const w = Math.max(1, Math.round(crop.sw * scale));
|
|
1456
|
+
const h = Math.max(1, Math.round(crop.sh * scale));
|
|
1457
|
+
const ctx = getScratchContext(w, h);
|
|
1458
|
+
if (!ctx) return void 0;
|
|
1459
|
+
try {
|
|
1460
|
+
ctx.drawImage(source, crop.sx, crop.sy, crop.sw, crop.sh, 0, 0, w, h);
|
|
1461
|
+
const { data } = ctx.getImageData(0, 0, w, h);
|
|
1462
|
+
const total = data.length / 4;
|
|
1463
|
+
let white = 0;
|
|
1464
|
+
let opaque = 0;
|
|
1465
|
+
let saturated = 0;
|
|
1466
|
+
let ink = 0;
|
|
1467
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
1468
|
+
if (data[i + 3] < 250) continue;
|
|
1469
|
+
opaque++;
|
|
1470
|
+
const max = Math.max(data[i], data[i + 1], data[i + 2]);
|
|
1471
|
+
const min = Math.min(data[i], data[i + 1], data[i + 2]);
|
|
1472
|
+
if (min > 200 && max - min < 32) white++;
|
|
1473
|
+
else if (max > 60 && (max - min) / max > 0.35) saturated++;
|
|
1474
|
+
if (max < 100) ink++;
|
|
1475
|
+
}
|
|
1476
|
+
const isPaper = opaque / total >= SCAN_OPAQUE_MIN_FRACTION && white / total >= SCAN_WHITE_MIN_FRACTION && saturated / total <= SCAN_SATURATED_MAX_FRACTION;
|
|
1477
|
+
if (!isPaper) return null;
|
|
1478
|
+
return { inked: ink >= SCAN_INK_MIN_PIXELS };
|
|
1479
|
+
} catch {
|
|
1480
|
+
return void 0;
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
function classifyScanPaper(source, crop) {
|
|
1484
|
+
const size = sourceSize(source);
|
|
1485
|
+
if (!size || size.width <= 0 || size.height <= 0) return null;
|
|
1486
|
+
const fullSource = !crop || crop.sx === 0 && crop.sy === 0 && crop.sw === size.width && crop.sh === size.height;
|
|
1487
|
+
const bitmap = fullSource && typeof ImageBitmap !== "undefined" && source instanceof ImageBitmap ? source : null;
|
|
1488
|
+
if (bitmap) {
|
|
1489
|
+
const cached = bitmapVerdicts.get(bitmap);
|
|
1490
|
+
if (cached !== void 0) return cached;
|
|
1491
|
+
}
|
|
1492
|
+
const region = {
|
|
1493
|
+
...crop ?? { sx: 0, sy: 0, sw: size.width, sh: size.height }
|
|
1494
|
+
};
|
|
1495
|
+
if (region.sw < 0) {
|
|
1496
|
+
region.sx += region.sw;
|
|
1497
|
+
region.sw = -region.sw;
|
|
1498
|
+
}
|
|
1499
|
+
if (region.sh < 0) {
|
|
1500
|
+
region.sy += region.sh;
|
|
1501
|
+
region.sh = -region.sh;
|
|
1502
|
+
}
|
|
1503
|
+
if (region.sw === 0 || region.sh === 0) return null;
|
|
1504
|
+
const verdict = sampleScanPaper(source, region);
|
|
1505
|
+
if (verdict === void 0) return null;
|
|
1506
|
+
if (bitmap) bitmapVerdicts.set(bitmap, verdict);
|
|
1507
|
+
return verdict;
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1420
1510
|
// src/lib/recolor-context.ts
|
|
1421
1511
|
var RECOLOR_CLEANUP = Symbol("lectorRecolorCleanup");
|
|
1422
1512
|
var RECOLOR_PAINTED = Symbol("lectorRecolorPainted");
|
|
@@ -1469,25 +1559,330 @@ function correctLuminosityMask(source, mappedWhiteLuma, mappedBlackLuma) {
|
|
|
1469
1559
|
tmpCtx.putImageData(image, 0, 0);
|
|
1470
1560
|
return tmp;
|
|
1471
1561
|
}
|
|
1472
|
-
function applyContextRecolor(ctx, map) {
|
|
1562
|
+
function applyContextRecolor(ctx, map, options) {
|
|
1473
1563
|
const target = ctx;
|
|
1474
1564
|
target[RECOLOR_CLEANUP]?.();
|
|
1475
1565
|
const mappedWhiteLuma = parseHexLuma(map("#ffffff"));
|
|
1476
1566
|
const mappedBlackLuma = parseHexLuma(map("#000000"));
|
|
1477
|
-
const
|
|
1567
|
+
const pageArea = options?.pageArea;
|
|
1568
|
+
const scanEnabled = !!pageArea && pageArea > 0 && mappedWhiteLuma !== null && mappedBlackLuma !== null;
|
|
1569
|
+
const scanInvertGray = scanEnabled ? 255 - Math.round(mappedWhiteLuma) : null;
|
|
1570
|
+
const scanTintCss = scanEnabled ? map("#ffffff") : null;
|
|
1571
|
+
let inkScaleGray = 255;
|
|
1572
|
+
let inkOffsetGray = 0;
|
|
1573
|
+
if (scanEnabled) {
|
|
1574
|
+
const inkLuma = 255 - mappedWhiteLuma;
|
|
1575
|
+
const span = inkLuma - mappedWhiteLuma;
|
|
1576
|
+
if (span > 8) {
|
|
1577
|
+
const scale = Math.min(
|
|
1578
|
+
1,
|
|
1579
|
+
Math.max(0, (mappedBlackLuma - mappedWhiteLuma) / span)
|
|
1580
|
+
);
|
|
1581
|
+
inkScaleGray = Math.round(scale * 255);
|
|
1582
|
+
inkOffsetGray = Math.round(mappedWhiteLuma * (1 - scale));
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
const needsInkRemap = inkScaleGray < 253 || inkOffsetGray > 2;
|
|
1586
|
+
const pristineFillRect = target.fillRect;
|
|
1587
|
+
const destRect = (args) => {
|
|
1588
|
+
if (args.length >= 9) {
|
|
1589
|
+
return [args[5], args[6], args[7], args[8]];
|
|
1590
|
+
}
|
|
1591
|
+
if (args.length >= 5) {
|
|
1592
|
+
return [args[1], args[2], args[3], args[4]];
|
|
1593
|
+
}
|
|
1594
|
+
const size = sourceSize(args[0]);
|
|
1595
|
+
if (!size) return null;
|
|
1596
|
+
return [
|
|
1597
|
+
args[1] ?? 0,
|
|
1598
|
+
args[2] ?? 0,
|
|
1599
|
+
size.width,
|
|
1600
|
+
size.height
|
|
1601
|
+
];
|
|
1602
|
+
};
|
|
1603
|
+
const scanCandidate = (self, args) => {
|
|
1604
|
+
if (!scanEnabled || !pageArea) return null;
|
|
1605
|
+
if (self.globalCompositeOperation !== "source-over") return null;
|
|
1606
|
+
if (self.globalAlpha < 0.99) return null;
|
|
1607
|
+
const filter = self.filter;
|
|
1608
|
+
if (typeof filter === "string" && filter !== "none" && filter !== "")
|
|
1609
|
+
return null;
|
|
1610
|
+
const rect = destRect(args);
|
|
1611
|
+
if (!rect) return null;
|
|
1612
|
+
const t = self.getTransform();
|
|
1613
|
+
const painted = Math.abs(t.a * t.d - t.b * t.c) * Math.abs(rect[2] * rect[3]);
|
|
1614
|
+
const areaFraction = painted / pageArea;
|
|
1615
|
+
if (areaFraction < SCAN_TILE_MIN_FRACTION) return null;
|
|
1616
|
+
const crop = args.length >= 9 ? {
|
|
1617
|
+
sx: args[1],
|
|
1618
|
+
sy: args[2],
|
|
1619
|
+
sw: args[3],
|
|
1620
|
+
sh: args[4]
|
|
1621
|
+
} : void 0;
|
|
1622
|
+
const paper = classifyScanPaper(args[0], crop);
|
|
1623
|
+
if (!paper) return null;
|
|
1624
|
+
return { rect, areaFraction, clipped: clipActive(), inked: paper.inked };
|
|
1625
|
+
};
|
|
1626
|
+
const grayCss = (value) => `rgb(${value}, ${value}, ${value})`;
|
|
1627
|
+
const clipFlags = [false];
|
|
1628
|
+
const clipActive = () => clipFlags[clipFlags.length - 1] === true;
|
|
1629
|
+
const pristineSave = target.save;
|
|
1630
|
+
const pristineRestore = target.restore;
|
|
1631
|
+
const pristineClip = target.clip;
|
|
1632
|
+
const invertScanRect = (self, rect, matrix) => {
|
|
1633
|
+
self.save();
|
|
1634
|
+
self.setTransform(matrix);
|
|
1635
|
+
self.globalAlpha = 1;
|
|
1636
|
+
self.shadowColor = "rgba(0, 0, 0, 0)";
|
|
1637
|
+
self.shadowBlur = 0;
|
|
1638
|
+
self.shadowOffsetX = 0;
|
|
1639
|
+
self.shadowOffsetY = 0;
|
|
1640
|
+
self.globalCompositeOperation = "difference";
|
|
1641
|
+
self.fillStyle = grayCss(scanInvertGray ?? 0);
|
|
1642
|
+
pristineFillRect.call(self, rect[0], rect[1], rect[2], rect[3]);
|
|
1643
|
+
if (needsInkRemap) {
|
|
1644
|
+
self.globalCompositeOperation = "multiply";
|
|
1645
|
+
self.fillStyle = grayCss(inkScaleGray);
|
|
1646
|
+
pristineFillRect.call(self, rect[0], rect[1], rect[2], rect[3]);
|
|
1647
|
+
if (inkOffsetGray > 0) {
|
|
1648
|
+
self.globalCompositeOperation = "lighter";
|
|
1649
|
+
self.fillStyle = grayCss(inkOffsetGray);
|
|
1650
|
+
pristineFillRect.call(self, rect[0], rect[1], rect[2], rect[3]);
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
if (scanTintCss) {
|
|
1654
|
+
self.globalCompositeOperation = "color";
|
|
1655
|
+
self.fillStyle = scanTintCss;
|
|
1656
|
+
pristineFillRect.call(self, rect[0], rect[1], rect[2], rect[3]);
|
|
1657
|
+
}
|
|
1658
|
+
self.restore();
|
|
1659
|
+
};
|
|
1660
|
+
const OVERLAP_TOLERANCE = 0.2;
|
|
1661
|
+
let pendingTileArea = 0;
|
|
1662
|
+
let pendingHasInk = false;
|
|
1663
|
+
const pendingTiles = [];
|
|
1664
|
+
const coveredBounds = [];
|
|
1665
|
+
let paintSerial = 0;
|
|
1666
|
+
let pendingBaselineSerial = 0;
|
|
1667
|
+
const tileBounds = (matrix, rect) => {
|
|
1668
|
+
const corners = [
|
|
1669
|
+
matrix.transformPoint({ x: rect[0], y: rect[1] }),
|
|
1670
|
+
matrix.transformPoint({ x: rect[0] + rect[2], y: rect[1] }),
|
|
1671
|
+
matrix.transformPoint({ x: rect[0], y: rect[1] + rect[3] }),
|
|
1672
|
+
matrix.transformPoint({ x: rect[0] + rect[2], y: rect[1] + rect[3] })
|
|
1673
|
+
];
|
|
1674
|
+
return [
|
|
1675
|
+
Math.min(...corners.map((c) => c.x)),
|
|
1676
|
+
Math.min(...corners.map((c) => c.y)),
|
|
1677
|
+
Math.max(...corners.map((c) => c.x)),
|
|
1678
|
+
Math.max(...corners.map((c) => c.y))
|
|
1679
|
+
];
|
|
1680
|
+
};
|
|
1681
|
+
const boundsArea = (b) => Math.max(0, b[2] - b[0]) * Math.max(0, b[3] - b[1]);
|
|
1682
|
+
const overlapsAny = (bounds, others) => others.some((other) => {
|
|
1683
|
+
const intersection = [
|
|
1684
|
+
Math.max(bounds[0], other[0]),
|
|
1685
|
+
Math.max(bounds[1], other[1]),
|
|
1686
|
+
Math.min(bounds[2], other[2]),
|
|
1687
|
+
Math.min(bounds[3], other[3])
|
|
1688
|
+
];
|
|
1689
|
+
const smaller = Math.min(boundsArea(bounds), boundsArea(other));
|
|
1690
|
+
return smaller > 0 && boundsArea(intersection) > OVERLAP_TOLERANCE * smaller;
|
|
1691
|
+
});
|
|
1692
|
+
const AXIS_EPS = 1e-6;
|
|
1693
|
+
const isAxisAligned = (m) => Math.abs(m.b) < AXIS_EPS && Math.abs(m.c) < AXIS_EPS;
|
|
1694
|
+
const IDENTITY_MATRIX = typeof DOMMatrix !== "undefined" ? new DOMMatrix() : null;
|
|
1695
|
+
const subtractBounds = (rects, hole) => {
|
|
1696
|
+
const out = [];
|
|
1697
|
+
for (const r2 of rects) {
|
|
1698
|
+
const ix0 = Math.max(r2[0], hole[0]);
|
|
1699
|
+
const iy0 = Math.max(r2[1], hole[1]);
|
|
1700
|
+
const ix1 = Math.min(r2[2], hole[2]);
|
|
1701
|
+
const iy1 = Math.min(r2[3], hole[3]);
|
|
1702
|
+
if (ix0 >= ix1 || iy0 >= iy1) {
|
|
1703
|
+
out.push(r2);
|
|
1704
|
+
continue;
|
|
1705
|
+
}
|
|
1706
|
+
if (r2[1] < iy0) out.push([r2[0], r2[1], r2[2], iy0]);
|
|
1707
|
+
if (iy1 < r2[3]) out.push([r2[0], iy1, r2[2], r2[3]]);
|
|
1708
|
+
if (r2[0] < ix0) out.push([r2[0], iy0, ix0, iy1]);
|
|
1709
|
+
if (ix1 < r2[2]) out.push([ix1, iy0, r2[2], iy1]);
|
|
1710
|
+
}
|
|
1711
|
+
return out;
|
|
1712
|
+
};
|
|
1713
|
+
const invertTileRemainder = (self, tile, holes) => {
|
|
1714
|
+
if (!isAxisAligned(tile.matrix) || !IDENTITY_MATRIX) {
|
|
1715
|
+
invertScanRect(self, tile.rect, tile.matrix);
|
|
1716
|
+
return;
|
|
1717
|
+
}
|
|
1718
|
+
let pieces = [tile.bounds];
|
|
1719
|
+
for (const hole of holes) pieces = subtractBounds(pieces, hole);
|
|
1720
|
+
for (const piece of pieces) {
|
|
1721
|
+
if (piece[2] - piece[0] < 0.5 || piece[3] - piece[1] < 0.5) continue;
|
|
1722
|
+
invertScanRect(
|
|
1723
|
+
self,
|
|
1724
|
+
[piece[0], piece[1], piece[2] - piece[0], piece[3] - piece[1]],
|
|
1725
|
+
IDENTITY_MATRIX
|
|
1726
|
+
);
|
|
1727
|
+
}
|
|
1728
|
+
};
|
|
1729
|
+
const pendingTriggerReady = (requireInk = true) => {
|
|
1730
|
+
if (pendingTileArea < SCAN_COVERAGE_MIN) return false;
|
|
1731
|
+
if (requireInk && !pendingHasInk) return false;
|
|
1732
|
+
const union = [
|
|
1733
|
+
Math.min(...pendingTiles.map((t) => t.bounds[0])),
|
|
1734
|
+
Math.min(...pendingTiles.map((t) => t.bounds[1])),
|
|
1735
|
+
Math.max(...pendingTiles.map((t) => t.bounds[2])),
|
|
1736
|
+
Math.max(...pendingTiles.map((t) => t.bounds[3]))
|
|
1737
|
+
];
|
|
1738
|
+
const unionArea = boundsArea(union);
|
|
1739
|
+
if (unionArea <= 0) return false;
|
|
1740
|
+
return pendingTileArea * (pageArea ?? 0) / unionArea >= SCAN_TILE_DENSITY_MIN;
|
|
1741
|
+
};
|
|
1742
|
+
const prunePendingToCleanSuffix = () => {
|
|
1743
|
+
if (paintSerial === pendingBaselineSerial) return;
|
|
1744
|
+
const kept = pendingTiles.filter((t) => t.serial === paintSerial);
|
|
1745
|
+
pendingTiles.length = 0;
|
|
1746
|
+
pendingTiles.push(...kept);
|
|
1747
|
+
pendingTileArea = kept.reduce((sum, t) => sum + t.area, 0);
|
|
1748
|
+
pendingHasInk = kept.some((t) => t.inked);
|
|
1749
|
+
pendingBaselineSerial = paintSerial;
|
|
1750
|
+
};
|
|
1751
|
+
const flushPendingTiles = (self) => {
|
|
1752
|
+
if (paintSerial !== pendingBaselineSerial) {
|
|
1753
|
+
prunePendingToCleanSuffix();
|
|
1754
|
+
if (pendingTiles.length === 0 || !pendingTriggerReady()) return;
|
|
1755
|
+
}
|
|
1756
|
+
for (const pending of pendingTiles) {
|
|
1757
|
+
invertTileRemainder(self, pending, coveredBounds);
|
|
1758
|
+
coveredBounds.push(pending.bounds);
|
|
1759
|
+
}
|
|
1760
|
+
pendingTiles.length = 0;
|
|
1761
|
+
pendingTileArea = 0;
|
|
1762
|
+
pendingHasInk = false;
|
|
1763
|
+
};
|
|
1764
|
+
const handleScanCandidate = (self, candidate) => {
|
|
1765
|
+
const matrix = self.getTransform();
|
|
1766
|
+
const bounds = tileBounds(matrix, candidate.rect);
|
|
1767
|
+
if (candidate.areaFraction >= SCAN_COVERAGE_MIN && !candidate.clipped) {
|
|
1768
|
+
for (let i = coveredBounds.length - 1; i >= 0; i--) {
|
|
1769
|
+
const covered = coveredBounds[i];
|
|
1770
|
+
const intersection = [
|
|
1771
|
+
Math.max(bounds[0], covered[0]),
|
|
1772
|
+
Math.max(bounds[1], covered[1]),
|
|
1773
|
+
Math.min(bounds[2], covered[2]),
|
|
1774
|
+
Math.min(bounds[3], covered[3])
|
|
1775
|
+
];
|
|
1776
|
+
const coveredArea = boundsArea(covered);
|
|
1777
|
+
if (coveredArea > 0 && boundsArea(intersection) >= 0.8 * coveredArea) {
|
|
1778
|
+
coveredBounds.splice(i, 1);
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
if (candidate.areaFraction >= SCAN_COVERAGE_MIN && candidate.clipped) {
|
|
1783
|
+
if (candidate.inked) {
|
|
1784
|
+
invertScanRect(self, candidate.rect, matrix);
|
|
1785
|
+
}
|
|
1786
|
+
pendingTiles.length = 0;
|
|
1787
|
+
pendingTileArea = 0;
|
|
1788
|
+
pendingHasInk = false;
|
|
1789
|
+
return;
|
|
1790
|
+
}
|
|
1791
|
+
if (coveredBounds.length > 0 && candidate.areaFraction <= 0.2) {
|
|
1792
|
+
const candidateArea = boundsArea(bounds);
|
|
1793
|
+
let insideArea = 0;
|
|
1794
|
+
for (const covered of coveredBounds) {
|
|
1795
|
+
const intersection = [
|
|
1796
|
+
Math.max(bounds[0], covered[0]),
|
|
1797
|
+
Math.max(bounds[1], covered[1]),
|
|
1798
|
+
Math.min(bounds[2], covered[2]),
|
|
1799
|
+
Math.min(bounds[3], covered[3])
|
|
1800
|
+
];
|
|
1801
|
+
insideArea += boundsArea(intersection);
|
|
1802
|
+
}
|
|
1803
|
+
if (candidateArea > 0 && insideArea >= 0.8 * candidateArea) return;
|
|
1804
|
+
}
|
|
1805
|
+
if (coveredBounds.length > 0 && !candidate.clipped) {
|
|
1806
|
+
invertScanRect(self, candidate.rect, matrix);
|
|
1807
|
+
coveredBounds.push(bounds);
|
|
1808
|
+
return;
|
|
1809
|
+
}
|
|
1810
|
+
if (candidate.areaFraction >= SCAN_COVERAGE_MIN && candidate.inked) {
|
|
1811
|
+
invertScanRect(self, candidate.rect, matrix);
|
|
1812
|
+
coveredBounds.push(bounds);
|
|
1813
|
+
flushPendingTiles(self);
|
|
1814
|
+
return;
|
|
1815
|
+
}
|
|
1816
|
+
if (candidate.clipped) {
|
|
1817
|
+
paintSerial++;
|
|
1818
|
+
return;
|
|
1819
|
+
}
|
|
1820
|
+
if (candidate.areaFraction >= SCAN_COVERAGE_MIN) {
|
|
1821
|
+
for (let i = pendingTiles.length - 1; i >= 0; i--) {
|
|
1822
|
+
const pending = pendingTiles[i];
|
|
1823
|
+
const intersection = [
|
|
1824
|
+
Math.max(bounds[0], pending.bounds[0]),
|
|
1825
|
+
Math.max(bounds[1], pending.bounds[1]),
|
|
1826
|
+
Math.min(bounds[2], pending.bounds[2]),
|
|
1827
|
+
Math.min(bounds[3], pending.bounds[3])
|
|
1828
|
+
];
|
|
1829
|
+
const pendingArea = boundsArea(pending.bounds);
|
|
1830
|
+
if (pendingArea > 0 && boundsArea(intersection) >= 0.8 * pendingArea) {
|
|
1831
|
+
pendingTiles.splice(i, 1);
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
pendingTileArea = pendingTiles.reduce((sum, t) => sum + t.area, 0);
|
|
1835
|
+
pendingHasInk = pendingTiles.some((t) => t.inked);
|
|
1836
|
+
}
|
|
1837
|
+
let contributedArea = candidate.areaFraction;
|
|
1838
|
+
if (isAxisAligned(matrix) && pageArea) {
|
|
1839
|
+
let pieces = [bounds];
|
|
1840
|
+
for (const pending of pendingTiles) {
|
|
1841
|
+
pieces = subtractBounds(pieces, pending.bounds);
|
|
1842
|
+
}
|
|
1843
|
+
contributedArea = pieces.reduce((sum, piece) => sum + boundsArea(piece), 0) / pageArea;
|
|
1844
|
+
} else if (overlapsAny(
|
|
1845
|
+
bounds,
|
|
1846
|
+
pendingTiles.map((t) => t.bounds)
|
|
1847
|
+
)) {
|
|
1848
|
+
return;
|
|
1849
|
+
}
|
|
1850
|
+
if (pendingTiles.length === 0) pendingBaselineSerial = paintSerial;
|
|
1851
|
+
pendingTiles.push({
|
|
1852
|
+
rect: candidate.rect,
|
|
1853
|
+
matrix,
|
|
1854
|
+
bounds,
|
|
1855
|
+
area: contributedArea,
|
|
1856
|
+
inked: candidate.inked,
|
|
1857
|
+
serial: paintSerial
|
|
1858
|
+
});
|
|
1859
|
+
pendingTileArea += contributedArea;
|
|
1860
|
+
pendingHasInk ||= candidate.inked;
|
|
1861
|
+
if (!pendingTriggerReady()) return;
|
|
1862
|
+
flushPendingTiles(self);
|
|
1863
|
+
};
|
|
1864
|
+
const withImageHandling = (original) => function(...args) {
|
|
1478
1865
|
if (mappedWhiteLuma !== null && mappedBlackLuma !== null && this.globalCompositeOperation === "destination-in" && typeof this.filter === "string" && this.filter.includes("luminosity")) {
|
|
1479
1866
|
const source = args[0];
|
|
1480
1867
|
const sourcePainted = (typeof HTMLCanvasElement !== "undefined" && source instanceof HTMLCanvasElement || typeof OffscreenCanvas !== "undefined" && source instanceof OffscreenCanvas) && source.getContext("2d")?.[RECOLOR_PAINTED] === true;
|
|
1481
1868
|
const corrected = sourcePainted ? correctLuminosityMask(source, mappedWhiteLuma, mappedBlackLuma) : null;
|
|
1869
|
+
paintSerial++;
|
|
1482
1870
|
if (corrected) {
|
|
1483
1871
|
const next = [corrected, ...args.slice(1)];
|
|
1484
1872
|
return original.apply(this, next);
|
|
1485
1873
|
}
|
|
1874
|
+
return original.apply(this, args);
|
|
1486
1875
|
}
|
|
1487
|
-
|
|
1876
|
+
const candidate = scanCandidate(this, args);
|
|
1877
|
+
if (!candidate) paintSerial++;
|
|
1878
|
+
const result = original.apply(this, args);
|
|
1879
|
+
if (candidate) handleScanCandidate(this, candidate);
|
|
1880
|
+
return result;
|
|
1488
1881
|
};
|
|
1489
1882
|
const withMappedStyle = (original, styleProp) => function(...args) {
|
|
1490
1883
|
const style = this[styleProp];
|
|
1884
|
+
const invisible = this.globalCompositeOperation === "source-over" && (this.globalAlpha === 0 || style === "transparent" || typeof style === "string" && style.startsWith("rgba(") && style.endsWith(", 0)"));
|
|
1885
|
+
if (!invisible) paintSerial++;
|
|
1491
1886
|
if (typeof style === "string") {
|
|
1492
1887
|
const mapped = map(style);
|
|
1493
1888
|
if (mapped !== style) {
|
|
@@ -1520,14 +1915,55 @@ function applyContextRecolor(ctx, map) {
|
|
|
1520
1915
|
record[name] = withMappedStyle(target[name], "strokeStyle");
|
|
1521
1916
|
for (const name of GRADIENT_METHODS)
|
|
1522
1917
|
record[name] = withMappedStops(target[name]);
|
|
1523
|
-
record.drawImage =
|
|
1524
|
-
const
|
|
1918
|
+
record.drawImage = withImageHandling(target.drawImage);
|
|
1919
|
+
const pristinePutImageData = target.putImageData;
|
|
1920
|
+
record.putImageData = function(...args) {
|
|
1921
|
+
paintSerial++;
|
|
1922
|
+
return pristinePutImageData.apply(this, args);
|
|
1923
|
+
};
|
|
1924
|
+
const pristineClearRect = target.clearRect;
|
|
1925
|
+
record.clearRect = function(...args) {
|
|
1926
|
+
paintSerial++;
|
|
1927
|
+
return pristineClearRect.apply(this, args);
|
|
1928
|
+
};
|
|
1929
|
+
record.save = function() {
|
|
1930
|
+
clipFlags.push(clipFlags[clipFlags.length - 1]);
|
|
1931
|
+
return pristineSave.call(this);
|
|
1932
|
+
};
|
|
1933
|
+
record.restore = function() {
|
|
1934
|
+
if (clipFlags.length > 1) clipFlags.pop();
|
|
1935
|
+
return pristineRestore.call(this);
|
|
1936
|
+
};
|
|
1937
|
+
record.clip = function(...args) {
|
|
1938
|
+
clipFlags[clipFlags.length - 1] = true;
|
|
1939
|
+
return pristineClip.apply(this, args);
|
|
1940
|
+
};
|
|
1941
|
+
const finalizeBlankScanPage = () => {
|
|
1942
|
+
if (pendingTiles.length === 0) return;
|
|
1943
|
+
prunePendingToCleanSuffix();
|
|
1944
|
+
if (pendingTiles.length === 0) return;
|
|
1945
|
+
if (!pendingTriggerReady(false)) return;
|
|
1946
|
+
for (const pending of pendingTiles) {
|
|
1947
|
+
invertTileRemainder(target, pending, coveredBounds);
|
|
1948
|
+
coveredBounds.push(pending.bounds);
|
|
1949
|
+
}
|
|
1950
|
+
pendingTiles.length = 0;
|
|
1951
|
+
pendingTileArea = 0;
|
|
1952
|
+
pendingHasInk = false;
|
|
1953
|
+
};
|
|
1954
|
+
const cleanup = (finalizeRender = false) => {
|
|
1525
1955
|
if (target[RECOLOR_CLEANUP] !== cleanup) return;
|
|
1956
|
+
if (finalizeRender) finalizeBlankScanPage();
|
|
1526
1957
|
for (const name of [
|
|
1527
1958
|
...FILL_METHODS,
|
|
1528
1959
|
...STROKE_METHODS,
|
|
1529
1960
|
...GRADIENT_METHODS,
|
|
1530
|
-
"drawImage"
|
|
1961
|
+
"drawImage",
|
|
1962
|
+
"putImageData",
|
|
1963
|
+
"clearRect",
|
|
1964
|
+
"save",
|
|
1965
|
+
"restore",
|
|
1966
|
+
"clip"
|
|
1531
1967
|
])
|
|
1532
1968
|
delete record[name];
|
|
1533
1969
|
delete target[RECOLOR_CLEANUP];
|
|
@@ -1792,7 +2228,9 @@ var useCanvasLayer = ({ background }) => {
|
|
|
1792
2228
|
};
|
|
1793
2229
|
let restoreRecolor = null;
|
|
1794
2230
|
if (recolor) {
|
|
1795
|
-
restoreRecolor = applyContextRecolor(renderCtx, recolor
|
|
2231
|
+
restoreRecolor = applyContextRecolor(renderCtx, recolor, {
|
|
2232
|
+
pageArea: viewport.width * viewport.height
|
|
2233
|
+
});
|
|
1796
2234
|
} else {
|
|
1797
2235
|
removeContextRecolor(renderCtx);
|
|
1798
2236
|
}
|
|
@@ -1809,19 +2247,20 @@ var useCanvasLayer = ({ background }) => {
|
|
|
1809
2247
|
releaseBuffer();
|
|
1810
2248
|
throw error;
|
|
1811
2249
|
}
|
|
1812
|
-
renderingTask.promise.
|
|
1813
|
-
restoreRecolor?.();
|
|
1814
|
-
}).then(() => {
|
|
2250
|
+
renderingTask.promise.then(() => {
|
|
1815
2251
|
if (cancelled) {
|
|
2252
|
+
restoreRecolor?.();
|
|
1816
2253
|
releaseBuffer();
|
|
1817
2254
|
return;
|
|
1818
2255
|
}
|
|
1819
2256
|
const state = store.getState();
|
|
1820
2257
|
const currentRecolorKey = state.colorScheme === "dark" ? `dark:${state.darkModeColors.background},${state.darkModeColors.foreground}` : void 0;
|
|
1821
2258
|
if (currentRecolorKey !== recolorKey) {
|
|
2259
|
+
restoreRecolor?.();
|
|
1822
2260
|
releaseBuffer();
|
|
1823
2261
|
return;
|
|
1824
2262
|
}
|
|
2263
|
+
restoreRecolor?.(true);
|
|
1825
2264
|
if (useBuffer) {
|
|
1826
2265
|
applyBackingSize();
|
|
1827
2266
|
context.drawImage(buffer, 0, 0);
|
|
@@ -1853,6 +2292,7 @@ var useCanvasLayer = ({ background }) => {
|
|
|
1853
2292
|
releaseBuffer();
|
|
1854
2293
|
}
|
|
1855
2294
|
}).catch((error) => {
|
|
2295
|
+
restoreRecolor?.();
|
|
1856
2296
|
releaseBuffer();
|
|
1857
2297
|
if (cancelled) return;
|
|
1858
2298
|
if (error?.name === "RenderingCancelledException") return;
|
|
@@ -2165,7 +2605,9 @@ var useDetailCanvasLayer = ({
|
|
|
2165
2605
|
const detailViewport = pdfPageProxy.getViewport({
|
|
2166
2606
|
scale: effectiveScale
|
|
2167
2607
|
});
|
|
2168
|
-
|
|
2608
|
+
const restoreRecolor = recolor ? applyContextRecolor(bufferCtx, recolor, {
|
|
2609
|
+
pageArea: detailViewport.width * detailViewport.height
|
|
2610
|
+
}) : null;
|
|
2169
2611
|
const releaseBuffer = () => {
|
|
2170
2612
|
buffer.width = 0;
|
|
2171
2613
|
buffer.height = 0;
|
|
@@ -2193,6 +2635,7 @@ var useDetailCanvasLayer = ({
|
|
|
2193
2635
|
const state = store.getState();
|
|
2194
2636
|
const currentRecolorKey = state.colorScheme === "dark" ? `dark:${state.darkModeColors.background},${state.darkModeColors.foreground}` : "light";
|
|
2195
2637
|
if (currentRecolorKey !== recolorKey) return;
|
|
2638
|
+
restoreRecolor?.(true);
|
|
2196
2639
|
detailCanvas.width = actualWidth;
|
|
2197
2640
|
detailCanvas.height = actualHeight;
|
|
2198
2641
|
detailCanvas.style.cssText = `${detailBaseStyle};display:block;opacity:1;width:${cssWidth}px;height:${cssHeight}px;transform-origin:center center;transform:translate(${rectLeft * zoom}px,${rectTop * zoom}px)`;
|
|
@@ -2264,6 +2707,7 @@ var useDetailCanvasLayer = ({
|
|
|
2264
2707
|
clearTimeout(renderTimeoutId);
|
|
2265
2708
|
}
|
|
2266
2709
|
void renderingTask?.cancel();
|
|
2710
|
+
renderingTask = null;
|
|
2267
2711
|
};
|
|
2268
2712
|
}, [
|
|
2269
2713
|
pdfPageProxy,
|
|
@@ -6651,6 +7095,7 @@ var useThumbnail = (pageNumber, config = {}) => {
|
|
|
6651
7095
|
useEffect(() => {
|
|
6652
7096
|
if (!isVisible && renderedProxyRef.current !== pageProxy) return;
|
|
6653
7097
|
renderedProxyRef.current = pageProxy;
|
|
7098
|
+
let stale = false;
|
|
6654
7099
|
const renderThumbnail = async () => {
|
|
6655
7100
|
const canvas2 = canvasRef.current;
|
|
6656
7101
|
if (!canvas2 || !pageProxy) return;
|
|
@@ -6671,7 +7116,9 @@ var useThumbnail = (pageNumber, config = {}) => {
|
|
|
6671
7116
|
if (!context) return;
|
|
6672
7117
|
let restoreRecolor = null;
|
|
6673
7118
|
if (recolor) {
|
|
6674
|
-
restoreRecolor = applyContextRecolor(context, recolor
|
|
7119
|
+
restoreRecolor = applyContextRecolor(context, recolor, {
|
|
7120
|
+
pageArea: scaledViewport.width * scaledViewport.height
|
|
7121
|
+
});
|
|
6675
7122
|
} else {
|
|
6676
7123
|
removeContextRecolor(context);
|
|
6677
7124
|
}
|
|
@@ -6683,6 +7130,7 @@ var useThumbnail = (pageNumber, config = {}) => {
|
|
|
6683
7130
|
});
|
|
6684
7131
|
renderTaskRef.current = renderTask;
|
|
6685
7132
|
await renderTask.promise;
|
|
7133
|
+
restoreRecolor?.(!stale);
|
|
6686
7134
|
} finally {
|
|
6687
7135
|
restoreRecolor?.();
|
|
6688
7136
|
}
|
|
@@ -6697,6 +7145,7 @@ var useThumbnail = (pageNumber, config = {}) => {
|
|
|
6697
7145
|
renderThumbnail();
|
|
6698
7146
|
const canvas = canvasRef.current;
|
|
6699
7147
|
return () => {
|
|
7148
|
+
stale = true;
|
|
6700
7149
|
renderTaskRef.current?.cancel();
|
|
6701
7150
|
if (canvas) {
|
|
6702
7151
|
canvas.width = 1;
|
package/package.json
CHANGED
|
@@ -1,103 +1,104 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
2
|
+
"name": "@anaralabs/lector",
|
|
3
|
+
"version": "3.14.1",
|
|
4
|
+
"description": "A headless PDF viewer for React",
|
|
5
|
+
"author": "andrewdr (https://github.com/andrewdoro)",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"module": "dist/index.js",
|
|
10
|
+
"types": "dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"require": "./esm-only.cjs"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"dev": "tsup --watch --onSuccess 'yalc push'",
|
|
26
|
+
"prebuild": "rm -rf dist",
|
|
27
|
+
"build": "tsup",
|
|
28
|
+
"lint": "biome check .",
|
|
29
|
+
"format": "biome format --write .",
|
|
30
|
+
"postbuild": "size-limit --json > size.json",
|
|
31
|
+
"test": "pnpm run '/^test:/'",
|
|
32
|
+
"test:unit": "vitest run",
|
|
33
|
+
"test:size": "size-limit",
|
|
34
|
+
"prepack": "./scripts/prepack.sh",
|
|
35
|
+
"prepare": "tsup"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@microsoft/api-extractor": "^7.48.1",
|
|
39
|
+
"@size-limit/preset-small-lib": "^11.1.6",
|
|
40
|
+
"@testing-library/react": "^16.0.0",
|
|
41
|
+
"@types/node": "^22.9.0",
|
|
42
|
+
"@types/react": "^19.0.1",
|
|
43
|
+
"@types/react-dom": "^19.0.2",
|
|
44
|
+
"@use-gesture/react": "^10.3.1",
|
|
45
|
+
"@vitejs/plugin-react": "^4.3.4",
|
|
46
|
+
"@vitest/browser": "^3.2.6",
|
|
47
|
+
"clsx": "^2.1.1",
|
|
48
|
+
"pdfjs-dist": "^5.5.207",
|
|
49
|
+
"playwright": "^1.45.3",
|
|
50
|
+
"react": "^19.1.1",
|
|
51
|
+
"size-limit": "^11.1.6",
|
|
52
|
+
"tsc-alias": "^1.8.10",
|
|
53
|
+
"tslib": "^2.6.3",
|
|
54
|
+
"tsup": "^8.3.5",
|
|
55
|
+
"typescript": "^5.5.3",
|
|
56
|
+
"vite": "^6.4.3",
|
|
57
|
+
"vitest": "^3.2.6",
|
|
58
|
+
"webdriverio": "^8.39.1"
|
|
59
|
+
},
|
|
60
|
+
"peerDependencies": {
|
|
61
|
+
"pdfjs-dist": "^5.5.207",
|
|
62
|
+
"react": ">=19"
|
|
63
|
+
},
|
|
64
|
+
"dependencies": {
|
|
65
|
+
"@floating-ui/react": "^0.26.28",
|
|
66
|
+
"@radix-ui/react-slot": "^1.1.0",
|
|
67
|
+
"@tanstack/react-virtual": "^3.10.9",
|
|
68
|
+
"react-dom": "^19.1.1",
|
|
69
|
+
"use-debounce": "^10.0.4",
|
|
70
|
+
"uuid": "^11.1.1",
|
|
71
|
+
"zustand": "^5.0.2"
|
|
72
|
+
},
|
|
73
|
+
"optionalDependencies": {
|
|
74
|
+
"@rollup/rollup-linux-x64-gnu": "^4.28.1"
|
|
75
|
+
},
|
|
76
|
+
"keywords": [
|
|
77
|
+
"pdf",
|
|
78
|
+
"react",
|
|
79
|
+
"headless",
|
|
80
|
+
"viewer",
|
|
81
|
+
"react-pdf",
|
|
82
|
+
"anaralabs",
|
|
83
|
+
"typescript"
|
|
84
|
+
],
|
|
85
|
+
"repository": {
|
|
86
|
+
"type": "git",
|
|
87
|
+
"url": "git+https://github.com/anaralabs/lector.git",
|
|
88
|
+
"directory": "packages/lector"
|
|
89
|
+
},
|
|
90
|
+
"bugs": {
|
|
91
|
+
"url": "https://github.com/anaralabs/lector/issues"
|
|
92
|
+
},
|
|
93
|
+
"homepage": "https://github.com/anaralabs/lector",
|
|
94
|
+
"size-limit": [
|
|
95
|
+
{
|
|
96
|
+
"name": "Client",
|
|
97
|
+
"path": "dist/index.js",
|
|
98
|
+
"limit": "150 kB",
|
|
99
|
+
"ignore": [
|
|
100
|
+
"react"
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
]
|
|
103
104
|
}
|