@succinctlabs/react-native-zcam1 0.3.0 → 0.3.13

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 (43) hide show
  1. package/ios/Zcam1Camera.swift +177 -9
  2. package/ios/Zcam1CameraFilmStyle.swift +18 -2
  3. package/ios/Zcam1CameraViewManager.m +4 -0
  4. package/ios/Zcam1DepthData.swift +219 -286
  5. package/lib/module/NativeZcam1Capture.js.map +1 -1
  6. package/lib/module/camera.js +113 -15
  7. package/lib/module/camera.js.map +1 -1
  8. package/lib/module/capture.js +21 -3
  9. package/lib/module/capture.js.map +1 -1
  10. package/lib/module/common.js +3 -2
  11. package/lib/module/common.js.map +1 -1
  12. package/lib/module/generated/zcam1_c2pa_utils.js +85 -6
  13. package/lib/module/generated/zcam1_c2pa_utils.js.map +1 -1
  14. package/lib/module/generated/zcam1_verify_utils.js +80 -3
  15. package/lib/module/generated/zcam1_verify_utils.js.map +1 -1
  16. package/lib/module/index.js +1 -1
  17. package/lib/module/index.js.map +1 -1
  18. package/lib/module/utils.js +5 -4
  19. package/lib/module/utils.js.map +1 -1
  20. package/lib/typescript/src/NativeZcam1Capture.d.ts +10 -0
  21. package/lib/typescript/src/NativeZcam1Capture.d.ts.map +1 -1
  22. package/lib/typescript/src/camera.d.ts +36 -0
  23. package/lib/typescript/src/camera.d.ts.map +1 -1
  24. package/lib/typescript/src/capture.d.ts +8 -1
  25. package/lib/typescript/src/capture.d.ts.map +1 -1
  26. package/lib/typescript/src/common.d.ts.map +1 -1
  27. package/lib/typescript/src/generated/zcam1_c2pa_utils.d.ts +60 -0
  28. package/lib/typescript/src/generated/zcam1_c2pa_utils.d.ts.map +1 -1
  29. package/lib/typescript/src/generated/zcam1_verify_utils.d.ts +134 -3
  30. package/lib/typescript/src/generated/zcam1_verify_utils.d.ts.map +1 -1
  31. package/lib/typescript/src/index.d.ts +2 -2
  32. package/lib/typescript/src/index.d.ts.map +1 -1
  33. package/lib/typescript/src/utils.d.ts +1 -1
  34. package/lib/typescript/src/utils.d.ts.map +1 -1
  35. package/package.json +2 -1
  36. package/src/NativeZcam1Capture.ts +12 -0
  37. package/src/camera.tsx +179 -9
  38. package/src/capture.tsx +30 -3
  39. package/src/common.tsx +3 -2
  40. package/src/generated/zcam1_c2pa_utils.ts +126 -3
  41. package/src/generated/zcam1_verify_utils.ts +92 -3
  42. package/src/index.ts +2 -1
  43. package/src/utils.ts +7 -3
package/src/camera.tsx CHANGED
@@ -1,14 +1,17 @@
1
+ import Geolocation from "@react-native-community/geolocation";
1
2
  import JailMonkey from "jail-monkey";
2
3
  import React from "react";
3
4
  import { requireNativeComponent, type StyleProp, type ViewStyle } from "react-native";
4
- import { Dirs, Util } from "react-native-file-access";
5
+ import { Dirs, FileSystem, Util } from "react-native-file-access";
5
6
 
