@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.
Files changed (161) hide show
  1. package/PdfCanvas.podspec +80 -0
  2. package/README.md +1391 -0
  3. package/android/build.gradle +175 -0
  4. package/android/consumer-rules.pro +3 -0
  5. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  6. package/android/gradle/wrapper/gradle-wrapper.properties +7 -0
  7. package/android/gradle.properties +2 -0
  8. package/android/gradlew +248 -0
  9. package/android/gradlew.bat +98 -0
  10. package/android/settings.gradle +34 -0
  11. package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasCancellationTest.java +226 -0
  12. package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasConcurrencyTest.java +239 -0
  13. package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasDeviceTimingTest.java +251 -0
  14. package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasFailureTest.java +221 -0
  15. package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasJsiTransportTest.java +783 -0
  16. package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasPhaseTimingTest.java +1388 -0
  17. package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasPoisonGuardTest.java +98 -0
  18. package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasQuirksTest.java +324 -0
  19. package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasRasterizerTest.java +775 -0
  20. package/android/src/androidTest/java/tools/reekon/pdfcanvas/PoisonsTheProcess.java +20 -0
  21. package/android/src/androidTest/java/tools/reekon/pdfcanvas/TestPdfs.java +558 -0
  22. package/android/src/jsi/cpp/CMakeLists.txt +33 -0
  23. package/android/src/jsi/cpp/pdfcanvas-jsi.cpp +469 -0
  24. package/android/src/jsi/java/tools/reekon/pdfcanvas/jsi/PdfCanvasPixelBridge.java +140 -0
  25. package/android/src/main/AndroidManifest.xml +7 -0
  26. package/android/src/main/java/tools/reekon/pdfcanvas/PageGeometry.java +45 -0
  27. package/android/src/main/java/tools/reekon/pdfcanvas/PdfCanvasRasterizer.java +564 -0
  28. package/android/src/main/java/tools/reekon/pdfcanvas/PdfErrorCode.java +33 -0
  29. package/android/src/main/java/tools/reekon/pdfcanvas/PdfPlatformQuirks.java +277 -0
  30. package/android/src/main/java/tools/reekon/pdfcanvas/PdfPreflight.java +214 -0
  31. package/android/src/main/java/tools/reekon/pdfcanvas/PdfRasterException.java +30 -0
  32. package/android/src/main/java/tools/reekon/pdfcanvas/PdfRendererPool.java +234 -0
  33. package/android/src/main/java/tools/reekon/pdfcanvas/RasterCancellation.java +47 -0
  34. package/android/src/main/java/tools/reekon/pdfcanvas/RasterPixels.java +143 -0
  35. package/android/src/main/java/tools/reekon/pdfcanvas/RasterRequest.java +95 -0
  36. package/android/src/reactnative/java/tools/reekon/pdfcanvas/rn/PdfCanvasModule.java +626 -0
  37. package/android/src/reactnative/java/tools/reekon/pdfcanvas/rn/PdfCanvasPackage.java +33 -0
  38. package/android/tools/compile-gate.sh +289 -0
  39. package/dist/cache.d.ts +161 -0
  40. package/dist/cache.js +389 -0
  41. package/dist/clock.d.ts +21 -0
  42. package/dist/clock.js +31 -0
  43. package/dist/controller.d.ts +131 -0
  44. package/dist/controller.js +1002 -0
  45. package/dist/geometry.d.ts +96 -0
  46. package/dist/geometry.js +248 -0
  47. package/dist/index.d.ts +52 -0
  48. package/dist/index.js +50 -0
  49. package/dist/index.native.d.ts +21 -0
  50. package/dist/index.native.js +23 -0
  51. package/dist/index.web.d.ts +24 -0
  52. package/dist/index.web.js +26 -0
  53. package/dist/layout.d.ts +78 -0
  54. package/dist/layout.js +152 -0
  55. package/dist/peer-check.d.ts +83 -0
  56. package/dist/peer-check.js +183 -0
  57. package/dist/planner.d.ts +35 -0
  58. package/dist/planner.js +530 -0
  59. package/dist/policy.d.ts +14 -0
  60. package/dist/policy.js +168 -0
  61. package/dist/rasterizer/android.d.ts +121 -0
  62. package/dist/rasterizer/android.js +126 -0
  63. package/dist/rasterizer/buffer.d.ts +18 -0
  64. package/dist/rasterizer/buffer.js +18 -0
  65. package/dist/rasterizer/fake.d.ts +92 -0
  66. package/dist/rasterizer/fake.js +433 -0
  67. package/dist/rasterizer/index.d.ts +38 -0
  68. package/dist/rasterizer/index.js +69 -0
  69. package/dist/rasterizer/index.native.d.ts +19 -0
  70. package/dist/rasterizer/index.native.js +80 -0
  71. package/dist/rasterizer/index.web.d.ts +63 -0
  72. package/dist/rasterizer/index.web.js +75 -0
  73. package/dist/rasterizer/ingest.d.ts +180 -0
  74. package/dist/rasterizer/ingest.js +307 -0
  75. package/dist/rasterizer/ios.d.ts +84 -0
  76. package/dist/rasterizer/ios.js +155 -0
  77. package/dist/rasterizer/native-bridge.d.ts +261 -0
  78. package/dist/rasterizer/native-bridge.js +413 -0
  79. package/dist/rasterizer/web/client.d.ts +84 -0
  80. package/dist/rasterizer/web/client.js +359 -0
  81. package/dist/rasterizer/web/engine.d.ts +69 -0
  82. package/dist/rasterizer/web/engine.js +308 -0
  83. package/dist/rasterizer/web/index.d.ts +26 -0
  84. package/dist/rasterizer/web/index.js +21 -0
  85. package/dist/rasterizer/web/pdfium.d.ts +213 -0
  86. package/dist/rasterizer/web/pdfium.js +170 -0
  87. package/dist/rasterizer/web/protocol.d.ts +138 -0
  88. package/dist/rasterizer/web/protocol.js +37 -0
  89. package/dist/rasterizer/web/session.d.ts +106 -0
  90. package/dist/rasterizer/web/session.js +413 -0
  91. package/dist/rasterizer/web/worker.d.ts +44 -0
  92. package/dist/rasterizer/web/worker.js +76 -0
  93. package/dist/react/PdfContentView.d.ts +98 -0
  94. package/dist/react/PdfContentView.js +189 -0
  95. package/dist/react/index.d.ts +6 -0
  96. package/dist/react/index.js +3 -0
  97. package/dist/react/usePdfDocument.d.ts +134 -0
  98. package/dist/react/usePdfDocument.js +275 -0
  99. package/dist/react/usePdfLayer.d.ts +94 -0
  100. package/dist/react/usePdfLayer.js +388 -0
  101. package/dist/skia/index.d.ts +26 -0
  102. package/dist/skia/index.js +25 -0
  103. package/dist/skia/index.native.d.ts +14 -0
  104. package/dist/skia/index.native.js +16 -0
  105. package/dist/skia/index.web.d.ts +12 -0
  106. package/dist/skia/index.web.js +14 -0
  107. package/dist/skia/resolve.native.d.ts +18 -0
  108. package/dist/skia/resolve.native.js +22 -0
  109. package/dist/skia/resolve.web.d.ts +41 -0
  110. package/dist/skia/resolve.web.js +62 -0
  111. package/dist/skia/skia-api.d.ts +94 -0
  112. package/dist/skia/skia-api.js +113 -0
  113. package/dist/testing/index.d.ts +27 -0
  114. package/dist/testing/index.js +28 -0
  115. package/dist/testing/scenes.d.ts +100 -0
  116. package/dist/testing/scenes.js +267 -0
  117. package/dist/types.d.ts +611 -0
  118. package/dist/types.js +21 -0
  119. package/dist/web-init.d.ts +68 -0
  120. package/dist/web-init.js +64 -0
  121. package/ios/Package.swift +58 -0
  122. package/ios/Sources/PdfCanvasBridge/PdfCanvasModule.mm +743 -0
  123. package/ios/Sources/PdfCanvasBridge/include/PdfCanvasModule.h +36 -0
  124. package/ios/Sources/PdfCanvasCore/PdfCanvasCancellation.m +38 -0
  125. package/ios/Sources/PdfCanvasCore/PdfCanvasDocumentPool.m +389 -0
  126. package/ios/Sources/PdfCanvasCore/PdfCanvasErrorCode.m +57 -0
  127. package/ios/Sources/PdfCanvasCore/PdfCanvasPageGeometry.m +44 -0
  128. package/ios/Sources/PdfCanvasCore/PdfCanvasPageTransform.m +82 -0
  129. package/ios/Sources/PdfCanvasCore/PdfCanvasPixelSink.m +37 -0
  130. package/ios/Sources/PdfCanvasCore/PdfCanvasPreflight.m +158 -0
  131. package/ios/Sources/PdfCanvasCore/PdfCanvasRasterPixels.m +80 -0
  132. package/ios/Sources/PdfCanvasCore/PdfCanvasRasterRequest.m +65 -0
  133. package/ios/Sources/PdfCanvasCore/PdfCanvasRasterizer.m +548 -0
  134. package/ios/Sources/PdfCanvasCore/include/PdfCanvasCancellation.h +48 -0
  135. package/ios/Sources/PdfCanvasCore/include/PdfCanvasCore.h +26 -0
  136. package/ios/Sources/PdfCanvasCore/include/PdfCanvasDocumentPool.h +122 -0
  137. package/ios/Sources/PdfCanvasCore/include/PdfCanvasErrorCode.h +80 -0
  138. package/ios/Sources/PdfCanvasCore/include/PdfCanvasPageGeometry.h +79 -0
  139. package/ios/Sources/PdfCanvasCore/include/PdfCanvasPageTransform.h +129 -0
  140. package/ios/Sources/PdfCanvasCore/include/PdfCanvasPixelSink.h +100 -0
  141. package/ios/Sources/PdfCanvasCore/include/PdfCanvasPreflight.h +65 -0
  142. package/ios/Sources/PdfCanvasCore/include/PdfCanvasRasterPixels.h +111 -0
  143. package/ios/Sources/PdfCanvasCore/include/PdfCanvasRasterRequest.h +85 -0
  144. package/ios/Sources/PdfCanvasCore/include/PdfCanvasRasterizer.h +163 -0
  145. package/ios/Sources/PdfCanvasSlots/PdfCanvasSlots.mm +297 -0
  146. package/ios/Sources/PdfCanvasSlots/include/PdfCanvasSlots.h +140 -0
  147. package/ios/Tests/PdfCanvasCoreTests/ConcurrencyTests.swift +358 -0
  148. package/ios/Tests/PdfCanvasCoreTests/FailureTests.swift +262 -0
  149. package/ios/Tests/PdfCanvasCoreTests/GeometryTests.swift +173 -0
  150. package/ios/Tests/PdfCanvasCoreTests/PreviewTests.swift +114 -0
  151. package/ios/Tests/PdfCanvasCoreTests/RasterAssertions.swift +158 -0
  152. package/ios/Tests/PdfCanvasCoreTests/RasterizerTests.swift +544 -0
  153. package/ios/Tests/PdfCanvasCoreTests/SinkTests.swift +287 -0
  154. package/ios/Tests/PdfCanvasCoreTests/SlotsTests.swift +396 -0
  155. package/ios/Tests/PdfCanvasCoreTests/TestPdfs.swift +639 -0
  156. package/ios/Tests/PdfCanvasCoreTests/TimingTests.swift +179 -0
  157. package/ios/Tests/PdfCanvasCoreTests/TransformTests.swift +283 -0
  158. package/package.json +144 -0
  159. package/react-native.config.js +40 -0
  160. package/scripts/check-skia-peer.mjs +86 -0
  161. package/scripts/skia-peer-check.mjs +363 -0
