@reekon-tools/react-native-pdf-canvas 0.2.0 → 0.2.1

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 CHANGED
@@ -1,6 +1,7 @@
1
1
  require "json"
2
2
 
3
3
  package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+ pdfium = JSON.parse(File.read(File.join(__dir__, "scripts", "pdfium-manifest.json")))
4
5
 
5
6
  # FETCH PDFIUM WHILE THE PODSPEC IS EVALUATED.
6
7
  #
@@ -50,10 +51,17 @@ Pod::Spec.new do |s|
50
51
  s.authors = { "REEKON Tools" => "hello@reekon.tools" }
51
52
  s.source = { :git => "https://gitlab.com/reekon-tools/software/rock-pro/react-native-pdf-canvas.git", :tag => "#{s.version}" }
52
53
 
53
- # 13.0 is the floor of the PDFium dylib bblanchon ships (MinimumOSVersion in
54
- # the framework's Info.plist is written to match). Nothing in this pod needs
55
- # anything newer.
56
- s.platforms = { :ios => "13.0" }
54
+ # THE FLOOR IS THE ENGINE'S, NOT OURS. Nothing this pod compiles needs anything
55
+ # newer than iOS 13, but the vendored PDFium dylib is built for whatever
56
+ # `ios_deployment_target` bblanchon used (17.0 since 2026-08-14), and that is
57
+ # what dyld enforces on the device and what App Store processing checks
58
+ # against the framework's Info.plist (ITMS-90208). The value is declared once
59
+ # in scripts/pdfium-manifest.json, verified against every slice's Mach-O by
60
+ # fetch-pdfium.mjs above, and written into the framework plist from the same
61
+ # field — so it is read here rather than restated. Declaring it as the pod's
62
+ # platform makes a host with a lower deployment target fail at `pod install`
63
+ # ("PdfCanvas does not support iOS 16.4") instead of at upload.
64
+ s.platforms = { :ios => pdfium["iosMinimumOSVersion"] }
57
65
  s.requires_arc = true
58
66
 
59
67
  # THE CORE IS C++ AND SHARED WITH ANDROID: `native/core` is compiled into this
package/README.md CHANGED
@@ -80,6 +80,11 @@ during CocoaPods podspec evaluation; Android retrieves it from a Gradle task. Ai
80
80
  may direct `PDFCANVAS_PDFIUM_CACHE` or `PDFCANVAS_PDFIUM_MIRROR` at a local copy, which is still
81
81
  verified against the manifest.
82
82
 
83
+ The prebuilt engine sets the native floors: **iOS 17.0** (`iosMinimumOSVersion` in the manifest,
84
+ declared by the pod and checked against the dylib at fetch time) and **Android API 24**. A host app
85
+ with a lower iOS deployment target fails at `pod install`; raise the target rather than the
86
+ framework's plist, since the dylib will not load on older systems either.
87
+
83
88
  ## Installation
84
89
 
85
90
  ```sh
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reekon-tools/react-native-pdf-canvas",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "PDF pages as Skia images, drawn at exact doc-space rects inside somebody else's transform. One PDFium engine on iOS, Android and web. Owns no canvas, no gestures, no viewport state.",
5
5
  "author": "REEKON Tools",
6
6
  "license": "Apache-2.0",
@@ -283,13 +283,56 @@ async function android() {
283
283
  * ios
284
284
  * ------------------------------------------------------------------ */
285
285
 
286
+ /**
287
+ * The minimum iOS version a Mach-O was built for, read from its LC_BUILD_VERSION
288
+ * (`minos`) via `vtool`. A fat binary prints one block per architecture; every
289
+ * slice must agree or the number is meaningless, so disagreement is an error.
290
+ *
291
+ * THIS IS THE FLOOR, NOT A GUESS. App Store processing compares the
292
+ * `MinimumOSVersion` a framework's Info.plist declares against what its binary
293
+ * was actually built for and rejects the upload (ITMS-90208) when the plist
294
+ * claims a lower floor. The first PDFium release of this package hardcoded
295
+ * 13.0 — a number inherited from the CoreGraphics era and from the upstream
296
+ * xcframework script this mirrors — while the dylib said 17.0, and the very
297
+ * first TestFlight upload was rejected. The plist is now written from the
298
+ * binary, and the manifest's declared value is checked against it, so the two
299
+ * cannot drift apart again.
300
+ */
301
+ function machOMinimumOS(binary) {
302
+ const text = execFileSync('xcrun', ['vtool', '-show-build', binary], {
303
+ encoding: 'utf8',
304
+ });
305
+ const found = new Set();
306
+ for (const line of text.split('\n')) {
307
+ const m = /^\s*minos\s+(\d+(?:\.\d+)*)\s*$/.exec(line);
308
+ if (m) found.add(m[1]);
309
+ }
310
+ if (found.size === 0) {
311
+ throw new Error(
312
+ `${binary} has no LC_BUILD_VERSION minos; cannot determine its iOS floor.`,
313
+ );
314
+ }
315
+ if (found.size > 1) {
316
+ throw new Error(
317
+ `${binary} slices disagree about their iOS floor: ${[...found].join(', ')}.`,
318
+ );
319
+ }
320
+ return [...found][0];
321
+ }
322
+
286
323
  /**
287
324
  * Wraps a bare dylib as an iOS `PDFium.framework` (shallow bundle) so it can be
288
325
  * embedded and code-signed like any other framework. Mirrors what
289
326
  * espresso3389/pdfium-xcframework's build.sh does, minus the macOS/Catalyst
290
327
  * slices this package does not ship.
291
328
  */
292
- function makeIosFramework(dylib, frameworkDir, headersDir, platform) {
329
+ function makeIosFramework(
330
+ dylib,
331
+ frameworkDir,
332
+ headersDir,
333
+ platform,
334
+ minimumOSVersion,
335
+ ) {
293
336
  resetDir(frameworkDir);
294
337
  const binary = join(frameworkDir, 'PDFium');
295
338
  cpSync(dylib, binary);
@@ -328,7 +371,7 @@ function makeIosFramework(dylib, frameworkDir, headersDir, platform) {
328
371
  <string>${platform}</string>
329
372
  </array>
330
373
  <key>MinimumOSVersion</key>
331
- <string>13.0</string>
374
+ <string>${minimumOSVersion}</string>
332
375
  </dict>
333
376
  </plist>
334
377
  `,
