@reekon-tools/react-native-pdf-canvas 0.1.0 → 0.2.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 (129) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +57 -0
  3. package/PdfCanvas.podspec +68 -39
  4. package/README.md +213 -1316
  5. package/android/build.gradle +67 -54
  6. package/android/consumer-rules.pro +7 -0
  7. package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasNativeTest.java +254 -0
  8. package/android/src/androidTest/java/tools/reekon/pdfcanvas/TestPdfs.java +60 -468
  9. package/android/src/main/cpp/CMakeLists.txt +54 -0
  10. package/android/src/main/cpp/pdfcanvas-jni.cpp +262 -0
  11. package/android/src/main/java/tools/reekon/pdfcanvas/PdfCanvasNative.java +167 -0
  12. package/android/src/main/java/tools/reekon/pdfcanvas/PdfErrorCode.java +14 -4
  13. package/android/src/main/java/tools/reekon/pdfcanvas/PdfRasterException.java +10 -4
  14. package/android/src/reactnative/java/tools/reekon/pdfcanvas/rn/PdfCanvasModule.java +160 -376
  15. package/android/tools/compile-gate.sh +81 -101
  16. package/dist/controller.d.ts +14 -0
  17. package/dist/controller.js +4 -3
  18. package/dist/index.native.d.ts +5 -0
  19. package/dist/index.native.js +5 -0
  20. package/dist/index.web.d.ts +10 -0
  21. package/dist/index.web.js +10 -0
  22. package/dist/rasterizer/index.native.d.ts +8 -12
  23. package/dist/rasterizer/index.native.js +15 -21
  24. package/dist/rasterizer/native-bridge.d.ts +51 -75
  25. package/dist/rasterizer/native-bridge.js +49 -33
  26. package/dist/rasterizer/native.d.ts +45 -0
  27. package/dist/rasterizer/native.js +123 -0
  28. package/dist/react/usePdfDocument.d.ts +5 -5
  29. package/dist/react/usePdfLayer.d.ts +10 -1
  30. package/dist/react/usePdfLayer.js +10 -2
  31. package/dist/skia/index.native.d.ts +7 -3
  32. package/dist/skia/index.native.js +7 -3
  33. package/dist/skia/index.web.d.ts +4 -2
  34. package/dist/skia/index.web.js +4 -2
  35. package/dist/testing/scenes.d.ts +3 -3
  36. package/dist/testing/scenes.js +4 -4
  37. package/dist/types.d.ts +36 -42
  38. package/ios/Sources/PdfCanvasBridge/PdfCanvasModule.mm +234 -536
  39. package/native/.clangd +5 -0
  40. package/native/CMakeLists.txt +89 -0
  41. package/native/core/include/pdfcanvas/document.h +90 -0
  42. package/native/core/include/pdfcanvas/error.h +47 -0
  43. package/native/core/include/pdfcanvas/jsi.h +18 -0
  44. package/native/core/include/pdfcanvas/jsi_config.h +30 -0
  45. package/native/core/include/pdfcanvas/library.h +28 -0
  46. package/native/core/include/pdfcanvas/pixels.h +92 -0
  47. package/native/core/include/pdfcanvas/service.h +87 -0
  48. package/native/core/include/pdfcanvas/slots.h +113 -0
  49. package/native/core/include/pdfcanvas/types.h +110 -0
  50. package/native/core/pdfcanvas-core.cmake +22 -0
  51. package/native/core/src/document.cpp +516 -0
  52. package/native/core/src/error.cpp +29 -0
  53. package/native/core/src/jsi.cpp +66 -0
  54. package/native/core/src/library.cpp +52 -0
  55. package/native/core/src/pixels.cpp +45 -0
  56. package/native/core/src/service.cpp +156 -0
  57. package/native/core/src/slots.cpp +139 -0
  58. package/native/tests/fixtures.cpp +451 -0
  59. package/native/tests/fixtures.h +96 -0
  60. package/native/tests/harness.h +122 -0
  61. package/native/tests/main.cpp +64 -0
  62. package/native/tests/pixels_util.h +126 -0
  63. package/native/tests/test_document.cpp +485 -0
  64. package/native/tests/test_service.cpp +130 -0
  65. package/native/tests/test_slots.cpp +250 -0
  66. package/package.json +38 -8
  67. package/react-native.config.js +5 -2
  68. package/scripts/fetch-pdfium.mjs +487 -0
  69. package/scripts/pdfium-manifest.json +46 -0
  70. package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasCancellationTest.java +0 -226
  71. package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasConcurrencyTest.java +0 -239
  72. package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasDeviceTimingTest.java +0 -251
  73. package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasFailureTest.java +0 -221
  74. package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasJsiTransportTest.java +0 -783
  75. package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasPhaseTimingTest.java +0 -1388
  76. package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasPoisonGuardTest.java +0 -98
  77. package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasQuirksTest.java +0 -324
  78. package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasRasterizerTest.java +0 -775
  79. package/android/src/androidTest/java/tools/reekon/pdfcanvas/PoisonsTheProcess.java +0 -20
  80. package/android/src/jsi/cpp/CMakeLists.txt +0 -33
  81. package/android/src/jsi/cpp/pdfcanvas-jsi.cpp +0 -469
  82. package/android/src/jsi/java/tools/reekon/pdfcanvas/jsi/PdfCanvasPixelBridge.java +0 -140
  83. package/android/src/main/java/tools/reekon/pdfcanvas/PageGeometry.java +0 -45
  84. package/android/src/main/java/tools/reekon/pdfcanvas/PdfCanvasRasterizer.java +0 -564
  85. package/android/src/main/java/tools/reekon/pdfcanvas/PdfPlatformQuirks.java +0 -277
  86. package/android/src/main/java/tools/reekon/pdfcanvas/PdfPreflight.java +0 -214
  87. package/android/src/main/java/tools/reekon/pdfcanvas/PdfRendererPool.java +0 -234
  88. package/android/src/main/java/tools/reekon/pdfcanvas/RasterCancellation.java +0 -47
  89. package/android/src/main/java/tools/reekon/pdfcanvas/RasterPixels.java +0 -143
  90. package/android/src/main/java/tools/reekon/pdfcanvas/RasterRequest.java +0 -95
  91. package/dist/rasterizer/android.d.ts +0 -121
  92. package/dist/rasterizer/android.js +0 -126
  93. package/dist/rasterizer/ios.d.ts +0 -84
  94. package/dist/rasterizer/ios.js +0 -155
  95. package/ios/Package.swift +0 -58
  96. package/ios/Sources/PdfCanvasCore/PdfCanvasCancellation.m +0 -38
  97. package/ios/Sources/PdfCanvasCore/PdfCanvasDocumentPool.m +0 -389
  98. package/ios/Sources/PdfCanvasCore/PdfCanvasErrorCode.m +0 -57
  99. package/ios/Sources/PdfCanvasCore/PdfCanvasPageGeometry.m +0 -44
  100. package/ios/Sources/PdfCanvasCore/PdfCanvasPageTransform.m +0 -82
  101. package/ios/Sources/PdfCanvasCore/PdfCanvasPixelSink.m +0 -37
  102. package/ios/Sources/PdfCanvasCore/PdfCanvasPreflight.m +0 -158
  103. package/ios/Sources/PdfCanvasCore/PdfCanvasRasterPixels.m +0 -80
  104. package/ios/Sources/PdfCanvasCore/PdfCanvasRasterRequest.m +0 -65
  105. package/ios/Sources/PdfCanvasCore/PdfCanvasRasterizer.m +0 -548
  106. package/ios/Sources/PdfCanvasCore/include/PdfCanvasCancellation.h +0 -48
  107. package/ios/Sources/PdfCanvasCore/include/PdfCanvasCore.h +0 -26
  108. package/ios/Sources/PdfCanvasCore/include/PdfCanvasDocumentPool.h +0 -122
  109. package/ios/Sources/PdfCanvasCore/include/PdfCanvasErrorCode.h +0 -80
  110. package/ios/Sources/PdfCanvasCore/include/PdfCanvasPageGeometry.h +0 -79
  111. package/ios/Sources/PdfCanvasCore/include/PdfCanvasPageTransform.h +0 -129
  112. package/ios/Sources/PdfCanvasCore/include/PdfCanvasPixelSink.h +0 -100
  113. package/ios/Sources/PdfCanvasCore/include/PdfCanvasPreflight.h +0 -65
  114. package/ios/Sources/PdfCanvasCore/include/PdfCanvasRasterPixels.h +0 -111
  115. package/ios/Sources/PdfCanvasCore/include/PdfCanvasRasterRequest.h +0 -85
  116. package/ios/Sources/PdfCanvasCore/include/PdfCanvasRasterizer.h +0 -163
  117. package/ios/Sources/PdfCanvasSlots/PdfCanvasSlots.mm +0 -297
  118. package/ios/Sources/PdfCanvasSlots/include/PdfCanvasSlots.h +0 -140
  119. package/ios/Tests/PdfCanvasCoreTests/ConcurrencyTests.swift +0 -358
  120. package/ios/Tests/PdfCanvasCoreTests/FailureTests.swift +0 -262
  121. package/ios/Tests/PdfCanvasCoreTests/GeometryTests.swift +0 -173
  122. package/ios/Tests/PdfCanvasCoreTests/PreviewTests.swift +0 -114
  123. package/ios/Tests/PdfCanvasCoreTests/RasterAssertions.swift +0 -158
  124. package/ios/Tests/PdfCanvasCoreTests/RasterizerTests.swift +0 -544
  125. package/ios/Tests/PdfCanvasCoreTests/SinkTests.swift +0 -287
  126. package/ios/Tests/PdfCanvasCoreTests/SlotsTests.swift +0 -396
  127. package/ios/Tests/PdfCanvasCoreTests/TestPdfs.swift +0 -639
  128. package/ios/Tests/PdfCanvasCoreTests/TimingTests.swift +0 -179
  129. package/ios/Tests/PdfCanvasCoreTests/TransformTests.swift +0 -283