6
7
  import {
8
+ AuthenticityData,
7
9
  buildSelfSignedCertificate,
8
10
  computeHash,
9
11
  DepthData,
10
12
  ExistingCertChain,
11
13
  formatFromPath,
14
+ LocationInfo,
12
15
  ManifestEditor,
13
16
  type PhotoMetadataInfo,
14
17
  SelfSignedCertChain,
@@ -43,6 +46,13 @@ export type CaptureFormat = "jpeg" | "dng";
43
46
  */
44
47
  export type CameraFilmStyle = "normal" | "mellow" | "nostalgic" | "bw";
45
48
 
49
+ /**
50
+ * Hardware shutter action type.
51
+ * - "photo": Full press on volume button or Camera Control. Trigger capture.
52
+ * - "focus": Light press on Camera Control. Trigger focus/zoom (optional).
53
+ */
54
+ export type HardwareShutterAction = "photo" | "focus";
55
+
46
56
  // ─────────────────────────────────────────────────────────────────────────────
47
57
  // Custom Film Style Recipe Types
48
58
  // ─────────────────────────────────────────────────────────────────────────────
@@ -153,6 +163,22 @@ export interface ZCameraProps {
153
163
  * Use with `filmStyle` prop by casting the custom name: `filmStyle={"myStyle" as CameraFilmStyle}`.
154
164
  */
155
165
  customFilmStyles?: Record<string, FilmStyleRecipe>;
166
+ /**
167
+ * When true, embeds a trusted GPS timestamp in the C2PA manifest at capture time.
168
+ * Requires location permission. The timestamp is sourced from GPS rather than device clock,
169
+ * making it tamper-evident. Stored as `trustedTimestamp` in photo/video metadata.
170
+ * @default false
171
+ */
172
+ captureTimestampEnabled?: boolean;
173
+ /**
174
+ * When true, embeds GPS coordinates in the C2PA manifest at capture time.
175
+ * Requires location permission. Stored as `location` in photo/video metadata.
176
+ * If location retrieval fails, `isLocationAvailable` is set to `false` and
177
+ * `locationRetrievalStatus` contains the error reason.
178
+ * @default false
179
+ */
180
+ captureLocationEnabled?: boolean;
181
+
156
182
  /**
157
183
  * Enable depth data capture at session level.
158
184
  * When true, depth data can be captured but zoom may be restricted on dual-camera devices.
@@ -167,6 +193,21 @@ export interface ZCameraProps {
167
193
  * @param orientation The new physical orientation of the device.
168
194
  */
169
195
  onOrientationChange?: (orientation: DeviceOrientation) => void;
196
+ /**
197
+ * Whether hardware buttons (volume buttons, Camera Control on iPhone 16)
198
+ * should trigger capture events via onHardwareShutter.
199
+ * When enabled, the system volume HUD is automatically suppressed while
200
+ * the camera is active. Requires iOS 17.2+; ignored on older versions.
201
+ * @default true
202
+ */
203
+ hardwareShutterEnabled?: boolean;
204
+ /**
205
+ * Callback fired when a hardware capture button is pressed.
206
+ * Volume buttons and Camera Control (iPhone 16) trigger "photo" action.
207
+ * Light press on Camera Control triggers "focus" action.
208
+ * @param action The type of hardware shutter event.
209
+ */
210
+ onHardwareShutter?: (action: HardwareShutterAction) => void;
170
211
  /** Capture information used to generate C2PA bindings for each photo. */
171
212
  captureInfo: CaptureInfo;
172
213
  /** Optional certificate chain used to sign the C2PA manifest. */
@@ -206,6 +247,8 @@ type NativeCameraViewProps = {
206
247
  customFilmStyles?: Record<string, FilmStyleEffect[]>;
207
248
  depthEnabled?: boolean;
208
249
  onOrientationChange?: (event: { nativeEvent: { orientation: string } }) => void;
250
+ hardwareShutterEnabled?: boolean;
251
+ onHardwareShutter?: (event: { nativeEvent: { action: string } }) => void;
209
252
  };
210
253
 
211
254
  /**
@@ -472,6 +515,16 @@ export class ZCamera extends React.PureComponent<ZCameraProps> {
472
515
  const when = new Date().toISOString().replace("T", " ").split(".")[0]!;
473
516
  const isJailBroken = JailMonkey.isJailBroken();
474
517
  const isLocationSpoofingAvailable = JailMonkey.canMockLocation();
518
+ const location = await retrieveLocationData(
519
+ this.props.captureTimestampEnabled,
520
+ this.props.captureLocationEnabled,
521
+ );
522
+ const authenticityData: AuthenticityData = {
523
+ isJailBroken,
524
+ isLocationSpoofingAvailable,
525
+ isLocationAvailable: location.isLocationAvailable,
526
+ locationRetrievalStatus: location.locationRetrievalStatus,
527
+ };
475
528
 
476
529
  result.filePath = await embedBindings(
477
530
  result.filePath,
@@ -492,11 +545,10 @@ export class ZCamera extends React.PureComponent<ZCameraProps> {
492
545
  audioCodec: result.audioCodec,
493
546
  audioSampleRate: result.audioSampleRate,
494
547
  audioChannels: result.audioChannels,
495
- authenticityData: {
496
- isJailBroken,
497
- isLocationSpoofingAvailable,
498
- },
548
+ authenticityData,
499
549
  filmStyle: this.resolveFilmStyleInfo(),
550
+ trustedTimestamp: location.trustedTimestamp,
551
+ location: location.coords,
500
552
  },
501
553
  this.props.captureInfo,
502
554
  this.certChainPem,
@@ -543,6 +595,15 @@ export class ZCamera extends React.PureComponent<ZCameraProps> {
543
595
  }
544
596
 
545
597
  const originalPath = result.filePath;
598
+ const depthHeatMapPath = result.depthHeatMapPath as string | undefined;
599
+ const depthRawHash = result.depthRawHash as string | undefined;
600
+
601
+ // Log depth diagnostics for troubleshooting.
602
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
603
+ const depthDiag = (result as any)._depthDiag;
604
+ if (depthDiag) {
605
+ console.log("[DEPTH_DIAG]", JSON.stringify(depthDiag, null, 2));
606
+ }
546
607
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
547
608
  const metadata = (result.metadata as any) ?? {};
548
609
 
@@ -555,6 +616,16 @@ export class ZCamera extends React.PureComponent<ZCameraProps> {
555
616
  const softwareVersion = tiff.Software || "Unknown";
556
617
  const isJailBroken = JailMonkey.isJailBroken();
557
618
  const isLocationSpoofingAvailable = JailMonkey.canMockLocation();
619
+ const location = await retrieveLocationData(
620
+ this.props.captureTimestampEnabled,
621
+ this.props.captureLocationEnabled,
622
+ );
623
+ const authenticityData: AuthenticityData = {
624
+ isJailBroken,
625
+ isLocationSpoofingAvailable,
626
+ isLocationAvailable: location.isLocationAvailable,
627
+ locationRetrievalStatus: location.locationRetrievalStatus,
628
+ };
558
629
 
559
630
  const destinationPath = await embedBindings(
560
631
  originalPath,
@@ -570,15 +641,16 @@ export class ZCamera extends React.PureComponent<ZCameraProps> {
570
641
  exposureTime: exif.ExposureTime,
571
642
  depthOfField: exif.FNumber,
572
643
  focalLength: exif.FocalLength,
573
- authenticityData: {
574
- isJailBroken,
575
- isLocationSpoofingAvailable,
576
- },
644
+ authenticityData,
577
645
  depthData: result.depthData as DepthData | undefined,
578
646
  filmStyle: this.resolveFilmStyleInfo(),
647
+ trustedTimestamp: location.trustedTimestamp,
648
+ location: location.coords,
579
649
  },
580
650
  this.props.captureInfo,
581
651
  this.certChainPem,
652
+ depthHeatMapPath,
653
+ depthRawHash,
582
654
  );
583
655
 
584
656
  return new ZPhoto(originalPath, destinationPath);
@@ -601,6 +673,8 @@ export class ZCamera extends React.PureComponent<ZCameraProps> {
601
673
  customFilmStyles,
602
674
  depthEnabled = false,
603
675
  onOrientationChange,
676
+ hardwareShutterEnabled = true,
677
+ onHardwareShutter,
604
678
  style,
605
679
  } = this.props;
606
680
 
@@ -629,6 +703,12 @@ export class ZCamera extends React.PureComponent<ZCameraProps> {
629
703
  ? (event) => onOrientationChange(event.nativeEvent.orientation as DeviceOrientation)
630
704
  : undefined
631
705
  }
706
+ hardwareShutterEnabled={hardwareShutterEnabled}
707
+ onHardwareShutter={
708
+ onHardwareShutter
709
+ ? (event) => onHardwareShutter(event.nativeEvent.action as HardwareShutterAction)
710
+ : undefined
711
+ }
632
712
  />
633
713
  );
634
714
  }
@@ -643,6 +723,8 @@ async function embedBindings(
643
723
  metadata: PhotoMetadataInfo | VideoMetadataInfo,
644
724
  captureInfo: CaptureInfo,
645
725
  certChainPem: string,
726
+ depthHeatMapPath?: string,
727
+ depthRawHash?: string,
646
728
  ): Promise<string> {
647
729
  originalPath = stripFileProtocol(originalPath);
648
730
  const dataHash = computeHash(originalPath);
@@ -670,10 +752,40 @@ async function embedBindings(
670
752
  normalizedMetadata = manifestEditor.addVideoMetadataAction(metadata as VideoMetadataInfo, when);
671
753
  }
672
754
 
755
+ // Embed depth heat map as a C2PA assertion if available.
756
+ // The heat map JPEG is read from disk (written by Swift during capture) — only the
757
+ // file path string crosses the RN bridge, not the binary data.
758
+ if (depthHeatMapPath && format.indexOf("video") < 0) {
759
+ try {
760
+ const heatMapBase64 = await FileSystem.readFile(depthHeatMapPath, "base64");
761
+ const depthData = (metadata as PhotoMetadataInfo).depthData;
762
+ manifestEditor.addAssertion(
763
+ "succinct.depth-heatmap",
764
+ JSON.stringify({
765
+ format: "image/jpeg",
766
+ width: depthData?.width,
767
+ height: depthData?.height,
768
+ colormap: "turbo",
769
+ depth_accuracy: depthData?.accuracy,
770
+ depth_range_min: depthData?.statistics?.min,
771
+ depth_range_max: depthData?.statistics?.max,
772
+ sensor_type: depthData?.pixelFormat,
773
+ raw_depth_hash: depthRawHash,
774
+ image_data: heatMapBase64,
775
+ }),
776
+ );
777
+ } catch (e) {
778
+ console.warn("[embedBindings] Failed to embed depth heat map:", e);
779
+ } finally {
780
+ FileSystem.unlink(depthHeatMapPath).catch(() => {});
781
+ }
782
+ }
783
+
673
784
  const assertion = await generateAppAttestAssertion(
674
785
  dataHash,
675
786
  normalizedMetadata,
676
787
  captureInfo.deviceKeyId,
788
+ captureInfo.production,
677
789
  );
678
790
 
679
791
  // Add an assertion containing all data needed to later generate a proof
@@ -692,3 +804,61 @@ async function embedBindings(
692
804
 
693
805
  return destinationPath;
694
806
  }
807
+
808
+ type LocationData = {
809
+ coords: LocationInfo | undefined;
810
+ trustedTimestamp: bigint | undefined;
811
+ isLocationAvailable: boolean | undefined;
812
+ locationRetrievalStatus: string | undefined;
813
+ };
814
+
815
+ function retrieveLocationData(
816
+ captureTimestampEnabled: boolean | undefined,
817
+ captureLocationEnabled: boolean | undefined,
818
+ ): Promise<LocationData> {
819
+ if (!captureTimestampEnabled && !captureLocationEnabled) {
820
+ return Promise.resolve({
821
+ coords: undefined,
822
+ trustedTimestamp: undefined,
823
+ isLocationAvailable: undefined,
824
+ locationRetrievalStatus: undefined,
825
+ });
826
+ }
827
+
828
+ return new Promise((resolve) => {
829
+ Geolocation.getCurrentPosition(
830
+ (position) => {
831
+ resolve({
832
+ coords: captureLocationEnabled
833
+ ? {
834
+ latitude: position.coords.latitude.toFixed(6),
835
+ longitude: position.coords.longitude.toFixed(6),
836
+ altitude: position.coords.altitude?.toFixed(6),
837
+ accuracy: position.coords.accuracy.toFixed(6),
838
+ altitudeAccuracy: position.coords.altitudeAccuracy?.toFixed(6),
839
+ }
840
+ : undefined,
841
+ trustedTimestamp: captureTimestampEnabled
842
+ ? BigInt(Math.trunc(position.timestamp))
843
+ : undefined,
844
+ isLocationAvailable: true,
845
+ locationRetrievalStatus: "success",
846
+ });
847
+ },
848
+ (error) => {
849
+ console.warn(`[ZCAM1] failed to retrieve GPS location data: ${error.message}`);
850
+ resolve({
851
+ coords: undefined,
852
+ trustedTimestamp: undefined,
853
+ isLocationAvailable: false,
854
+ locationRetrievalStatus: error.message,
855
+ });
856
+ },
857
+ {
858
+ timeout: 1000, // 1 second
859
+ maximumAge: 60 * 1000, // 1 minute
860
+ enableHighAccuracy: true,
861
+ },
862
+ );
863
+ });
864
+ }
package/src/capture.tsx CHANGED
@@ -1,4 +1,5 @@
1
1
  import { generateHardwareKey, getAttestation } from "@pagopa/io-react-native-integrity";
2
+ import Geolocation from "@react-native-community/geolocation";
2
3
  import EncryptedStorage from "react-native-encrypted-storage";
3
4
 
4
5
  import { type ECKey, getContentPublicKey, getSecureEnclaveKeyId } from "./common";
@@ -51,6 +52,7 @@ export type {
51
52
  */
52
53
  export type CaptureInfo = {
53
54
  appId: string;
55
+ production: boolean;
54
56
  deviceKeyId: string;
55
57
  contentPublicKey: ECKey;
56
58
  contentKeyId: Uint8Array;
@@ -102,6 +104,12 @@ export async function initCapture(settings: Settings): Promise<CaptureInfo> {
102
104
  // If running in simulator, hardware key generation is not supported
103
105
  const err = error as { code?: string; message?: string } | undefined;
104
106
  if (err?.code === "-1" || err?.message?.includes("UNSUPPORTED_SERVICE")) {
107
+ if (settings.production) {
108
+ throw new Error(
109
+ "ZCAM: Simulator is not supported in production mode. Set production: false for development.",
110
+ );
111
+ }
112
+
105
113
  console.warn(
106
114
  "[ZCAM] Running in simulator - using mock device key. This is for development only.",
107
115
  );
@@ -126,6 +134,7 @@ export async function initCapture(settings: Settings): Promise<CaptureInfo> {
126
134
 
127
135
  return {
128
136
  appId: settings.appId,
137
+ production: settings.production,
129
138
  deviceKeyId,
130
139
  contentPublicKey,
131
140
  contentKeyId,
@@ -139,8 +148,8 @@ export async function initCapture(settings: Settings): Promise<CaptureInfo> {
139
148
  * @param settings - Configuration settings for registration
140
149
  * @returns Attestation data and challenge
141
150
  */
142
- export async function updateRegistration(keyId: string, _settings: Settings): Promise<string> {
143
- // Try to get real attestation, but fall back to mock for simulator
151
+ export async function updateRegistration(keyId: string, settings: Settings): Promise<string> {
152
+ // Try to get real attestation, but fall back to mock for simulator in dev mode
144
153
  let attestation: string;
145
154
  try {
146
155
  attestation = await getAttestation(keyId, keyId);
@@ -148,11 +157,15 @@ export async function updateRegistration(keyId: string, _settings: Settings): Pr
148
157
  // If running in simulator, App Attest is not supported
149
158
  const err = error as { code?: string; message?: string } | undefined;
150
159
  if (err?.code === "-1" || err?.message?.includes("UNSUPPORTED_SERVICE")) {
160
+ if (settings.production) {
161
+ throw new Error(
162
+ "ZCAM: Simulator is not supported in production mode. Set production: false for development.",
163
+ );
164
+ }
151
165
  console.warn(
152
166
  "[ZCAM] Running in simulator - using mock attestation. This is for development only.",
153
167
  );
154
168
  // Use a mock attestation for simulator testing
155
- // In production, this would need to be rejected by the backend
156
169
  return `SIMULATOR_MOCK_${keyId}_${Date.now()}`;
157
170
  } else {
158
171
  throw error;
@@ -163,3 +176,17 @@ export async function updateRegistration(keyId: string, _settings: Settings): Pr
163
176
 
164
177
  return attestation;
165
178
  }
179
+
180
+ /**
181
+ * Requests location permission from the user.
182
+ * This function triggers the native location authorization prompt on the device.
183
+ * @throws {string} Error message if permission request fails
184
+ */
185
+ export function requestLocationPermission() {
186
+ Geolocation.requestAuthorization(
187
+ () => {},
188
+ (error) => {
189
+ throw error.message;
190
+ },
191
+ );
192
+ }
package/src/common.tsx CHANGED
@@ -24,8 +24,9 @@ function flexibleBase64Decode(str: string): Uint8Array {
24
24
  }
25
25
 
26
26
  export async function getContentPublicKey(): Promise<PublicKey> {
27
- return await getPublicKeyFixed(CONTENT_KEY_TAG).catch(() => {
28
- return generate(CONTENT_KEY_TAG);
27
+ return await getPublicKeyFixed(CONTENT_KEY_TAG).catch(async () => {
28
+ await generate(CONTENT_KEY_TAG);
29
+ return getPublicKeyFixed(CONTENT_KEY_TAG);
29
30
  });
30
31
  }
31
32
 
@@ -169,6 +169,8 @@ export function formatFromPath(path: string): string | undefined {
169
169
  export type AuthenticityData = {
170
170
  isJailBroken: boolean;
171
171
  isLocationSpoofingAvailable: boolean;
172
+ isLocationAvailable: boolean | undefined;
173
+ locationRetrievalStatus: string | undefined;
172
174
  };
173
175
 
174
176
  /**
@@ -206,16 +208,22 @@ const FfiConverterTypeAuthenticityData = (() => {
206
208
  return {
207
209
  isJailBroken: FfiConverterBool.read(from),
208
210
  isLocationSpoofingAvailable: FfiConverterBool.read(from),
211
+ isLocationAvailable: FfiConverterOptionalBool.read(from),
212
+ locationRetrievalStatus: FfiConverterOptionalString.read(from),
209
213
  };
210
214
  }
211
215
  write(value: TypeName, into: RustBuffer): void {
212
216
  FfiConverterBool.write(value.isJailBroken, into);
213
217
  FfiConverterBool.write(value.isLocationSpoofingAvailable, into);
218
+ FfiConverterOptionalBool.write(value.isLocationAvailable, into);
219
+ FfiConverterOptionalString.write(value.locationRetrievalStatus, into);
214
220
  }
215
221
  allocationSize(value: TypeName): number {
216
222
  return (
217
223
  FfiConverterBool.allocationSize(value.isJailBroken) +
218
- FfiConverterBool.allocationSize(value.isLocationSpoofingAvailable)
224
+ FfiConverterBool.allocationSize(value.isLocationSpoofingAvailable) +
225
+ FfiConverterOptionalBool.allocationSize(value.isLocationAvailable) +
226
+ FfiConverterOptionalString.allocationSize(value.locationRetrievalStatus)
219
227
  );
220
228
  }
221
229
  }
@@ -605,6 +613,95 @@ const FfiConverterTypeFilmStyleInfo = (() => {
605
613
  return new FFIConverter();
606
614
  })();
607
615
 
616
+ /**
617
+ * GPS location captured at the time of photo/video creation.
618
+ *
619
+ * Coordinates and accuracy are stored as strings to preserve the original
620
+ * precision from the device without floating-point rounding.
621
+ */
622
+ export type LocationInfo = {
623
+ /**
624
+ * Latitude in decimal degrees (e.g. "37.7749").
625
+ */
626
+ latitude: string;
627
+ /**
628
+ * Longitude in decimal degrees (e.g. "-122.4194").
629
+ */
630
+ longitude: string;
631
+ /**
632
+ * Altitude in meters above sea level, if available.
633
+ */
634
+ altitude: string | undefined;
635
+ /**
636
+ * Horizontal accuracy radius in meters.
637
+ */
638
+ accuracy: string;
639
+ /**
640
+ * Vertical accuracy in meters, if available.
641
+ */
642
+ altitudeAccuracy: string | undefined;
643
+ };
644
+
645
+ /**
646
+ * Generated factory for {@link LocationInfo} record objects.
647
+ */
648
+ export const LocationInfo = (() => {
649
+ const defaults = () => ({});
650
+ const create = (() => {
651
+ return uniffiCreateRecord<LocationInfo, ReturnType<typeof defaults>>(defaults);
652
+ })();
653
+ return Object.freeze({
654
+ /**
655
+ * Create a frozen instance of {@link LocationInfo}, with defaults specified
656
+ * in Rust, in the {@link zcam1_c2pa_utils} crate.
657
+ */
658
+ create,
659
+
660
+ /**
661
+ * Create a frozen instance of {@link LocationInfo}, with defaults specified
662
+ * in Rust, in the {@link zcam1_c2pa_utils} crate.
663
+ */
664
+ new: create,
665
+
666
+ /**
667
+ * Defaults specified in the {@link zcam1_c2pa_utils} crate.
668
+ */
669
+ defaults: () => Object.freeze(defaults()) as Partial<LocationInfo>,
670
+ });
671
+ })();
672
+
673
+ const FfiConverterTypeLocationInfo = (() => {
674
+ type TypeName = LocationInfo;
675
+ class FFIConverter extends AbstractFfiConverterByteArray<TypeName> {
676
+ read(from: RustBuffer): TypeName {
677
+ return {
678
+ latitude: FfiConverterString.read(from),
679
+ longitude: FfiConverterString.read(from),
680
+ altitude: FfiConverterOptionalString.read(from),
681
+ accuracy: FfiConverterString.read(from),
682
+ altitudeAccuracy: FfiConverterOptionalString.read(from),
683
+ };
684
+ }
685
+ write(value: TypeName, into: RustBuffer): void {
686
+ FfiConverterString.write(value.latitude, into);
687
+ FfiConverterString.write(value.longitude, into);
688
+ FfiConverterOptionalString.write(value.altitude, into);
689
+ FfiConverterString.write(value.accuracy, into);
690
+ FfiConverterOptionalString.write(value.altitudeAccuracy, into);
691
+ }
692
+ allocationSize(value: TypeName): number {
693
+ return (
694
+ FfiConverterString.allocationSize(value.latitude) +
695
+ FfiConverterString.allocationSize(value.longitude) +
696
+ FfiConverterOptionalString.allocationSize(value.altitude) +
697
+ FfiConverterString.allocationSize(value.accuracy) +
698
+ FfiConverterOptionalString.allocationSize(value.altitudeAccuracy)
699
+ );
700
+ }
701
+ }
702
+ return new FFIConverter();
703
+ })();
704
+
608
705
  export type PhotoMetadataInfo = {
609
706
  deviceMake: string;
610
707
  deviceModel: string;
@@ -619,6 +716,8 @@ export type PhotoMetadataInfo = {
619
716
  authenticityData: AuthenticityData;
620
717
  depthData: DepthData | undefined;
621
718
  filmStyle: FilmStyleInfo | undefined;
719
+ trustedTimestamp: /*u64*/ bigint | undefined;
720
+ location: LocationInfo | undefined;
622
721
  };
623
722
 
624
723
  /**
@@ -667,6 +766,8 @@ const FfiConverterTypePhotoMetadataInfo = (() => {
667
766
  authenticityData: FfiConverterTypeAuthenticityData.read(from),
668
767
  depthData: FfiConverterOptionalTypeDepthData.read(from),
669
768
  filmStyle: FfiConverterOptionalTypeFilmStyleInfo.read(from),
769
+ trustedTimestamp: FfiConverterOptionalUInt64.read(from),
770
+ location: FfiConverterOptionalTypeLocationInfo.read(from),
670
771
  };
671
772
  }
672
773
  write(value: TypeName, into: RustBuffer): void {
@@ -683,6 +784,8 @@ const FfiConverterTypePhotoMetadataInfo = (() => {
683
784
  FfiConverterTypeAuthenticityData.write(value.authenticityData, into);
684
785
  FfiConverterOptionalTypeDepthData.write(value.depthData, into);
685
786
  FfiConverterOptionalTypeFilmStyleInfo.write(value.filmStyle, into);
787
+ FfiConverterOptionalUInt64.write(value.trustedTimestamp, into);
788
+ FfiConverterOptionalTypeLocationInfo.write(value.location, into);
686
789
  }
687
790
  allocationSize(value: TypeName): number {
688
791
  return (
@@ -698,7 +801,9 @@ const FfiConverterTypePhotoMetadataInfo = (() => {
698
801
  FfiConverterUInt32.allocationSize(value.focalLength) +
699
802
  FfiConverterTypeAuthenticityData.allocationSize(value.authenticityData) +
700
803
  FfiConverterOptionalTypeDepthData.allocationSize(value.depthData) +
701
- FfiConverterOptionalTypeFilmStyleInfo.allocationSize(value.filmStyle)
804
+ FfiConverterOptionalTypeFilmStyleInfo.allocationSize(value.filmStyle) +
805
+ FfiConverterOptionalUInt64.allocationSize(value.trustedTimestamp) +
806
+ FfiConverterOptionalTypeLocationInfo.allocationSize(value.location)
702
807
  );
703
808
  }
704
809
  }
@@ -779,6 +884,8 @@ export type VideoMetadataInfo = {
779
884
  audioChannels: /*u32*/ number | undefined;
780
885
  authenticityData: AuthenticityData;
781
886
  filmStyle: FilmStyleInfo | undefined;
887
+ trustedTimestamp: /*u64*/ bigint | undefined;
888
+ location: LocationInfo | undefined;
782
889
  };
783
890
 
784
891
  /**
@@ -831,6 +938,8 @@ const FfiConverterTypeVideoMetadataInfo = (() => {
831
938
  audioChannels: FfiConverterOptionalUInt32.read(from),
832
939
  authenticityData: FfiConverterTypeAuthenticityData.read(from),
833
940
  filmStyle: FfiConverterOptionalTypeFilmStyleInfo.read(from),
941
+ trustedTimestamp: FfiConverterOptionalUInt64.read(from),
942
+ location: FfiConverterOptionalTypeLocationInfo.read(from),
834
943
  };
835
944
  }
836
945
  write(value: TypeName, into: RustBuffer): void {
@@ -851,6 +960,8 @@ const FfiConverterTypeVideoMetadataInfo = (() => {
851
960
  FfiConverterOptionalUInt32.write(value.audioChannels, into);
852
961
  FfiConverterTypeAuthenticityData.write(value.authenticityData, into);
853
962
  FfiConverterOptionalTypeFilmStyleInfo.write(value.filmStyle, into);
963
+ FfiConverterOptionalUInt64.write(value.trustedTimestamp, into);
964
+ FfiConverterOptionalTypeLocationInfo.write(value.location, into);
854
965
  }
855
966
  allocationSize(value: TypeName): number {
856
967
  return (
@@ -870,7 +981,9 @@ const FfiConverterTypeVideoMetadataInfo = (() => {
870
981
  FfiConverterOptionalUInt32.allocationSize(value.audioSampleRate) +
871
982
  FfiConverterOptionalUInt32.allocationSize(value.audioChannels) +
872
983
  FfiConverterTypeAuthenticityData.allocationSize(value.authenticityData) +
873
- FfiConverterOptionalTypeFilmStyleInfo.allocationSize(value.filmStyle)
984
+ FfiConverterOptionalTypeFilmStyleInfo.allocationSize(value.filmStyle) +
985
+ FfiConverterOptionalUInt64.allocationSize(value.trustedTimestamp) +
986
+ FfiConverterOptionalTypeLocationInfo.allocationSize(value.location)
874
987
  );
875
988
  }
876
989
  }
@@ -1683,6 +1796,9 @@ const uniffiTypeManifestStoreObjectFactory: UniffiObjectFactory<ManifestStoreInt
1683
1796
  // FfiConverter for ManifestStoreInterface
1684
1797
  const FfiConverterTypeManifestStore = new FfiConverterObject(uniffiTypeManifestStoreObjectFactory);
1685
1798
 
1799
+ // FfiConverter for boolean | undefined
1800
+ const FfiConverterOptionalBool = new FfiConverterOptional(FfiConverterBool);
1801
+
1686
1802
  // FfiConverter for DepthData | undefined
1687
1803
  const FfiConverterOptionalTypeDepthData = new FfiConverterOptional(FfiConverterTypeDepthData);
1688
1804
 
@@ -1696,6 +1812,9 @@ const FfiConverterOptionalTypeFilmStyleInfo = new FfiConverterOptional(
1696
1812
  FfiConverterTypeFilmStyleInfo,
1697
1813
  );
1698
1814
 
1815
+ // FfiConverter for LocationInfo | undefined
1816
+ const FfiConverterOptionalTypeLocationInfo = new FfiConverterOptional(FfiConverterTypeLocationInfo);
1817
+
1699
1818
  // FfiConverter for Proof | undefined
1700
1819
  const FfiConverterOptionalTypeProof = new FfiConverterOptional(FfiConverterTypeProof);
1701
1820
 
@@ -1705,6 +1824,9 @@ const FfiConverterOptionalString = new FfiConverterOptional(FfiConverterString);
1705
1824
  // FfiConverter for /*u32*/number | undefined
1706
1825
  const FfiConverterOptionalUInt32 = new FfiConverterOptional(FfiConverterUInt32);
1707
1826
 
1827
+ // FfiConverter for /*u64*/bigint | undefined
1828
+ const FfiConverterOptionalUInt64 = new FfiConverterOptional(FfiConverterUInt64);
1829
+
1708
1830
  /**
1709
1831
  * This should be called before anything else.
1710
1832
  *
@@ -1856,6 +1978,7 @@ export default Object.freeze({
1856
1978
  FfiConverterTypeDeviceBindings,
1857
1979
  FfiConverterTypeExclusion,
1858
1980
  FfiConverterTypeFilmStyleInfo,
1981
+ FfiConverterTypeLocationInfo,
1859
1982
  FfiConverterTypeManifest,
1860
1983
  FfiConverterTypeManifestEditor,
1861
1984
  FfiConverterTypeManifestStore,