@reekon-tools/react-native-pdf-canvas 0.1.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/PdfCanvas.podspec +80 -0
- package/README.md +1391 -0
- package/android/build.gradle +175 -0
- package/android/consumer-rules.pro +3 -0
- package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/android/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/android/gradle.properties +2 -0
- package/android/gradlew +248 -0
- package/android/gradlew.bat +98 -0
- package/android/settings.gradle +34 -0
- package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasCancellationTest.java +226 -0
- package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasConcurrencyTest.java +239 -0
- package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasDeviceTimingTest.java +251 -0
- package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasFailureTest.java +221 -0
- package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasJsiTransportTest.java +783 -0
- package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasPhaseTimingTest.java +1388 -0
- package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasPoisonGuardTest.java +98 -0
- package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasQuirksTest.java +324 -0
- package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasRasterizerTest.java +775 -0
- package/android/src/androidTest/java/tools/reekon/pdfcanvas/PoisonsTheProcess.java +20 -0
- package/android/src/androidTest/java/tools/reekon/pdfcanvas/TestPdfs.java +558 -0
- package/android/src/jsi/cpp/CMakeLists.txt +33 -0
- package/android/src/jsi/cpp/pdfcanvas-jsi.cpp +469 -0
- package/android/src/jsi/java/tools/reekon/pdfcanvas/jsi/PdfCanvasPixelBridge.java +140 -0
- package/android/src/main/AndroidManifest.xml +7 -0
- package/android/src/main/java/tools/reekon/pdfcanvas/PageGeometry.java +45 -0
- package/android/src/main/java/tools/reekon/pdfcanvas/PdfCanvasRasterizer.java +564 -0
- package/android/src/main/java/tools/reekon/pdfcanvas/PdfErrorCode.java +33 -0
- package/android/src/main/java/tools/reekon/pdfcanvas/PdfPlatformQuirks.java +277 -0
- package/android/src/main/java/tools/reekon/pdfcanvas/PdfPreflight.java +214 -0
- package/android/src/main/java/tools/reekon/pdfcanvas/PdfRasterException.java +30 -0
- package/android/src/main/java/tools/reekon/pdfcanvas/PdfRendererPool.java +234 -0
- package/android/src/main/java/tools/reekon/pdfcanvas/RasterCancellation.java +47 -0
- package/android/src/main/java/tools/reekon/pdfcanvas/RasterPixels.java +143 -0
- package/android/src/main/java/tools/reekon/pdfcanvas/RasterRequest.java +95 -0
- package/android/src/reactnative/java/tools/reekon/pdfcanvas/rn/PdfCanvasModule.java +626 -0
- package/android/src/reactnative/java/tools/reekon/pdfcanvas/rn/PdfCanvasPackage.java +33 -0
- package/android/tools/compile-gate.sh +289 -0
- package/dist/cache.d.ts +161 -0
- package/dist/cache.js +389 -0
- package/dist/clock.d.ts +21 -0
- package/dist/clock.js +31 -0
- package/dist/controller.d.ts +131 -0
- package/dist/controller.js +1002 -0
- package/dist/geometry.d.ts +96 -0
- package/dist/geometry.js +248 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.js +50 -0
- package/dist/index.native.d.ts +21 -0
- package/dist/index.native.js +23 -0
- package/dist/index.web.d.ts +24 -0
- package/dist/index.web.js +26 -0
- package/dist/layout.d.ts +78 -0
- package/dist/layout.js +152 -0
- package/dist/peer-check.d.ts +83 -0
- package/dist/peer-check.js +183 -0
- package/dist/planner.d.ts +35 -0
- package/dist/planner.js +530 -0
- package/dist/policy.d.ts +14 -0
- package/dist/policy.js +168 -0
- package/dist/rasterizer/android.d.ts +121 -0
- package/dist/rasterizer/android.js +126 -0
- package/dist/rasterizer/buffer.d.ts +18 -0
- package/dist/rasterizer/buffer.js +18 -0
- package/dist/rasterizer/fake.d.ts +92 -0
- package/dist/rasterizer/fake.js +433 -0
- package/dist/rasterizer/index.d.ts +38 -0
- package/dist/rasterizer/index.js +69 -0
- package/dist/rasterizer/index.native.d.ts +19 -0
- package/dist/rasterizer/index.native.js +80 -0
- package/dist/rasterizer/index.web.d.ts +63 -0
- package/dist/rasterizer/index.web.js +75 -0
- package/dist/rasterizer/ingest.d.ts +180 -0
- package/dist/rasterizer/ingest.js +307 -0
- package/dist/rasterizer/ios.d.ts +84 -0
- package/dist/rasterizer/ios.js +155 -0
- package/dist/rasterizer/native-bridge.d.ts +261 -0
- package/dist/rasterizer/native-bridge.js +413 -0
- package/dist/rasterizer/web/client.d.ts +84 -0
- package/dist/rasterizer/web/client.js +359 -0
- package/dist/rasterizer/web/engine.d.ts +69 -0
- package/dist/rasterizer/web/engine.js +308 -0
- package/dist/rasterizer/web/index.d.ts +26 -0
- package/dist/rasterizer/web/index.js +21 -0
- package/dist/rasterizer/web/pdfium.d.ts +213 -0
- package/dist/rasterizer/web/pdfium.js +170 -0
- package/dist/rasterizer/web/protocol.d.ts +138 -0
- package/dist/rasterizer/web/protocol.js +37 -0
- package/dist/rasterizer/web/session.d.ts +106 -0
- package/dist/rasterizer/web/session.js +413 -0
- package/dist/rasterizer/web/worker.d.ts +44 -0
- package/dist/rasterizer/web/worker.js +76 -0
- package/dist/react/PdfContentView.d.ts +98 -0
- package/dist/react/PdfContentView.js +189 -0
- package/dist/react/index.d.ts +6 -0
- package/dist/react/index.js +3 -0
- package/dist/react/usePdfDocument.d.ts +134 -0
- package/dist/react/usePdfDocument.js +275 -0
- package/dist/react/usePdfLayer.d.ts +94 -0
- package/dist/react/usePdfLayer.js +388 -0
- package/dist/skia/index.d.ts +26 -0
- package/dist/skia/index.js +25 -0
- package/dist/skia/index.native.d.ts +14 -0
- package/dist/skia/index.native.js +16 -0
- package/dist/skia/index.web.d.ts +12 -0
- package/dist/skia/index.web.js +14 -0
- package/dist/skia/resolve.native.d.ts +18 -0
- package/dist/skia/resolve.native.js +22 -0
- package/dist/skia/resolve.web.d.ts +41 -0
- package/dist/skia/resolve.web.js +62 -0
- package/dist/skia/skia-api.d.ts +94 -0
- package/dist/skia/skia-api.js +113 -0
- package/dist/testing/index.d.ts +27 -0
- package/dist/testing/index.js +28 -0
- package/dist/testing/scenes.d.ts +100 -0
- package/dist/testing/scenes.js +267 -0
- package/dist/types.d.ts +611 -0
- package/dist/types.js +21 -0
- package/dist/web-init.d.ts +68 -0
- package/dist/web-init.js +64 -0
- package/ios/Package.swift +58 -0
- package/ios/Sources/PdfCanvasBridge/PdfCanvasModule.mm +743 -0
- package/ios/Sources/PdfCanvasBridge/include/PdfCanvasModule.h +36 -0
- package/ios/Sources/PdfCanvasCore/PdfCanvasCancellation.m +38 -0
- package/ios/Sources/PdfCanvasCore/PdfCanvasDocumentPool.m +389 -0
- package/ios/Sources/PdfCanvasCore/PdfCanvasErrorCode.m +57 -0
- package/ios/Sources/PdfCanvasCore/PdfCanvasPageGeometry.m +44 -0
- package/ios/Sources/PdfCanvasCore/PdfCanvasPageTransform.m +82 -0
- package/ios/Sources/PdfCanvasCore/PdfCanvasPixelSink.m +37 -0
- package/ios/Sources/PdfCanvasCore/PdfCanvasPreflight.m +158 -0
- package/ios/Sources/PdfCanvasCore/PdfCanvasRasterPixels.m +80 -0
- package/ios/Sources/PdfCanvasCore/PdfCanvasRasterRequest.m +65 -0
- package/ios/Sources/PdfCanvasCore/PdfCanvasRasterizer.m +548 -0
- package/ios/Sources/PdfCanvasCore/include/PdfCanvasCancellation.h +48 -0
- package/ios/Sources/PdfCanvasCore/include/PdfCanvasCore.h +26 -0
- package/ios/Sources/PdfCanvasCore/include/PdfCanvasDocumentPool.h +122 -0
- package/ios/Sources/PdfCanvasCore/include/PdfCanvasErrorCode.h +80 -0
- package/ios/Sources/PdfCanvasCore/include/PdfCanvasPageGeometry.h +79 -0
- package/ios/Sources/PdfCanvasCore/include/PdfCanvasPageTransform.h +129 -0
- package/ios/Sources/PdfCanvasCore/include/PdfCanvasPixelSink.h +100 -0
- package/ios/Sources/PdfCanvasCore/include/PdfCanvasPreflight.h +65 -0
- package/ios/Sources/PdfCanvasCore/include/PdfCanvasRasterPixels.h +111 -0
- package/ios/Sources/PdfCanvasCore/include/PdfCanvasRasterRequest.h +85 -0
- package/ios/Sources/PdfCanvasCore/include/PdfCanvasRasterizer.h +163 -0
- package/ios/Sources/PdfCanvasSlots/PdfCanvasSlots.mm +297 -0
- package/ios/Sources/PdfCanvasSlots/include/PdfCanvasSlots.h +140 -0
- package/ios/Tests/PdfCanvasCoreTests/ConcurrencyTests.swift +358 -0
- package/ios/Tests/PdfCanvasCoreTests/FailureTests.swift +262 -0
- package/ios/Tests/PdfCanvasCoreTests/GeometryTests.swift +173 -0
- package/ios/Tests/PdfCanvasCoreTests/PreviewTests.swift +114 -0
- package/ios/Tests/PdfCanvasCoreTests/RasterAssertions.swift +158 -0
- package/ios/Tests/PdfCanvasCoreTests/RasterizerTests.swift +544 -0
- package/ios/Tests/PdfCanvasCoreTests/SinkTests.swift +287 -0
- package/ios/Tests/PdfCanvasCoreTests/SlotsTests.swift +396 -0
- package/ios/Tests/PdfCanvasCoreTests/TestPdfs.swift +639 -0
- package/ios/Tests/PdfCanvasCoreTests/TimingTests.swift +179 -0
- package/ios/Tests/PdfCanvasCoreTests/TransformTests.swift +283 -0
- package/package.json +144 -0
- package/react-native.config.js +40 -0
- package/scripts/check-skia-peer.mjs +86 -0
- package/scripts/skia-peer-check.mjs +363 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The WEB resolver: build the Skia api from `globalThis.CanvasKit`, lazily.
|
|
3
|
+
*
|
|
4
|
+
* WEB ONLY. Nothing native ever imports this file — the `react-native`
|
|
5
|
+
* condition of every `exports` entry points at an `index.native.js` that reaches
|
|
6
|
+
* `./resolve.native.js` instead — so the whole JsiSk* web implementation stays
|
|
7
|
+
* out of the RN bundle.
|
|
8
|
+
*
|
|
9
|
+
* ------------------------------------------------------------------------
|
|
10
|
+
* WHY `JsiSkApi(CanvasKit)` AND NOT AN IMPORT OF `Skia`
|
|
11
|
+
* ------------------------------------------------------------------------
|
|
12
|
+
*
|
|
13
|
+
* Because it is the same expression, evaluated at the right time.
|
|
14
|
+
* `lib/module/skia/Skia.web.js` IS `JsiSkApi(global.CanvasKit)`; the only thing
|
|
15
|
+
* wrong with it is WHEN it runs. Importing `JsiSkApi` from the deep
|
|
16
|
+
* `skia/web` path gets the factory without evaluating the module that calls it
|
|
17
|
+
* too early — VERIFIED by bundling, not by reading imports: a Vite build of
|
|
18
|
+
* `lib/module/skia/web/index.js` contains no `JsiSkApi(global.CanvasKit)`, while
|
|
19
|
+
* a build of the bare `@shopify/react-native-skia` specifier does.
|
|
20
|
+
*
|
|
21
|
+
* The api this produces is a SECOND JsiSkApi object, distinct from the one
|
|
22
|
+
* `Skia.web.js` builds for RN Skia's own renderer. That is harmless and was
|
|
23
|
+
* checked rather than assumed: both come from the same `skia/web` module, so the
|
|
24
|
+
* classes (`JsiSkImage`, `JsiSkData`, …) are identical objects and an image made
|
|
25
|
+
* here is exactly the image the reconciler expects. Only the factory wrapper is
|
|
26
|
+
* duplicated, and it holds no cross-call state that matters here.
|
|
27
|
+
*
|
|
28
|
+
* The deep path is legal because `@shopify/react-native-skia` publishes `lib/**`
|
|
29
|
+
* and declares no `exports` map. Same standing as boldr-utils' import of
|
|
30
|
+
* `@shopify/react-native-skia/lib/module/web/index.js` for `WithSkiaWeb`. If
|
|
31
|
+
* they ever add an `exports` map, both break together and the fix is whatever
|
|
32
|
+
* public entry replaces them.
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* Install the web resolver.
|
|
36
|
+
*
|
|
37
|
+
* Idempotent and free: it stores a closure. Nothing reads `globalThis.CanvasKit`
|
|
38
|
+
* until the first raster is ingested, which is the entire point — a route module
|
|
39
|
+
* that merely names this package must not decide anything about CanvasKit.
|
|
40
|
+
*/
|
|
41
|
+
export declare function installWebSkiaResolver(): void;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The WEB resolver: build the Skia api from `globalThis.CanvasKit`, lazily.
|
|
3
|
+
*
|
|
4
|
+
* WEB ONLY. Nothing native ever imports this file — the `react-native`
|
|
5
|
+
* condition of every `exports` entry points at an `index.native.js` that reaches
|
|
6
|
+
* `./resolve.native.js` instead — so the whole JsiSk* web implementation stays
|
|
7
|
+
* out of the RN bundle.
|
|
8
|
+
*
|
|
9
|
+
* ------------------------------------------------------------------------
|
|
10
|
+
* WHY `JsiSkApi(CanvasKit)` AND NOT AN IMPORT OF `Skia`
|
|
11
|
+
* ------------------------------------------------------------------------
|
|
12
|
+
*
|
|
13
|
+
* Because it is the same expression, evaluated at the right time.
|
|
14
|
+
* `lib/module/skia/Skia.web.js` IS `JsiSkApi(global.CanvasKit)`; the only thing
|
|
15
|
+
* wrong with it is WHEN it runs. Importing `JsiSkApi` from the deep
|
|
16
|
+
* `skia/web` path gets the factory without evaluating the module that calls it
|
|
17
|
+
* too early — VERIFIED by bundling, not by reading imports: a Vite build of
|
|
18
|
+
* `lib/module/skia/web/index.js` contains no `JsiSkApi(global.CanvasKit)`, while
|
|
19
|
+
* a build of the bare `@shopify/react-native-skia` specifier does.
|
|
20
|
+
*
|
|
21
|
+
* The api this produces is a SECOND JsiSkApi object, distinct from the one
|
|
22
|
+
* `Skia.web.js` builds for RN Skia's own renderer. That is harmless and was
|
|
23
|
+
* checked rather than assumed: both come from the same `skia/web` module, so the
|
|
24
|
+
* classes (`JsiSkImage`, `JsiSkData`, …) are identical objects and an image made
|
|
25
|
+
* here is exactly the image the reconciler expects. Only the factory wrapper is
|
|
26
|
+
* duplicated, and it holds no cross-call state that matters here.
|
|
27
|
+
*
|
|
28
|
+
* The deep path is legal because `@shopify/react-native-skia` publishes `lib/**`
|
|
29
|
+
* and declares no `exports` map. Same standing as boldr-utils' import of
|
|
30
|
+
* `@shopify/react-native-skia/lib/module/web/index.js` for `WithSkiaWeb`. If
|
|
31
|
+
* they ever add an `exports` map, both break together and the fix is whatever
|
|
32
|
+
* public entry replaces them.
|
|
33
|
+
*/
|
|
34
|
+
import { JsiSkApi } from '@shopify/react-native-skia/lib/module/skia/web/index.js';
|
|
35
|
+
import { setSkiaApiResolver } from './skia-api.js';
|
|
36
|
+
/**
|
|
37
|
+
* Install the web resolver.
|
|
38
|
+
*
|
|
39
|
+
* Idempotent and free: it stores a closure. Nothing reads `globalThis.CanvasKit`
|
|
40
|
+
* until the first raster is ingested, which is the entire point — a route module
|
|
41
|
+
* that merely names this package must not decide anything about CanvasKit.
|
|
42
|
+
*/
|
|
43
|
+
export function installWebSkiaResolver() {
|
|
44
|
+
setSkiaApiResolver(() => {
|
|
45
|
+
const canvasKit = globalThis.CanvasKit;
|
|
46
|
+
// Null, not a throw: `skiaApi()` owns the one error message, and "CanvasKit
|
|
47
|
+
// is not loaded yet" is exactly the state that message describes.
|
|
48
|
+
if (canvasKit === undefined || canvasKit === null) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
// THE CAST IS AN ARTIFACT OF DUPLICATE DECLARATIONS, NOT A LOOSENING.
|
|
52
|
+
//
|
|
53
|
+
// RN Skia ships its types twice — `lib/typescript/src/**` (what the bare
|
|
54
|
+
// specifier resolves to, and what `SkiaApi` is taken from) and co-located
|
|
55
|
+
// `lib/module/**` copies (what this deep import resolves to). The two are
|
|
56
|
+
// generated from the same source, but TypeScript treats their `NodeType`
|
|
57
|
+
// enums as nominally distinct, so the structurally-identical results do not
|
|
58
|
+
// assign. `unknown` in the middle rather than a direct assertion because TS
|
|
59
|
+
// rejects even the direct one for the same reason.
|
|
60
|
+
return JsiSkApi(canvasKit);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Skia runtime, resolved AT CALL TIME.
|
|
3
|
+
*
|
|
4
|
+
* ------------------------------------------------------------------------
|
|
5
|
+
* WHY THIS FILE EXISTS: `Skia` ON WEB IS BUILT ONCE, AT IMPORT TIME.
|
|
6
|
+
* ------------------------------------------------------------------------
|
|
7
|
+
*
|
|
8
|
+
* `@shopify/react-native-skia`'s web build is, in its entirety:
|
|
9
|
+
*
|
|
10
|
+
* // lib/module/skia/Skia.web.js
|
|
11
|
+
* import {JsiSkApi} from "./web";
|
|
12
|
+
* export const Skia = JsiSkApi(global.CanvasKit);
|
|
13
|
+
*
|
|
14
|
+
* That is a MODULE-SCOPE call. If the module is evaluated before `LoadSkiaWeb`
|
|
15
|
+
* has resolved, `global.CanvasKit` is `undefined`, every factory in the returned
|
|
16
|
+
* object closes over `undefined`, and the damage is PERMANENT and unrecoverable
|
|
17
|
+
* in-bundle: re-importing the specifier returns the identical broken object,
|
|
18
|
+
* because ES module evaluation happens once.
|
|
19
|
+
*
|
|
20
|
+
* And it is not hard to trigger. `Skia.web.js` is reached from the package root
|
|
21
|
+
* barrel (`lib/module/index.js` -> `export * from "./skia"` -> `export {Skia}
|
|
22
|
+
* from "./Skia"`), `@shopify/react-native-skia` declares no `sideEffects: false`
|
|
23
|
+
* and no `exports` map, so **any** value import from the bare specifier — a
|
|
24
|
+
* `<Group>`, a `ColorType` — evaluates it. rock-desktop is `{ssr: true}` with no
|
|
25
|
+
* prerender, so every route module is evaluated in Node on every request; one
|
|
26
|
+
* route that names a barrel which names Skia's barrel is enough.
|
|
27
|
+
*
|
|
28
|
+
* So this module never names `Skia` at module scope. It holds a RESOLVER, and
|
|
29
|
+
* `skiaApi()` calls it on the first raster and caches the answer.
|
|
30
|
+
*
|
|
31
|
+
* ------------------------------------------------------------------------
|
|
32
|
+
* WHO INSTALLS THE RESOLVER
|
|
33
|
+
* ------------------------------------------------------------------------
|
|
34
|
+
*
|
|
35
|
+
* native `./resolve.native.js`, from `src/index.native.ts` and
|
|
36
|
+
* `src/skia/index.native.ts`. It DOES import the bare specifier —
|
|
37
|
+
* correctly, because on native `Skia` is `global.SkiaApi`, installed
|
|
38
|
+
* by RN Skia's own `NativeSetup` side effect at import. There is no
|
|
39
|
+
* ordering hazard on native at all; this whole file is a web problem.
|
|
40
|
+
* web `./resolve.web.js`, from `src/index.web.ts` and
|
|
41
|
+
* `src/skia/index.web.ts`. It builds the api from
|
|
42
|
+
* `globalThis.CanvasKit` on first use, so nothing is decided at
|
|
43
|
+
* import time.
|
|
44
|
+
* host `loadPdfCanvasSkiaWeb()` from the `./web-init` subpath installs it
|
|
45
|
+
* too, after awaiting CanvasKit. That is the explicit, awaitable
|
|
46
|
+
* ordering guarantee a web host should prefer.
|
|
47
|
+
*
|
|
48
|
+
* A host with its own arrangement can call `setSkiaApiResolver` directly.
|
|
49
|
+
*/
|
|
50
|
+
import type { Skia as SkiaConst } from '@shopify/react-native-skia';
|
|
51
|
+
/**
|
|
52
|
+
* The shape of RN Skia's `Skia`, taken from the BARE specifier.
|
|
53
|
+
*
|
|
54
|
+
* `import type` is erased entirely — it reaches no module at runtime and is
|
|
55
|
+
* therefore the one form of `@shopify/react-native-skia` import that is always
|
|
56
|
+
* safe here. `typeof` rather than a direct type import because the package's
|
|
57
|
+
* root exports `Skia` as a VALUE (`export {Skia} from "./Skia"`), which shadows
|
|
58
|
+
* the same-named interface its `./types` star-export carries.
|
|
59
|
+
*
|
|
60
|
+
* It must come from the bare specifier and not from a deep `lib/module/...`
|
|
61
|
+
* path: the package ships its declarations twice (`lib/typescript/src/**`, which
|
|
62
|
+
* the bare specifier resolves to, and co-located `lib/module/**` copies), and
|
|
63
|
+
* TypeScript treats the two copies' `NodeType` enums as NOMINALLY distinct. Mix
|
|
64
|
+
* them and every assignment needs a cast.
|
|
65
|
+
*/
|
|
66
|
+
export type SkiaApi = typeof SkiaConst;
|
|
67
|
+
/**
|
|
68
|
+
* Produce the Skia api, or null if it is not available YET.
|
|
69
|
+
*
|
|
70
|
+
* Returning null rather than throwing matters: an installed resolver that cannot
|
|
71
|
+
* answer yet falls through to the remaining strategies below instead of
|
|
72
|
+
* short-circuiting them, and the single error message at the end of the chain is
|
|
73
|
+
* the one the caller sees.
|
|
74
|
+
*/
|
|
75
|
+
export type SkiaApiResolver = () => SkiaApi | null;
|
|
76
|
+
/**
|
|
77
|
+
* Install the resolver. Returns the previous one so a test can restore it.
|
|
78
|
+
*
|
|
79
|
+
* Installing CLEARS the cache. A resolver swap is a statement that the previous
|
|
80
|
+
* answer is no longer the right one — the alternative (keeping a stale api that
|
|
81
|
+
* a `LoadSkiaWeb` retry has just superseded) is the failure this whole module
|
|
82
|
+
* exists to prevent.
|
|
83
|
+
*/
|
|
84
|
+
export declare function setSkiaApiResolver(next: SkiaApiResolver | null): SkiaApiResolver | null;
|
|
85
|
+
/**
|
|
86
|
+
* The Skia api, resolved on first use and cached.
|
|
87
|
+
*
|
|
88
|
+
* Call this INSIDE the function that needs it, never at module scope — the
|
|
89
|
+
* moment the result is stored in a module-level `const` this file has
|
|
90
|
+
* reintroduced the bug it exists to remove.
|
|
91
|
+
*/
|
|
92
|
+
export declare function skiaApi(): SkiaApi;
|
|
93
|
+
/** Drop the cached api AND the installed resolver. Tests only. */
|
|
94
|
+
export declare function resetSkiaApiForTests(): void;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Skia runtime, resolved AT CALL TIME.
|
|
3
|
+
*
|
|
4
|
+
* ------------------------------------------------------------------------
|
|
5
|
+
* WHY THIS FILE EXISTS: `Skia` ON WEB IS BUILT ONCE, AT IMPORT TIME.
|
|
6
|
+
* ------------------------------------------------------------------------
|
|
7
|
+
*
|
|
8
|
+
* `@shopify/react-native-skia`'s web build is, in its entirety:
|
|
9
|
+
*
|
|
10
|
+
* // lib/module/skia/Skia.web.js
|
|
11
|
+
* import {JsiSkApi} from "./web";
|
|
12
|
+
* export const Skia = JsiSkApi(global.CanvasKit);
|
|
13
|
+
*
|
|
14
|
+
* That is a MODULE-SCOPE call. If the module is evaluated before `LoadSkiaWeb`
|
|
15
|
+
* has resolved, `global.CanvasKit` is `undefined`, every factory in the returned
|
|
16
|
+
* object closes over `undefined`, and the damage is PERMANENT and unrecoverable
|
|
17
|
+
* in-bundle: re-importing the specifier returns the identical broken object,
|
|
18
|
+
* because ES module evaluation happens once.
|
|
19
|
+
*
|
|
20
|
+
* And it is not hard to trigger. `Skia.web.js` is reached from the package root
|
|
21
|
+
* barrel (`lib/module/index.js` -> `export * from "./skia"` -> `export {Skia}
|
|
22
|
+
* from "./Skia"`), `@shopify/react-native-skia` declares no `sideEffects: false`
|
|
23
|
+
* and no `exports` map, so **any** value import from the bare specifier — a
|
|
24
|
+
* `<Group>`, a `ColorType` — evaluates it. rock-desktop is `{ssr: true}` with no
|
|
25
|
+
* prerender, so every route module is evaluated in Node on every request; one
|
|
26
|
+
* route that names a barrel which names Skia's barrel is enough.
|
|
27
|
+
*
|
|
28
|
+
* So this module never names `Skia` at module scope. It holds a RESOLVER, and
|
|
29
|
+
* `skiaApi()` calls it on the first raster and caches the answer.
|
|
30
|
+
*
|
|
31
|
+
* ------------------------------------------------------------------------
|
|
32
|
+
* WHO INSTALLS THE RESOLVER
|
|
33
|
+
* ------------------------------------------------------------------------
|
|
34
|
+
*
|
|
35
|
+
* native `./resolve.native.js`, from `src/index.native.ts` and
|
|
36
|
+
* `src/skia/index.native.ts`. It DOES import the bare specifier —
|
|
37
|
+
* correctly, because on native `Skia` is `global.SkiaApi`, installed
|
|
38
|
+
* by RN Skia's own `NativeSetup` side effect at import. There is no
|
|
39
|
+
* ordering hazard on native at all; this whole file is a web problem.
|
|
40
|
+
* web `./resolve.web.js`, from `src/index.web.ts` and
|
|
41
|
+
* `src/skia/index.web.ts`. It builds the api from
|
|
42
|
+
* `globalThis.CanvasKit` on first use, so nothing is decided at
|
|
43
|
+
* import time.
|
|
44
|
+
* host `loadPdfCanvasSkiaWeb()` from the `./web-init` subpath installs it
|
|
45
|
+
* too, after awaiting CanvasKit. That is the explicit, awaitable
|
|
46
|
+
* ordering guarantee a web host should prefer.
|
|
47
|
+
*
|
|
48
|
+
* A host with its own arrangement can call `setSkiaApiResolver` directly.
|
|
49
|
+
*/
|
|
50
|
+
import { PdfError } from '../types.js';
|
|
51
|
+
let resolver = null;
|
|
52
|
+
let cached = null;
|
|
53
|
+
/**
|
|
54
|
+
* Install the resolver. Returns the previous one so a test can restore it.
|
|
55
|
+
*
|
|
56
|
+
* Installing CLEARS the cache. A resolver swap is a statement that the previous
|
|
57
|
+
* answer is no longer the right one — the alternative (keeping a stale api that
|
|
58
|
+
* a `LoadSkiaWeb` retry has just superseded) is the failure this whole module
|
|
59
|
+
* exists to prevent.
|
|
60
|
+
*/
|
|
61
|
+
export function setSkiaApiResolver(next) {
|
|
62
|
+
const previous = resolver;
|
|
63
|
+
resolver = next;
|
|
64
|
+
cached = null;
|
|
65
|
+
return previous;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The native JSI global, read the way RN Skia itself reads it.
|
|
69
|
+
*
|
|
70
|
+
* `lib/module/skia/Skia.js` — the NATIVE half of the same platform pair — is
|
|
71
|
+
* literally `export const Skia = SkiaApi;`, i.e. a read of the global that
|
|
72
|
+
* `NativeSetup` installs. Consulting it here is not a guess about internals; it
|
|
73
|
+
* is the same one-liner, deferred. It exists so that a native consumer who
|
|
74
|
+
* reached this module through a shared submodule (rather than through
|
|
75
|
+
* `index.native.ts`) still gets a working api instead of an error about web
|
|
76
|
+
* initialisation.
|
|
77
|
+
*
|
|
78
|
+
* On web and under vitest the global is absent, so this contributes nothing.
|
|
79
|
+
*/
|
|
80
|
+
function nativeGlobalSkia() {
|
|
81
|
+
const global = globalThis;
|
|
82
|
+
return global.SkiaApi ?? null;
|
|
83
|
+
}
|
|
84
|
+
const UNAVAILABLE = 'The Skia runtime is not available yet. On web, CanvasKit is loaded ' +
|
|
85
|
+
'asynchronously and `Skia` is built from it ONCE, so it must be loaded ' +
|
|
86
|
+
'before the first raster: await `loadPdfCanvasSkiaWeb()` from ' +
|
|
87
|
+
"'@reekon-tools/react-native-pdf-canvas/web-init' (or RN Skia's own " +
|
|
88
|
+
'`LoadSkiaWeb`) during startup. On native this means ' +
|
|
89
|
+
'@shopify/react-native-skia never installed its JSI bindings — rebuild the ' +
|
|
90
|
+
'app rather than reloading the bundle.';
|
|
91
|
+
/**
|
|
92
|
+
* The Skia api, resolved on first use and cached.
|
|
93
|
+
*
|
|
94
|
+
* Call this INSIDE the function that needs it, never at module scope — the
|
|
95
|
+
* moment the result is stored in a module-level `const` this file has
|
|
96
|
+
* reintroduced the bug it exists to remove.
|
|
97
|
+
*/
|
|
98
|
+
export function skiaApi() {
|
|
99
|
+
if (cached !== null) {
|
|
100
|
+
return cached;
|
|
101
|
+
}
|
|
102
|
+
const resolved = resolver?.() ?? nativeGlobalSkia();
|
|
103
|
+
if (resolved === null || resolved === undefined) {
|
|
104
|
+
throw new PdfError('unsupported', UNAVAILABLE);
|
|
105
|
+
}
|
|
106
|
+
cached = resolved;
|
|
107
|
+
return resolved;
|
|
108
|
+
}
|
|
109
|
+
/** Drop the cached api AND the installed resolver. Tests only. */
|
|
110
|
+
export function resetSkiaApiForTests() {
|
|
111
|
+
resolver = null;
|
|
112
|
+
cached = null;
|
|
113
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `@reekon-tools/react-native-pdf-canvas/testing` subpath.
|
|
3
|
+
*
|
|
4
|
+
* SKIA-FREE BY CONTRACT. Nothing reachable from this file may import
|
|
5
|
+
* `@shopify/react-native-skia`, react-native or reanimated — it is imported by
|
|
6
|
+
* vitest suites running in a bare node environment, where any of those would
|
|
7
|
+
* fail at module load. In particular it reaches `../rasterizer/fake.js`
|
|
8
|
+
* directly and never `../rasterizer/index.js`, which pulls in the Skia ingest
|
|
9
|
+
* path.
|
|
10
|
+
*/
|
|
11
|
+
export { ARCH_D, ARCH_E, LETTER, createFakeRasterizer, decodeFakeDocRect, fakePageColor, fakePixelSize, } from '../rasterizer/fake.js';
|
|
12
|
+
export type { FakeFailureModes, FakeRasterizerOptions, } from '../rasterizer/fake.js';
|
|
13
|
+
/**
|
|
14
|
+
* The reference scenes BUILD STEP 1 produced: the composition contract as data,
|
|
15
|
+
* for a backend suite to replay against its own rasterizer. Pure — it reaches
|
|
16
|
+
* `planRasters` and nothing else.
|
|
17
|
+
*/
|
|
18
|
+
export { buildReferenceScenes, docRectToCanvas, docToCanvas, referenceScene, sceneRasterMeta, sceneRasterRequest, } from './scenes.js';
|
|
19
|
+
export type { CanvasPoint, ProbeExpectation, ReferenceScene, SceneProbe, SceneRaster, SceneTransform, } from './scenes.js';
|
|
20
|
+
import type { DocRect, DocSize, PageGeometry, PageRotation, ViewportSnapshot } from '../types.js';
|
|
21
|
+
export declare function makePageGeometry(index: number, size: DocSize, rotation?: PageRotation): PageGeometry;
|
|
22
|
+
/**
|
|
23
|
+
* `scale` is DEVICE PIXELS PER PDF POINT — zoom multiplied by pixel ratio,
|
|
24
|
+
* already combined. Passing a bare zoom here is the single easiest way to write
|
|
25
|
+
* a test that agrees with itself and disagrees with the device.
|
|
26
|
+
*/
|
|
27
|
+
export declare function makeViewport(visibleDocRect: DocRect, scale: number): ViewportSnapshot;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `@reekon-tools/react-native-pdf-canvas/testing` subpath.
|
|
3
|
+
*
|
|
4
|
+
* SKIA-FREE BY CONTRACT. Nothing reachable from this file may import
|
|
5
|
+
* `@shopify/react-native-skia`, react-native or reanimated — it is imported by
|
|
6
|
+
* vitest suites running in a bare node environment, where any of those would
|
|
7
|
+
* fail at module load. In particular it reaches `../rasterizer/fake.js`
|
|
8
|
+
* directly and never `../rasterizer/index.js`, which pulls in the Skia ingest
|
|
9
|
+
* path.
|
|
10
|
+
*/
|
|
11
|
+
export { ARCH_D, ARCH_E, LETTER, createFakeRasterizer, decodeFakeDocRect, fakePageColor, fakePixelSize, } from '../rasterizer/fake.js';
|
|
12
|
+
/**
|
|
13
|
+
* The reference scenes BUILD STEP 1 produced: the composition contract as data,
|
|
14
|
+
* for a backend suite to replay against its own rasterizer. Pure — it reaches
|
|
15
|
+
* `planRasters` and nothing else.
|
|
16
|
+
*/
|
|
17
|
+
export { buildReferenceScenes, docRectToCanvas, docToCanvas, referenceScene, sceneRasterMeta, sceneRasterRequest, } from './scenes.js';
|
|
18
|
+
export function makePageGeometry(index, size, rotation = 0) {
|
|
19
|
+
return { index, width: size.width, height: size.height, rotation };
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* `scale` is DEVICE PIXELS PER PDF POINT — zoom multiplied by pixel ratio,
|
|
23
|
+
* already combined. Passing a bare zoom here is the single easiest way to write
|
|
24
|
+
* a test that agrees with itself and disagrees with the device.
|
|
25
|
+
*/
|
|
26
|
+
export function makeViewport(visibleDocRect, scale) {
|
|
27
|
+
return { visibleDocRect, scale };
|
|
28
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reference scenes: the composition contract, stated as DATA.
|
|
3
|
+
*
|
|
4
|
+
* BUILD STEP 1 pixel-snapshots the composition of a set of rasters offscreen and
|
|
5
|
+
* calls the result the reference every backend is later checked against. The
|
|
6
|
+
* IMAGES cannot be that reference on their own — they are specific to whichever
|
|
7
|
+
* document produced the bytes, so a PDFium suite and a PDFKit suite would never
|
|
8
|
+
* agree byte for byte. What generalises is the SCENE: which rasters the planner
|
|
9
|
+
* asked for, where the host transform puts them, and which canvas coordinates
|
|
10
|
+
* must be covered by what.
|
|
11
|
+
*
|
|
12
|
+
* A backend suite therefore uses this module as follows:
|
|
13
|
+
*
|
|
14
|
+
* 1. `buildReferenceScenes()` and pick a scene.
|
|
15
|
+
* 2. For each `scene.rasters`, hand `.request` to your own `RasterizerHandle`
|
|
16
|
+
* and `.meta` to your `ingest`.
|
|
17
|
+
* 3. Compose them into a `PdfContent`, draw it through `PdfContentView` inside
|
|
18
|
+
* `<Group transform={referenceTransform(scene.transform)}>` on a
|
|
19
|
+
* `scene.canvas`-sized surface.
|
|
20
|
+
* 4. Assert `scene.probes`: every 'raster' probe must be opaque, every
|
|
21
|
+
* 'nothing' probe must be untouched.
|
|
22
|
+
*
|
|
23
|
+
* SKIA-FREE, like the rest of `src/testing/`. It reaches `planRasters` (pure) and
|
|
24
|
+
* nothing else — the plan is DERIVED here rather than hand-written, so a change
|
|
25
|
+
* to the splitting rule shows up as a changed scene rather than as a scene that
|
|
26
|
+
* quietly stops describing what the planner does.
|
|
27
|
+
*/
|
|
28
|
+
import type { RasterIngestMeta } from '../controller.js';
|
|
29
|
+
import type { DocRect, PageGeometry, PixelSize, RasterPlan, RasterPlanItem, RasterPolicy, RasterRequest, ViewportSnapshot } from '../types.js';
|
|
30
|
+
/**
|
|
31
|
+
* The host's doc -> canvas mapping: `canvas = doc * scale + translate`.
|
|
32
|
+
*
|
|
33
|
+
* Deliberately NOT a Skia matrix. `src/testing/` may not import Skia, and the
|
|
34
|
+
* package is transform-agnostic anyway — this is the arithmetic a consumer's
|
|
35
|
+
* own world transform performs, whatever type it stores it in.
|
|
36
|
+
*/
|
|
37
|
+
export interface SceneTransform {
|
|
38
|
+
scale: number;
|
|
39
|
+
translateX: number;
|
|
40
|
+
translateY: number;
|
|
41
|
+
}
|
|
42
|
+
export interface CanvasPoint {
|
|
43
|
+
x: number;
|
|
44
|
+
y: number;
|
|
45
|
+
}
|
|
46
|
+
export declare function docToCanvas(transform: SceneTransform, docX: number, docY: number): CanvasPoint;
|
|
47
|
+
export declare function docRectToCanvas(transform: SceneTransform, rect: DocRect): {
|
|
48
|
+
x: number;
|
|
49
|
+
y: number;
|
|
50
|
+
width: number;
|
|
51
|
+
height: number;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* What must be true at a doc-space point once the scene is composited.
|
|
55
|
+
*
|
|
56
|
+
* Colour-free on purpose: a probe that named an RGB value would only ever be
|
|
57
|
+
* checkable against the fake. 'raster' means SOMETHING opaque was painted;
|
|
58
|
+
* 'nothing' means the surface was never touched. Those two hold for every
|
|
59
|
+
* backend and catch the placement bugs that matter.
|
|
60
|
+
*/
|
|
61
|
+
export type ProbeExpectation = 'raster' | 'nothing';
|
|
62
|
+
export interface SceneProbe {
|
|
63
|
+
docX: number;
|
|
64
|
+
docY: number;
|
|
65
|
+
expect: ProbeExpectation;
|
|
66
|
+
/** Which bug flips this probe. Not decoration — see the suite. */
|
|
67
|
+
why: string;
|
|
68
|
+
}
|
|
69
|
+
/** One raster the scene needs, in both the shapes a backend harness wants. */
|
|
70
|
+
export interface SceneRaster {
|
|
71
|
+
item: RasterPlanItem;
|
|
72
|
+
request: RasterRequest;
|
|
73
|
+
meta: RasterIngestMeta;
|
|
74
|
+
}
|
|
75
|
+
export interface ReferenceScene {
|
|
76
|
+
id: string;
|
|
77
|
+
description: string;
|
|
78
|
+
/** Offscreen surface size, in canvas pixels. */
|
|
79
|
+
canvas: PixelSize;
|
|
80
|
+
transform: SceneTransform;
|
|
81
|
+
pages: PageGeometry[];
|
|
82
|
+
/** Parallel to `pages`. */
|
|
83
|
+
pageRects: DocRect[];
|
|
84
|
+
viewport: ViewportSnapshot;
|
|
85
|
+
policy: RasterPolicy;
|
|
86
|
+
plan: RasterPlan;
|
|
87
|
+
rasters: SceneRaster[];
|
|
88
|
+
probes: SceneProbe[];
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Plan item -> backend request.
|
|
92
|
+
*
|
|
93
|
+
* `annotations: false` and `background: 'white'` match what the tile path asks
|
|
94
|
+
* for in production (the app draws its own annotations above, and an unfilled
|
|
95
|
+
* buffer composites as black — see `ingest.ts`).
|
|
96
|
+
*/
|
|
97
|
+
export declare function sceneRasterRequest(item: RasterPlanItem, over?: Partial<RasterRequest>): RasterRequest;
|
|
98
|
+
export declare function sceneRasterMeta(item: RasterPlanItem, epoch?: number): RasterIngestMeta;
|
|
99
|
+
export declare function buildReferenceScenes(): ReferenceScene[];
|
|
100
|
+
export declare function referenceScene(id: string): ReferenceScene;
|