@@ -0,0 +1,267 @@
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 { planRasters } from '../planner.js';
29
+ import { resolvePolicy } from '../policy.js';
30
+ import { LETTER } from '../rasterizer/fake.js';
31
+ export function docToCanvas(transform, docX, docY) {
32
+ return {
33
+ x: docX * transform.scale + transform.translateX,
34
+ y: docY * transform.scale + transform.translateY,
35
+ };
36
+ }
37
+ export function docRectToCanvas(transform, rect) {
38
+ const origin = docToCanvas(transform, rect.x, rect.y);
39
+ return {
40
+ x: origin.x,
41
+ y: origin.y,
42
+ width: rect.width * transform.scale,
43
+ height: rect.height * transform.scale,
44
+ };
45
+ }
46
+ /**
47
+ * Plan item -> backend request.
48
+ *
49
+ * `annotations: false` and `background: 'white'` match what the tile path asks
50
+ * for in production (the app draws its own annotations above, and an unfilled
51
+ * buffer composites as black — see `ingest.ts`).
52
+ */
53
+ export function sceneRasterRequest(item, over = {}) {
54
+ return {
55
+ page: item.page,
56
+ docRect: item.docRect,
57
+ scale: item.scale,
58
+ annotations: false,
59
+ background: 'white',
60
+ ...over,
61
+ };
62
+ }
63
+ export function sceneRasterMeta(item, epoch = 1) {
64
+ return {
65
+ key: item.key,
66
+ page: item.page,
67
+ role: item.role,
68
+ docRect: item.docRect,
69
+ scale: item.scale,
70
+ epoch,
71
+ };
72
+ }
73
+ function toSceneRasters(plan, epoch = 1) {
74
+ return plan.items.map(item => ({
75
+ item,
76
+ request: sceneRasterRequest(item),
77
+ meta: sceneRasterMeta(item, epoch),
78
+ }));
79
+ }
80
+ function letterPage() {
81
+ return {
82
+ pages: [
83
+ { index: 0, width: LETTER.width, height: LETTER.height, rotation: 0 },
84
+ ],
85
+ pageRects: [{ x: 0, y: 0, width: LETTER.width, height: LETTER.height }],
86
+ };
87
+ }
88
+ /**
89
+ * Scene 1 — a whole LETTER page at fit zoom, drawn under a translated,
90
+ * downscaled host transform.
91
+ *
92
+ * The planner emits a BASE and no detail here (the base is already at the
93
+ * display scale, so detail would be the same pixels twice), which makes this the
94
+ * scene that proves the base layer alone paints the page and paints it inside
95
+ * the page rect.
96
+ */
97
+ function fitScene() {
98
+ const { pages, pageRects } = letterPage();
99
+ const policy = resolvePolicy();
100
+ const viewport = {
101
+ visibleDocRect: { x: 0, y: 0, width: LETTER.width, height: LETTER.height },
102
+ scale: 0.5,
103
+ };
104
+ const plan = planRasters({
105
+ viewport,
106
+ pages,
107
+ pageRects,
108
+ visiblePages: [0],
109
+ policy,
110
+ });
111
+ const transform = {
112
+ scale: 0.5,
113
+ translateX: 20,
114
+ translateY: 20,
115
+ };
116
+ return {
117
+ id: 'letter-fit',
118
+ description: 'One LETTER page at fit zoom under a translated 0.5x host transform. ' +
119
+ 'Base raster only — the planner suppresses detail when the base is ' +
120
+ 'already at the display scale.',
121
+ canvas: { width: 400, height: 440 },
122
+ transform,
123
+ pages,
124
+ pageRects,
125
+ viewport,
126
+ policy,
127
+ plan,
128
+ rasters: toSceneRasters(plan),
129
+ probes: [
130
+ {
131
+ docX: LETTER.width / 2,
132
+ docY: LETTER.height / 2,
133
+ expect: 'raster',
134
+ why: 'the base raster must cover the middle of the page',
135
+ },
136
+ {
137
+ docX: 1,
138
+ docY: 1,
139
+ expect: 'raster',
140
+ why: 'the top-left of the page is inside the base rect; a base drawn ' +
141
+ 'letterboxed (fit="contain") or offset by the host translate leaves ' +
142
+ 'it blank',
143
+ },
144
+ {
145
+ docX: LETTER.width - 1,
146
+ docY: LETTER.height - 1,
147
+ expect: 'raster',
148
+ why: 'the bottom-right corner of the page, same reasoning',
149
+ },
150
+ {
151
+ docX: -20,
152
+ docY: -20,
153
+ expect: 'nothing',
154
+ why: 'outside the page rect. A base that ignored its doc rect and filled ' +
155
+ 'the canvas would paint here.',
156
+ },
157
+ ],
158
+ };
159
+ }
160
+ /**
161
+ * Scene 2 — a detail set the planner SPLITS into a 2x2 grid, over a base that
162
+ * stays ONE piece.
163
+ *
164
+ * `maxTextureDimension` is lowered so the split is the PLANNER's decision, driven
165
+ * by the same code path a 4096-limited device takes on a large sheet. The canvas
166
+ * is 1:1 with the detail scale, so a correctly-placed piece lands on whole canvas
167
+ * pixels and the composite can be compared against the same region rastered as a
168
+ * single piece.
169
+ *
170
+ * THE NUMBER IS 512 BECAUSE THE SHAPE IS CHOSEN, NOT INHERITED. It was 320, and
171
+ * `min(maxPieceDimension, maxTextureDimension)` therefore tiled the BASE into two
172
+ * pieces as well (the base is 309x400 device px here) — a change no probe in this
173
+ * scene could see, because the second base piece begins at doc y ~394 and the
174
+ * canvas stops at doc y 360. A backend suite is diffed against these scenes, so
175
+ * how many rasters one asks for is part of the contract and may not drift out of
176
+ * a default. 512 divides the 600px detail target and leaves the base whole; the
177
+ * detail grid is byte-for-byte the one 320 produced, because both give
178
+ * `ceil(600 / usable) == 2`. `composition.test.ts` pins both counts.
179
+ *
180
+ * The host translate is POSITIVE and the canvas is larger than the detail
181
+ * target, which is what buys the scene a 'nothing' probe. With the target
182
+ * mapped exactly onto the canvas — the obvious framing — every canvas pixel is
183
+ * inside the page, the whole-page base paints all of them, and no coordinate is
184
+ * left for "must be untouched" to describe. A scene with only 'raster' probes
185
+ * asserts nothing about a raster that overruns its rect.
186
+ */
187
+ function splitDetailScene() {
188
+ const { pages, pageRects } = letterPage();
189
+ const policy = resolvePolicy({ maxTextureDimension: 512 });
190
+ const viewport = {
191
+ visibleDocRect: { x: 100, y: 100, width: 200, height: 200 },
192
+ scale: 2,
193
+ };
194
+ const plan = planRasters({
195
+ viewport,
196
+ pages,
197
+ pageRects,
198
+ visiblePages: [0],
199
+ policy,
200
+ });
201
+ // detailOverflow 0.25 inflates {100,100,200,200} to {50,50,300,300}, entirely
202
+ // inside the page, so the covered rect is exactly that. The translate puts
203
+ // doc (0,0) at canvas (20,20), so the target occupies canvas [120, 720) on
204
+ // both axes and a 20px margin of NEGATIVE doc space stays visible.
205
+ const transform = {
206
+ scale: 2,
207
+ translateX: 20,
208
+ translateY: 20,
209
+ };
210
+ return {
211
+ id: 'letter-split-detail',
212
+ description: 'A 300x300pt detail target at scale 2, split into a 2x2 grid by a ' +
213
+ '512px texture limit, over a single whole-page base. Canvas is 1:1 with ' +
214
+ 'the detail scale, with a margin of off-page canvas around it.',
215
+ canvas: { width: 740, height: 740 },
216
+ transform,
217
+ pages,
218
+ pageRects,
219
+ viewport,
220
+ policy,
221
+ plan,
222
+ rasters: toSceneRasters(plan),
223
+ probes: [
224
+ {
225
+ docX: 50.25,
226
+ docY: 50.25,
227
+ expect: 'raster',
228
+ why: 'top-left corner of the detail target',
229
+ },
230
+ {
231
+ docX: 349.75,
232
+ docY: 349.75,
233
+ expect: 'raster',
234
+ why: 'bottom-right corner of the detail target',
235
+ },
236
+ {
237
+ docX: 200,
238
+ docY: 200,
239
+ expect: 'raster',
240
+ why: 'the interior seam. Pieces OVERLAP rather than abut, so no doc ' +
241
+ 'coordinate inside the target may be uncovered.',
242
+ },
243
+ {
244
+ docX: -5,
245
+ docY: -5,
246
+ expect: 'nothing',
247
+ why: 'off the page entirely, and the only kind of point that can be ' +
248
+ 'unpainted in this scene — the base covers the whole sheet. A base ' +
249
+ 'or a detail piece that ignored its doc rect and filled the canvas ' +
250
+ 'paints here, and every other probe in this scene would still pass.',
251
+ },
252
+ ],
253
+ };
254
+ }
255
+ export function buildReferenceScenes() {
256
+ return [fitScene(), splitDetailScene()];
257
+ }
258
+ export function referenceScene(id) {
259
+ const scene = buildReferenceScenes().find(s => s.id === id);
260
+ if (!scene) {
261
+ throw new Error(`No reference scene '${id}'. Known: ` +
262
+ buildReferenceScenes()
263
+ .map(s => s.id)
264
+ .join(', '));
265
+ }
266
+ return scene;
267
+ }