@anaralabs/lector 3.14.0 → 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.
@@ -147,9 +147,11 @@ declare interface AnnotationTooltipProps {
147
147
  * (policy in ./scan-invert.ts).
148
148
  *
149
149
  * Wrapping installs own properties on the context instance (the prototype is
150
- * 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.
151
153
  */
152
- export declare function applyContextRecolor(ctx: CanvasRenderingContext2D, map: RenderColorMap, options?: RecolorContextOptions): () => void;
154
+ export declare function applyContextRecolor(ctx: CanvasRenderingContext2D, map: RenderColorMap, options?: RecolorContextOptions): (finalizeRender?: boolean) => void;
153
155
 
154
156
  /**
155
157
  * Calculates the highlight rectangles for a given text match.
package/dist/index.js CHANGED
@@ -1726,8 +1726,9 @@ function applyContextRecolor(ctx, map, options) {
1726
1726
  );
1727
1727
  }
1728
1728
  };
1729
- const pendingTriggerReady = () => {
1730
- if (pendingTileArea < SCAN_COVERAGE_MIN || !pendingHasInk) return false;
1729
+ const pendingTriggerReady = (requireInk = true) => {
1730
+ if (pendingTileArea < SCAN_COVERAGE_MIN) return false;
1731
+ if (requireInk && !pendingHasInk) return false;
1731
1732
  const union = [
1732
1733
  Math.min(...pendingTiles.map((t) => t.bounds[0])),
1733
1734
  Math.min(...pendingTiles.map((t) => t.bounds[1])),
@@ -1738,14 +1739,18 @@ function applyContextRecolor(ctx, map, options) {
1738
1739
  if (unionArea <= 0) return false;
1739
1740
  return pendingTileArea * (pageArea ?? 0) / unionArea >= SCAN_TILE_DENSITY_MIN;
1740
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
+ };
1741
1751
  const flushPendingTiles = (self) => {
1742
1752
  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;
1753
+ prunePendingToCleanSuffix();
1749
1754
  if (pendingTiles.length === 0 || !pendingTriggerReady()) return;
1750
1755
  }
1751
1756
  for (const pending of pendingTiles) {
@@ -1933,8 +1938,22 @@ function applyContextRecolor(ctx, map, options) {
1933
1938
  clipFlags[clipFlags.length - 1] = true;
1934
1939
  return pristineClip.apply(this, args);
1935
1940
  };
1936
- const cleanup = () => {
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) => {
1937
1955
  if (target[RECOLOR_CLEANUP] !== cleanup) return;
1956
+ if (finalizeRender) finalizeBlankScanPage();
1938
1957
  for (const name of [
1939
1958
  ...FILL_METHODS,
1940
1959
  ...STROKE_METHODS,
@@ -2228,19 +2247,20 @@ var useCanvasLayer = ({ background }) => {
2228
2247
  releaseBuffer();
2229
2248
  throw error;
2230
2249
  }
2231
- renderingTask.promise.finally(() => {
2232
- restoreRecolor?.();
2233
- }).then(() => {
2250
+ renderingTask.promise.then(() => {
2234
2251
  if (cancelled) {
2252
+ restoreRecolor?.();
2235
2253
  releaseBuffer();
2236
2254
  return;
2237
2255
  }
2238
2256
  const state = store.getState();
2239
2257
  const currentRecolorKey = state.colorScheme === "dark" ? `dark:${state.darkModeColors.background},${state.darkModeColors.foreground}` : void 0;
2240
2258
  if (currentRecolorKey !== recolorKey) {
2259
+ restoreRecolor?.();
2241
2260
  releaseBuffer();
2242
2261
  return;
2243
2262
  }
2263
+ restoreRecolor?.(true);
2244
2264
  if (useBuffer) {
2245
2265
  applyBackingSize();
2246
2266
  context.drawImage(buffer, 0, 0);
@@ -2272,6 +2292,7 @@ var useCanvasLayer = ({ background }) => {
2272
2292
  releaseBuffer();
2273
2293
  }
2274
2294
  }).catch((error) => {
2295
+ restoreRecolor?.();
2275
2296
  releaseBuffer();
2276
2297
  if (cancelled) return;
2277
2298
  if (error?.name === "RenderingCancelledException") return;
@@ -2584,10 +2605,9 @@ var useDetailCanvasLayer = ({
2584
2605
  const detailViewport = pdfPageProxy.getViewport({
2585
2606
  scale: effectiveScale
2586
2607
  });
2587
- if (recolor)
2588
- applyContextRecolor(bufferCtx, recolor, {
2589
- pageArea: detailViewport.width * detailViewport.height
2590
- });
2608
+ const restoreRecolor = recolor ? applyContextRecolor(bufferCtx, recolor, {
2609
+ pageArea: detailViewport.width * detailViewport.height
2610
+ }) : null;
2591
2611
  const releaseBuffer = () => {
2592
2612
  buffer.width = 0;
2593
2613
  buffer.height = 0;
@@ -2615,6 +2635,7 @@ var useDetailCanvasLayer = ({
2615
2635
  const state = store.getState();
2616
2636
  const currentRecolorKey = state.colorScheme === "dark" ? `dark:${state.darkModeColors.background},${state.darkModeColors.foreground}` : "light";
2617
2637
  if (currentRecolorKey !== recolorKey) return;
2638
+ restoreRecolor?.(true);
2618
2639
  detailCanvas.width = actualWidth;
2619
2640
  detailCanvas.height = actualHeight;
2620
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)`;
@@ -2686,6 +2707,7 @@ var useDetailCanvasLayer = ({
2686
2707
  clearTimeout(renderTimeoutId);
2687
2708
  }
2688
2709
  void renderingTask?.cancel();
2710
+ renderingTask = null;
2689
2711
  };
2690
2712
  }, [
2691
2713
  pdfPageProxy,
@@ -7073,6 +7095,7 @@ var useThumbnail = (pageNumber, config = {}) => {
7073
7095
  useEffect(() => {
7074
7096
  if (!isVisible && renderedProxyRef.current !== pageProxy) return;
7075
7097
  renderedProxyRef.current = pageProxy;
7098
+ let stale = false;
7076
7099
  const renderThumbnail = async () => {
7077
7100
  const canvas2 = canvasRef.current;
7078
7101
  if (!canvas2 || !pageProxy) return;
@@ -7107,6 +7130,7 @@ var useThumbnail = (pageNumber, config = {}) => {
7107
7130
  });
7108
7131
  renderTaskRef.current = renderTask;
7109
7132
  await renderTask.promise;
7133
+ restoreRecolor?.(!stale);
7110
7134
  } finally {
7111
7135
  restoreRecolor?.();
7112
7136
  }
@@ -7121,6 +7145,7 @@ var useThumbnail = (pageNumber, config = {}) => {
7121
7145
  renderThumbnail();
7122
7146
  const canvas = canvasRef.current;
7123
7147
  return () => {
7148
+ stale = true;
7124
7149
  renderTaskRef.current?.cancel();
7125
7150
  if (canvas) {
7126
7151
  canvas.width = 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anaralabs/lector",
3
- "version": "3.14.0",
3
+ "version": "3.14.1",
4
4
  "description": "A headless PDF viewer for React",
5
5
  "author": "andrewdr (https://github.com/andrewdoro)",
6
6
  "license": "MIT",