@anaralabs/lector 3.13.3 → 3.14.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 +88 -4
- package/dist/index.js +432 -8
- package/package.json +102 -101
|
@@ -138,16 +138,18 @@ 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
150
|
* never modified). Returns a cleanup that restores the pristine context.
|
|
149
151
|
*/
|
|
150
|
-
export declare function applyContextRecolor(ctx: CanvasRenderingContext2D, map: RenderColorMap): () => void;
|
|
152
|
+
export declare function applyContextRecolor(ctx: CanvasRenderingContext2D, map: RenderColorMap, options?: RecolorContextOptions): () => void;
|
|
151
153
|
|
|
152
154
|
/**
|
|
153
155
|
* Calculates the highlight rectangles for a given text match.
|
|
@@ -183,6 +185,8 @@ export declare const clamp: (value: number, minimum: number, maximum: number) =>
|
|
|
183
185
|
|
|
184
186
|
export declare function clampScaleForPage(targetScale: number, pageWidth: number, pageHeight: number, maxPixels?: number): number;
|
|
185
187
|
|
|
188
|
+
export declare function classifyScanPaper(source: CanvasImageSource, crop?: SourceCrop): ScanPaperClass | null;
|
|
189
|
+
|
|
186
190
|
declare type CollapsibleSelection = {
|
|
187
191
|
highlights: HighlightRect[];
|
|
188
192
|
underlines?: HighlightRect[];
|
|
@@ -331,6 +335,8 @@ export declare interface InitialPDFState {
|
|
|
331
335
|
|
|
332
336
|
export declare const IS_MOBILE_DEVICE: boolean;
|
|
333
337
|
|
|
338
|
+
export declare function isScanPaperSource(source: CanvasImageSource, crop?: SourceCrop): boolean;
|
|
339
|
+
|
|
334
340
|
export declare class LinkService {
|
|
335
341
|
_pdfDocumentProxy?: PDFDocumentProxy;
|
|
336
342
|
externalLinkEnabled: boolean;
|
|
@@ -463,6 +469,18 @@ export declare type PDFVirtualizer = Virtualizer<any, any>;
|
|
|
463
469
|
|
|
464
470
|
export declare const PreviousPage: () => void;
|
|
465
471
|
|
|
472
|
+
export declare interface RecolorContextOptions {
|
|
473
|
+
/**
|
|
474
|
+
* Full-page area in the context's device space (viewport.width × height).
|
|
475
|
+
* When provided, image draws that paper the page (≥ SCAN_COVERAGE_MIN of
|
|
476
|
+
* this area) with near-white pixels — scanned pages — are inverted toward
|
|
477
|
+
* the palette poles. Omit on pdf.js scratch canvases, whose device space
|
|
478
|
+
* is not the page; a page-covering scan painted through one is still
|
|
479
|
+
* caught when the scratch canvas is composed back onto a page context.
|
|
480
|
+
*/
|
|
481
|
+
pageArea?: number;
|
|
482
|
+
}
|
|
483
|
+
|
|
466
484
|
/**
|
|
467
485
|
* Removes any recolor wrapper installed on the context. For long-lived
|
|
468
486
|
* contexts (the visible canvas in the no-buffer fallback, thumbnails) that
|
|
@@ -488,6 +506,60 @@ export declare const Root: ForwardRefExoticComponent<Omit<Omit<HTMLProps<HTMLDiv
|
|
|
488
506
|
loader?: ReactNode;
|
|
489
507
|
}, "ref"> & RefAttributes<unknown>>;
|
|
490
508
|
|
|
509
|
+
/** Painted area relative to the page above which a draw "papers the page". */
|
|
510
|
+
export declare const SCAN_COVERAGE_MIN = 0.7;
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Minimum count of dark "ink" pixels in the downsample. A pure-white raster
|
|
514
|
+
* is an MRC background layer (its ink lives in a separate dark layer that
|
|
515
|
+
* must stay un-inverted to remain readable) or a blank page — skip both.
|
|
516
|
+
* A single genuine dark sample suffices so sparse pages (a signature, a few
|
|
517
|
+
* lines) still qualify; downsample averaging already erases isolated noise.
|
|
518
|
+
*/
|
|
519
|
+
export declare const SCAN_INK_MIN_PIXELS = 1;
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Minimum opaque fraction: scans are fully opaque; a cut-out PNG would let
|
|
523
|
+
* the inversion fill bleed into its transparent holes.
|
|
524
|
+
*/
|
|
525
|
+
export declare const SCAN_OPAQUE_MIN_FRACTION = 0.99;
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Maximum saturated fraction. Stamps and seals on real scans measure a few
|
|
529
|
+
* percent saturated; a rasterized slide with a colored chart measures far
|
|
530
|
+
* more — inverting it would render the chart as a negative.
|
|
531
|
+
*/
|
|
532
|
+
export declare const SCAN_SATURATED_MAX_FRACTION = 0.15;
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Minimum density (summed tile area / union bounding box area) for pending
|
|
536
|
+
* tiles to read as one contiguous scan: strips partitioning a page are dense,
|
|
537
|
+
* scattered white screenshots on a text page are not.
|
|
538
|
+
*/
|
|
539
|
+
export declare const SCAN_TILE_DENSITY_MIN = 0.85;
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Minimum painted fraction for a draw to count as a scan tile: some raster
|
|
543
|
+
* PDFs encode a page as strips or grids (8×8 grids put single tiles under
|
|
544
|
+
* 2%) whose areas only paper the page in sum. The floor only excludes
|
|
545
|
+
* degenerate sliver spam — the density, saturation, page-level ink and
|
|
546
|
+
* paint-serial guards do the real false-positive filtering.
|
|
547
|
+
*/
|
|
548
|
+
export declare const SCAN_TILE_MIN_FRACTION = 0.005;
|
|
549
|
+
|
|
550
|
+
/** Fraction of near-white opaque pixels above which a source is scan paper. */
|
|
551
|
+
export declare const SCAN_WHITE_MIN_FRACTION = 0.6;
|
|
552
|
+
|
|
553
|
+
export declare interface ScanPaperClass {
|
|
554
|
+
/**
|
|
555
|
+
* Whether the paper carries dark ink pixels. A pure-white raster is an
|
|
556
|
+
* MRC background layer (its ink lives in a separate dark layer that must
|
|
557
|
+
* stay readable) or a blank margin tile — pages only invert when ink is
|
|
558
|
+
* present somewhere, but blank tiles still count toward tiled coverage.
|
|
559
|
+
*/
|
|
560
|
+
inked: boolean;
|
|
561
|
+
}
|
|
562
|
+
|
|
491
563
|
export declare const Search: ({ children, loading }: SearchProps) => ReactNode;
|
|
492
564
|
|
|
493
565
|
declare interface SearchOptions {
|
|
@@ -524,6 +596,18 @@ declare interface SelectionTooltipProps {
|
|
|
524
596
|
|
|
525
597
|
export declare type Source = string | URL | TypedArray | ArrayBuffer | DocumentInitParameters;
|
|
526
598
|
|
|
599
|
+
export declare interface SourceCrop {
|
|
600
|
+
sx: number;
|
|
601
|
+
sy: number;
|
|
602
|
+
sw: number;
|
|
603
|
+
sh: number;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
export declare function sourceSize(source: CanvasImageSource): {
|
|
607
|
+
width: number;
|
|
608
|
+
height: number;
|
|
609
|
+
} | null;
|
|
610
|
+
|
|
527
611
|
export declare const subscribeToViewportInvalidation: (viewport: HTMLDivElement, callback: ViewportCallback) => () => void;
|
|
528
612
|
|
|
529
613
|
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,325 @@ 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 = () => {
|
|
1730
|
+
if (pendingTileArea < SCAN_COVERAGE_MIN || !pendingHasInk) return false;
|
|
1731
|
+
const union = [
|
|
1732
|
+
Math.min(...pendingTiles.map((t) => t.bounds[0])),
|
|
1733
|
+
Math.min(...pendingTiles.map((t) => t.bounds[1])),
|
|
1734
|
+
Math.max(...pendingTiles.map((t) => t.bounds[2])),
|
|
1735
|
+
Math.max(...pendingTiles.map((t) => t.bounds[3]))
|
|
1736
|
+
];
|
|
1737
|
+
const unionArea = boundsArea(union);
|
|
1738
|
+
if (unionArea <= 0) return false;
|
|
1739
|
+
return pendingTileArea * (pageArea ?? 0) / unionArea >= SCAN_TILE_DENSITY_MIN;
|
|
1740
|
+
};
|
|
1741
|
+
const flushPendingTiles = (self) => {
|
|
1742
|
+
if (paintSerial !== pendingBaselineSerial) {
|
|
1743
|
+
const kept = pendingTiles.filter((t) => t.serial === paintSerial);
|
|
1744
|
+
pendingTiles.length = 0;
|
|
1745
|
+
pendingTiles.push(...kept);
|
|
1746
|
+
pendingTileArea = kept.reduce((sum, t) => sum + t.area, 0);
|
|
1747
|
+
pendingHasInk = kept.some((t) => t.inked);
|
|
1748
|
+
pendingBaselineSerial = paintSerial;
|
|
1749
|
+
if (pendingTiles.length === 0 || !pendingTriggerReady()) return;
|
|
1750
|
+
}
|
|
1751
|
+
for (const pending of pendingTiles) {
|
|
1752
|
+
invertTileRemainder(self, pending, coveredBounds);
|
|
1753
|
+
coveredBounds.push(pending.bounds);
|
|
1754
|
+
}
|
|
1755
|
+
pendingTiles.length = 0;
|
|
1756
|
+
pendingTileArea = 0;
|
|
1757
|
+
pendingHasInk = false;
|
|
1758
|
+
};
|
|
1759
|
+
const handleScanCandidate = (self, candidate) => {
|
|
1760
|
+
const matrix = self.getTransform();
|
|
1761
|
+
const bounds = tileBounds(matrix, candidate.rect);
|
|
1762
|
+
if (candidate.areaFraction >= SCAN_COVERAGE_MIN && !candidate.clipped) {
|
|
1763
|
+
for (let i = coveredBounds.length - 1; i >= 0; i--) {
|
|
1764
|
+
const covered = coveredBounds[i];
|
|
1765
|
+
const intersection = [
|
|
1766
|
+
Math.max(bounds[0], covered[0]),
|
|
1767
|
+
Math.max(bounds[1], covered[1]),
|
|
1768
|
+
Math.min(bounds[2], covered[2]),
|
|
1769
|
+
Math.min(bounds[3], covered[3])
|
|
1770
|
+
];
|
|
1771
|
+
const coveredArea = boundsArea(covered);
|
|
1772
|
+
if (coveredArea > 0 && boundsArea(intersection) >= 0.8 * coveredArea) {
|
|
1773
|
+
coveredBounds.splice(i, 1);
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
if (candidate.areaFraction >= SCAN_COVERAGE_MIN && candidate.clipped) {
|
|
1778
|
+
if (candidate.inked) {
|
|
1779
|
+
invertScanRect(self, candidate.rect, matrix);
|
|
1780
|
+
}
|
|
1781
|
+
pendingTiles.length = 0;
|
|
1782
|
+
pendingTileArea = 0;
|
|
1783
|
+
pendingHasInk = false;
|
|
1784
|
+
return;
|
|
1785
|
+
}
|
|
1786
|
+
if (coveredBounds.length > 0 && candidate.areaFraction <= 0.2) {
|
|
1787
|
+
const candidateArea = boundsArea(bounds);
|
|
1788
|
+
let insideArea = 0;
|
|
1789
|
+
for (const covered of coveredBounds) {
|
|
1790
|
+
const intersection = [
|
|
1791
|
+
Math.max(bounds[0], covered[0]),
|
|
1792
|
+
Math.max(bounds[1], covered[1]),
|
|
1793
|
+
Math.min(bounds[2], covered[2]),
|
|
1794
|
+
Math.min(bounds[3], covered[3])
|
|
1795
|
+
];
|
|
1796
|
+
insideArea += boundsArea(intersection);
|
|
1797
|
+
}
|
|
1798
|
+
if (candidateArea > 0 && insideArea >= 0.8 * candidateArea) return;
|
|
1799
|
+
}
|
|
1800
|
+
if (coveredBounds.length > 0 && !candidate.clipped) {
|
|
1801
|
+
invertScanRect(self, candidate.rect, matrix);
|
|
1802
|
+
coveredBounds.push(bounds);
|
|
1803
|
+
return;
|
|
1804
|
+
}
|
|
1805
|
+
if (candidate.areaFraction >= SCAN_COVERAGE_MIN && candidate.inked) {
|
|
1806
|
+
invertScanRect(self, candidate.rect, matrix);
|
|
1807
|
+
coveredBounds.push(bounds);
|
|
1808
|
+
flushPendingTiles(self);
|
|
1809
|
+
return;
|
|
1810
|
+
}
|
|
1811
|
+
if (candidate.clipped) {
|
|
1812
|
+
paintSerial++;
|
|
1813
|
+
return;
|
|
1814
|
+
}
|
|
1815
|
+
if (candidate.areaFraction >= SCAN_COVERAGE_MIN) {
|
|
1816
|
+
for (let i = pendingTiles.length - 1; i >= 0; i--) {
|
|
1817
|
+
const pending = pendingTiles[i];
|
|
1818
|
+
const intersection = [
|
|
1819
|
+
Math.max(bounds[0], pending.bounds[0]),
|
|
1820
|
+
Math.max(bounds[1], pending.bounds[1]),
|
|
1821
|
+
Math.min(bounds[2], pending.bounds[2]),
|
|
1822
|
+
Math.min(bounds[3], pending.bounds[3])
|
|
1823
|
+
];
|
|
1824
|
+
const pendingArea = boundsArea(pending.bounds);
|
|
1825
|
+
if (pendingArea > 0 && boundsArea(intersection) >= 0.8 * pendingArea) {
|
|
1826
|
+
pendingTiles.splice(i, 1);
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
pendingTileArea = pendingTiles.reduce((sum, t) => sum + t.area, 0);
|
|
1830
|
+
pendingHasInk = pendingTiles.some((t) => t.inked);
|
|
1831
|
+
}
|
|
1832
|
+
let contributedArea = candidate.areaFraction;
|
|
1833
|
+
if (isAxisAligned(matrix) && pageArea) {
|
|
1834
|
+
let pieces = [bounds];
|
|
1835
|
+
for (const pending of pendingTiles) {
|
|
1836
|
+
pieces = subtractBounds(pieces, pending.bounds);
|
|
1837
|
+
}
|
|
1838
|
+
contributedArea = pieces.reduce((sum, piece) => sum + boundsArea(piece), 0) / pageArea;
|
|
1839
|
+
} else if (overlapsAny(
|
|
1840
|
+
bounds,
|
|
1841
|
+
pendingTiles.map((t) => t.bounds)
|
|
1842
|
+
)) {
|
|
1843
|
+
return;
|
|
1844
|
+
}
|
|
1845
|
+
if (pendingTiles.length === 0) pendingBaselineSerial = paintSerial;
|
|
1846
|
+
pendingTiles.push({
|
|
1847
|
+
rect: candidate.rect,
|
|
1848
|
+
matrix,
|
|
1849
|
+
bounds,
|
|
1850
|
+
area: contributedArea,
|
|
1851
|
+
inked: candidate.inked,
|
|
1852
|
+
serial: paintSerial
|
|
1853
|
+
});
|
|
1854
|
+
pendingTileArea += contributedArea;
|
|
1855
|
+
pendingHasInk ||= candidate.inked;
|
|
1856
|
+
if (!pendingTriggerReady()) return;
|
|
1857
|
+
flushPendingTiles(self);
|
|
1858
|
+
};
|
|
1859
|
+
const withImageHandling = (original) => function(...args) {
|
|
1478
1860
|
if (mappedWhiteLuma !== null && mappedBlackLuma !== null && this.globalCompositeOperation === "destination-in" && typeof this.filter === "string" && this.filter.includes("luminosity")) {
|
|
1479
1861
|
const source = args[0];
|
|
1480
1862
|
const sourcePainted = (typeof HTMLCanvasElement !== "undefined" && source instanceof HTMLCanvasElement || typeof OffscreenCanvas !== "undefined" && source instanceof OffscreenCanvas) && source.getContext("2d")?.[RECOLOR_PAINTED] === true;
|
|
1481
1863
|
const corrected = sourcePainted ? correctLuminosityMask(source, mappedWhiteLuma, mappedBlackLuma) : null;
|
|
1864
|
+
paintSerial++;
|
|
1482
1865
|
if (corrected) {
|
|
1483
1866
|
const next = [corrected, ...args.slice(1)];
|
|
1484
1867
|
return original.apply(this, next);
|
|
1485
1868
|
}
|
|
1869
|
+
return original.apply(this, args);
|
|
1486
1870
|
}
|
|
1487
|
-
|
|
1871
|
+
const candidate = scanCandidate(this, args);
|
|
1872
|
+
if (!candidate) paintSerial++;
|
|
1873
|
+
const result = original.apply(this, args);
|
|
1874
|
+
if (candidate) handleScanCandidate(this, candidate);
|
|
1875
|
+
return result;
|
|
1488
1876
|
};
|
|
1489
1877
|
const withMappedStyle = (original, styleProp) => function(...args) {
|
|
1490
1878
|
const style = this[styleProp];
|
|
1879
|
+
const invisible = this.globalCompositeOperation === "source-over" && (this.globalAlpha === 0 || style === "transparent" || typeof style === "string" && style.startsWith("rgba(") && style.endsWith(", 0)"));
|
|
1880
|
+
if (!invisible) paintSerial++;
|
|
1491
1881
|
if (typeof style === "string") {
|
|
1492
1882
|
const mapped = map(style);
|
|
1493
1883
|
if (mapped !== style) {
|
|
@@ -1520,14 +1910,41 @@ function applyContextRecolor(ctx, map) {
|
|
|
1520
1910
|
record[name] = withMappedStyle(target[name], "strokeStyle");
|
|
1521
1911
|
for (const name of GRADIENT_METHODS)
|
|
1522
1912
|
record[name] = withMappedStops(target[name]);
|
|
1523
|
-
record.drawImage =
|
|
1913
|
+
record.drawImage = withImageHandling(target.drawImage);
|
|
1914
|
+
const pristinePutImageData = target.putImageData;
|
|
1915
|
+
record.putImageData = function(...args) {
|
|
1916
|
+
paintSerial++;
|
|
1917
|
+
return pristinePutImageData.apply(this, args);
|
|
1918
|
+
};
|
|
1919
|
+
const pristineClearRect = target.clearRect;
|
|
1920
|
+
record.clearRect = function(...args) {
|
|
1921
|
+
paintSerial++;
|
|
1922
|
+
return pristineClearRect.apply(this, args);
|
|
1923
|
+
};
|
|
1924
|
+
record.save = function() {
|
|
1925
|
+
clipFlags.push(clipFlags[clipFlags.length - 1]);
|
|
1926
|
+
return pristineSave.call(this);
|
|
1927
|
+
};
|
|
1928
|
+
record.restore = function() {
|
|
1929
|
+
if (clipFlags.length > 1) clipFlags.pop();
|
|
1930
|
+
return pristineRestore.call(this);
|
|
1931
|
+
};
|
|
1932
|
+
record.clip = function(...args) {
|
|
1933
|
+
clipFlags[clipFlags.length - 1] = true;
|
|
1934
|
+
return pristineClip.apply(this, args);
|
|
1935
|
+
};
|
|
1524
1936
|
const cleanup = () => {
|
|
1525
1937
|
if (target[RECOLOR_CLEANUP] !== cleanup) return;
|
|
1526
1938
|
for (const name of [
|
|
1527
1939
|
...FILL_METHODS,
|
|
1528
1940
|
...STROKE_METHODS,
|
|
1529
1941
|
...GRADIENT_METHODS,
|
|
1530
|
-
"drawImage"
|
|
1942
|
+
"drawImage",
|
|
1943
|
+
"putImageData",
|
|
1944
|
+
"clearRect",
|
|
1945
|
+
"save",
|
|
1946
|
+
"restore",
|
|
1947
|
+
"clip"
|
|
1531
1948
|
])
|
|
1532
1949
|
delete record[name];
|
|
1533
1950
|
delete target[RECOLOR_CLEANUP];
|
|
@@ -1792,7 +2209,9 @@ var useCanvasLayer = ({ background }) => {
|
|
|
1792
2209
|
};
|
|
1793
2210
|
let restoreRecolor = null;
|
|
1794
2211
|
if (recolor) {
|
|
1795
|
-
restoreRecolor = applyContextRecolor(renderCtx, recolor
|
|
2212
|
+
restoreRecolor = applyContextRecolor(renderCtx, recolor, {
|
|
2213
|
+
pageArea: viewport.width * viewport.height
|
|
2214
|
+
});
|
|
1796
2215
|
} else {
|
|
1797
2216
|
removeContextRecolor(renderCtx);
|
|
1798
2217
|
}
|
|
@@ -2165,7 +2584,10 @@ var useDetailCanvasLayer = ({
|
|
|
2165
2584
|
const detailViewport = pdfPageProxy.getViewport({
|
|
2166
2585
|
scale: effectiveScale
|
|
2167
2586
|
});
|
|
2168
|
-
if (recolor)
|
|
2587
|
+
if (recolor)
|
|
2588
|
+
applyContextRecolor(bufferCtx, recolor, {
|
|
2589
|
+
pageArea: detailViewport.width * detailViewport.height
|
|
2590
|
+
});
|
|
2169
2591
|
const releaseBuffer = () => {
|
|
2170
2592
|
buffer.width = 0;
|
|
2171
2593
|
buffer.height = 0;
|
|
@@ -6671,7 +7093,9 @@ var useThumbnail = (pageNumber, config = {}) => {
|
|
|
6671
7093
|
if (!context) return;
|
|
6672
7094
|
let restoreRecolor = null;
|
|
6673
7095
|
if (recolor) {
|
|
6674
|
-
restoreRecolor = applyContextRecolor(context, recolor
|
|
7096
|
+
restoreRecolor = applyContextRecolor(context, recolor, {
|
|
7097
|
+
pageArea: scaledViewport.width * scaledViewport.height
|
|
7098
|
+
});
|
|
6675
7099
|
} else {
|
|
6676
7100
|
removeContextRecolor(context);
|
|
6677
7101
|
}
|
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.0",
|
|
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
|
}
|