@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
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,611 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The type contract for @reekon-tools/react-native-pdf-canvas.
|
|
3
|
+
*
|
|
4
|
+
* THE ONE RULE: the transform decides WHEN to rasterize, never WHAT to draw.
|
|
5
|
+
*
|
|
6
|
+
* Every rect in this file is in DOC SPACE — PDF points, y-down, origin at the
|
|
7
|
+
* page's top-left. That is exactly what PDFium's CPDF_Page::GetDisplayMatrix()
|
|
8
|
+
* emits, so a PDFium backend is a pass-through and every other backend adapts
|
|
9
|
+
* to it. There is deliberately no `coordinateSystem` option: two conventions in
|
|
10
|
+
* one package is how device pixel ratio gets applied twice.
|
|
11
|
+
*
|
|
12
|
+
* Nothing here imports React, react-native, or reanimated. The only external
|
|
13
|
+
* type is SkImage, and it is type-only.
|
|
14
|
+
*/
|
|
15
|
+
import type { SkImage } from '@shopify/react-native-skia';
|
|
16
|
+
/** A rect in doc space: PDF points, y-down, origin top-left. */
|
|
17
|
+
export interface DocRect {
|
|
18
|
+
x: number;
|
|
19
|
+
y: number;
|
|
20
|
+
width: number;
|
|
21
|
+
height: number;
|
|
22
|
+
}
|
|
23
|
+
export interface DocSize {
|
|
24
|
+
width: number;
|
|
25
|
+
height: number;
|
|
26
|
+
}
|
|
27
|
+
/** Device-pixel size. Distinct from DocSize so the two cannot be swapped. */
|
|
28
|
+
export interface PixelSize {
|
|
29
|
+
width: number;
|
|
30
|
+
height: number;
|
|
31
|
+
}
|
|
32
|
+
export type PageRotation = 0 | 90 | 180 | 270;
|
|
33
|
+
/** A page's intrinsic geometry, in PDF points, already accounting for /Rotate. */
|
|
34
|
+
export interface PageGeometry {
|
|
35
|
+
index: number;
|
|
36
|
+
/** CropBox width in points, after rotation is applied. */
|
|
37
|
+
width: number;
|
|
38
|
+
/** CropBox height in points, after rotation is applied. */
|
|
39
|
+
height: number;
|
|
40
|
+
rotation: PageRotation;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* What the host tells us about where it is looking. Pushed at settle rate; the
|
|
44
|
+
* package never reads a shared value to obtain it.
|
|
45
|
+
*
|
|
46
|
+
* `scale` is DEVICE PIXELS PER PDF POINT — i.e. zoom * pixelRatio, already
|
|
47
|
+
* combined. The package never derives it, because a host may wrap the scene in
|
|
48
|
+
* a further transform the package cannot see.
|
|
49
|
+
*/
|
|
50
|
+
export interface ViewportSnapshot {
|
|
51
|
+
visibleDocRect: DocRect;
|
|
52
|
+
scale: number;
|
|
53
|
+
}
|
|
54
|
+
export type PdfSource = {
|
|
55
|
+
uri: string;
|
|
56
|
+
password?: string;
|
|
57
|
+
} | {
|
|
58
|
+
data: Uint8Array;
|
|
59
|
+
password?: string;
|
|
60
|
+
};
|
|
61
|
+
export type PdfErrorCode = 'not-found' | 'password-required' | 'password-incorrect' | 'corrupt' | 'unsupported' | 'cancelled' | 'out-of-memory' | 'backend-failure';
|
|
62
|
+
export declare class PdfError extends Error {
|
|
63
|
+
readonly code: PdfErrorCode;
|
|
64
|
+
constructor(code: PdfErrorCode, message: string, options?: {
|
|
65
|
+
cause?: unknown;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Byte order of a raster. The three backends produce different orderings and
|
|
70
|
+
* the descriptor travels with the bytes rather than being assumed:
|
|
71
|
+
* PDFium emits BGRA natively (or RGBA with FPDF_REVERSE_BYTE_ORDER), Android
|
|
72
|
+
* Bitmap ARGB_8888 is RGBA in memory, CGBitmapContext is configured explicitly.
|
|
73
|
+
*/
|
|
74
|
+
export type PixelFormat = 'rgba8888' | 'bgra8888';
|
|
75
|
+
/**
|
|
76
|
+
* Whether a raster's colour channels have been multiplied by its alpha channel.
|
|
77
|
+
*
|
|
78
|
+
* SAME STANDING AS `PixelFormat`, and required for the same reason: the engines
|
|
79
|
+
* genuinely disagree, so the descriptor travels with the bytes rather than being
|
|
80
|
+
* assumed. Getting the byte order wrong swaps red and blue; getting this wrong
|
|
81
|
+
* darkens or brightens every partially-transparent pixel, which is subtler and
|
|
82
|
+
* therefore worse.
|
|
83
|
+
*
|
|
84
|
+
* `'premultiplied'` — each channel is already scaled by alpha, so a 62%-covered
|
|
85
|
+
* white edge is stored `[158, 158, 158, 158]`. Android's `Bitmap` ARGB_8888 is
|
|
86
|
+
* this by default (`isPremultiplied()` is true unless a caller opts out) and so
|
|
87
|
+
* is a `CGBitmapContext` created `kCGImageAlphaPremultipliedFirst`.
|
|
88
|
+
*
|
|
89
|
+
* `'straight'` — the channels carry the colour and alpha carries the coverage
|
|
90
|
+
* independently, so the same edge is stored `[255, 255, 255, 158]`. MEASURED:
|
|
91
|
+
* PDFium reads `[255, 255, 255, 157]` and pdf.js `[255, 255, 255, 160]` for an
|
|
92
|
+
* antialiased opaque-white edge over a transparent page; premultiplied would
|
|
93
|
+
* have been `~[157, 157, 157, 157]`. A canvas `getImageData` buffer is likewise
|
|
94
|
+
* straight (a 50% grey patch reads `[128, 128, 128, 128]`).
|
|
95
|
+
*
|
|
96
|
+
* NOT DEFAULTABLE, on purpose. Two of the three engines this package knows about
|
|
97
|
+
* emit `'straight'` and one emits `'premultiplied'`, so there is no default that
|
|
98
|
+
* is right more often than it is wrong — and a default is exactly how a global
|
|
99
|
+
* `AlphaType.Premul` constant survived in `ingest.ts` until the first backend
|
|
100
|
+
* that renders `background: 'transparent'` would have been silently wrong.
|
|
101
|
+
*
|
|
102
|
+
* INVISIBLE UNDER `background: 'white'`. A fully opaque pixel is byte-identical
|
|
103
|
+
* in both encodings, which is why this can be stated wrongly for a long time
|
|
104
|
+
* without anything going visibly bad. It only bites on a transparent
|
|
105
|
+
* background — which is in the `RasterRequest` contract.
|
|
106
|
+
*/
|
|
107
|
+
export type AlphaEncoding = 'premultiplied' | 'straight';
|
|
108
|
+
/**
|
|
109
|
+
* How a backend's bytes reached JS.
|
|
110
|
+
*
|
|
111
|
+
* Not a capability in the "can it do X" sense — it is a fact about the build and
|
|
112
|
+
* the device, resolved at runtime, and the reason it is surfaced at all is that
|
|
113
|
+
* the difference between these two is ~85x on a tile. A silent fallback is how a
|
|
114
|
+
* transport rewrite ships as no change whatsoever, so both
|
|
115
|
+
* `RasterizerCapabilities.transport` and `RasterPhaseTimings.transport` carry it:
|
|
116
|
+
* the first says what was negotiated, the second says what each tile actually
|
|
117
|
+
* used.
|
|
118
|
+
*
|
|
119
|
+
* - `base64` — a string across the bridge, decoded in JS. Works everywhere.
|
|
120
|
+
* - `jsi-arraybuffer` — an ArrayBuffer wrapping native memory, no string and no
|
|
121
|
+
* decode.
|
|
122
|
+
* - `direct` — the backend produced the bytes in JS already (the fake one).
|
|
123
|
+
*/
|
|
124
|
+
export type RasterTransport = 'base64' | 'jsi-arraybuffer' | 'direct';
|
|
125
|
+
/**
|
|
126
|
+
* What a backend spent getting its pixels as far as `RasterPixels`.
|
|
127
|
+
*
|
|
128
|
+
* OPTIONAL AND PURELY OBSERVATIONAL. Nothing in the package reads these to make
|
|
129
|
+
* a decision; the controller copies them into a `raster-phases` diagnostic and
|
|
130
|
+
* that is all. A backend that reports nothing is not degraded in any way.
|
|
131
|
+
*
|
|
132
|
+
* It exists because the cost of a tile is split across two modules that never
|
|
133
|
+
* see each other: the BACKEND owns the transport (a bridge hop, a base64
|
|
134
|
+
* decode), and the CONTROLLER owns the ingest (`Skia.Data.fromBytes` +
|
|
135
|
+
* `Skia.Image.MakeImage`). Subtracting one total from another is how the
|
|
136
|
+
* three terms got conflated in the first place, and the difference matters
|
|
137
|
+
* enormously — a transport rewrite deletes the first two and cannot touch the
|
|
138
|
+
* third.
|
|
139
|
+
*/
|
|
140
|
+
export interface RasterPhaseTimings {
|
|
141
|
+
/**
|
|
142
|
+
* The transport round trip, INCLUDING whatever the backend did at the far end
|
|
143
|
+
* of it.
|
|
144
|
+
*
|
|
145
|
+
* On Android this is `await native.render(...)`: bridge marshalling in both
|
|
146
|
+
* directions plus the ~27.5 ms p50 of Java work (open, render, extract,
|
|
147
|
+
* base64-encode) that the instrumented `PdfCanvasPhaseTimingTest` measures
|
|
148
|
+
* separately. Subtracting the Java figure from this one is what isolates
|
|
149
|
+
* marshalling.
|
|
150
|
+
*
|
|
151
|
+
* CONTAMINATED BY CONCURRENCY, and unavoidably so: this is wall time across an
|
|
152
|
+
* `await`, so with more than one render lane it also contains whatever the JS
|
|
153
|
+
* thread did for the OTHER lanes in the meantime. `decodeMs` and `ingestMs`
|
|
154
|
+
* are synchronous blocks and carry no such contamination.
|
|
155
|
+
*/
|
|
156
|
+
bridgeMs: number;
|
|
157
|
+
/** Decoding the transport payload into bytes. Zero for a backend with none. */
|
|
158
|
+
decodeMs: number;
|
|
159
|
+
/**
|
|
160
|
+
* Characters of transport payload — the base64 string's length on Android.
|
|
161
|
+
* Zero when a backend moves bytes without stringifying them, which is the
|
|
162
|
+
* whole point of measuring it.
|
|
163
|
+
*/
|
|
164
|
+
payloadChars: number;
|
|
165
|
+
/**
|
|
166
|
+
* Whether `bytes` is already exactly its own ArrayBuffer.
|
|
167
|
+
*
|
|
168
|
+
* When false, ingest must copy the whole buffer before Skia sees it (see
|
|
169
|
+
* `isTightBuffer`). Reported rather than inferred so a transport change can be
|
|
170
|
+
* held to it.
|
|
171
|
+
*/
|
|
172
|
+
tight: boolean;
|
|
173
|
+
/**
|
|
174
|
+
* Which transport produced these bytes.
|
|
175
|
+
*
|
|
176
|
+
* OPTIONAL, because it was added after the field shipped and a backend that
|
|
177
|
+
* has only ever had one transport has nothing to disambiguate. Present on
|
|
178
|
+
* Android, where two transports coexist and the negotiated one can differ from
|
|
179
|
+
* the one a given tile got — a store that failed mid-session falls the whole
|
|
180
|
+
* rasterizer back, and the tile that triggered it reports `base64` while the
|
|
181
|
+
* capability still says otherwise until the next negotiation.
|
|
182
|
+
*/
|
|
183
|
+
transport?: RasterTransport;
|
|
184
|
+
}
|
|
185
|
+
export interface RasterPixels {
|
|
186
|
+
bytes: Uint8Array;
|
|
187
|
+
width: number;
|
|
188
|
+
height: number;
|
|
189
|
+
/** Required. Skia.Image.MakeImage needs it and a wrong value shears the image. */
|
|
190
|
+
rowBytes: number;
|
|
191
|
+
format: PixelFormat;
|
|
192
|
+
/**
|
|
193
|
+
* Required, and deliberately not optional — see `AlphaEncoding`. Every backend
|
|
194
|
+
* states what it produces so none can inherit a default that is wrong for it.
|
|
195
|
+
*/
|
|
196
|
+
alpha: AlphaEncoding;
|
|
197
|
+
/**
|
|
198
|
+
* ADDITIVE AND OPTIONAL. `RasterPixels` is the shipped seam that the web
|
|
199
|
+
* backend and the fake rasterizer also implement, so this had to cost them
|
|
200
|
+
* nothing: omitting it is the documented normal case, and no consumer branches
|
|
201
|
+
* on its presence except the controller's diagnostic emit.
|
|
202
|
+
*/
|
|
203
|
+
timings?: RasterPhaseTimings;
|
|
204
|
+
}
|
|
205
|
+
export interface RasterRequest {
|
|
206
|
+
page: number;
|
|
207
|
+
/** The doc-space rect to render. Backends render exactly this rect. */
|
|
208
|
+
docRect: DocRect;
|
|
209
|
+
/** Device pixels per PDF point. */
|
|
210
|
+
scale: number;
|
|
211
|
+
/**
|
|
212
|
+
* Whether PDF-embedded annotations are drawn into the raster.
|
|
213
|
+
*
|
|
214
|
+
* TRUE for file rasters (thumbnails, print) — that is what the outgoing
|
|
215
|
+
* react-native-pdf-thumbnail did via PDFKit, and the app depends on it.
|
|
216
|
+
* FALSE for tiles, because the Skia layer draws the app's own annotations and
|
|
217
|
+
* they would double-draw.
|
|
218
|
+
*/
|
|
219
|
+
annotations: boolean;
|
|
220
|
+
background: 'white' | 'transparent';
|
|
221
|
+
}
|
|
222
|
+
export interface RasterizerCapabilities {
|
|
223
|
+
/** Can render PDF-embedded annotations into the raster. */
|
|
224
|
+
annotations: boolean;
|
|
225
|
+
/** Honours the abort signal mid-render, not merely between requests. */
|
|
226
|
+
interruptibleRender: boolean;
|
|
227
|
+
text: boolean;
|
|
228
|
+
search: boolean;
|
|
229
|
+
links: boolean;
|
|
230
|
+
/**
|
|
231
|
+
* How many `render` calls this backend can usefully have in flight at once.
|
|
232
|
+
*
|
|
233
|
+
* A PROPERTY OF THE BACKEND, never a constant in the controller. Android's
|
|
234
|
+
* ceiling is the `PdfRendererPool` size — each slot is one more `PdfRenderer`
|
|
235
|
+
* on its own dup'd file descriptor, and a request beyond that blocks on the
|
|
236
|
+
* pool's queue rather than doing anything. A backend with one document handle
|
|
237
|
+
* and no pool declares 1 and is scheduled exactly as it is today.
|
|
238
|
+
*
|
|
239
|
+
* OPTIONAL, DEFAULTING TO 1, and that default is the safe one: an unaware
|
|
240
|
+
* backend keeps the strictly-sequential behaviour it was written against. The
|
|
241
|
+
* controller reads it as `min(bound, plan.items.length)`.
|
|
242
|
+
*
|
|
243
|
+
* IT IS NOT A PROMISE OF SPEEDUP, AND ON ANDROID IT IS A PROMISE OF THE
|
|
244
|
+
* OPPOSITE. Measured on an LG V60 (Snapdragon 865, API 33): AOSP serialises
|
|
245
|
+
* `PdfRenderer` behind a process-wide PDFium lock, so `openPage` throughput is
|
|
246
|
+
* FLAT at 0.084 / 0.073 / 0.074 ops/ms across 1 / 2 / 4 threads while an
|
|
247
|
+
* identical pure-Java workload scales 2.8x — and the lock CONVOYS, so
|
|
248
|
+
* contending for it makes each call slower rather than merely unparallel. The
|
|
249
|
+
* Android backend therefore declares 1. It used to declare 2 to PIPELINE its
|
|
250
|
+
* base64 decode against the next tile's native work; the JSI transport deleted
|
|
251
|
+
* that decode, and re-measuring on the JSI path put every lane count above 1
|
|
252
|
+
* behind it on real documents. See `AndroidRasterizerOptions.poolSize` for the
|
|
253
|
+
* numbers.
|
|
254
|
+
*
|
|
255
|
+
* A backend whose rasterizing genuinely parallelises should still declare it.
|
|
256
|
+
* The lane machinery is not Android-specific and the controller has no opinion;
|
|
257
|
+
* this field is where a backend states what it can actually do.
|
|
258
|
+
*/
|
|
259
|
+
maxConcurrentRenders?: number;
|
|
260
|
+
/**
|
|
261
|
+
* How this backend moves pixels into JS, as NEGOTIATED — resolved once when
|
|
262
|
+
* the rasterizer is constructed, not per render.
|
|
263
|
+
*
|
|
264
|
+
* OPTIONAL and observational. Nothing in the package branches on it; it exists
|
|
265
|
+
* so a host can SEE which path is live, because the fallback is invisible from
|
|
266
|
+
* the outside and the two differ by roughly 85x per tile. A backend that omits
|
|
267
|
+
* it is not making any claim.
|
|
268
|
+
*/
|
|
269
|
+
transport?: RasterTransport;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* The backend seam. Three implementations: platform rasterizers on native, a
|
|
273
|
+
* PDFium worker on web, a deterministic fake for tests. Injected per document,
|
|
274
|
+
* defaulting to the platform module.
|
|
275
|
+
*/
|
|
276
|
+
export interface PageRasterizer {
|
|
277
|
+
/** Stable identifier, surfaced in diagnostics so a bug report names a backend. */
|
|
278
|
+
readonly id: string;
|
|
279
|
+
readonly capabilities: RasterizerCapabilities;
|
|
280
|
+
open(source: PdfSource): Promise<RasterizerHandle>;
|
|
281
|
+
}
|
|
282
|
+
export interface RasterizerHandle {
|
|
283
|
+
readonly pageCount: number;
|
|
284
|
+
/** Cheap: must not load a page. */
|
|
285
|
+
pageGeometry(index: number): PageGeometry;
|
|
286
|
+
render(request: RasterRequest, signal?: AbortSignal): Promise<RasterPixels>;
|
|
287
|
+
close(): void;
|
|
288
|
+
}
|
|
289
|
+
export type RasterRole = 'base' | 'detail';
|
|
290
|
+
/**
|
|
291
|
+
* One rasterized piece, ready to draw.
|
|
292
|
+
*
|
|
293
|
+
* INVARIANT: `docRect` is exactly the rect the bytes were rendered for. It is
|
|
294
|
+
* never inset. Adjacent detail pieces OVERLAP rather than abut, because
|
|
295
|
+
* RN Skia's <Image> has no src-rect prop — both draw paths hardcode src to the
|
|
296
|
+
* whole image, so drawing an inset rect would squash the full bitmap into it
|
|
297
|
+
* and misregister every piece against the layers above.
|
|
298
|
+
*/
|
|
299
|
+
export interface PdfRaster {
|
|
300
|
+
readonly key: string;
|
|
301
|
+
readonly page: number;
|
|
302
|
+
readonly role: RasterRole;
|
|
303
|
+
readonly image: SkImage;
|
|
304
|
+
readonly docRect: DocRect;
|
|
305
|
+
/** Device pixels per PDF point this raster was rendered at. */
|
|
306
|
+
readonly scale: number;
|
|
307
|
+
readonly epoch: number;
|
|
308
|
+
/**
|
|
309
|
+
* What this raster costs while it is MOUNTED, which is ~2x its pixels.
|
|
310
|
+
*
|
|
311
|
+
* Two allocations, not one. `SkImages::RasterFromData(info, data, rowBytes)`
|
|
312
|
+
* — what `Skia.Image.MakeImage` calls — SHARES the `SkData` and keeps it alive
|
|
313
|
+
* for the image's whole life, and the first time that image is drawn on a GPU
|
|
314
|
+
* canvas Skia uploads a budgeted texture of the same dimensions. Both are
|
|
315
|
+
* resident for as long as the raster is in the tree.
|
|
316
|
+
*
|
|
317
|
+
* `rasterByteLength` books both copies. See its comment for what that does to
|
|
318
|
+
* the meaning of `cacheBudgetBytes`.
|
|
319
|
+
*/
|
|
320
|
+
readonly bytes: number;
|
|
321
|
+
}
|
|
322
|
+
export interface PdfPageContent {
|
|
323
|
+
readonly page: number;
|
|
324
|
+
/** Where this page sits in doc space, per the layout function. */
|
|
325
|
+
readonly pageRect: DocRect;
|
|
326
|
+
/**
|
|
327
|
+
* The whole page at fit scale, as one or more pieces whose union is exactly
|
|
328
|
+
* `pageRect`.
|
|
329
|
+
*
|
|
330
|
+
* ANTI-BLANK INVARIANT: never EMPTIED once populated. It was "never nulled
|
|
331
|
+
* once rastered" while this was a single raster; tiling the base makes it a
|
|
332
|
+
* set, and the promise is the same one — for a page inside the live window
|
|
333
|
+
* there is always something drawable under the detail layer. (A page that
|
|
334
|
+
* leaves the window by more than the base radius is deliberately emptied; that
|
|
335
|
+
* is the promise being kept for the pages you are LOOKING at.)
|
|
336
|
+
*
|
|
337
|
+
* Usually length 1. It splits for the same reason detail does — see
|
|
338
|
+
* `RasterPolicy.maxPieceDimension`, which bounds the in-flight transient of a
|
|
339
|
+
* single raster, and `maxTextureDimension`, which a piece may never exceed.
|
|
340
|
+
*/
|
|
341
|
+
readonly base: readonly PdfRaster[];
|
|
342
|
+
/** Sharp overlays for the current viewport. Usually length 1. */
|
|
343
|
+
readonly detail: readonly PdfRaster[];
|
|
344
|
+
/** Previous detail set, held across the swap and released next commit. */
|
|
345
|
+
readonly retiring: readonly PdfRaster[];
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* A NEW FROZEN OBJECT PER EPOCH. Identity change is the swap signal.
|
|
349
|
+
*
|
|
350
|
+
* A "referentially stable handle mutated in place" cannot work: RN Skia's
|
|
351
|
+
* shallowEq is reference equality on every key but children, and prepareUpdate
|
|
352
|
+
* returns null on equality — the new SkImages would never enter the Skia tree,
|
|
353
|
+
* the raster would complete, memory would be spent, and nothing on screen would
|
|
354
|
+
* change. That commit is the expensive thing the cadence policy exists to make
|
|
355
|
+
* rare.
|
|
356
|
+
*/
|
|
357
|
+
export interface PdfContent {
|
|
358
|
+
readonly pages: readonly PdfPageContent[];
|
|
359
|
+
readonly epoch: number;
|
|
360
|
+
/** True while any planned raster for the current viewport is still missing. */
|
|
361
|
+
readonly settling: boolean;
|
|
362
|
+
}
|
|
363
|
+
/** Where pages sit in doc space. The package has no opinion. */
|
|
364
|
+
export type PdfPageLayout = (pages: readonly DocSize[]) => DocRect[];
|
|
365
|
+
/**
|
|
366
|
+
* Every constant the cadence depends on. All of these are DERIVED, not
|
|
367
|
+
* measured — see docs. A consumer with letter-size text documents on a DPR-1
|
|
368
|
+
* display swaps two fields rather than forking the package.
|
|
369
|
+
*/
|
|
370
|
+
export interface RasterPolicy {
|
|
371
|
+
/**
|
|
372
|
+
* Multiplier on the fit-zoom display scale for the whole-page base raster.
|
|
373
|
+
*
|
|
374
|
+
* NOT a fixed max-edge cap. A 2048px cap is calibrated for Letter pages; on
|
|
375
|
+
* an ARCH-D sheet it yields 0.790 px/pt against a fit display scale of 0.677,
|
|
376
|
+
* so the base is drawn MINIFIED — the aliasing regime, and unfixable because
|
|
377
|
+
* ImageCmd::draw hardcodes kStrict_SrcRectConstraint, which disables mipmaps.
|
|
378
|
+
*/
|
|
379
|
+
baseQuality: number;
|
|
380
|
+
/** Hard ceiling on base raster pixels, after baseQuality. */
|
|
381
|
+
baseMaxPixels: number;
|
|
382
|
+
/** Fraction of the visible rect added on each side of a detail raster. */
|
|
383
|
+
detailOverflow: number;
|
|
384
|
+
/**
|
|
385
|
+
* HARDWARE ceiling on a piece's pixel DIMENSION, in device px. A piece may
|
|
386
|
+
* never exceed it: past the limit the texture upload FAILS and the piece draws
|
|
387
|
+
* nothing. 4096 is the only near-universal value.
|
|
388
|
+
*
|
|
389
|
+
* Distinct from `maxPieceDimension` below, and the two must not be merged.
|
|
390
|
+
* This one is a property of the device; that one is a choice about memory.
|
|
391
|
+
*/
|
|
392
|
+
maxTextureDimension: number;
|
|
393
|
+
/**
|
|
394
|
+
* MEMORY policy on a piece's pixel DIMENSION, in device px, applied to the
|
|
395
|
+
* base layer and the detail layer alike.
|
|
396
|
+
*
|
|
397
|
+
* Nothing breaks when a piece exceeds it — this is not a limit the hardware
|
|
398
|
+
* has an opinion about. It exists to bound the TRANSIENT: one piece's bytes
|
|
399
|
+
* exist simultaneously as a platform bitmap, a JS `Uint8Array` and an
|
|
400
|
+
* `SkData`, so the whole-flow peak scales with the largest SINGLE raster in
|
|
401
|
+
* flight, not with the epoch's total. Splitting the same total area into more,
|
|
402
|
+
* smaller pieces leaves the resident bytes unchanged and cuts that peak.
|
|
403
|
+
*
|
|
404
|
+
* The effective cap is `min(maxPieceDimension, maxTextureDimension)`.
|
|
405
|
+
*/
|
|
406
|
+
maxPieceDimension: number;
|
|
407
|
+
/** Hard ceiling on a single raster request, in pixels. */
|
|
408
|
+
maxRasterPixels: number;
|
|
409
|
+
/**
|
|
410
|
+
* Re-raster when currentScale / epochScale leaves [1/band, band].
|
|
411
|
+
* 1.414 at >=200ppi effective; 1.2 on DPR-1, where pixels are visible before
|
|
412
|
+
* any magnification.
|
|
413
|
+
*/
|
|
414
|
+
sharpnessBand: number;
|
|
415
|
+
/**
|
|
416
|
+
* pdf.js's MOVEMENT_THRESHOLD, adopted verbatim. Re-raster when the padding
|
|
417
|
+
* remaining AHEAD of the motion is less than this fraction of the padding
|
|
418
|
+
* behind it.
|
|
419
|
+
*/
|
|
420
|
+
movementThreshold: number;
|
|
421
|
+
/** Quiet-timer delay after a scale change (continuous input only). */
|
|
422
|
+
quietZoomMs: number;
|
|
423
|
+
/** Quiet-timer delay after a pan-only change. */
|
|
424
|
+
quietPanMs: number;
|
|
425
|
+
/** Force one raster this long after the last committed one under continued input. */
|
|
426
|
+
maxStaleMs: number;
|
|
427
|
+
/**
|
|
428
|
+
* Floor between epochs, enforced on BOTH paths.
|
|
429
|
+
*
|
|
430
|
+
* PDFKit's draw(with:to:) and Android's PdfRenderer.render are synchronous and
|
|
431
|
+
* uninterruptible once entered, so an abort only prevents the NEXT piece
|
|
432
|
+
* starting. Without a floor a user pinch-nudging three times while reading a
|
|
433
|
+
* dimension never sees a detail raster at all — every render is superseded
|
|
434
|
+
* before it lands. Default to the measured p95 raster time.
|
|
435
|
+
*/
|
|
436
|
+
minEpochIntervalMs: number;
|
|
437
|
+
/** Soft cap on cached raster bytes. NOT a memory budget — see docs. */
|
|
438
|
+
cacheBudgetBytes: number;
|
|
439
|
+
/** Keep the previous detail set mounted after its replacement paints. */
|
|
440
|
+
retainPreviousDetail: boolean;
|
|
441
|
+
}
|
|
442
|
+
/** One raster the planner wants to exist. */
|
|
443
|
+
export interface RasterPlanItem {
|
|
444
|
+
key: string;
|
|
445
|
+
page: number;
|
|
446
|
+
role: RasterRole;
|
|
447
|
+
docRect: DocRect;
|
|
448
|
+
scale: number;
|
|
449
|
+
pixels: PixelSize;
|
|
450
|
+
}
|
|
451
|
+
export interface RasterPlan {
|
|
452
|
+
items: RasterPlanItem[];
|
|
453
|
+
/** Scale the detail items were planned at, for the sharpness predicate. */
|
|
454
|
+
detailScale: number;
|
|
455
|
+
/**
|
|
456
|
+
* Bounding rect of everything this plan INTENDS to cover — the detail items
|
|
457
|
+
* plus `baseCoveredDocRect`. Null when the plan covers nothing.
|
|
458
|
+
*
|
|
459
|
+
* It is what the plan wanted, not what a run of it achieved, so it is NOT what
|
|
460
|
+
* an epoch records: see `EpochState.coveredRects`.
|
|
461
|
+
*/
|
|
462
|
+
coveredDocRect: DocRect | null;
|
|
463
|
+
/**
|
|
464
|
+
* The part of `coveredDocRect` that no detail ITEM accounts for: the pages
|
|
465
|
+
* whose base is already at least as dense as the detail would be, and which
|
|
466
|
+
* therefore get no detail raster at all (see `planRasters`).
|
|
467
|
+
*
|
|
468
|
+
* Reported separately because the controller rebuilds the epoch's coverage
|
|
469
|
+
* from the rasters it actually PRODUCED, and there is no produced raster to
|
|
470
|
+
* rebuild this half from — the base is standing in for the detail here. A hole
|
|
471
|
+
* in the base itself is caught by the controller's per-piece base check
|
|
472
|
+
* instead, which forces a 'layout' epoch.
|
|
473
|
+
*/
|
|
474
|
+
baseCoveredDocRect: DocRect | null;
|
|
475
|
+
}
|
|
476
|
+
/** What the planner needs to know about the current epoch to decide. */
|
|
477
|
+
export interface EpochState {
|
|
478
|
+
detailScale: number;
|
|
479
|
+
/**
|
|
480
|
+
* Where this epoch was RESPONSIBLE for coverage: the doc rect of every page it
|
|
481
|
+
* planned.
|
|
482
|
+
*
|
|
483
|
+
* Coverage is judged over these regions and nowhere else, because most of the
|
|
484
|
+
* doc plane is not page. `continuousVertical` leaves a 12pt gutter between
|
|
485
|
+
* pages and centres narrower ones, so a viewport that spans two pages — the
|
|
486
|
+
* ordinary reading posture — always has doc area in it that NO raster can ever
|
|
487
|
+
* cover. Judging the whole visible rect would report that as a hole and fire
|
|
488
|
+
* 'coverage' on every settle for the life of the document.
|
|
489
|
+
*
|
|
490
|
+
* It is also what answers "am I looking somewhere this epoch never planned":
|
|
491
|
+
* the pages BEYOND these rects are the ones a scroll reaches, and their bounds
|
|
492
|
+
* is the containment test that used to be made against the covered rect.
|
|
493
|
+
*/
|
|
494
|
+
pageRects: readonly DocRect[];
|
|
495
|
+
/**
|
|
496
|
+
* EXACTLY the doc rects this epoch has detail-quality coverage of — one entry
|
|
497
|
+
* per raster that was produced, plus the plan's `baseCoveredDocRect` where a
|
|
498
|
+
* base is standing in for detail.
|
|
499
|
+
*
|
|
500
|
+
* A SET, not the bounding rect, and that is the whole point. A tiled epoch
|
|
501
|
+
* that loses one piece (backend error, budget refusal) keeps its bounding rect
|
|
502
|
+
* intact — remove any single cell of a 2x2 grid and the other three still span
|
|
503
|
+
* it — so a bounding-rect predicate reports full coverage over a hole and
|
|
504
|
+
* never re-fires for it. `rectsCover` is an exact union test and does not.
|
|
505
|
+
*
|
|
506
|
+
* Empty means nothing is covered, which fires 'coverage' unconditionally.
|
|
507
|
+
*/
|
|
508
|
+
coveredRects: readonly DocRect[];
|
|
509
|
+
committedAtMs: number;
|
|
510
|
+
}
|
|
511
|
+
export interface PdfController {
|
|
512
|
+
/**
|
|
513
|
+
* Continuous input — wheel, trackpad, momentum. Restarts a quiet timer.
|
|
514
|
+
* Cheap and idempotent: an identical plan is a no-op, because any state
|
|
515
|
+
* change costs a full re-record of the CONSUMER'S entire scene.
|
|
516
|
+
*/
|
|
517
|
+
hint(viewport: ViewportSnapshot): void;
|
|
518
|
+
/**
|
|
519
|
+
* Discrete end — gesture lift-off, or an animation's completion callback.
|
|
520
|
+
* Evaluates and dispatches immediately, subject to minEpochIntervalMs.
|
|
521
|
+
*
|
|
522
|
+
* This MUST be called by the host. Settle cannot be sniffed: a withDecay
|
|
523
|
+
* fling at 2000px/s animates for ~38 seconds at reanimated's default
|
|
524
|
+
* deceleration, changing the shared value every frame, and `_animation` is
|
|
525
|
+
* cleared only on interruption — never on natural completion.
|
|
526
|
+
*/
|
|
527
|
+
settle(viewport: ViewportSnapshot): void;
|
|
528
|
+
/** Gesture begin. Suppresses the continuous path entirely. */
|
|
529
|
+
suppress(): void;
|
|
530
|
+
/** Gesture end. Does not itself dispatch; call settle() for that. */
|
|
531
|
+
resume(): void;
|
|
532
|
+
}
|
|
533
|
+
export type PdfDiagnostic = {
|
|
534
|
+
type: 'raster';
|
|
535
|
+
key: string;
|
|
536
|
+
ms: number;
|
|
537
|
+
pixels: number;
|
|
538
|
+
backend: string;
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* The same tile as `raster`, broken into the terms a transport change can and
|
|
542
|
+
* cannot remove. Emitted ONLY when the backend reported `RasterPixels.timings`
|
|
543
|
+
* — so the fake backend, and therefore the whole vitest suite, never sees it.
|
|
544
|
+
*
|
|
545
|
+
* Note that `raster.ms` is NOT `totalMs`: `raster` is emitted BEFORE ingest
|
|
546
|
+
* runs, so it covers bridge + decode and stops there. This one is the whole
|
|
547
|
+
* per-tile cost the JS thread can see.
|
|
548
|
+
*/
|
|
549
|
+
| {
|
|
550
|
+
type: 'raster-phases';
|
|
551
|
+
key: string;
|
|
552
|
+
backend: string;
|
|
553
|
+
/** Transport round trip including the native work. See RasterPhaseTimings. */
|
|
554
|
+
bridgeMs: number;
|
|
555
|
+
/** The payload decode — a hand-rolled base64 loop on Android today. */
|
|
556
|
+
decodeMs: number;
|
|
557
|
+
/**
|
|
558
|
+
* The `ingest` call and nothing else — `Skia.Data.fromBytes` +
|
|
559
|
+
* `Skia.Image.MakeImage`. The cache put is deliberately outside it.
|
|
560
|
+
*
|
|
561
|
+
* THE TERM NO TRANSPORT CHANGE CAN DELETE. `fromBytes` is
|
|
562
|
+
* `SkData::MakeWithCopy` and the image retains that copy; both survive any
|
|
563
|
+
* rewrite of how the bytes reached JS.
|
|
564
|
+
*/
|
|
565
|
+
ingestMs: number;
|
|
566
|
+
totalMs: number;
|
|
567
|
+
payloadChars: number;
|
|
568
|
+
/** Decoded raster bytes, i.e. what a zero-copy transport would move. */
|
|
569
|
+
byteLength: number;
|
|
570
|
+
/** False means ingest had to copy the buffer before Skia saw it. */
|
|
571
|
+
tight: boolean;
|
|
572
|
+
} | {
|
|
573
|
+
type: 'cache-hit';
|
|
574
|
+
key: string;
|
|
575
|
+
} | {
|
|
576
|
+
type: 'evict';
|
|
577
|
+
key: string;
|
|
578
|
+
bytes: number;
|
|
579
|
+
reason: 'budget' | 'page-window' | 'epoch';
|
|
580
|
+
} | {
|
|
581
|
+
type: 'epoch';
|
|
582
|
+
epoch: number;
|
|
583
|
+
items: number;
|
|
584
|
+
reason: EpochReason;
|
|
585
|
+
/**
|
|
586
|
+
* Planned pieces this epoch did NOT mount — a backend failure or a budget
|
|
587
|
+
* refusal. The field probe could only see `settling`, which is one bit for
|
|
588
|
+
* three different situations; this is the number that says which.
|
|
589
|
+
*/
|
|
590
|
+
missing: number;
|
|
591
|
+
} | {
|
|
592
|
+
type: 'skip';
|
|
593
|
+
reason: 'min-interval' | 'identical-plan' | 'suppressed';
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* A re-evaluation the CONTROLLER armed for itself, because an epoch committed
|
|
597
|
+
* with a hole in it and no host input is guaranteed to arrive. See
|
|
598
|
+
* `commit`'s coverage note in `controller.ts`.
|
|
599
|
+
*/
|
|
600
|
+
| {
|
|
601
|
+
type: 'repair';
|
|
602
|
+
missing: number;
|
|
603
|
+
inMs: number;
|
|
604
|
+
attempt: number;
|
|
605
|
+
} | {
|
|
606
|
+
type: 'error';
|
|
607
|
+
key: string;
|
|
608
|
+
error: PdfError;
|
|
609
|
+
};
|
|
610
|
+
export type EpochReason = 'initial' | 'coverage' | 'sharpness' | 'look-ahead' | 'stale' | 'layout';
|
|
611
|
+
export type Sampling = 'linear' | 'nearest' | 'cubic';
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The type contract for @reekon-tools/react-native-pdf-canvas.
|
|
3
|
+
*
|
|
4
|
+
* THE ONE RULE: the transform decides WHEN to rasterize, never WHAT to draw.
|
|
5
|
+
*
|
|
6
|
+
* Every rect in this file is in DOC SPACE — PDF points, y-down, origin at the
|
|
7
|
+
* page's top-left. That is exactly what PDFium's CPDF_Page::GetDisplayMatrix()
|
|
8
|
+
* emits, so a PDFium backend is a pass-through and every other backend adapts
|
|
9
|
+
* to it. There is deliberately no `coordinateSystem` option: two conventions in
|
|
10
|
+
* one package is how device pixel ratio gets applied twice.
|
|
11
|
+
*
|
|
12
|
+
* Nothing here imports React, react-native, or reanimated. The only external
|
|
13
|
+
* type is SkImage, and it is type-only.
|
|
14
|
+
*/
|
|
15
|
+
export class PdfError extends Error {
|
|
16
|
+
constructor(code, message, options) {
|
|
17
|
+
super(message, options);
|
|
18
|
+
this.name = 'PdfError';
|
|
19
|
+
this.code = code;
|
|
20
|
+
}
|
|
21
|
+
}
|