@@ -343,7 +386,18 @@ async function ios() {
343
386
  );
344
387
  }
345
388
  const out = join(outputRoot, 'ios');
346
- const key = manifest.release;
389
+ const declaredMinOS = manifest.iosMinimumOSVersion;
390
+ if (
391
+ typeof declaredMinOS !== 'string' ||
392
+ !/^\d+(\.\d+)*$/.test(declaredMinOS)
393
+ ) {
394
+ throw new Error(
395
+ 'pdfium-manifest.json must declare iosMinimumOSVersion (e.g. "17.0").',
396
+ );
397
+ }
398
+ // The floor is part of the stamp so an output assembled by an older script
399
+ // (which wrote a hardcoded plist) is rebuilt rather than trusted.
400
+ const key = `${manifest.release} ios-min=${declaredMinOS}`;
347
401
  if (isFresh(out, key)) {
348
402
  console.log(`[pdfium] ios: ${manifest.release} already present`);
349
403
  return;
@@ -362,6 +416,22 @@ async function ios() {
362
416
  extract(await fetchArtifact(name), dir);
363
417
  slices[name] = dir;
364
418
  }
419
+ // Every slice must have been built for the floor the manifest declares. A
420
+ // mismatch means the release was bumped without re-reading its args.gn: fix the
421
+ // manifest (and tell every consumer, whose deployment target must be >= it).
422
+ for (const name of Object.keys(slices)) {
423
+ const actual = machOMinimumOS(join(slices[name], 'lib', 'libpdfium.dylib'));
424
+ if (actual !== declaredMinOS) {
425
+ throw new Error(
426
+ `${name} in ${manifest.release} was built for iOS ${actual}, but ` +
427
+ `pdfium-manifest.json declares iosMinimumOSVersion "${declaredMinOS}". ` +
428
+ 'Update the manifest to the real floor; consumers must raise their ' +
429
+ 'deployment target to match before they can ship this release.',
430
+ );
431
+ }
432
+ }
433
+ console.log(`[pdfium] ios: every slice is built for iOS ${declaredMinOS}`);
434
+
365
435
  copyHeaders(
366
436
  join(slices['ios-device-arm64'], 'include'),
367
437
  join(out, 'include'),
@@ -393,12 +463,14 @@ async function ios() {
393
463
  deviceFramework,
394
464
  join(out, 'include'),
395
465
  'iPhoneOS',
466
+ declaredMinOS,
396
467
  );
397
468
  makeIosFramework(
398
469
  simulatorFat,
399
470
  simulatorFramework,
400
471
  join(out, 'include'),
401
472
  'iPhoneSimulator',
473
+ declaredMinOS,
402
474
  );
403
475
 
404
476
  const xcframework = join(out, 'PDFium.xcframework');
@@ -3,6 +3,8 @@
3
3
  "release": "chromium/8044",
4
4
  "version": "144.0.8044.0",
5
5
  "baseUrl": "https://github.com/bblanchon/pdfium-binaries/releases/download",
6
+ "//iosMinimumOSVersion": "THE iOS FLOOR THIS RELEASE'S DYLIB WAS BUILT FOR (`ios_deployment_target` in the tarball's args.gn, `minos` in the Mach-O LC_BUILD_VERSION). It is declared here, not discovered, so that bumping the release forces whoever bumps it to look: fetch-pdfium.mjs reads the real value out of every iOS slice with `vtool` and refuses to assemble the framework if any slice disagrees with this line. The same value is written into PDFium.framework/Info.plist as MinimumOSVersion — App Store processing rejects a framework whose plist claims a lower floor than its binary (ITMS-90208) — and is the floor PdfCanvas.podspec declares, so a host app whose deployment target is lower fails at `pod install` with a CocoaPods platform error instead of at upload. bblanchon pins 17.0 for non-V8 builds since 2026-08-14; before that the floor tracked Chromium's default and was never below 17.4 in 2025-2026. A host that must ship below this value needs a PDFium built with a lower ios_deployment_target, not a smaller number here.",
7
+ "iosMinimumOSVersion": "17.0",
6
8
  "//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
9
  "artifacts": {
8
10
  "android-arm": {