canvu-react 0.4.70 → 0.4.71

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.
@@ -179,6 +179,37 @@ type RasterImageCanvasRenderRequest = {
179
179
  cameraZoom: number;
180
180
  devicePixelRatio: number;
181
181
  };
182
+ /**
183
+ * Bitmap-like source returned by `imageCanvasRendering.resolveCanvasSource`.
184
+ *
185
+ * Apps can render into an offscreen canvas, create an `ImageBitmap`, or wrap any
186
+ * other canvas-compatible source and let Canvu copy it into the visible canvas
187
+ * without changing the underlying SVG image URL.
188
+ */
189
+ type RasterImageCanvasSource = {
190
+ width: number;
191
+ height: number;
192
+ draw: (context: CanvasRenderingContext2D) => void;
193
+ close?: () => void;
194
+ };
195
+ /**
196
+ * Input passed to `imageCanvasRendering.resolveCanvasSource`.
197
+ *
198
+ * Use this for custom async rendering pipelines such as PDF page rasterization.
199
+ * Canvu keeps the previous canvas visible while this request resolves and aborts
200
+ * stale requests when the item leaves the viewport or a sharper target is queued.
201
+ */
202
+ type RasterImageCanvasSourceRequest = RasterImageCanvasRenderRequest & {
203
+ targetHref: string;
204
+ sourceKey: string;
205
+ signal: AbortSignal;
206
+ };
207
+ /**
208
+ * Maps an image item plus requested bitmap size to a custom canvas source.
209
+ *
210
+ * Return `null`/`undefined` to let Canvu fetch and decode the resolved URL.
211
+ */
212
+ type RasterImageCanvasSourceResolver = (request: RasterImageCanvasSourceRequest) => RasterImageCanvasSource | null | undefined | Promise<RasterImageCanvasSource | null | undefined>;
182
213
  /**
183
214
  * Resolved raster source for a zoom-aware canvas redraw.
184
215
  *
@@ -209,6 +240,7 @@ type RasterImageCanvasRenderTargetResolver = (request: RasterImageCanvasRenderRe
209
240
  type RasterImageCanvasRenderingOptions = {
210
241
  resolveSourceSize?: RasterImageCanvasSourceSizeResolver;
211
242
  resolveRenderTarget?: RasterImageCanvasRenderTargetResolver;
243
+ resolveCanvasSource?: RasterImageCanvasSourceResolver;
212
244
  devicePixelRatio?: number;
213
245
  pixelHeadroom?: number;
214
246
  maxPixelCount?: number;
@@ -216,4 +248,4 @@ type RasterImageCanvasRenderingOptions = {
216
248
  upscaleRedrawRatio?: number;
217
249
  };
218
250
 
219
- export type { RasterImageCanvasRenderingOptions as R, VectorViewportAssetKind as V, VectorViewportAssetStore as a, RasterImageCanvasRenderRequest as b, RasterImageCanvasRenderTarget as c, RasterImageCanvasRenderTargetResolver as d, RasterImageCanvasSourceSizeRequest as e, RasterImageCanvasSourceSizeResolver as f, VectorViewportAssetHydrationRequest as g, VectorViewportAssetResolveRequest as h, VectorViewportAssetResolveResult as i, VectorViewportAssetUploadRequest as j, VectorViewportAssetUploadResult as k };
251
+ export type { RasterImageCanvasRenderingOptions as R, VectorViewportAssetKind as V, VectorViewportAssetStore as a, RasterImageCanvasRenderRequest as b, RasterImageCanvasRenderTarget as c, RasterImageCanvasRenderTargetResolver as d, RasterImageCanvasSource as e, RasterImageCanvasSourceRequest as f, RasterImageCanvasSourceResolver as g, RasterImageCanvasSourceSizeRequest as h, RasterImageCanvasSourceSizeResolver as i, VectorViewportAssetHydrationRequest as j, VectorViewportAssetResolveRequest as k, VectorViewportAssetResolveResult as l, VectorViewportAssetUploadRequest as m, VectorViewportAssetUploadResult as n };
@@ -179,6 +179,37 @@ type RasterImageCanvasRenderRequest = {
179
179
  cameraZoom: number;
180
180
  devicePixelRatio: number;
181
181
  };
182
+ /**
183
+ * Bitmap-like source returned by `imageCanvasRendering.resolveCanvasSource`.
184
+ *
185
+ * Apps can render into an offscreen canvas, create an `ImageBitmap`, or wrap any
186
+ * other canvas-compatible source and let Canvu copy it into the visible canvas
187
+ * without changing the underlying SVG image URL.
188
+ */
189
+ type RasterImageCanvasSource = {
190
+ width: number;
191
+ height: number;
192
+ draw: (context: CanvasRenderingContext2D) => void;
193
+ close?: () => void;
194
+ };
195
+ /**
196
+ * Input passed to `imageCanvasRendering.resolveCanvasSource`.
197
+ *
198
+ * Use this for custom async rendering pipelines such as PDF page rasterization.
199
+ * Canvu keeps the previous canvas visible while this request resolves and aborts
200
+ * stale requests when the item leaves the viewport or a sharper target is queued.
201
+ */
202
+ type RasterImageCanvasSourceRequest = RasterImageCanvasRenderRequest & {
203
+ targetHref: string;
204
+ sourceKey: string;
205
+ signal: AbortSignal;
206
+ };
207
+ /**
208
+ * Maps an image item plus requested bitmap size to a custom canvas source.
209
+ *
210
+ * Return `null`/`undefined` to let Canvu fetch and decode the resolved URL.
211
+ */
212
+ type RasterImageCanvasSourceResolver = (request: RasterImageCanvasSourceRequest) => RasterImageCanvasSource | null | undefined | Promise<RasterImageCanvasSource | null | undefined>;
182
213
  /**
183
214
  * Resolved raster source for a zoom-aware canvas redraw.
184
215
  *
@@ -209,6 +240,7 @@ type RasterImageCanvasRenderTargetResolver = (request: RasterImageCanvasRenderRe
209
240
  type RasterImageCanvasRenderingOptions = {
210
241
  resolveSourceSize?: RasterImageCanvasSourceSizeResolver;
211
242
  resolveRenderTarget?: RasterImageCanvasRenderTargetResolver;
243
+ resolveCanvasSource?: RasterImageCanvasSourceResolver;
212
244
  devicePixelRatio?: number;
213
245
  pixelHeadroom?: number;
214
246
  maxPixelCount?: number;
@@ -216,4 +248,4 @@ type RasterImageCanvasRenderingOptions = {
216
248
  upscaleRedrawRatio?: number;
217
249
  };
218
250
 
219
- export type { RasterImageCanvasRenderingOptions as R, VectorViewportAssetKind as V, VectorViewportAssetStore as a, RasterImageCanvasRenderRequest as b, RasterImageCanvasRenderTarget as c, RasterImageCanvasRenderTargetResolver as d, RasterImageCanvasSourceSizeRequest as e, RasterImageCanvasSourceSizeResolver as f, VectorViewportAssetHydrationRequest as g, VectorViewportAssetResolveRequest as h, VectorViewportAssetResolveResult as i, VectorViewportAssetUploadRequest as j, VectorViewportAssetUploadResult as k };
251
+ export type { RasterImageCanvasRenderingOptions as R, VectorViewportAssetKind as V, VectorViewportAssetStore as a, RasterImageCanvasRenderRequest as b, RasterImageCanvasRenderTarget as c, RasterImageCanvasRenderTargetResolver as d, RasterImageCanvasSource as e, RasterImageCanvasSourceRequest as f, RasterImageCanvasSourceResolver as g, RasterImageCanvasSourceSizeRequest as h, RasterImageCanvasSourceSizeResolver as i, VectorViewportAssetHydrationRequest as j, VectorViewportAssetResolveRequest as k, VectorViewportAssetResolveResult as l, VectorViewportAssetUploadRequest as m, VectorViewportAssetUploadResult as n };
package/dist/react.cjs CHANGED
@@ -6896,6 +6896,7 @@ function resolveRasterImageCanvasRenderingOptions(options) {
6896
6896
  return {
6897
6897
  resolveSourceSize: options.resolveSourceSize,
6898
6898
  resolveRenderTarget: options.resolveRenderTarget,
6899
+ resolveCanvasSource: options.resolveCanvasSource,
6899
6900
  devicePixelRatio: toPositiveFiniteNumber(
6900
6901
  options.devicePixelRatio,
6901
6902
  fallbackDevicePixelRatio
@@ -7160,6 +7161,36 @@ function loadImageElement(href, signal) {
7160
7161
  image.src = href;
7161
7162
  });
7162
7163
  }
7164
+ function normalizeDecodedRasterImageSource(source) {
7165
+ if (!source) return null;
7166
+ const width = Math.max(1, Math.round(source.width));
7167
+ const height = Math.max(1, Math.round(source.height));
7168
+ if (!Number.isFinite(width) || !Number.isFinite(height)) return null;
7169
+ return {
7170
+ width,
7171
+ height,
7172
+ draw: source.draw,
7173
+ close: source.close ?? (() => {
7174
+ })
7175
+ };
7176
+ }
7177
+ async function resolveDecodedRasterImageSource({
7178
+ options,
7179
+ request,
7180
+ width,
7181
+ height,
7182
+ signal
7183
+ }) {
7184
+ if (options?.resolveCanvasSource) {
7185
+ const source = await options.resolveCanvasSource({
7186
+ ...request,
7187
+ signal
7188
+ });
7189
+ const decoded = normalizeDecodedRasterImageSource(source);
7190
+ if (decoded) return decoded;
7191
+ }
7192
+ return decodeRasterImage(request.targetHref, width, height, signal);
7193
+ }
7163
7194
  var SvgVectorRenderer = class {
7164
7195
  container;
7165
7196
  scene;
@@ -7392,7 +7423,20 @@ var SvgVectorRenderer = class {
7392
7423
  }
7393
7424
  const request = {
7394
7425
  itemHref: item.imageRasterHref,
7395
- target
7426
+ target,
7427
+ sourceRequest: {
7428
+ item,
7429
+ href: item.imageRasterHref,
7430
+ intrinsicSize: item.imageIntrinsicSize,
7431
+ sourceSize,
7432
+ contentRect: target.contentRect,
7433
+ targetSize: target.targetSize,
7434
+ viewportSize,
7435
+ cameraZoom: this.camera.zoom,
7436
+ devicePixelRatio: options.devicePixelRatio,
7437
+ targetHref: target.href,
7438
+ sourceKey: target.sourceKey
7439
+ }
7396
7440
  };
7397
7441
  if (rasterCanvas.abortController) {
7398
7442
  if (rasterCanvas.loadingItemHref !== item.imageRasterHref) {
@@ -7482,7 +7526,13 @@ var SvgVectorRenderer = class {
7482
7526
  rasterCanvas.loadingWidth = width;
7483
7527
  rasterCanvas.loadingHeight = height;
7484
7528
  rasterCanvas.queuedTarget = null;
7485
- decodeRasterImage(target.href, width, height, abortController.signal).then((decoded) => {
7529
+ resolveDecodedRasterImageSource({
7530
+ options: this.rasterImageCanvasRendering,
7531
+ request: request.sourceRequest,
7532
+ width,
7533
+ height,
7534
+ signal: abortController.signal
7535
+ }).then((decoded) => {
7486
7536
  if (abortController.signal.aborted || rasterCanvas.loadSequence !== sequence) {
7487
7537
  decoded.close();
7488
7538
  return;
@@ -8390,47 +8440,6 @@ function shallowEqualStringArray(a, b) {
8390
8440
  return true;
8391
8441
  }
8392
8442
 
8393
- // src/react/stroke-input.ts
8394
- function getPointerEventSamples(event) {
8395
- if (typeof event.getCoalescedEvents !== "function") {
8396
- return [event];
8397
- }
8398
- const samples = event.getCoalescedEvents();
8399
- return samples.length > 0 ? samples : [event];
8400
- }
8401
- var resolveInterpolatedPressure = (lastPoint, nextPoint, ratio) => {
8402
- if (lastPoint.pressure != null && nextPoint.pressure != null) {
8403
- return lastPoint.pressure + (nextPoint.pressure - lastPoint.pressure) * ratio;
8404
- }
8405
- return ratio === 1 ? nextPoint.pressure : void 0;
8406
- };
8407
- var resolveMaxStepWorld = (maxStepWorld) => Number.isFinite(maxStepWorld) && maxStepWorld > 0 ? maxStepWorld : Number.POSITIVE_INFINITY;
8408
- function appendInterpolatedStrokePoint(points, nextPoint, maxStepWorld) {
8409
- if (points.length === 0) return [nextPoint];
8410
- const lastPoint = points[points.length - 1];
8411
- if (!lastPoint) return [...points, nextPoint];
8412
- const deltaX = nextPoint.x - lastPoint.x;
8413
- const deltaY = nextPoint.y - lastPoint.y;
8414
- const distanceSquared = deltaX * deltaX + deltaY * deltaY;
8415
- if (distanceSquared < 1e-12) return points;
8416
- const distance = Math.sqrt(distanceSquared);
8417
- const stepCount = Math.max(
8418
- 1,
8419
- Math.ceil(distance / resolveMaxStepWorld(maxStepWorld))
8420
- );
8421
- if (stepCount === 1) return [...points, nextPoint];
8422
- const interpolatedPoints = Array.from({ length: stepCount }, (_, index) => {
8423
- const ratio = (index + 1) / stepCount;
8424
- const pressure = resolveInterpolatedPressure(lastPoint, nextPoint, ratio);
8425
- return {
8426
- x: lastPoint.x + deltaX * ratio,
8427
- y: lastPoint.y + deltaY * ratio,
8428
- ...pressure != null ? { pressure } : {}
8429
- };
8430
- });
8431
- return [...points, ...interpolatedPoints];
8432
- }
8433
-
8434
8443
  // src/react/TextEditOverlay.tsx
8435
8444
  init_rect();
8436
8445
  init_text_svg();
@@ -8690,6 +8699,13 @@ function pointInSelectedItemBounds(item, worldX, worldY) {
8690
8699
  );
8691
8700
  return local.x >= 0 && local.x <= bounds.width && local.y >= 0 && local.y <= bounds.height;
8692
8701
  }
8702
+ function pointerEventSamples(ev) {
8703
+ if (ev.pointerType !== "pen" || typeof ev.getCoalescedEvents !== "function") {
8704
+ return [ev];
8705
+ }
8706
+ const samples = ev.getCoalescedEvents();
8707
+ return samples.length > 0 ? samples : [ev];
8708
+ }
8693
8709
  var CANVU_PEN_ACTIVE_UI_BLOCK_CSS = `
8694
8710
  [data-canvu-pen-active="true"] [data-slot="vector-canvas-toolbar"],
8695
8711
  [data-canvu-pen-active="true"] [data-slot="vector-canvas-toolbar"] *,
@@ -8791,6 +8807,15 @@ function VectorViewportPluginInstances({
8791
8807
  ] }, plugin.id);
8792
8808
  }) });
8793
8809
  }
8810
+ function appendInterpolatedPoints(points, next, maxStepWorld) {
8811
+ if (points.length === 0) return [next];
8812
+ const last = points[points.length - 1];
8813
+ if (!last) return [...points, next];
8814
+ const dx = next.x - last.x;
8815
+ const dy = next.y - last.y;
8816
+ if (dx * dx + dy * dy < 1e-12) return points;
8817
+ return [...points, next];
8818
+ }
8794
8819
  function pointerSampleToWorldPoint(screenToWorldFn, clientX, clientY, pressure) {
8795
8820
  const { worldX, worldY } = screenToWorldFn(clientX, clientY);
8796
8821
  const safePressure = pressure != null && Number.isFinite(pressure) ? Math.min(1, Math.max(0, pressure)) : void 0;
@@ -8821,19 +8846,15 @@ function isLikelyStylusTouch(touch) {
8821
8846
  }
8822
8847
  function appendPointerEventSamplesToStrokePoints(points, ev, zoom, screenToWorldFn) {
8823
8848
  let interpolated = points;
8824
- const maxStepWorld = ev.pointerType === "pen" ? 0.35 / zoom : 1 / zoom;
8825
- for (const sample of getPointerEventSamples(ev)) {
8849
+ ev.pointerType === "pen" ? 0.35 / zoom : 1 / zoom;
8850
+ for (const sample of pointerEventSamples(ev)) {
8826
8851
  const nextPoint = pointerSampleToWorldPoint(
8827
8852
  screenToWorldFn,
8828
8853
  sample.clientX,
8829
8854
  sample.clientY,
8830
8855
  sample.pointerType === "pen" ? sample.pressure : void 0
8831
8856
  );
8832
- interpolated = appendInterpolatedStrokePoint(
8833
- interpolated,
8834
- nextPoint,
8835
- maxStepWorld
8836
- );
8857
+ interpolated = appendInterpolatedPoints(interpolated, nextPoint);
8837
8858
  }
8838
8859
  return interpolated;
8839
8860
  }
@@ -8844,7 +8865,7 @@ function appendTouchToStrokePoints(points, touch, zoom, screenToWorldFn) {
8844
8865
  touch.clientY,
8845
8866
  touchPressure(touch)
8846
8867
  );
8847
- return appendInterpolatedStrokePoint(points, nextPoint, 0.35 / zoom);
8868
+ return appendInterpolatedPoints(points, nextPoint);
8848
8869
  }
8849
8870
  function createStraightStrokeState(anchorPoint, clientX, clientY) {
8850
8871
  return {