@@ -0,0 +1,487 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Fetches the pinned PDFium binaries this package renders with.
4
+ *
5
+ * ONE SCRIPT, THREE CONSUMERS, ONE MANIFEST.
6
+ *
7
+ * node scripts/fetch-pdfium.mjs android [--abis arm64-v8a,armeabi-v7a,x86,x86_64]
8
+ * -> native/.pdfium/android/include/*.h
9
+ * native/.pdfium/android/jniLibs/<abi>/libpdfium.so
10
+ * Called by android/build.gradle before CMake configures. The ABI list is
11
+ * whatever the app asked for (`reactNativeArchitectures`), so the AAR never
12
+ * carries an ABI the app does not.
13
+ *
14
+ * node scripts/fetch-pdfium.mjs ios
15
+ * -> native/.pdfium/ios/include/*.h
16
+ * native/.pdfium/ios/PDFium.xcframework (device arm64 + simulator arm64/x64)
17
+ * Called from PdfCanvas.podspec while CocoaPods evaluates it. Needs macOS:
18
+ * the xcframework is assembled with `lipo`, `install_name_tool` and
19
+ * `xcodebuild -create-xcframework`, which exist wherever `pod install` runs.
20
+ *
21
+ * node scripts/fetch-pdfium.mjs host [--target win-x64|mac-arm64|mac-x64|linux-x64]
22
+ * -> native/.pdfium/host/{include,lib,bin}
23
+ * For `native/tests`, the laptop proof of the C++ core. Defaults to the
24
+ * machine it runs on.
25
+ *
26
+ * EVERY DOWNLOAD IS VERIFIED against the SHA-256 in `pdfium-manifest.json`
27
+ * before a single byte of it is extracted, and a mismatch deletes the file and
28
+ * fails the build. The digests come from the release's own SLSA attestation
29
+ * (`pdfium-attestation.json`), not from a first download — a manifest that
30
+ * records whatever the network happened to return is a manifest that pins
31
+ * nothing.
32
+ *
33
+ * IDEMPOTENT AND CHEAP WHEN DONE. A `.stamp` file records the release tag each
34
+ * output was produced from; a run that finds a matching stamp does nothing, so
35
+ * calling this from every Gradle configure and every `pod install` costs a stat.
36
+ *
37
+ * OFFLINE / AIR-GAPPED BUILDS. `PDFCANVAS_PDFIUM_MIRROR` replaces the GitHub
38
+ * base URL (same file names, e.g. an internal artifact store), and
39
+ * `PDFCANVAS_PDFIUM_CACHE` names a directory of already-downloaded `.tgz` files
40
+ * that is consulted before the network. Both still verify the digest.
41
+ *
42
+ * NO DEPENDENCIES, by the same rule as the rest of `scripts/`: this runs from a
43
+ * consumer's install where none of this package's devDependencies exist.
44
+ */
45
+
46
+ import {createHash} from 'node:crypto';
47
+ import {
48
+ createWriteStream,
49
+ existsSync,
50
+ mkdirSync,
51
+ readFileSync,
52
+ renameSync,
53
+ rmSync,
54
+ statSync,
55
+ writeFileSync,
56
+ cpSync,
57
+ readdirSync,
58
+ } from 'node:fs';
59
+ import {tmpdir} from 'node:os';
60
+ import {dirname, join, resolve} from 'node:path';
61
+ import {execFileSync} from 'node:child_process';
62
+ import {fileURLToPath} from 'node:url';
63
+ import {pipeline} from 'node:stream/promises';
64
+ import {Readable} from 'node:stream';
65
+
66
+ const here = dirname(fileURLToPath(import.meta.url));
67
+ const root = resolve(here, '..');
68
+ const manifestPath = join(here, 'pdfium-manifest.json');
69
+ const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
70
+
71
+ /** Where every output lands. Gitignored; the stamp files make it self-healing. */
72
+ const outputRoot =
73
+ process.env.PDFCANVAS_PDFIUM_OUT ?? join(root, 'native', '.pdfium');
74
+
75
+ const baseUrl = (
76
+ process.env.PDFCANVAS_PDFIUM_MIRROR ?? manifest.baseUrl
77
+ ).replace(/\/+$/, '');
78
+
79
+ /* ------------------------------------------------------------------ *
80
+ * Arguments
81
+ * ------------------------------------------------------------------ */
82
+
83
+ const [, , command, ...rest] = process.argv;
84
+ const flags = new Map();
85
+ for (let i = 0; i < rest.length; i++) {
86
+ const arg = rest[i];
87
+ if (arg.startsWith('--')) {
88
+ const key = arg.slice(2);
89
+ const value =
90
+ rest[i + 1] !== undefined && !rest[i + 1].startsWith('--')
91
+ ? rest[++i]
92
+ : 'true';
93
+ flags.set(key, value);
94
+ }
95
+ }
96
+
97
+ function usage(code) {
98
+ console.error(
99
+ 'usage: fetch-pdfium.mjs android [--abis a,b] | ios | host [--target t] | print-version',
100
+ );
101
+ process.exit(code);
102
+ }
103
+
104
+ /* ------------------------------------------------------------------ *
105
+ * Download + verify
106
+ * ------------------------------------------------------------------ */
107
+
108
+ function sha256File(path) {
109
+ const hash = createHash('sha256');
110
+ hash.update(readFileSync(path));
111
+ return hash.digest('hex');
112
+ }
113
+
114
+ function artifactUrl(name) {
115
+ return `${baseUrl}/${manifest.release}/pdfium-${name}.tgz`;
116
+ }
117
+
118
+ /**
119
+ * Returns the path of a verified `.tgz` for `name`, downloading if needed.
120
+ *
121
+ * Downloads go to a temp file and are renamed into place only after the digest
122
+ * matches, so a half-written or tampered file can never be mistaken for a good
123
+ * one on the next run.
124
+ */
125
+ async function fetchArtifact(name) {
126
+ const entry = manifest.artifacts[name];
127
+ if (!entry) {
128
+ throw new Error(`pdfium-manifest.json has no artifact named "${name}".`);
129
+ }
130
+ const cacheDir =
131
+ process.env.PDFCANVAS_PDFIUM_CACHE ?? join(outputRoot, 'downloads');
132
+ mkdirSync(cacheDir, {recursive: true});
133
+ const fileName = `pdfium-${name}-${manifest.release.replace('/', '-')}.tgz`;
134
+ const cached = join(cacheDir, fileName);
135
+
136
+ if (existsSync(cached)) {
137
+ const digest = sha256File(cached);
138
+ if (digest === entry.sha256) return cached;
139
+ console.warn(
140
+ `[pdfium] cached ${fileName} has digest ${digest}; re-downloading`,
141
+ );
142
+ rmSync(cached);
143
+ }
144
+
145
+ const url = artifactUrl(name);
146
+ console.log(`[pdfium] downloading ${url}`);
147
+ const response = await fetch(url, {redirect: 'follow'});
148
+ if (!response.ok || response.body === null) {
149
+ throw new Error(`Could not download ${url}: HTTP ${response.status}`);
150
+ }
151
+ const partial = `${cached}.part`;
152
+ await pipeline(Readable.fromWeb(response.body), createWriteStream(partial));
153
+
154
+ const digest = sha256File(partial);
155
+ if (digest !== entry.sha256) {
156
+ rmSync(partial);
157
+ throw new Error(
158
+ `SHA-256 mismatch for ${url}:\n expected ${entry.sha256}\n got ${digest}\n` +
159
+ 'Refusing to use it. If the release was re-uploaded, update ' +
160
+ 'scripts/pdfium-manifest.json from the release attestation.',
161
+ );
162
+ }
163
+ renameSync(partial, cached);
164
+ return cached;
165
+ }
166
+
167
+ /**
168
+ * The tar to extract with.
169
+ *
170
+ * On Windows this is ALWAYS the system bsdtar, named by absolute path. A
171
+ * developer shell here usually has MSYS (Git Bash) tar first on PATH, and GNU
172
+ * tar reads `C:\...` as `host:path` and tries to connect to a machine called
173
+ * `C` — the failure reads "Cannot connect to C: resolve failed", which is a
174
+ * long way from "wrong tar". Windows 10 1803+ ships bsdtar at System32.
175
+ */
176
+ function tarBinary() {
177
+ if (process.platform === 'win32') {
178
+ const system = join(
179
+ process.env.SystemRoot ?? 'C:\\Windows',
180
+ 'System32',
181
+ 'tar.exe',
182
+ );
183
+ if (existsSync(system)) return system;
184
+ }
185
+ return 'tar';
186
+ }
187
+
188
+ function extract(tgz, into) {
189
+ mkdirSync(into, {recursive: true});
190
+ // `tar` exists on every platform this runs on: macOS and Linux natively, and
191
+ // Windows 10+ ships bsdtar. Node has no built-in tar and this script may not
192
+ // take a dependency.
193
+ execFileSync(tarBinary(), ['-xzf', tgz, '-C', into], {stdio: 'inherit'});
194
+ }
195
+
196
+ /* ------------------------------------------------------------------ *
197
+ * Stamps
198
+ * ------------------------------------------------------------------ */
199
+
200
+ function stampPath(dir) {
201
+ return join(dir, '.stamp');
202
+ }
203
+
204
+ function isFresh(dir, key) {
205
+ const stamp = stampPath(dir);
206
+ return existsSync(stamp) && readFileSync(stamp, 'utf8').trim() === key;
207
+ }
208
+
209
+ function writeStamp(dir, key) {
210
+ writeFileSync(stampPath(dir), `${key}\n`);
211
+ }
212
+
213
+ function resetDir(dir) {
214
+ rmSync(dir, {recursive: true, force: true});
215
+ mkdirSync(dir, {recursive: true});
216
+ }
217
+
218
+ /** Copies PDFium's public headers, dropping the `.orig` file the release carries. */
219
+ function copyHeaders(from, to) {
220
+ mkdirSync(to, {recursive: true});
221
+ for (const name of readdirSync(from)) {
222
+ if (name.endsWith('.orig')) continue;
223
+ cpSync(join(from, name), join(to, name), {recursive: true});
224
+ }
225
+ }
226
+
227
+ /* ------------------------------------------------------------------ *
228
+ * android
229
+ * ------------------------------------------------------------------ */
230
+
231
+ const ANDROID_ARTIFACT_BY_ABI = Object.fromEntries(
232
+ Object.entries(manifest.artifacts)
233
+ .filter(([, value]) => typeof value.abi === 'string')
234
+ .map(([name, value]) => [value.abi, name]),
235
+ );
236
+
237
+ async function android() {
238
+ const abis = (
239
+ flags.get('abis') ?? Object.keys(ANDROID_ARTIFACT_BY_ABI).join(',')
240
+ )
241
+ .split(',')
242
+ .map(s => s.trim())
243
+ .filter(Boolean);
244
+ for (const abi of abis) {
245
+ if (!ANDROID_ARTIFACT_BY_ABI[abi]) {
246
+ throw new Error(
247
+ `No PDFium build for ABI "${abi}". Known: ${Object.keys(ANDROID_ARTIFACT_BY_ABI).join(', ')}`,
248
+ );
249
+ }
250
+ }
251
+ const out = join(outputRoot, 'android');
252
+ const key = `${manifest.release} ${[...abis].sort().join(',')}`;
253
+ if (isFresh(out, key)) {
254
+ console.log(
255
+ `[pdfium] android: ${manifest.release} for ${abis.join(', ')} already present`,
256
+ );
257
+ return;
258
+ }
259
+ resetDir(out);
260
+ let headersCopied = false;
261
+ for (const abi of abis) {
262
+ const tgz = await fetchArtifact(ANDROID_ARTIFACT_BY_ABI[abi]);
263
+ const scratch = join(out, `.extract-${abi}`);
264
+ extract(tgz, scratch);
265
+ const libDir = join(out, 'jniLibs', abi);
266
+ mkdirSync(libDir, {recursive: true});
267
+ cpSync(join(scratch, 'lib', 'libpdfium.so'), join(libDir, 'libpdfium.so'));
268
+ if (!headersCopied) {
269
+ copyHeaders(join(scratch, 'include'), join(out, 'include'));
270
+ cpSync(join(scratch, 'LICENSE'), join(out, 'LICENSE'));
271
+ cpSync(join(scratch, 'licenses'), join(out, 'licenses'), {
272
+ recursive: true,
273
+ });
274
+ headersCopied = true;
275
+ }
276
+ rmSync(scratch, {recursive: true, force: true});
277
+ }
278
+ writeStamp(out, key);
279
+ console.log(`[pdfium] android: ready at ${out}`);
280
+ }
281
+
282
+ /* ------------------------------------------------------------------ *
283
+ * ios
284
+ * ------------------------------------------------------------------ */
285
+
286
+ /**
287
+ * Wraps a bare dylib as an iOS `PDFium.framework` (shallow bundle) so it can be
288
+ * embedded and code-signed like any other framework. Mirrors what
289
+ * espresso3389/pdfium-xcframework's build.sh does, minus the macOS/Catalyst
290
+ * slices this package does not ship.
291
+ */
292
+ function makeIosFramework(dylib, frameworkDir, headersDir, platform) {
293
+ resetDir(frameworkDir);
294
+ const binary = join(frameworkDir, 'PDFium');
295
+ cpSync(dylib, binary);
296
+ execFileSync('chmod', ['+w', binary]);
297
+ execFileSync(
298
+ 'install_name_tool',
299
+ ['-id', '@rpath/PDFium.framework/PDFium', binary],
300
+ {
301
+ stdio: 'inherit',
302
+ },
303
+ );
304
+ copyHeaders(headersDir, join(frameworkDir, 'Headers'));
305
+ const shortVersion = manifest.version.split('.').slice(0, 3).join('.');
306
+ writeFileSync(
307
+ join(frameworkDir, 'Info.plist'),
308
+ `<?xml version="1.0" encoding="UTF-8"?>
309
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
310
+ <plist version="1.0">
311
+ <dict>
312
+ <key>CFBundleExecutable</key>
313
+ <string>PDFium</string>
314
+ <key>CFBundleIdentifier</key>
315
+ <string>tools.reekon.pdfcanvas.PDFium</string>
316
+ <key>CFBundleInfoDictionaryVersion</key>
317
+ <string>6.0</string>
318
+ <key>CFBundleName</key>
319
+ <string>PDFium</string>
320
+ <key>CFBundlePackageType</key>
321
+ <string>FMWK</string>
322
+ <key>CFBundleShortVersionString</key>
323
+ <string>${shortVersion}</string>
324
+ <key>CFBundleVersion</key>
325
+ <string>${manifest.version}</string>
326
+ <key>CFBundleSupportedPlatforms</key>
327
+ <array>
328
+ <string>${platform}</string>
329
+ </array>
330
+ <key>MinimumOSVersion</key>
331
+ <string>13.0</string>
332
+ </dict>
333
+ </plist>
334
+ `,
335
+ );
336
+ }
337
+
338
+ async function ios() {
339
+ if (process.platform !== 'darwin') {
340
+ throw new Error(
341
+ 'The iOS target assembles an xcframework with lipo/install_name_tool/xcodebuild ' +
342
+ 'and must run on macOS (it is invoked by `pod install`).',
343
+ );
344
+ }
345
+ const out = join(outputRoot, 'ios');
346
+ const key = manifest.release;
347
+ if (isFresh(out, key)) {
348
+ console.log(`[pdfium] ios: ${manifest.release} already present`);
349
+ return;
350
+ }
351
+ resetDir(out);
352
+ const work = join(out, '.work');
353
+ mkdirSync(work, {recursive: true});
354
+
355
+ const slices = {};
356
+ for (const name of [
357
+ 'ios-device-arm64',
358
+ 'ios-simulator-arm64',
359
+ 'ios-simulator-x64',
360
+ ]) {
361
+ const dir = join(work, name);
362
+ extract(await fetchArtifact(name), dir);
363
+ slices[name] = dir;
364
+ }
365
+ copyHeaders(
366
+ join(slices['ios-device-arm64'], 'include'),
367
+ join(out, 'include'),
368
+ );
369
+ cpSync(join(slices['ios-device-arm64'], 'LICENSE'), join(out, 'LICENSE'));
370
+ cpSync(join(slices['ios-device-arm64'], 'licenses'), join(out, 'licenses'), {
371
+ recursive: true,
372
+ });
373
+
374
+ // Both simulator architectures have to be ONE fat dylib: an xcframework holds
375
+ // one library per (platform, variant), so two simulator slices must be lipo'd.
376
+ const simulatorFat = join(work, 'libpdfium-simulator.dylib');
377
+ execFileSync(
378
+ 'lipo',
379
+ [
380
+ '-create',
381
+ join(slices['ios-simulator-arm64'], 'lib', 'libpdfium.dylib'),
382
+ join(slices['ios-simulator-x64'], 'lib', 'libpdfium.dylib'),
383
+ '-output',
384
+ simulatorFat,
385
+ ],
386
+ {stdio: 'inherit'},
387
+ );
388
+
389
+ const deviceFramework = join(work, 'device', 'PDFium.framework');
390
+ const simulatorFramework = join(work, 'simulator', 'PDFium.framework');
391
+ makeIosFramework(
392
+ join(slices['ios-device-arm64'], 'lib', 'libpdfium.dylib'),
393
+ deviceFramework,
394
+ join(out, 'include'),
395
+ 'iPhoneOS',
396
+ );
397
+ makeIosFramework(
398
+ simulatorFat,
399
+ simulatorFramework,
400
+ join(out, 'include'),
401
+ 'iPhoneSimulator',
402
+ );
403
+
404
+ const xcframework = join(out, 'PDFium.xcframework');
405
+ execFileSync(
406
+ 'xcodebuild',
407
+ [
408
+ '-create-xcframework',
409
+ '-framework',
410
+ deviceFramework,
411
+ '-framework',
412
+ simulatorFramework,
413
+ '-output',
414
+ xcframework,
415
+ ],
416
+ {stdio: 'inherit'},
417
+ );
418
+ rmSync(work, {recursive: true, force: true});
419
+ writeStamp(out, key);
420
+ console.log(`[pdfium] ios: ready at ${xcframework}`);
421
+ }
422
+
423
+ /* ------------------------------------------------------------------ *
424
+ * host
425
+ * ------------------------------------------------------------------ */
426
+
427
+ function defaultHostTarget() {
428
+ const arch = process.arch === 'arm64' ? 'arm64' : 'x64';
429
+ switch (process.platform) {
430
+ case 'win32':
431
+ return 'win-x64';
432
+ case 'darwin':
433
+ return `mac-${arch}`;
434
+ case 'linux':
435
+ return 'linux-x64';
436
+ default:
437
+ throw new Error(
438
+ `No PDFium host build for ${process.platform}/${process.arch}.`,
439
+ );
440
+ }
441
+ }
442
+
443
+ async function host() {
444
+ const target = flags.get('target') ?? defaultHostTarget();
445
+ const out = join(outputRoot, 'host');
446
+ const key = `${manifest.release} ${target}`;
447
+ if (isFresh(out, key)) {
448
+ console.log(`[pdfium] host: ${manifest.release} ${target} already present`);
449
+ return;
450
+ }
451
+ resetDir(out);
452
+ extract(await fetchArtifact(target), out);
453
+ // The release's headers dir carries `fpdfview.h.orig`; drop it so an include
454
+ // path sweep never picks up the wrong one.
455
+ const orig = join(out, 'include', 'fpdfview.h.orig');
456
+ if (existsSync(orig)) rmSync(orig);
457
+ writeStamp(out, key);
458
+ console.log(`[pdfium] host: ready at ${out}`);
459
+ }
460
+
461
+ /* ------------------------------------------------------------------ *
462
+ * main
463
+ * ------------------------------------------------------------------ */
464
+
465
+ try {
466
+ switch (command) {
467
+ case 'android':
468
+ await android();
469
+ break;
470
+ case 'ios':
471
+ await ios();
472
+ break;
473
+ case 'host':
474
+ await host();
475
+ break;
476
+ case 'print-version':
477
+ process.stdout.write(`${manifest.version}\n`);
478
+ break;
479
+ default:
480
+ usage(command === undefined ? 1 : 2);
481
+ }
482
+ } catch (error) {
483
+ console.error(
484
+ `[pdfium] ${error instanceof Error ? error.message : String(error)}`,
485
+ );
486
+ process.exit(1);
487
+ }
@@ -0,0 +1,46 @@
1
+ {
2
+ "//": "THE ONE PLACE THE PDFIUM BUILD IS NAMED. `scripts/fetch-pdfium.mjs` downloads exactly these artifacts, verifies exactly these digests, and refuses anything else. Bumping PDFium is a one-file change: new tag, new digests (taken from the release's `pdfium-attestation.json` subjects, never from a download you have not verified), then `yarn pdfium:fetch host && yarn native:test` before the bump is trusted. Every platform — Android, iOS, the host test suite — is built from the SAME release, which is what makes a tile byte-identical across all of them rather than merely similar.",
3
+ "release": "chromium/8044",
4
+ "version": "144.0.8044.0",
5
+ "baseUrl": "https://github.com/bblanchon/pdfium-binaries/releases/download",
6
+ "//artifacts": "Keyed by the bblanchon artifact name minus the `pdfium-` prefix and `.tgz` suffix. The android/ios/mac/win/linux entries are the plain (non-V8, non-XFA) builds: this package runs no JavaScript inside a PDF and XFA forms are out of scope, and the V8 builds are ~4x the size for nothing.",
7
+ "artifacts": {
8
+ "android-arm": {
9
+ "sha256": "7fba2fd334c9591e659c69263ff6c43b986f3db2200f6c156c848bb625f5364a",
10
+ "abi": "armeabi-v7a"
11
+ },
12
+ "android-arm64": {
13
+ "sha256": "37686e64fa005484d619550a78805500de4c5a6f4df7aa06d8576145bfbee98f",
14
+ "abi": "arm64-v8a"
15
+ },
16
+ "android-x64": {
17
+ "sha256": "e7e3072b8ad6f36ef58db999c2d91a22fa165a904d69385f13db4537c233d736",
18
+ "abi": "x86_64"
19
+ },
20
+ "android-x86": {
21
+ "sha256": "5a7bf631adac4d01c24e234172b8dc98d9e8e611c3d9b52135498d1b8c61e052",
22
+ "abi": "x86"
23
+ },
24
+ "ios-device-arm64": {
25
+ "sha256": "52be735d07b6c498ed8ce68b7f0cb5a209bbe61593c7a86c556756b80abbb938"
26
+ },
27
+ "ios-simulator-arm64": {
28
+ "sha256": "544537fce5257146e0df501c632d490d1aa95768737770f1912c8e23b1dd238d"
29
+ },
30
+ "ios-simulator-x64": {
31
+ "sha256": "8d4e33e415e29894aa98481bc9a9162a42039a1672796db14c9fed9cd545c881"
32
+ },
33
+ "mac-arm64": {
34
+ "sha256": "61424884d4a7f153b808deba6437848e4400834ce30aaf95d3050da44df8f420"
35
+ },
36
+ "mac-x64": {
37
+ "sha256": "a93d44238e05de20028446561b951d50988b849efbbe56fe40c0d376c05b45e8"
38
+ },
39
+ "win-x64": {
40
+ "sha256": "78a17d9a5f14467631c26a3ac8741b27a0471ecc05bd6a119b523598160a0537"
41
+ },
42
+ "linux-x64": {
43
+ "sha256": "eb142f416aed3a72fc5a02dbd5884868a16cb99dc0cf53e6bdd64afbf67b05f4"
44
+ }
45
+ }
46
+ }