@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.
- package/LICENSE +202 -0
- package/NOTICE +57 -0
- package/PdfCanvas.podspec +68 -39
- package/README.md +213 -1316
- package/android/build.gradle +67 -54
- package/android/consumer-rules.pro +7 -0
- package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasNativeTest.java +254 -0
- package/android/src/androidTest/java/tools/reekon/pdfcanvas/TestPdfs.java +60 -468
- package/android/src/main/cpp/CMakeLists.txt +54 -0
- package/android/src/main/cpp/pdfcanvas-jni.cpp +262 -0
- package/android/src/main/java/tools/reekon/pdfcanvas/PdfCanvasNative.java +167 -0
- package/android/src/main/java/tools/reekon/pdfcanvas/PdfErrorCode.java +14 -4
- package/android/src/main/java/tools/reekon/pdfcanvas/PdfRasterException.java +10 -4
- package/android/src/reactnative/java/tools/reekon/pdfcanvas/rn/PdfCanvasModule.java +160 -376
- package/android/tools/compile-gate.sh +81 -101
- package/dist/controller.d.ts +14 -0
- package/dist/controller.js +4 -3
- package/dist/index.native.d.ts +5 -0
- package/dist/index.native.js +5 -0
- package/dist/index.web.d.ts +10 -0
- package/dist/index.web.js +10 -0
- package/dist/rasterizer/index.native.d.ts +8 -12
- package/dist/rasterizer/index.native.js +15 -21
- package/dist/rasterizer/native-bridge.d.ts +51 -75
- package/dist/rasterizer/native-bridge.js +49 -33
- package/dist/rasterizer/native.d.ts +45 -0
- package/dist/rasterizer/native.js +123 -0
- package/dist/react/usePdfDocument.d.ts +5 -5
- package/dist/react/usePdfLayer.d.ts +10 -1
- package/dist/react/usePdfLayer.js +10 -2
- package/dist/skia/index.native.d.ts +7 -3
- package/dist/skia/index.native.js +7 -3
- package/dist/skia/index.web.d.ts +4 -2
- package/dist/skia/index.web.js +4 -2
- package/dist/testing/scenes.d.ts +3 -3
- package/dist/testing/scenes.js +4 -4
- package/dist/types.d.ts +36 -42
- package/ios/Sources/PdfCanvasBridge/PdfCanvasModule.mm +234 -536
- package/native/.clangd +5 -0
- package/native/CMakeLists.txt +89 -0
- package/native/core/include/pdfcanvas/document.h +90 -0
- package/native/core/include/pdfcanvas/error.h +47 -0
- package/native/core/include/pdfcanvas/jsi.h +18 -0
- package/native/core/include/pdfcanvas/jsi_config.h +30 -0
- package/native/core/include/pdfcanvas/library.h +28 -0
- package/native/core/include/pdfcanvas/pixels.h +92 -0
- package/native/core/include/pdfcanvas/service.h +87 -0
- package/native/core/include/pdfcanvas/slots.h +113 -0
- package/native/core/include/pdfcanvas/types.h +110 -0
- package/native/core/pdfcanvas-core.cmake +22 -0
- package/native/core/src/document.cpp +516 -0
- package/native/core/src/error.cpp +29 -0
- package/native/core/src/jsi.cpp +66 -0
- package/native/core/src/library.cpp +52 -0
- package/native/core/src/pixels.cpp +45 -0
- package/native/core/src/service.cpp +156 -0
- package/native/core/src/slots.cpp +139 -0
- package/native/tests/fixtures.cpp +451 -0
- package/native/tests/fixtures.h +96 -0
- package/native/tests/harness.h +122 -0
- package/native/tests/main.cpp +64 -0
- package/native/tests/pixels_util.h +126 -0
- package/native/tests/test_document.cpp +485 -0
- package/native/tests/test_service.cpp +130 -0
- package/native/tests/test_slots.cpp +250 -0
- package/package.json +38 -8
- package/react-native.config.js +5 -2
- package/scripts/fetch-pdfium.mjs +487 -0
- package/scripts/pdfium-manifest.json +46 -0
- package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasCancellationTest.java +0 -226
- package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasConcurrencyTest.java +0 -239
- package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasDeviceTimingTest.java +0 -251
- package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasFailureTest.java +0 -221
- package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasJsiTransportTest.java +0 -783
- package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasPhaseTimingTest.java +0 -1388
- package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasPoisonGuardTest.java +0 -98
- package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasQuirksTest.java +0 -324
- package/android/src/androidTest/java/tools/reekon/pdfcanvas/PdfCanvasRasterizerTest.java +0 -775
- package/android/src/androidTest/java/tools/reekon/pdfcanvas/PoisonsTheProcess.java +0 -20
- package/android/src/jsi/cpp/CMakeLists.txt +0 -33
- package/android/src/jsi/cpp/pdfcanvas-jsi.cpp +0 -469
- package/android/src/jsi/java/tools/reekon/pdfcanvas/jsi/PdfCanvasPixelBridge.java +0 -140
- package/android/src/main/java/tools/reekon/pdfcanvas/PageGeometry.java +0 -45
- package/android/src/main/java/tools/reekon/pdfcanvas/PdfCanvasRasterizer.java +0 -564
- package/android/src/main/java/tools/reekon/pdfcanvas/PdfPlatformQuirks.java +0 -277
- package/android/src/main/java/tools/reekon/pdfcanvas/PdfPreflight.java +0 -214
- package/android/src/main/java/tools/reekon/pdfcanvas/PdfRendererPool.java +0 -234
- package/android/src/main/java/tools/reekon/pdfcanvas/RasterCancellation.java +0 -47
- package/android/src/main/java/tools/reekon/pdfcanvas/RasterPixels.java +0 -143
- package/android/src/main/java/tools/reekon/pdfcanvas/RasterRequest.java +0 -95
- package/dist/rasterizer/android.d.ts +0 -121
- package/dist/rasterizer/android.js +0 -126
- package/dist/rasterizer/ios.d.ts +0 -84
- package/dist/rasterizer/ios.js +0 -155
- package/ios/Package.swift +0 -58
- package/ios/Sources/PdfCanvasCore/PdfCanvasCancellation.m +0 -38
- package/ios/Sources/PdfCanvasCore/PdfCanvasDocumentPool.m +0 -389
- package/ios/Sources/PdfCanvasCore/PdfCanvasErrorCode.m +0 -57
- package/ios/Sources/PdfCanvasCore/PdfCanvasPageGeometry.m +0 -44
- package/ios/Sources/PdfCanvasCore/PdfCanvasPageTransform.m +0 -82
- package/ios/Sources/PdfCanvasCore/PdfCanvasPixelSink.m +0 -37
- package/ios/Sources/PdfCanvasCore/PdfCanvasPreflight.m +0 -158
- package/ios/Sources/PdfCanvasCore/PdfCanvasRasterPixels.m +0 -80
- package/ios/Sources/PdfCanvasCore/PdfCanvasRasterRequest.m +0 -65
- package/ios/Sources/PdfCanvasCore/PdfCanvasRasterizer.m +0 -548
- package/ios/Sources/PdfCanvasCore/include/PdfCanvasCancellation.h +0 -48
- package/ios/Sources/PdfCanvasCore/include/PdfCanvasCore.h +0 -26
- package/ios/Sources/PdfCanvasCore/include/PdfCanvasDocumentPool.h +0 -122
- package/ios/Sources/PdfCanvasCore/include/PdfCanvasErrorCode.h +0 -80
- package/ios/Sources/PdfCanvasCore/include/PdfCanvasPageGeometry.h +0 -79
- package/ios/Sources/PdfCanvasCore/include/PdfCanvasPageTransform.h +0 -129
- package/ios/Sources/PdfCanvasCore/include/PdfCanvasPixelSink.h +0 -100
- package/ios/Sources/PdfCanvasCore/include/PdfCanvasPreflight.h +0 -65
- package/ios/Sources/PdfCanvasCore/include/PdfCanvasRasterPixels.h +0 -111
- package/ios/Sources/PdfCanvasCore/include/PdfCanvasRasterRequest.h +0 -85
- package/ios/Sources/PdfCanvasCore/include/PdfCanvasRasterizer.h +0 -163
- package/ios/Sources/PdfCanvasSlots/PdfCanvasSlots.mm +0 -297
- package/ios/Sources/PdfCanvasSlots/include/PdfCanvasSlots.h +0 -140
- package/ios/Tests/PdfCanvasCoreTests/ConcurrencyTests.swift +0 -358
- package/ios/Tests/PdfCanvasCoreTests/FailureTests.swift +0 -262
- package/ios/Tests/PdfCanvasCoreTests/GeometryTests.swift +0 -173
- package/ios/Tests/PdfCanvasCoreTests/PreviewTests.swift +0 -114
- package/ios/Tests/PdfCanvasCoreTests/RasterAssertions.swift +0 -158
- package/ios/Tests/PdfCanvasCoreTests/RasterizerTests.swift +0 -544
- package/ios/Tests/PdfCanvasCoreTests/SinkTests.swift +0 -287
- package/ios/Tests/PdfCanvasCoreTests/SlotsTests.swift +0 -396
- package/ios/Tests/PdfCanvasCoreTests/TestPdfs.swift +0 -639
- package/ios/Tests/PdfCanvasCoreTests/TimingTests.swift +0 -179
- package/ios/Tests/PdfCanvasCoreTests/TransformTests.swift +0 -283
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The TypeScript half of the Android backend.
|
|
3
|
-
*
|
|
4
|
-
* THIN, AND IT USED TO BE THE WHOLE THING. Everything that is not
|
|
5
|
-
* Android-specific — error mapping, page-geometry marshalling, the transport
|
|
6
|
-
* negotiation, the base64 decode, the cancellation wiring, the phase timings —
|
|
7
|
-
* moved to `./native-bridge.ts` when the iOS backend landed, because the two
|
|
8
|
-
* platforms speak the identical wire format and the spec's claim for the iOS step
|
|
9
|
-
* is that the seam is a seam. What is left here is the four things that are
|
|
10
|
-
* genuinely Android's: the id, the capabilities, the alpha encoding, and what
|
|
11
|
-
* `PdfSource` it can accept.
|
|
12
|
-
*
|
|
13
|
-
* The names exported from `./native-bridge.ts` are re-exported below under their
|
|
14
|
-
* original `Android*` spellings, so nothing that imported them from here breaks.
|
|
15
|
-
*
|
|
16
|
-
* PURE ON PURPOSE. Nothing here imports `react-native`, which is what lets the
|
|
17
|
-
* whole adapter be tested in vitest against a fake native module.
|
|
18
|
-
* `./index.native.ts` is the only file that resolves the real one, and it does
|
|
19
|
-
* nothing except hand it to `createAndroidRasterizer`.
|
|
20
|
-
*
|
|
21
|
-
* The native side is `android/src/main/java/tools/reekon/pdfcanvas/`, a
|
|
22
|
-
* React-free Android library that is proven on device by
|
|
23
|
-
* `./gradlew connectedDebugAndroidTest`. That split is the reason a bug here is
|
|
24
|
-
* a bug in marshalling and never a bug in rasterizing.
|
|
25
|
-
*/
|
|
26
|
-
import { createNativeRasterizer } from './native-bridge.js';
|
|
27
|
-
import { PdfError } from '../types.js';
|
|
28
|
-
export { TRANSPORT_PROBE_BYTES, bytesFromResult, decodeBase64, lookupTakePixels, negotiateTransport, toPageGeometry, } from './native-bridge.js';
|
|
29
|
-
/* ------------------------------------------------------------------ *
|
|
30
|
-
* What is actually Android's
|
|
31
|
-
* ------------------------------------------------------------------ */
|
|
32
|
-
/**
|
|
33
|
-
* A CONSTANT AND NOT PART OF THE WIRE RESULT, unlike `format`.
|
|
34
|
-
*
|
|
35
|
-
* `format` is per-render because the Java side could in principle hand back
|
|
36
|
-
* either byte order; the alpha encoding cannot vary, because it is a property of
|
|
37
|
-
* `Bitmap` itself. `PdfCanvasRasterizer.extract` reads an ARGB_8888 Bitmap, and
|
|
38
|
-
* Android bitmaps are PREMULTIPLIED unless somebody calls
|
|
39
|
-
* `setPremultiplied(false)` — which that code deliberately never does, since
|
|
40
|
-
* un-premultiplying would make a `background: 'transparent'` raster composite
|
|
41
|
-
* wrongly rather than fail visibly. So there is nothing for the native side to
|
|
42
|
-
* report and no reason to widen the wire format to carry it.
|
|
43
|
-
*
|
|
44
|
-
* IT MATCHES iOS AND THAT IS A COINCIDENCE OF MECHANISM, not a shared choice.
|
|
45
|
-
* `CGBitmapContext` has no non-premultiplied 8-bit RGBA configuration at all —
|
|
46
|
-
* Apple's supported-formats table offers premultiplied or alpha-none and nothing
|
|
47
|
-
* between — so `ios.ts` declares the same value for a completely different
|
|
48
|
-
* reason. PDFium and pdf.js, meanwhile, both emit STRAIGHT alpha (measured — see
|
|
49
|
-
* `AlphaEncoding`), which is the whole reason `RasterPixels.alpha` exists rather
|
|
50
|
-
* than a single `AlphaType.Premul` in `ingest.ts`. Do not "align" any of these
|
|
51
|
-
* four values with each other.
|
|
52
|
-
*/
|
|
53
|
-
const ANDROID_ALPHA = 'premultiplied';
|
|
54
|
-
const CAPABILITIES = {
|
|
55
|
-
/**
|
|
56
|
-
* True in the sense the field means — annotations ARE drawn into the raster.
|
|
57
|
-
*
|
|
58
|
-
* What is NOT true, and has no field to say so: they cannot be turned OFF.
|
|
59
|
-
* `PdfRenderer.Page.render` takes a render mode and no annotation switch, and
|
|
60
|
-
* PDFium's `FPDF_ANNOT` flag is unreachable through the platform class at
|
|
61
|
-
* every API level. So `RasterRequest.annotations: false` — which is what the
|
|
62
|
-
* controller passes for every tile — is recorded and ignored, and an app that
|
|
63
|
-
* draws its own copy of a PDF-embedded annotation will see it twice on
|
|
64
|
-
* Android. The fix is a PDFium backend, not a different mode here.
|
|
65
|
-
*
|
|
66
|
-
* iOS DOES HONOUR THE FALSE CASE, which is worth knowing when a bug report
|
|
67
|
-
* says annotations appear twice on one platform only: `ios.ts` picks
|
|
68
|
-
* `CGContextDrawPDFPage` for `false` and PDFKit for `true`, and the two really
|
|
69
|
-
* do differ in what they composite.
|
|
70
|
-
*/
|
|
71
|
-
annotations: true,
|
|
72
|
-
/**
|
|
73
|
-
* False, and honestly so. `PdfRenderer.Page.render` is synchronous and
|
|
74
|
-
* uninterruptible once entered; an abort can only stop the NEXT piece
|
|
75
|
-
* starting, or discard a finished raster that is no longer wanted.
|
|
76
|
-
*/
|
|
77
|
-
interruptibleRender: false,
|
|
78
|
-
text: false,
|
|
79
|
-
search: false,
|
|
80
|
-
links: false,
|
|
81
|
-
// Overwritten per rasterizer with the pool size it was actually opened with —
|
|
82
|
-
// see `createNativeRasterizer`. The constant carries the conservative value so
|
|
83
|
-
// a partially-constructed object is never the fast one.
|
|
84
|
-
maxConcurrentRenders: 1,
|
|
85
|
-
};
|
|
86
|
-
/**
|
|
87
|
-
* Mirrors `PdfRendererPool.MAX_POOL_SIZE` on the Java side, which is where the
|
|
88
|
-
* clamp is actually enforced (`PdfRendererPool.open`). Restating it here is not
|
|
89
|
-
* a second source of truth: it is what makes the DECLARED capability match the
|
|
90
|
-
* pool the native side will really hand out, rather than promising four lanes to
|
|
91
|
-
* a pool that silently opened with four... or with one.
|
|
92
|
-
*/
|
|
93
|
-
const MAX_POOL_SIZE = 4;
|
|
94
|
-
const clampPoolSize = (size) => Math.max(1, Math.min(MAX_POOL_SIZE, Math.floor(size) || 1));
|
|
95
|
-
function toNativeSource(source) {
|
|
96
|
-
const password = source.password;
|
|
97
|
-
if ('uri' in source) {
|
|
98
|
-
return password === undefined
|
|
99
|
-
? { uri: source.uri }
|
|
100
|
-
: { uri: source.uri, password };
|
|
101
|
-
}
|
|
102
|
-
throw new PdfError('unsupported', 'Passing a document as bytes is not wired through the Android binding yet. ' +
|
|
103
|
-
'The native side supports it (PdfCanvasRasterizer.openBytes spools to a ' +
|
|
104
|
-
'file, because PdfRenderer needs a seekable descriptor), but sending the ' +
|
|
105
|
-
'bytes over the bridge costs a base64 round trip of the whole document. ' +
|
|
106
|
-
'Pass a `uri` instead.');
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Wraps a native module in a `PageRasterizer`.
|
|
110
|
-
*
|
|
111
|
-
* The native module is INJECTED rather than imported so this file stays pure.
|
|
112
|
-
* `getDefaultRasterizer()` in `./index.native.ts` supplies the real one.
|
|
113
|
-
*/
|
|
114
|
-
export function createAndroidRasterizer(native, options = {}) {
|
|
115
|
-
return createNativeRasterizer(native, {
|
|
116
|
-
id: options.id ?? 'android-pdfrenderer',
|
|
117
|
-
capabilities: CAPABILITIES,
|
|
118
|
-
alpha: ANDROID_ALPHA,
|
|
119
|
-
poolSize: clampPoolSize(options.poolSize ?? 1),
|
|
120
|
-
label: 'Android',
|
|
121
|
-
toNativeSource,
|
|
122
|
-
...(options.lookupTakePixels === undefined
|
|
123
|
-
? {}
|
|
124
|
-
: { lookupTakePixels: options.lookupTakePixels }),
|
|
125
|
-
});
|
|
126
|
-
}
|
package/dist/rasterizer/ios.d.ts
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The TypeScript half of the iOS backend.
|
|
3
|
-
*
|
|
4
|
-
* FOUR THINGS, and nothing else. The wire format, the error mapping, the
|
|
5
|
-
* transport negotiation, the base64 fallback, the cancellation wiring and the
|
|
6
|
-
* phase timings are all in `./native-bridge.ts`, shared byte-for-byte with
|
|
7
|
-
* Android. What is iOS's own is the id, the capabilities, the alpha encoding and
|
|
8
|
-
* which `PdfSource` shapes it accepts — which is the whole point of the seam
|
|
9
|
-
* existing.
|
|
10
|
-
*
|
|
11
|
-
* PURE ON PURPOSE, like `./android.ts`. Nothing here imports `react-native`, so
|
|
12
|
-
* the adapter is testable in vitest against a fake native module.
|
|
13
|
-
* `./index.native.ts` is the only file that resolves the real one.
|
|
14
|
-
*
|
|
15
|
-
* The native side is `ios/Sources/PdfCanvasCore/`, a React-free Objective-C++
|
|
16
|
-
* library that also builds under SwiftPM (`ios/Package.swift`) so its geometry,
|
|
17
|
-
* rotation, stride, alpha and error mapping are provable with `swift test` on a
|
|
18
|
-
* Mac — no device, no app, no simulator. `ios/Sources/PdfCanvasBridge/` is the
|
|
19
|
-
* React binding and the JSI slot store, and joins the build only through the
|
|
20
|
-
* podspec.
|
|
21
|
-
*
|
|
22
|
-
* ------------------------------------------------------------------------
|
|
23
|
-
* TWO ENGINES BEHIND ONE BACKEND, chosen per render by `annotations`.
|
|
24
|
-
* ------------------------------------------------------------------------
|
|
25
|
-
*
|
|
26
|
-
* `annotations: false` — every tile the controller asks for — draws through
|
|
27
|
-
* `CGContextDrawPDFPage`, which composites the page's content stream and NOT its
|
|
28
|
-
* annotation appearance streams. `annotations: true` — `renderPage`'s default,
|
|
29
|
-
* which is the thumbnail and print path — draws through PDFKit's
|
|
30
|
-
* `-[PDFPage drawWithBox:toContext:]`, which composites both.
|
|
31
|
-
*
|
|
32
|
-
* THAT MAKES iOS THE ONLY BACKEND THAT HONOURS `annotations: false`. Android
|
|
33
|
-
* cannot (`PdfRenderer.Page.render` has no annotation switch at any API level)
|
|
34
|
-
* and the PDFium web backend does not pass `FPDF_ANNOT` either. So an app that
|
|
35
|
-
* draws its own copy of a PDF-embedded annotation on the Skia layer sees it once
|
|
36
|
-
* on iOS and twice on Android — a real cross-platform difference, and the right
|
|
37
|
-
* way round.
|
|
38
|
-
*/
|
|
39
|
-
import type { TakePixels } from './native-bridge.js';
|
|
40
|
-
import type { PageRasterizer } from '../types.js';
|
|
41
|
-
export interface IosRasterizerOptions {
|
|
42
|
-
/**
|
|
43
|
-
* Concurrent renders. Clamped to [1, 4] natively — each slot is one more
|
|
44
|
-
* `CGPDFDocumentRef` over its own `CGDataProvider`, plus, once an
|
|
45
|
-
* `annotations: true` render asks for one, its own lazily-opened `PDFDocument`.
|
|
46
|
-
*
|
|
47
|
-
* ONE, AND FOR A WEAKER REASON THAN ANDROID'S. Android declares 1 because
|
|
48
|
-
* lanes above 1 were measured to make things WORSE — AOSP serialises
|
|
49
|
-
* `PdfRenderer` behind a process-wide PDFium lock that convoys, so four threads
|
|
50
|
-
* inflate the per-tile PDFium phases by up to 30x. None of that evidence is
|
|
51
|
-
* about CoreGraphics, and it must not be cited as though it were: the lock
|
|
52
|
-
* being measured there lives in `android_graphics_pdf_PdfRenderer.cpp`.
|
|
53
|
-
*
|
|
54
|
-
* WHAT IS ACTUALLY KNOWN ABOUT iOS. The pool exists so that concurrency is
|
|
55
|
-
* expressible at all: `CGPDFDocumentRef` is not documented thread-safe, and a
|
|
56
|
-
* single one shared across lanes would have to be serialised by a lock we own,
|
|
57
|
-
* which buys nothing. Opening the file once per lane gives each lane an
|
|
58
|
-
* independent parse — the same argument `PdfRendererPool` makes for not using
|
|
59
|
-
* `dup(2)`. Whether that then converts into wall-clock is unmeasured, which is
|
|
60
|
-
* why the default is the conservative 1 rather than the optimistic 2: an
|
|
61
|
-
* unmeasured speedup that is really a slowdown is exactly the mistake Android's
|
|
62
|
-
* `poolSize: 2` was, and it survived for months.
|
|
63
|
-
*
|
|
64
|
-
* Raising it does two things at once, as on Android: it opens more documents
|
|
65
|
-
* AND it widens the controller's lane bound, because it is also what the
|
|
66
|
-
* rasterizer reports as `RasterizerCapabilities.maxConcurrentRenders`. Measure
|
|
67
|
-
* before raising it, on a real drawing rather than a synthetic fixture — the
|
|
68
|
-
* Android note explains why that distinction inverted the answer there.
|
|
69
|
-
*/
|
|
70
|
-
poolSize?: number;
|
|
71
|
-
id?: string;
|
|
72
|
-
/**
|
|
73
|
-
* How to find the installed JSI host function. Injected only by tests; the
|
|
74
|
-
* default reads `globalThis`.
|
|
75
|
-
*/
|
|
76
|
-
lookupTakePixels?: () => TakePixels | null;
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Wraps a native module in a `PageRasterizer`.
|
|
80
|
-
*
|
|
81
|
-
* The native module is INJECTED rather than imported so this file stays pure.
|
|
82
|
-
* `getDefaultRasterizer()` in `./index.native.ts` supplies the real one.
|
|
83
|
-
*/
|
|
84
|
-
export declare function createIosRasterizer(native: import('./native-bridge.js').PdfCanvasNativeModule, options?: IosRasterizerOptions): PageRasterizer;
|
package/dist/rasterizer/ios.js
DELETED
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The TypeScript half of the iOS backend.
|
|
3
|
-
*
|
|
4
|
-
* FOUR THINGS, and nothing else. The wire format, the error mapping, the
|
|
5
|
-
* transport negotiation, the base64 fallback, the cancellation wiring and the
|
|
6
|
-
* phase timings are all in `./native-bridge.ts`, shared byte-for-byte with
|
|
7
|
-
* Android. What is iOS's own is the id, the capabilities, the alpha encoding and
|
|
8
|
-
* which `PdfSource` shapes it accepts — which is the whole point of the seam
|
|
9
|
-
* existing.
|
|
10
|
-
*
|
|
11
|
-
* PURE ON PURPOSE, like `./android.ts`. Nothing here imports `react-native`, so
|
|
12
|
-
* the adapter is testable in vitest against a fake native module.
|
|
13
|
-
* `./index.native.ts` is the only file that resolves the real one.
|
|
14
|
-
*
|
|
15
|
-
* The native side is `ios/Sources/PdfCanvasCore/`, a React-free Objective-C++
|
|
16
|
-
* library that also builds under SwiftPM (`ios/Package.swift`) so its geometry,
|
|
17
|
-
* rotation, stride, alpha and error mapping are provable with `swift test` on a
|
|
18
|
-
* Mac — no device, no app, no simulator. `ios/Sources/PdfCanvasBridge/` is the
|
|
19
|
-
* React binding and the JSI slot store, and joins the build only through the
|
|
20
|
-
* podspec.
|
|
21
|
-
*
|
|
22
|
-
* ------------------------------------------------------------------------
|
|
23
|
-
* TWO ENGINES BEHIND ONE BACKEND, chosen per render by `annotations`.
|
|
24
|
-
* ------------------------------------------------------------------------
|
|
25
|
-
*
|
|
26
|
-
* `annotations: false` — every tile the controller asks for — draws through
|
|
27
|
-
* `CGContextDrawPDFPage`, which composites the page's content stream and NOT its
|
|
28
|
-
* annotation appearance streams. `annotations: true` — `renderPage`'s default,
|
|
29
|
-
* which is the thumbnail and print path — draws through PDFKit's
|
|
30
|
-
* `-[PDFPage drawWithBox:toContext:]`, which composites both.
|
|
31
|
-
*
|
|
32
|
-
* THAT MAKES iOS THE ONLY BACKEND THAT HONOURS `annotations: false`. Android
|
|
33
|
-
* cannot (`PdfRenderer.Page.render` has no annotation switch at any API level)
|
|
34
|
-
* and the PDFium web backend does not pass `FPDF_ANNOT` either. So an app that
|
|
35
|
-
* draws its own copy of a PDF-embedded annotation on the Skia layer sees it once
|
|
36
|
-
* on iOS and twice on Android — a real cross-platform difference, and the right
|
|
37
|
-
* way round.
|
|
38
|
-
*/
|
|
39
|
-
import { createNativeRasterizer } from './native-bridge.js';
|
|
40
|
-
import { PdfError } from '../types.js';
|
|
41
|
-
/**
|
|
42
|
-
* A CONSTANT AND NOT PART OF THE WIRE RESULT, unlike `format`, and — unusually
|
|
43
|
-
* for this package — a fact about an API rather than a measurement.
|
|
44
|
-
*
|
|
45
|
-
* There is no non-premultiplied 8-bit RGBA `CGBitmapContext`. Apple's supported
|
|
46
|
-
* pixel formats for a 32-bit-per-pixel, 8-bits-per-component RGB bitmap context
|
|
47
|
-
* are `kCGImageAlphaPremultipliedFirst`, `kCGImageAlphaPremultipliedLast`,
|
|
48
|
-
* `kCGImageAlphaNoneSkipFirst` and `kCGImageAlphaNoneSkipLast` — premultiplied,
|
|
49
|
-
* or no alpha at all. `kCGImageAlphaLast` (straight) is not in the table and
|
|
50
|
-
* `CGBitmapContextCreate` returns NULL for it. So the only two choices a backend
|
|
51
|
-
* has here are "premultiplied" and "throw away the alpha channel", and throwing
|
|
52
|
-
* it away would break `background: 'transparent'`.
|
|
53
|
-
*
|
|
54
|
-
* `ios/Sources/PdfCanvasCore/PdfCanvasRasterizer.mm` creates exactly one context
|
|
55
|
-
* configuration and this constant states it. The `alphaIsPremultiplied` case in
|
|
56
|
-
* the Swift suite reads the bytes back and checks the encoding on an antialiased
|
|
57
|
-
* edge over a transparent page — the one cell the spec's alpha table had marked
|
|
58
|
-
* NOT MEASURED — so this is asserted rather than believed.
|
|
59
|
-
*
|
|
60
|
-
* IT AGREES WITH ANDROID BY COINCIDENCE OF MECHANISM. Android's value comes from
|
|
61
|
-
* `Bitmap` being premultiplied by default; this one comes from CoreGraphics
|
|
62
|
-
* having no other option. PDFium and pdf.js both emit STRAIGHT alpha. Four
|
|
63
|
-
* backends, three independent reasons, no default that would be right — which is
|
|
64
|
-
* why `RasterPixels.alpha` is a required field.
|
|
65
|
-
*/
|
|
66
|
-
const IOS_ALPHA = 'premultiplied';
|
|
67
|
-
const CAPABILITIES = {
|
|
68
|
-
/**
|
|
69
|
-
* True, and — alone among the four backends — true in BOTH directions.
|
|
70
|
-
*
|
|
71
|
-
* `annotations: true` draws them (PDFKit) and `annotations: false` does not
|
|
72
|
-
* (`CGContextDrawPDFPage`). Android records the flag and ignores it; the web
|
|
73
|
-
* PDFium backend does not pass `FPDF_ANNOT`. The field only claims "can render
|
|
74
|
-
* PDF-embedded annotations into the raster", so all three answer true — the
|
|
75
|
-
* asymmetry is documented at the top of this file rather than inventable from
|
|
76
|
-
* the capability.
|
|
77
|
-
*/
|
|
78
|
-
annotations: true,
|
|
79
|
-
/**
|
|
80
|
-
* False, and honestly so. `CGContextDrawPDFPage` and
|
|
81
|
-
* `-[PDFPage drawWithBox:toContext:]` are both synchronous and uninterruptible
|
|
82
|
-
* once entered; an abort can only stop the NEXT piece starting, or discard a
|
|
83
|
-
* finished raster that is no longer wanted. This is the platform
|
|
84
|
-
* `RasterPolicy.minEpochIntervalMs` was written for — PDFKit is named in its
|
|
85
|
-
* doc comment.
|
|
86
|
-
*/
|
|
87
|
-
interruptibleRender: false,
|
|
88
|
-
/**
|
|
89
|
-
* FALSE, AND THIS ONE IS A DECLARATION ABOUT THE PACKAGE, NOT THE PLATFORM.
|
|
90
|
-
* PDFKit really does have `-[PDFPage string]`, `-[PDFDocument findString:]` and
|
|
91
|
-
* link annotations, so iOS could answer true for all three the day a text
|
|
92
|
-
* surface exists. It does not exist — see the Non-goals — and a capability that
|
|
93
|
-
* says "yes" about an API no consumer can reach is worse than one that says
|
|
94
|
-
* "no". Every backend declares false today for the same reason.
|
|
95
|
-
*/
|
|
96
|
-
text: false,
|
|
97
|
-
search: false,
|
|
98
|
-
links: false,
|
|
99
|
-
// Overwritten per rasterizer with the pool size it was actually opened with —
|
|
100
|
-
// see `createNativeRasterizer`.
|
|
101
|
-
maxConcurrentRenders: 1,
|
|
102
|
-
};
|
|
103
|
-
/**
|
|
104
|
-
* Mirrors `kPdfCanvasMaxPoolSize` in
|
|
105
|
-
* `ios/Sources/PdfCanvasCore/PdfCanvasDocumentPool.mm`, which is where the clamp
|
|
106
|
-
* is actually enforced. Restating it here is what makes the DECLARED capability
|
|
107
|
-
* match the pool the native side will really hand out.
|
|
108
|
-
*/
|
|
109
|
-
const MAX_POOL_SIZE = 4;
|
|
110
|
-
const clampPoolSize = (size) => Math.max(1, Math.min(MAX_POOL_SIZE, Math.floor(size) || 1));
|
|
111
|
-
function toNativeSource(source) {
|
|
112
|
-
const password = source.password;
|
|
113
|
-
if ('uri' in source) {
|
|
114
|
-
return password === undefined
|
|
115
|
-
? { uri: source.uri }
|
|
116
|
-
: { uri: source.uri, password };
|
|
117
|
-
}
|
|
118
|
-
// REFUSED FOR THE SAME REASON AS ANDROID, and it is the bridge's reason rather
|
|
119
|
-
// than the platform's. iOS could take bytes with no spool at all —
|
|
120
|
-
// `CGPDFDocumentCreateWithProvider` over a `CFData` needs no file, and the
|
|
121
|
-
// native `open` does implement the `base64` field of the shared wire format for
|
|
122
|
-
// exactly that — but getting the bytes THERE means base64-encoding the whole
|
|
123
|
-
// document across the bridge, which is the one thing the spec's "two rules that
|
|
124
|
-
// are now measured" says never to do.
|
|
125
|
-
throw new PdfError('unsupported', 'Passing a document as bytes is not wired through the iOS binding. The ' +
|
|
126
|
-
'native side supports it (no spool file is needed — CoreGraphics reads a ' +
|
|
127
|
-
'CGDataProvider over the bytes directly), but sending them over the ' +
|
|
128
|
-
'bridge costs a base64 round trip of the whole document. Pass a `uri` ' +
|
|
129
|
-
'instead: a plain path, a `file://` URL, or a security-scoped URL the app ' +
|
|
130
|
-
'can still read.');
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* Wraps a native module in a `PageRasterizer`.
|
|
134
|
-
*
|
|
135
|
-
* The native module is INJECTED rather than imported so this file stays pure.
|
|
136
|
-
* `getDefaultRasterizer()` in `./index.native.ts` supplies the real one.
|
|
137
|
-
*/
|
|
138
|
-
export function createIosRasterizer(native, options = {}) {
|
|
139
|
-
return createNativeRasterizer(native, {
|
|
140
|
-
// NOT `ios-pdfkit`, though the README's plan said PDFKit and an earlier draft
|
|
141
|
-
// of this file used that id. Half the renders never touch PDFKit — every tile
|
|
142
|
-
// goes through `CGContextDrawPDFPage` — and an id is what a bug report
|
|
143
|
-
// quotes, so naming the framework that handles the minority of renders would
|
|
144
|
-
// misdirect every investigation of a tile.
|
|
145
|
-
id: options.id ?? 'ios-coregraphics',
|
|
146
|
-
capabilities: CAPABILITIES,
|
|
147
|
-
alpha: IOS_ALPHA,
|
|
148
|
-
poolSize: clampPoolSize(options.poolSize ?? 1),
|
|
149
|
-
label: 'iOS',
|
|
150
|
-
toNativeSource,
|
|
151
|
-
...(options.lookupTakePixels === undefined
|
|
152
|
-
? {}
|
|
153
|
-
: { lookupTakePixels: options.lookupTakePixels }),
|
|
154
|
-
});
|
|
155
|
-
}
|
package/ios/Package.swift
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
// swift-tools-version:5.9
|
|
2
|
-
//
|
|
3
|
-
// A SECOND BUILD SYSTEM FOR THE SAME SOURCES, and it earns its keep.
|
|
4
|
-
//
|
|
5
|
-
// `PdfCanvasCore` is the React-free iOS rasterizer. The podspec at the repository
|
|
6
|
-
// root compiles it into a host app; this manifest compiles the identical files
|
|
7
|
-
// for macOS so that `swift test` proves them on a laptop — no device, no
|
|
8
|
-
// simulator, no app, no CocoaPods. That is the same arrangement Android has, where
|
|
9
|
-
// `android/src/main` is a React-free library provable by
|
|
10
|
-
// `./gradlew connectedDebugAndroidTest`, and it exists for the same reason: a bug
|
|
11
|
-
// in the React binding must never be able to hide as a bug in rasterizing.
|
|
12
|
-
//
|
|
13
|
-
// It also enforces a rule that would otherwise be a comment. CoreGraphics and
|
|
14
|
-
// PDFKit exist on macOS; UIKit does not. So a UIKit import anywhere in
|
|
15
|
-
// `PdfCanvasCore` breaks this build immediately, which is exactly when it should
|
|
16
|
-
// break — rather than at the point somebody tries to reuse the core somewhere it
|
|
17
|
-
// cannot go.
|
|
18
|
-
//
|
|
19
|
-
// WHAT IS DELIBERATELY NOT HERE: `PdfCanvasBridge`. It imports `<jsi/jsi.h>` and
|
|
20
|
-
// `<React/RCTBridgeModule.h>`, which come from a pod and cannot be resolved by
|
|
21
|
-
// SwiftPM. Its only build is the podspec's, and its only proof is a device
|
|
22
|
-
// session — which is precisely the split the Android README describes and the
|
|
23
|
-
// reason both platforms name what is unproven.
|
|
24
|
-
//
|
|
25
|
-
// The test target is SWIFT, not Objective-C, which forces the core's public
|
|
26
|
-
// headers to stay importable from Swift: no C++ in a header, no `void *` where a
|
|
27
|
-
// typed pointer belongs, nothing that only an Objective-C++ translation unit can
|
|
28
|
-
// parse. That constraint is worth having on a library whose one C++ file lives on
|
|
29
|
-
// the other side of a protocol.
|
|
30
|
-
|
|
31
|
-
import PackageDescription
|
|
32
|
-
|
|
33
|
-
let package = Package(
|
|
34
|
-
name: "PdfCanvasCore",
|
|
35
|
-
platforms: [.macOS(.v11), .iOS(.v13)],
|
|
36
|
-
products: [
|
|
37
|
-
.library(name: "PdfCanvasCore", targets: ["PdfCanvasCore", "PdfCanvasSlots"])
|
|
38
|
-
],
|
|
39
|
-
targets: [
|
|
40
|
-
.target(
|
|
41
|
-
name: "PdfCanvasCore",
|
|
42
|
-
path: "Sources/PdfCanvasCore",
|
|
43
|
-
publicHeadersPath: "include"
|
|
44
|
-
),
|
|
45
|
-
.target(
|
|
46
|
-
name: "PdfCanvasSlots",
|
|
47
|
-
dependencies: ["PdfCanvasCore"],
|
|
48
|
-
path: "Sources/PdfCanvasSlots",
|
|
49
|
-
publicHeadersPath: "include"
|
|
50
|
-
),
|
|
51
|
-
.testTarget(
|
|
52
|
-
name: "PdfCanvasCoreTests",
|
|
53
|
-
dependencies: ["PdfCanvasCore", "PdfCanvasSlots"],
|
|
54
|
-
path: "Tests/PdfCanvasCoreTests"
|
|
55
|
-
),
|
|
56
|
-
],
|
|
57
|
-
cxxLanguageStandard: .cxx17
|
|
58
|
-
)
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
#import "PdfCanvasCancellation.h"
|
|
2
|
-
|
|
3
|
-
#import <stdatomic.h>
|
|
4
|
-
|
|
5
|
-
@implementation PdfCanvasCancellation {
|
|
6
|
-
_Atomic(bool) _cancelled;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
- (instancetype)init {
|
|
10
|
-
self = [super init];
|
|
11
|
-
if (self != nil) {
|
|
12
|
-
atomic_init(&_cancelled, false);
|
|
13
|
-
}
|
|
14
|
-
return self;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
- (void)cancel {
|
|
18
|
-
atomic_store_explicit(&_cancelled, true, memory_order_relaxed);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
- (BOOL)isCancelled {
|
|
22
|
-
return atomic_load_explicit(&_cancelled, memory_order_relaxed) ? YES : NO;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
@end
|
|
26
|
-
|
|
27
|
-
BOOL PdfCanvasCheckCancelled(
|
|
28
|
-
PdfCanvasCancellation *_Nullable signal,
|
|
29
|
-
NSString *stage,
|
|
30
|
-
NSError *_Nullable *_Nullable error) {
|
|
31
|
-
if (signal == nil || !signal.isCancelled) {
|
|
32
|
-
return YES;
|
|
33
|
-
}
|
|
34
|
-
PdfCanvasFail(
|
|
35
|
-
error, PdfCanvasErrorCodeCancelled,
|
|
36
|
-
[NSString stringWithFormat:@"Render cancelled (%@).", stage], nil);
|
|
37
|
-
return NO;
|
|
38
|
-
}
|