From b12cdf99c97446aac2e25352393e00ffb2625bfd Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Tue, 12 Nov 2024 10:51:57 -0800 Subject: [PATCH 01/35] Update sidebars.js Add manifest tasks to sidebar nav. --- sidebars.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sidebars.js b/sidebars.js index 76fc0530..cfab4f09 100644 --- a/sidebars.js +++ b/sidebars.js @@ -44,6 +44,10 @@ const sidebars = { type: 'doc', id: 'manifest/signing-manifests', }, + { + type: 'doc', + id: 'manifest/tasks', + }, { type: 'doc', id: 'manifest/manifest-examples', From 79228a2208b84191c6325cdf4d067534cb2b6b7e Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Tue, 12 Nov 2024 11:08:27 -0800 Subject: [PATCH 02/35] Update manifest-tasks.mdx Add Python --- docs/manifest/manifest-tasks.mdx | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/docs/manifest/manifest-tasks.mdx b/docs/manifest/manifest-tasks.mdx index d24fb6a9..af974ea5 100644 --- a/docs/manifest/manifest-tasks.mdx +++ b/docs/manifest/manifest-tasks.mdx @@ -15,11 +15,37 @@ import TabItem from '@theme/TabItem'; - This is how to read a manifest using Python. + +Use the `Reader` object to read manifest data from a file or stream and perform validation on the manifest store. Use the `json()` method to return a JSON manifest report; If there are validation errors, the report includes a `validation_status` field. + +Use the `resource_to_file` and `resource_to_stream` methods to write resources to a file or stream, respectively. + +An asset file may contain many manifests in a manifest store. The most recent manifest is identified by the value of the `active_manifest` field in the manifests map. Retrieve binary resources such as thumbnails from the manifest data, use the `resource_to_stream` or `resource_to_file` methods using the associated `identifier` field values and a `uri`. + +```py +try: + # Create a reader from a file path + reader = c2pa.Reader.from_file("path/to/media_file.jpg") + + # Print the JSON for a manifest. + print("manifest store:", reader.json()) + + # Get the active manifest. + manifest = reader.get_active_manifest() + if manifest != None: + + # Get the URI to the manifest's thumbnail and write it to a file + uri = manifest["thumbnail"]["identifier"] + reader.resource_to_file(uri, "thumbnail_v2.jpg") + +except Exception as err: + print(err) +``` + - This is how to read a manifest using C++. Do we want also want C? + This is how to read a manifest using C++. From fc7ecbebdc320bbb3a0843c3791c511a32a41b21 Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Tue, 12 Nov 2024 11:42:27 -0800 Subject: [PATCH 03/35] Update manifest-tasks.mdx Add JavaScript --- docs/manifest/manifest-tasks.mdx | 38 +++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/docs/manifest/manifest-tasks.mdx b/docs/manifest/manifest-tasks.mdx index af974ea5..3c1af4bb 100644 --- a/docs/manifest/manifest-tasks.mdx +++ b/docs/manifest/manifest-tasks.mdx @@ -11,7 +11,43 @@ import TabItem from '@theme/TabItem'; - This is how to read a manifest using JavaScript. + +If the input provided to [`c2pa.read`](../../js-sdk/api/c2pa.c2pa#methods) has a C2PA manifest and was processed without errors, the returned [`c2paReadResult`](../../js-sdk/api/c2pa.c2pareadresult) contains a [`manifestStore`](../../js-sdk/api/c2pa.c2pareadresult.manifeststore). + +The [`manifestStore`](../../js-sdk/api/c2pa.c2pareadresult.manifeststore) object contains a few properties: + +- **manifests**: An object containing _all_ manifests found in an asset, keyed by UUID. +- **activeManifest**: A pointer to the latest [`manifest`](../../js-sdk/api/c2pa.manifest) in the manifest store. Effectively the "parent" manifest, this is the likely starting point when inspecting an asset's C2PA data. +- **validationStatus**: A list of any validation errors the library generated when processing an asset. See [Validation](./validation) for more information. + +[`Manifest`](../../js-sdk/api/c2pa.manifest) objects contain properties pertaining to an asset's provenance, along with convenient interfaces for [accessing assertion data](../../js-sdk/api/c2pa.assertionaccessor) and [generating a thumbnail](../../js-sdk/api/c2pa.thumbnail). + +```js + const { manifestStore, source } = await c2pa.read(sampleImage); + const activeManifest = manifestStore?.activeManifest; + if (activeManifest) { + // Get thumbnail + // Note: You would normally call `dispose()` when working with a + // component-based UI library (e.g. on component un-mount) + // @ts-expect-error noUnusedLocals + const { url, dispose } = source.thumbnail.getUrl(); + + // Get properties + const properties: Record = { + title: activeManifest.title, + format: activeManifest.format, + claimGenerator: activeManifest.claimGenerator.split('(')[0]?.trim(), + producer: selectProducer(activeManifest)?.name ?? 'Unknown', + thumbnail: ``, + ingredients: (activeManifest.ingredients ?? []) + .map((i) => i.title) + .join(', '), + signatureIssuer: activeManifest.signatureInfo?.issuer, + signatureDate: activeManifest.signatureInfo?.time + ? parseISO(activeManifest.signatureInfo.time).toString() + : 'No date available', + }; +``` From c66b543ed4167394bdc702a56b635aa5b683e899 Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Tue, 12 Nov 2024 11:45:05 -0800 Subject: [PATCH 04/35] Update manifest-tasks.mdx Add Node.js --- docs/manifest/manifest-tasks.mdx | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/docs/manifest/manifest-tasks.mdx b/docs/manifest/manifest-tasks.mdx index 3c1af4bb..aeb333ac 100644 --- a/docs/manifest/manifest-tasks.mdx +++ b/docs/manifest/manifest-tasks.mdx @@ -78,15 +78,37 @@ except Exception as err: print(err) ``` - - - - This is how to read a manifest using C++. - This is how to read a manifest using Node.js. +Use the `c2pa.read()` function to read a manifest; for example: + +```ts +import { createC2pa } from 'c2pa-node'; +import { readFile } from 'node:fs/promises'; + +const c2pa = createC2pa(); + +async function read(path, mimeType) { + const buffer = await readFile(path); + const result = await c2pa.read({ buffer, mimeType }); + + if (result) { + const { active_manifest, manifests, validation_status } = result; + console.log(active_manifest); + } else { + console.log('No claim found'); + } +} + +await read('my-c2pa-file.jpg', 'image/jpeg'); +``` + + + This is how to read a manifest using C++. + + ## Getting resources from a manifest From 44a5dd57bbdcd37e64d6c1883104f084bbc93c5a Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Tue, 12 Nov 2024 11:46:19 -0800 Subject: [PATCH 05/35] Update manifest-tasks.mdx Add cpp. --- docs/manifest/manifest-tasks.mdx | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/docs/manifest/manifest-tasks.mdx b/docs/manifest/manifest-tasks.mdx index aeb333ac..e39b5527 100644 --- a/docs/manifest/manifest-tasks.mdx +++ b/docs/manifest/manifest-tasks.mdx @@ -106,7 +106,23 @@ await read('my-c2pa-file.jpg', 'image/jpeg'); - This is how to read a manifest using C++. +Use the `read_file` function to read C2PA data from the specified file. This function examines the specified asset file for C2PA data and its return value is a JSON report if it finds C2PA data. If there are validation errors, the report includes a `validation_status` field. Exceptions are thrown on errors. + +```cpp +auto json_store = C2pa::read_file("", "") +``` + +Where: + +- ``- The asset file to read; The file must be one of the [supported file formats](#supported-file-formats). +- `` - Optional path to data output directory; If provided, the function extracts any binary resources, such as thumbnails, icons, and C2PA data into that directory. These files are referenced by the identifier fields in the manifest store report. + +For example: + +```cpp +auto json_store = C2pa::read_file("work/media_file.jpg", "output/data_dir") +``` + From 305b50c8a7098c9677b772ca1a37631da7016db6 Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Thu, 14 Nov 2024 16:52:27 -0800 Subject: [PATCH 06/35] Move into separate tasks dir/sidebar branch --- docs/manifest/manifest-tasks.mdx | 197 ------------------------------- docs/tasks/build.mdx | 22 ++++ docs/tasks/get-resources.mdx | 48 ++++++++ docs/tasks/index.md | 12 ++ docs/tasks/read.mdx | 157 ++++++++++++++++++++++++ docs/tasks/sign.mdx | 21 ++++ docs/tasks/write.mdx | 21 ++++ sidebars.js | 33 +++++- 8 files changed, 310 insertions(+), 201 deletions(-) delete mode 100644 docs/manifest/manifest-tasks.mdx create mode 100644 docs/tasks/build.mdx create mode 100644 docs/tasks/get-resources.mdx create mode 100644 docs/tasks/index.md create mode 100644 docs/tasks/read.mdx create mode 100644 docs/tasks/sign.mdx create mode 100644 docs/tasks/write.mdx diff --git a/docs/manifest/manifest-tasks.mdx b/docs/manifest/manifest-tasks.mdx deleted file mode 100644 index e39b5527..00000000 --- a/docs/manifest/manifest-tasks.mdx +++ /dev/null @@ -1,197 +0,0 @@ ---- -id: tasks -title: Common tasks working with manifest data ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -## Reading a manifest - - - - - -If the input provided to [`c2pa.read`](../../js-sdk/api/c2pa.c2pa#methods) has a C2PA manifest and was processed without errors, the returned [`c2paReadResult`](../../js-sdk/api/c2pa.c2pareadresult) contains a [`manifestStore`](../../js-sdk/api/c2pa.c2pareadresult.manifeststore). - -The [`manifestStore`](../../js-sdk/api/c2pa.c2pareadresult.manifeststore) object contains a few properties: - -- **manifests**: An object containing _all_ manifests found in an asset, keyed by UUID. -- **activeManifest**: A pointer to the latest [`manifest`](../../js-sdk/api/c2pa.manifest) in the manifest store. Effectively the "parent" manifest, this is the likely starting point when inspecting an asset's C2PA data. -- **validationStatus**: A list of any validation errors the library generated when processing an asset. See [Validation](./validation) for more information. - -[`Manifest`](../../js-sdk/api/c2pa.manifest) objects contain properties pertaining to an asset's provenance, along with convenient interfaces for [accessing assertion data](../../js-sdk/api/c2pa.assertionaccessor) and [generating a thumbnail](../../js-sdk/api/c2pa.thumbnail). - -```js - const { manifestStore, source } = await c2pa.read(sampleImage); - const activeManifest = manifestStore?.activeManifest; - if (activeManifest) { - // Get thumbnail - // Note: You would normally call `dispose()` when working with a - // component-based UI library (e.g. on component un-mount) - // @ts-expect-error noUnusedLocals - const { url, dispose } = source.thumbnail.getUrl(); - - // Get properties - const properties: Record = { - title: activeManifest.title, - format: activeManifest.format, - claimGenerator: activeManifest.claimGenerator.split('(')[0]?.trim(), - producer: selectProducer(activeManifest)?.name ?? 'Unknown', - thumbnail: ``, - ingredients: (activeManifest.ingredients ?? []) - .map((i) => i.title) - .join(', '), - signatureIssuer: activeManifest.signatureInfo?.issuer, - signatureDate: activeManifest.signatureInfo?.time - ? parseISO(activeManifest.signatureInfo.time).toString() - : 'No date available', - }; -``` - - - - -Use the `Reader` object to read manifest data from a file or stream and perform validation on the manifest store. Use the `json()` method to return a JSON manifest report; If there are validation errors, the report includes a `validation_status` field. - -Use the `resource_to_file` and `resource_to_stream` methods to write resources to a file or stream, respectively. - -An asset file may contain many manifests in a manifest store. The most recent manifest is identified by the value of the `active_manifest` field in the manifests map. Retrieve binary resources such as thumbnails from the manifest data, use the `resource_to_stream` or `resource_to_file` methods using the associated `identifier` field values and a `uri`. - -```py -try: - # Create a reader from a file path - reader = c2pa.Reader.from_file("path/to/media_file.jpg") - - # Print the JSON for a manifest. - print("manifest store:", reader.json()) - - # Get the active manifest. - manifest = reader.get_active_manifest() - if manifest != None: - - # Get the URI to the manifest's thumbnail and write it to a file - uri = manifest["thumbnail"]["identifier"] - reader.resource_to_file(uri, "thumbnail_v2.jpg") - -except Exception as err: - print(err) -``` - - - - -Use the `c2pa.read()` function to read a manifest; for example: - -```ts -import { createC2pa } from 'c2pa-node'; -import { readFile } from 'node:fs/promises'; - -const c2pa = createC2pa(); - -async function read(path, mimeType) { - const buffer = await readFile(path); - const result = await c2pa.read({ buffer, mimeType }); - - if (result) { - const { active_manifest, manifests, validation_status } = result; - console.log(active_manifest); - } else { - console.log('No claim found'); - } -} - -await read('my-c2pa-file.jpg', 'image/jpeg'); -``` - - - -Use the `read_file` function to read C2PA data from the specified file. This function examines the specified asset file for C2PA data and its return value is a JSON report if it finds C2PA data. If there are validation errors, the report includes a `validation_status` field. Exceptions are thrown on errors. - -```cpp -auto json_store = C2pa::read_file("", "") -``` - -Where: - -- ``- The asset file to read; The file must be one of the [supported file formats](#supported-file-formats). -- `` - Optional path to data output directory; If provided, the function extracts any binary resources, such as thumbnails, icons, and C2PA data into that directory. These files are referenced by the identifier fields in the manifest store report. - -For example: - -```cpp -auto json_store = C2pa::read_file("work/media_file.jpg", "output/data_dir") -``` - - - - - -## Getting resources from a manifest - - - - This is how to get resources from a manifest using JavaScript. - - - - This is how to get resources from a manifest using Python. - - - - This is how to get resources from a manifest using C++. - - - - This is how to get resources from a manifest using Node.js. - - - -## Building a manifest - - - - - This is how to build a manifest using Python. - - - - This is how to build a manifest using C++. - - - - This is how to build a manifest using Node.js. - - - -## Writing a manifest - - - - This is how to write a manifest using Python. - - - - This is how to write a manifest using C++. - - - - This is how to write a manifest using Node.js. - - - -## Signing a manifest - - - - This is how to sign a manifest using Python. - - - - This is how to sign a manifest using C++. - - - - This is how to sign a manifest using Node.js. - - diff --git a/docs/tasks/build.mdx b/docs/tasks/build.mdx new file mode 100644 index 00000000..2d7a3a1a --- /dev/null +++ b/docs/tasks/build.mdx @@ -0,0 +1,22 @@ +--- +id: build +title: Building a manifest +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + + + + This is how to build a manifest using Python. + + + + This is how to build a manifest using C++. + + + + This is how to build a manifest using Node.js. + + diff --git a/docs/tasks/get-resources.mdx b/docs/tasks/get-resources.mdx new file mode 100644 index 00000000..60456bc7 --- /dev/null +++ b/docs/tasks/get-resources.mdx @@ -0,0 +1,48 @@ +--- +id: get-resources +title: Getting resources from a manifest +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +Manifest data can include binary resources such as thumbnail and icon images which are referenced by JUMBF URIs in manifest data. + + + + This is how to get resources from a manifest using JavaScript. + + + + +Retrieve binary resources such as thumbnails from the manifest data, use the `resource_to_stream` or `resource_to_file` methods using the associated `identifier` field values and a `uri`. + +NOTE: Need to add example of using `reader.resource_to_stream()`. + +```python +try: +# Create a reader from a file path +reader = c2pa.Reader.from_file("path/to/media_file.jpg") + +# Get the active manifest. +manifest = reader.get_active_manifest() +if manifest != None: + + # get the uri to the manifest's thumbnail and write it to a file + uri = manifest["thumbnail"]["identifier"] + reader.resource_to_file(uri, "thumbnail_v2.jpg") + +except Exception as err: + print(err) +``` + + + + + This is how to get resources from a manifest using C++. + + + + This is how to get resources from a manifest using Node.js. + + diff --git a/docs/tasks/index.md b/docs/tasks/index.md new file mode 100644 index 00000000..0ec0dcad --- /dev/null +++ b/docs/tasks/index.md @@ -0,0 +1,12 @@ +--- +id: common-tasks +title: Common tasks +--- + +There are a number of common tasks when working with manifests: + +- [Reading manifest data](./read.mdx) +- [Getting resources from a manifest](./get-resources.mdx) +- [Building a manifest](./build.mdx) +- [Writing a manifest](./write.mdx) +- [Signing a manifest](./sign.mdx) \ No newline at end of file diff --git a/docs/tasks/read.mdx b/docs/tasks/read.mdx new file mode 100644 index 00000000..5bfae4cd --- /dev/null +++ b/docs/tasks/read.mdx @@ -0,0 +1,157 @@ +--- +id: read +title: Reading manifest data +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + + + + +Use [`c2pa.read`](../../docs/js-sdk/api/c2pa.c2pa#methods) to read manifest data from an asset; if the asset has a C2PA manifest and was processed without errors, the returned [`c2paReadResult`](../../docs/js-sdk/api/c2pa.c2pareadresult) contains a [`manifestStore`](../../docs/js-sdk/api/c2pa.c2pareadresult.manifeststore) object with several useful properties: + +- **manifests**: An object containing all the asset's manifests ([`Manifest`](../../docs/js-sdk/api/c2pa.manifest) objects), keyed by UUID. +- **activeManifest**: A pointer to the latest [`manifest`](../../docs/js-sdk/api/c2pa.manifest) in the manifest store. Effectively the "parent" manifest, this is the likely starting point when inspecting an asset's C2PA data. +- **validationStatus**: A list of any validation errors encountered. See [Validation](../../docs/js-sdk/guides/validation) for more information. + +```js +import { createC2pa } from 'https://cdn.jsdelivr.net/npm/c2pa@0.17.2/+esm'; + +const sampleImage = ''; + +(async () => { + // Initialize the c2pa-js SDK + const c2pa = await createC2pa({ + wasmSrc: + 'https://cdn.jsdelivr.net/npm/c2pa@0.17.2/dist/assets/wasm/toolkit_bg.wasm', + workerSrc: + 'https://cdn.jsdelivr.net/npm/c2pa@0.17.2/dist/c2pa.worker.min.js', + }); + + try { + // Read in image and get a manifest store + const { manifestStore } = await c2pa.read(sampleImage); + console.log('manifestStore', manifestStore); + + // Get the active manifest + const activeManifest = manifestStore?.activeManifest; + console.log('activeManifest', activeManifest); + } catch (err) { + console.error('Error reading image:', err); + } +})(); +``` + + + + + +Use the `Reader` object to read manifest data from a file or stream and perform validation on the manifest store. Use the `json()` method to return a JSON manifest report; If there are validation errors, the report includes a `validation_status` field. + +An asset file may contain many manifests in a manifest store. The most recent manifest is identified by the value of the `active_manifest` field in the manifests map. + +```py +try: + # Create a reader from a file path + reader = c2pa.Reader.from_file("path/to/media_file.jpg") + + # Print the JSON for a manifest. + print("manifest store:", reader.json()) + + # Get the active manifest. + manifest = reader.get_active_manifest() + if manifest != None: + + # Get the URI to the manifest's thumbnail and write it to a file + uri = manifest["thumbnail"]["identifier"] + reader.resource_to_file(uri, "thumbnail_v2.jpg") + +except Exception as err: + print(err) +``` + + + + + +Use the `c2pa.read()` function to read a manifest; for example: + +```ts +import { createC2pa } from 'c2pa-node'; +import { readFile } from 'node:fs/promises'; + +const c2pa = createC2pa(); + +async function read(path, mimeType) { + const buffer = await readFile(path); + const result = await c2pa.read({ buffer, mimeType }); + + if (result) { + const { active_manifest, manifests, validation_status } = result; + console.log(active_manifest); + } else { + console.log('No claim found'); + } +} + +await read('my-c2pa-file.jpg', 'image/jpeg'); +``` + + + + + +Use the `read_file` function to read C2PA data from the specified file. This function examines the specified asset file for C2PA data and its return value is a JSON report if it finds C2PA data. If there are validation errors, the report includes a `validation_status` field. Exceptions are thrown on errors. + +```cpp +auto json_store = C2pa::read_file("", "") +``` + +Where: + +- ``- The asset file to read. +- `` - Optional path to data output directory; If provided, the function extracts any binary resources, such as thumbnails, icons, and C2PA data into that directory. These files are referenced by the identifier fields in the manifest store report. + +For example: + +```cpp +auto json_store = C2pa::read_file("work/media_file.jpg", "output/data_dir") +``` + + + + + +Use the [`Reader`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html) struct to read manifest data from a file or stream. + +### Reading from a file + +Use [`from_file`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html#method.from_file) to read manifest data from a file: + +```rs +use c2pa::Reader; +let reader = Reader::from_file("path/to/file.jpg").unwrap(); +``` + +There is also an asynchronous version of this method, [`from_stream_async`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html#method.from_file_async). + +### Reading from a stream + +Use [`from_stream`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html#method.from_stream) to read manifest data from a stream: + +```rs +use std::io::Cursor; + +use c2pa::Reader; +let mut stream = Cursor::new(include_bytes!("../tests/fixtures/CA.jpg")); +let reader = Reader::from_stream("image/jpeg", stream).unwrap(); +println!("{}", reader.json()); +``` + +There is also an asynchronous version of this method, [`from_stream_async`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html#method.from_stream_async). + + + + diff --git a/docs/tasks/sign.mdx b/docs/tasks/sign.mdx new file mode 100644 index 00000000..9ac76f4c --- /dev/null +++ b/docs/tasks/sign.mdx @@ -0,0 +1,21 @@ +--- +id: sign +title: Signing manifest data +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + + + This is how to sign a manifest using Python. + + + + This is how to sign a manifest using C++. + + + + This is how to sign a manifest using Node.js. + + diff --git a/docs/tasks/write.mdx b/docs/tasks/write.mdx new file mode 100644 index 00000000..38aaa3a4 --- /dev/null +++ b/docs/tasks/write.mdx @@ -0,0 +1,21 @@ +--- +id: write +title: Writing a manifest +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + + + This is how to write a manifest using Python. + + + + This is how to write a manifest using C++. + + + + This is how to write a manifest using Node.js. + + diff --git a/sidebars.js b/sidebars.js index cfab4f09..a4962e56 100644 --- a/sidebars.js +++ b/sidebars.js @@ -44,10 +44,6 @@ const sidebars = { type: 'doc', id: 'manifest/signing-manifests', }, - { - type: 'doc', - id: 'manifest/tasks', - }, { type: 'doc', id: 'manifest/manifest-examples', @@ -65,6 +61,35 @@ const sidebars = { ], }, + { + type: 'category', + label: 'Common tasks', + link: { type: 'doc', id: 'tasks/common-tasks' }, + collapsed: true, + items: [ + { + type: 'doc', + id: 'tasks/read', + }, + { + type: 'doc', + id: 'tasks/get-resources', + }, + { + type: 'doc', + id: 'tasks/build', + }, + { + type: 'doc', + id: 'tasks/write', + }, + { + type: 'doc', + id: 'tasks/sign', + }, + ], + }, + { type: 'category', label: 'C2PA Tool', From 99fcfc90bf19787ee8084c80692514798f755e88 Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Wed, 20 Nov 2024 16:04:04 -0800 Subject: [PATCH 07/35] Add rust --- docs/tasks/build.mdx | 10 ++++++++-- docs/tasks/get-resources.mdx | 13 ++++++++++--- docs/tasks/read.mdx | 15 ++++++--------- docs/tasks/sign.mdx | 10 ++++++++-- docs/tasks/write.mdx | 9 +++++++-- 5 files changed, 39 insertions(+), 18 deletions(-) diff --git a/docs/tasks/build.mdx b/docs/tasks/build.mdx index 2d7a3a1a..7c478036 100644 --- a/docs/tasks/build.mdx +++ b/docs/tasks/build.mdx @@ -16,7 +16,13 @@ import TabItem from '@theme/TabItem'; This is how to build a manifest using C++. - - This is how to build a manifest using Node.js. +{' '} + + This is how to build a manifest using Node.js. + + + + This is how to build a manifest using Rust. + diff --git a/docs/tasks/get-resources.mdx b/docs/tasks/get-resources.mdx index 60456bc7..7a3ffd43 100644 --- a/docs/tasks/get-resources.mdx +++ b/docs/tasks/get-resources.mdx @@ -42,7 +42,14 @@ except Exception as err: This is how to get resources from a manifest using C++. - - This is how to get resources from a manifest using Node.js. - +{' '} + + This is how to get resources from a manifest using Node.js. + + +{' '} + + This is how to get resources using Rust. + + diff --git a/docs/tasks/read.mdx b/docs/tasks/read.mdx index 5bfae4cd..163c3f4b 100644 --- a/docs/tasks/read.mdx +++ b/docs/tasks/read.mdx @@ -1,6 +1,7 @@ --- id: read title: Reading manifest data +hide_table_of_contents: true --- import Tabs from '@theme/Tabs'; @@ -57,17 +58,13 @@ try: # Create a reader from a file path reader = c2pa.Reader.from_file("path/to/media_file.jpg") + # Alternatively, create a reader from a stream + stream = open("path/to/media_file.jpg", "rb") + reader = c2pa.Reader("image/jpeg", stream) + # Print the JSON for a manifest. print("manifest store:", reader.json()) - # Get the active manifest. - manifest = reader.get_active_manifest() - if manifest != None: - - # Get the URI to the manifest's thumbnail and write it to a file - uri = manifest["thumbnail"]["identifier"] - reader.resource_to_file(uri, "thumbnail_v2.jpg") - except Exception as err: print(err) ``` @@ -103,7 +100,7 @@ await read('my-c2pa-file.jpg', 'image/jpeg'); -Use the `read_file` function to read C2PA data from the specified file. This function examines the specified asset file for C2PA data and its return value is a JSON report if it finds C2PA data. If there are validation errors, the report includes a `validation_status` field. Exceptions are thrown on errors. +Use the `read_file` function to read C2PA data from the specified file. This function examines the specified asset file for C2PA data and returns a JSON report if it finds any; it throws exceptions on errors. If there are validation errors, the report includes a `validation_status` field. ```cpp auto json_store = C2pa::read_file("", "") diff --git a/docs/tasks/sign.mdx b/docs/tasks/sign.mdx index 9ac76f4c..bf57db89 100644 --- a/docs/tasks/sign.mdx +++ b/docs/tasks/sign.mdx @@ -15,7 +15,13 @@ import TabItem from '@theme/TabItem'; This is how to sign a manifest using C++. - - This is how to sign a manifest using Node.js. +{' '} + + This is how to sign a manifest using Node.js. + + + + This is how to sign a manifest using Rust. + diff --git a/docs/tasks/write.mdx b/docs/tasks/write.mdx index 38aaa3a4..3ea55402 100644 --- a/docs/tasks/write.mdx +++ b/docs/tasks/write.mdx @@ -15,7 +15,12 @@ import TabItem from '@theme/TabItem'; This is how to write a manifest using C++. - - This is how to write a manifest using Node.js. +{' '} + + This is how to write a manifest using Node.js. + + + + This is how to write a manifest using Rust. From 4b9e7d52abcb7cacd72127a11c5dc58a31f77b8a Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Wed, 4 Dec 2024 13:05:49 -0800 Subject: [PATCH 08/35] Add Rust everywhere, add some examples, update .gitignore --- .gitignore | 15 +++- docs/tasks/build.mdx | 131 ++++++++++++++++++++++++++++++++--- docs/tasks/get-resources.mdx | 74 ++++++++++++++++++-- docs/tasks/sign.mdx | 9 +-- docs/tasks/write.mdx | 9 +-- sidebars.js | 4 +- 6 files changed, 212 insertions(+), 30 deletions(-) diff --git a/.gitignore b/.gitignore index da86b312..d07e58e0 100644 --- a/.gitignore +++ b/.gitignore @@ -8,8 +8,17 @@ .docusaurus .cache-loader /docs/js-sdk/api -/docs/c2patool -/docs/rust-sdk +/docs/c2patool/*.md +/docs/c2patool/docs/*.md +/docs/c2pa-node/*.md +/docs/c2pa-node-example/*.md +/docs/c2pa-node/docs/*.md +/docs/c2pa-python/*.md +/docs/c2pa-python/docs/*.md +/docs/c2pa-c/*.md +/docs/c2pa-c/docs/*.md +/docs/rust-sdk/*.md +/docs/rust-sdk/docs/*.md /docs/**/readme.md # Misc @@ -23,4 +32,4 @@ npm-debug.log* yarn-debug.log* yarn-error.log* -.dccache +.dccache \ No newline at end of file diff --git a/docs/tasks/build.mdx b/docs/tasks/build.mdx index 7c478036..166fadf2 100644 --- a/docs/tasks/build.mdx +++ b/docs/tasks/build.mdx @@ -1,6 +1,7 @@ --- id: build title: Building a manifest +hide_table_of_contents: true --- import Tabs from '@theme/Tabs'; @@ -8,18 +9,128 @@ import TabItem from '@theme/TabItem'; - - This is how to build a manifest using Python. - + - - This is how to build a manifest using C++. - +```python +try: + # Define a function to sign the claim bytes + # In this case we are using a pre-defined sign_ps256 method, passing in our private cert + # Normally this cert would be kept safe in some other location + def private_sign(data: bytes) -> bytes: + return sign_ps256(data, "tests/fixtures/ps256.pem") -{' '} - - This is how to build a manifest using Node.js. - + # read our public certs into memory + certs = open(data_dir + "ps256.pub", "rb").read() + + # Create a signer from the private signer, certs and a time stamp service url + signer = create_signer(private_sign, SigningAlg.PS256, certs, "http://timestamp.digicert.com") + + # Create a builder add a thumbnail resource and an ingredient file. + builder = Builder(manifest_json) + + # Add the resource from a stream + a_thumbnail_jpg_stream = open("tests/fixtures/A_thumbnail.jpg", "rb") + builder.add_resource("image/jpeg", a_thumbnail_jpg_stream) + + # Define an ingredient, in this case a parent ingredient named A.jpg, with a thumbnail + ingredient_json = { + "title": "A.jpg", + "relationship": "parentOf", # "parentOf", "componentOf" or "inputTo" + "thumbnail": { + "identifier": "thumbnail", + "format": "image/jpeg" + } + } + + # Add the ingredient from a stream + a_jpg_stream = open("tests/fixtures/A.jpg", "rb") + builder.add_ingredient("image/jpeg", a_jpg_stream) + + # At this point we could archive or unarchive our Builder to continue later. + # In this example we use a bytearray for the archive stream. + # all ingredients and resources will be saved in the archive + archive = io.BytesIO(bytearray()) + builder.to_archive(archive) + archive.seek() + builder = builder.from_archive(archive) + + # Sign the builder with a stream and output it to a stream + # This returns the binary manifest data that could be uploaded to cloud storage. + input_stream = open("tests/fixtures/A.jpg", "rb") + output_stream = open("target/out.jpg", "wb") + c2pa_data = builder.sign(signer, "image/jpeg", input_stream, output_stream) + +except Exception as err: + print(err) +``` + + + + + +```ts +import { ManifestBuilder } from 'c2pa-node'; + +const manifest = new ManifestBuilder({ + claim_generator: 'my-app/1.0.0', + format: 'image/jpeg', + title: 'node_test_local_signer.jpg', + assertions: [ + { + label: 'c2pa.actions', + data: { + actions: [ + { + action: 'c2pa.created', + }, + ], + }, + }, + { + label: 'com.custom.my-assertion', + data: { + description: 'My custom test assertion', + version: '1.0.0', + }, + }, + ], +}); +``` + + + + + +```cpp +const std::string manifest_json = R"{ + "claim_generator": "c2pa_c_test/0.1", + "claim_generator_info": [ + { + "name": "c2pa-c test", + "version": "0.1" + } + ], + "assertions": [ + { + "label": "c2pa.training-mining", + "data": { + "entries": { + "c2pa.ai_generative_training": { "use": "notAllowed" }, + "c2pa.ai_inference": { "use": "notAllowed" }, + "c2pa.ai_training": { "use": "notAllowed" }, + "c2pa.data_mining": { "use": "notAllowed" } + } + } + } + ] + }; + +auto builder = Builder(manifest_json); + + +``` + + This is how to build a manifest using Rust. diff --git a/docs/tasks/get-resources.mdx b/docs/tasks/get-resources.mdx index 7a3ffd43..6570d788 100644 --- a/docs/tasks/get-resources.mdx +++ b/docs/tasks/get-resources.mdx @@ -10,7 +10,69 @@ Manifest data can include binary resources such as thumbnail and icon images whi - This is how to get resources from a manifest using JavaScript. + +```JavaScript +import { createC2pa, selectProducer } from 'c2pa'; +import wasmSrc from 'c2pa/dist/assets/wasm/toolkit_bg.wasm?url'; +import workerSrc from 'c2pa/dist/c2pa.worker.js?url'; +import { parseISO } from 'date-fns'; + +const sampleImage = + 'https://raw.githubusercontent.com/contentauth/c2pa-js/main/tools/testing/fixtures/images/CAICAI.jpg'; + +(async () => { + let output: string[] = []; + + const c2pa = await createC2pa({ + wasmSrc, + workerSrc, + }); + + const { manifestStore, source } = await c2pa.read(sampleImage); + const activeManifest = manifestStore?.activeManifest; + if (activeManifest) { + // Get thumbnail + // Note: You would normally call `dispose()` when working with a + // component-based UI library (e.g. on component un-mount) + // @ts-expect-error noUnusedLocals + const { url, dispose } = source.thumbnail.getUrl(); + + // Get properties + const properties: Record = { + title: activeManifest.title, + format: activeManifest.format, + claimGenerator: activeManifest.claimGenerator.split('(')[0]?.trim(), + producer: selectProducer(activeManifest)?.name ?? 'Unknown', + thumbnail: ``, + ingredients: (activeManifest.ingredients ?? []) + .map((i) => i.title) + .join(', '), + signatureIssuer: activeManifest.signatureInfo?.issuer, + signatureDate: activeManifest.signatureInfo?.time + ? parseISO(activeManifest.signatureInfo.time).toString() + : 'No date available', + }; + + output = Object.keys(properties).map((key) => { + return ` + + ${key} + ${properties[key]} + + `; + }); + } else { + output.push(` + + No provenance data found + + `); + } + + document.querySelector('#results tbody')!.innerHTML = output.join(''); +})(); +``` + @@ -38,16 +100,14 @@ except Exception as err: - - This is how to get resources from a manifest using C++. - - -{' '} This is how to get resources from a manifest using Node.js. -{' '} + + This is how to get resources from a manifest using C++. + + This is how to get resources using Rust. diff --git a/docs/tasks/sign.mdx b/docs/tasks/sign.mdx index bf57db89..e34ec4ea 100644 --- a/docs/tasks/sign.mdx +++ b/docs/tasks/sign.mdx @@ -11,15 +11,16 @@ import TabItem from '@theme/TabItem'; This is how to sign a manifest using Python. - - This is how to sign a manifest using C++. - - {' '} + This is how to sign a manifest using Node.js. + + This is how to sign a manifest using C++. + + This is how to sign a manifest using Rust. diff --git a/docs/tasks/write.mdx b/docs/tasks/write.mdx index 3ea55402..e776b2cd 100644 --- a/docs/tasks/write.mdx +++ b/docs/tasks/write.mdx @@ -11,15 +11,16 @@ import TabItem from '@theme/TabItem'; This is how to write a manifest using Python. - - This is how to write a manifest using C++. - - {' '} This is how to write a manifest using Node.js. +{' '} + + This is how to write a manifest using C++. + + This is how to write a manifest using Rust. diff --git a/sidebars.js b/sidebars.js index a4962e56..832c166e 100644 --- a/sidebars.js +++ b/sidebars.js @@ -98,12 +98,12 @@ const sidebars = { items: [ { type: 'doc', - id: 'c2patool/manifest', + id: 'c2patool/docs/manifest', label: 'Using a manifest file', }, { type: 'doc', - id: 'c2patool/x_509', + id: 'c2patool/docs/x_509', label: 'Creating and using a certificate', }, { From 131ac2b9b33951b01e0ee97e49a4bdd35dcbace7 Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Wed, 4 Dec 2024 16:03:24 -0800 Subject: [PATCH 09/35] Change page titles --- docs/manifest/understanding.md | 2 +- docs/tasks/index.md | 7 ++++--- docs/tasks/read.mdx | 9 +++++---- docs/tasks/write.mdx | 2 -- sidebars.js | 6 +++--- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/manifest/understanding.md b/docs/manifest/understanding.md index 4202b445..594b6476 100644 --- a/docs/manifest/understanding.md +++ b/docs/manifest/understanding.md @@ -1,6 +1,6 @@ --- id: understanding-manifest -title: Working with manifests +title: Understanding manifests --- ## Overview diff --git a/docs/tasks/index.md b/docs/tasks/index.md index 0ec0dcad..dbc57f21 100644 --- a/docs/tasks/index.md +++ b/docs/tasks/index.md @@ -1,9 +1,10 @@ --- -id: common-tasks -title: Common tasks +id: working-manifests +title: Working with manifests --- -There are a number of common tasks when working with manifests: +There are a number of common tasks when working with manifests. +Although the tasks and APIs are similar at a high level, the way you accomplish each task is specific to the language being used. - [Reading manifest data](./read.mdx) - [Getting resources from a manifest](./get-resources.mdx) diff --git a/docs/tasks/read.mdx b/docs/tasks/read.mdx index 163c3f4b..d75ef2eb 100644 --- a/docs/tasks/read.mdx +++ b/docs/tasks/read.mdx @@ -18,17 +18,18 @@ Use [`c2pa.read`](../../docs/js-sdk/api/c2pa.c2pa#methods) to read manifest data - **validationStatus**: A list of any validation errors encountered. See [Validation](../../docs/js-sdk/guides/validation) for more information. ```js -import { createC2pa } from 'https://cdn.jsdelivr.net/npm/c2pa@0.17.2/+esm'; - +const version = '0.17.2'; const sampleImage = ''; +import { createC2pa } from 'https://cdn.jsdelivr.net/npm/c2pa@${version}/+esm'; + (async () => { // Initialize the c2pa-js SDK const c2pa = await createC2pa({ wasmSrc: - 'https://cdn.jsdelivr.net/npm/c2pa@0.17.2/dist/assets/wasm/toolkit_bg.wasm', + 'https://cdn.jsdelivr.net/npm/c2pa@${version}/dist/assets/wasm/toolkit_bg.wasm', workerSrc: - 'https://cdn.jsdelivr.net/npm/c2pa@0.17.2/dist/c2pa.worker.min.js', + 'https://cdn.jsdelivr.net/npm/c2pa@${version}/dist/c2pa.worker.min.js', }); try { diff --git a/docs/tasks/write.mdx b/docs/tasks/write.mdx index e776b2cd..571fb175 100644 --- a/docs/tasks/write.mdx +++ b/docs/tasks/write.mdx @@ -11,12 +11,10 @@ import TabItem from '@theme/TabItem'; This is how to write a manifest using Python. -{' '} This is how to write a manifest using Node.js. -{' '} This is how to write a manifest using C++. diff --git a/sidebars.js b/sidebars.js index 00fbd544..021768ef 100644 --- a/sidebars.js +++ b/sidebars.js @@ -24,7 +24,7 @@ const sidebars = { }, { type: 'category', - label: 'Working with manifests', + label: 'Understanding manifests', link: { type: 'doc', id: 'manifest/understanding-manifest' }, collapsed: true, items: [ @@ -63,8 +63,8 @@ const sidebars = { { type: 'category', - label: 'Common tasks', - link: { type: 'doc', id: 'tasks/common-tasks' }, + label: 'Working with manifests', + link: { type: 'doc', id: 'tasks/working-manifests' }, collapsed: true, items: [ { From 27bd8a7fb9709bfe6cc8dc533a00acd74205ee02 Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Thu, 19 Dec 2024 15:04:44 -0800 Subject: [PATCH 10/35] reword --- docs/tasks/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tasks/index.md b/docs/tasks/index.md index dbc57f21..bcf676ee 100644 --- a/docs/tasks/index.md +++ b/docs/tasks/index.md @@ -4,7 +4,7 @@ title: Working with manifests --- There are a number of common tasks when working with manifests. -Although the tasks and APIs are similar at a high level, the way you accomplish each task is specific to the language being used. +The way you accomplish each task is specific to the language you're using, although at a high level the process is similar. - [Reading manifest data](./read.mdx) - [Getting resources from a manifest](./get-resources.mdx) From fbc60518f6efc54d608882d8541af909a2943434 Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Fri, 20 Dec 2024 11:35:38 -0800 Subject: [PATCH 11/35] Add setup --- docs/tasks/index.md | 3 +- docs/tasks/setup.mdx | 93 ++++++++++++++++++++++++++++++++++++++++++++ docs/tasks/sign.mdx | 2 - sidebars.js | 4 ++ 4 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 docs/tasks/setup.mdx diff --git a/docs/tasks/index.md b/docs/tasks/index.md index bcf676ee..2c830f2c 100644 --- a/docs/tasks/index.md +++ b/docs/tasks/index.md @@ -6,8 +6,9 @@ title: Working with manifests There are a number of common tasks when working with manifests. The way you accomplish each task is specific to the language you're using, although at a high level the process is similar. +- [Preliminary setup](./setup.mdx) - [Reading manifest data](./read.mdx) - [Getting resources from a manifest](./get-resources.mdx) - [Building a manifest](./build.mdx) - [Writing a manifest](./write.mdx) -- [Signing a manifest](./sign.mdx) \ No newline at end of file +- [Signing a manifest](./sign.mdx) diff --git a/docs/tasks/setup.mdx b/docs/tasks/setup.mdx new file mode 100644 index 00000000..ab2705c8 --- /dev/null +++ b/docs/tasks/setup.mdx @@ -0,0 +1,93 @@ +--- +id: setup +title: Setup +hide_table_of_contents: true +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + + +{' '} + + This is how to setup the JavaScript library. + + + + This is how to setup the Python library. + +```python +# Import the C2PA Python package +from c2pa import * + +# Import standard general-purpose packages +import os +import io +import logging +import json +import base64 + +# Import web packages used in example implementation +from flask import Flask, request, abort +from flask_cors import CORS +from waitress import serve + +# Import AWS SDK package (to use KMS) +import boto3 +``` + + + + + This is how to setup the Node.js library. + +```js +import { createC2pa } from 'c2pa-node'; +import { readFile } from 'node:fs/promises'; + +const c2pa = createC2pa(); +``` + + + + + +```cpp +#include +#include +#include +#include +#include +#include +#include +#include +#include "c2pa.hpp" +#include "test_signer.hpp" +#include + +// this example uses nlohmann json for parsing the manifest +using json = nlohmann::json; +using namespace std; +namespace fs = std::filesystem; +using namespace c2pa; +``` + + + + + This is how to setup the Rust library. + +```rust +use std::{ + io::{Cursor, Write}, + process::{Command, Stdio}, +}; + +use anyhow::Result; +use c2pa::{settings::load_settings_from_str, Builder, CallbackSigner, SigningAlg}; +``` + + + + diff --git a/docs/tasks/sign.mdx b/docs/tasks/sign.mdx index e34ec4ea..1ec2cb57 100644 --- a/docs/tasks/sign.mdx +++ b/docs/tasks/sign.mdx @@ -11,8 +11,6 @@ import TabItem from '@theme/TabItem'; This is how to sign a manifest using Python. -{' '} - This is how to sign a manifest using Node.js. diff --git a/sidebars.js b/sidebars.js index 021768ef..8d0f593a 100644 --- a/sidebars.js +++ b/sidebars.js @@ -67,6 +67,10 @@ const sidebars = { link: { type: 'doc', id: 'tasks/working-manifests' }, collapsed: true, items: [ + { + type: 'doc', + id: 'tasks/setup', + }, { type: 'doc', id: 'tasks/read', From 9aa2deb03e7ad26e55ee5b8608d60cc48b8643db Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Fri, 10 Jan 2025 11:43:53 -0800 Subject: [PATCH 12/35] Add more example code --- docs/tasks/read.mdx | 14 --- docs/tasks/setup.mdx | 28 ++++- docs/tasks/sign.mdx | 252 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 277 insertions(+), 17 deletions(-) diff --git a/docs/tasks/read.mdx b/docs/tasks/read.mdx index d75ef2eb..e92eeb40 100644 --- a/docs/tasks/read.mdx +++ b/docs/tasks/read.mdx @@ -18,20 +18,6 @@ Use [`c2pa.read`](../../docs/js-sdk/api/c2pa.c2pa#methods) to read manifest data - **validationStatus**: A list of any validation errors encountered. See [Validation](../../docs/js-sdk/guides/validation) for more information. ```js -const version = '0.17.2'; -const sampleImage = ''; - -import { createC2pa } from 'https://cdn.jsdelivr.net/npm/c2pa@${version}/+esm'; - -(async () => { - // Initialize the c2pa-js SDK - const c2pa = await createC2pa({ - wasmSrc: - 'https://cdn.jsdelivr.net/npm/c2pa@${version}/dist/assets/wasm/toolkit_bg.wasm', - workerSrc: - 'https://cdn.jsdelivr.net/npm/c2pa@${version}/dist/c2pa.worker.min.js', - }); - try { // Read in image and get a manifest store const { manifestStore } = await c2pa.read(sampleImage); diff --git a/docs/tasks/setup.mdx b/docs/tasks/setup.mdx index ab2705c8..f37f3651 100644 --- a/docs/tasks/setup.mdx +++ b/docs/tasks/setup.mdx @@ -9,9 +9,24 @@ import TabItem from '@theme/TabItem'; -{' '} - This is how to setup the JavaScript library. + +```js +const version = '0.24.2'; +const sampleImage = ''; + +import { createC2pa } from 'https://cdn.jsdelivr.net/npm/c2pa@${version}/+esm'; + +(async () => { + // Initialize the c2pa-js SDK + const c2pa = await createC2pa({ + wasmSrc: + 'https://cdn.jsdelivr.net/npm/c2pa@${version}/dist/assets/wasm/toolkit_bg.wasm', + workerSrc: + 'https://cdn.jsdelivr.net/npm/c2pa@${version}/dist/c2pa.worker.min.js', + }); +``` + @@ -85,7 +100,14 @@ use std::{ }; use anyhow::Result; -use c2pa::{settings::load_settings_from_str, Builder, CallbackSigner, SigningAlg}; +use c2pa::{settings::load_settings_from_str, Builder, CallbackSigner, Reader}; + +use c2pa_crypto::raw_signature::SigningAlg; +use serde_json::json; + +const TEST_IMAGE: &[u8] = include_bytes!("../tests/fixtures/CA.jpg"); +const CERTS: &[u8] = include_bytes!("../tests/fixtures/certs/ed25519.pub"); +const PRIVATE_KEY: &[u8] = include_bytes!("../tests/fixtures/certs/ed25519.pem"); ``` diff --git a/docs/tasks/sign.mdx b/docs/tasks/sign.mdx index 1ec2cb57..925f9b76 100644 --- a/docs/tasks/sign.mdx +++ b/docs/tasks/sign.mdx @@ -9,10 +9,262 @@ import TabItem from '@theme/TabItem'; This is how to sign a manifest using Python. + +```python +request_data = ... # This is the asset being signed +content_type = ... # MIME type of the asset + +manifest = json.dumps({ + "title": "image.jpg", + "format": "image/jpeg", + "claim_generator_info": [ + { + "name": "Documentation example", + "version": "0.0.1" + } + ], + "assertions": [ + { + "label": "c2pa.actions", + "data": { + "actions": [ + { + "action": "c2pa.edited", + "softwareAgent": { + "name": "C2PA Python Example", + "version": "0.1.0" + } + } + ] + } + } + ] +}) + +try: + builder = Builder(manifest) + + signer = create_signer(kms_sign, signing_alg, + cert_chain, timestamp_url) + + result = io.BytesIO(b"") + builder.sign(signer, content_type, io.BytesIO(request_data), result) + + return result.getvalue() +except Exception as e: + logging.error(e) + abort(500, description=e) +``` + This is how to sign a manifest using Node.js. + +Use the `c2pa.sign()` method to sign an ingredient, either locally if you have a signing certificate and key available, or by using a remote signing API. + +## Signing buffers + +If you have an asset file's data loaded into memory, you can sign the the asset using the loaded buffer. + +**NOTE**: Signing using a buffer is currently supported only for `image/jpeg` and `image/png` data. For all other file types, use the [file-based approach](#signing-files) . + +```ts +import { readFile } from 'node:fs/promises'; +import { createC2pa, createTestSigner } from 'c2pa-node'; + +// read an asset into a buffer +const buffer = await readFile('to-be-signed.jpg'); +const asset: Asset = { buffer, mimeType: 'image/jpeg' }; + +// build a manifest to use for signing +const manifest = new ManifestBuilder( + { + claim_generator: 'my-app/1.0.0', + format: 'image/jpeg', + title: 'buffer_signer.jpg', + assertions: [ + { + label: 'c2pa.actions', + data: { + actions: [ + { + action: 'c2pa.created', + }, + ], + }, + }, + { + label: 'com.custom.my-assertion', + data: { + description: 'My custom test assertion', + version: '1.0.0', + }, + }, + ], + }, + { vendor: 'cai' }, +); + +// create a signing function +async function sign(asset, manifest) { + const signer = await createTestSigner(); + const c2pa = createC2pa({ + signer, + }); + + const { signedAsset, signedManifest } = await c2pa.sign({ + asset, + manifest, + }); +} + +// sign +await sign(asset, manifest); +``` + +## Signing files + +To avoid loading the entire asset into memory (or for file types other than JPEG and PNG that don't support in-memory signing), pass in the file path to the asset file to sign it; for example: + +```ts +import { resolve } from 'node:path'; +import { createC2pa, createTestSigner } from 'c2pa-node'; + +// get the asset full path +const asset = { + path: resolve('to-be-signed.jpg'), +}; +// define a location where to place the signed asset +const outputPath = resolve('signed.jpg'); + +// create a signing function +async function sign(asset, manifest) { + const signer = await createTestSigner(); + const c2pa = createC2pa({ + signer, + }); + + const { signedAsset, signedManifest } = await c2pa.sign({ + manifest, + asset, + options: { + outputPath, + }, + }); +} + +// build a manifest to use for signing +const manifest = new ManifestBuilder( + { + claim_generator: 'my-app/1.0.0', + format: 'image/jpeg', + title: 'buffer_signer.jpg', + assertions: [ + { + label: 'c2pa.actions', + data: { + actions: [ + { + action: 'c2pa.created', + }, + ], + }, + }, + { + label: 'com.custom.my-assertion', + data: { + description: 'My custom test assertion', + version: '1.0.0', + }, + }, + ], + }, + { vendor: 'cai' }, +); + +// sign +await sign(asset, manifest); +``` + +## Remote signing + +If you have access to a web service that performs signing, you can use it to sign remotely; for example: + +```ts +import { readFile } from 'node:fs/promises'; +import { fetch, Headers } from 'node-fetch'; +import { createC2pa, SigningAlgorithm } from 'c2pa-node'; + +function createRemoteSigner() { + return { + type: 'remote', + async reserveSize() { + const url = `https://my.signing.service/box-size`; + const res = await fetch(url); + const data = (await res.json()) as { boxSize: number }; + return data.boxSize; + }, + async sign({ reserveSize, toBeSigned }) { + const url = `https://my.signing.service/sign?boxSize=${reserveSize}`; + const res = await fetch(url, { + method: 'POST', + headers: new Headers({ + 'Content-Type': 'application/octet-stream', + }), + body: toBeSigned, + }); + return res.buffer(); + }, + }; +} + +async function sign(asset, manifest) { + const signer = createRemoteSigner(); + const c2pa = createC2pa({ + signer, + }); + + const { signedAsset, signedManifest } = await c2pa.sign({ + asset, + manifest, + }); +} + +const buffer = await readFile('to-be-signed.jpg'); +const asset: Asset = { buffer, mimeType: 'image/jpeg' }; + +const manifest = new ManifestBuilder( + { + claim_generator: 'my-app/1.0.0', + format: 'image/jpeg', + title: 'buffer_signer.jpg', + assertions: [ + { + label: 'c2pa.actions', + data: { + actions: [ + { + action: 'c2pa.created', + }, + ], + }, + }, + { + label: 'com.custom.my-assertion', + data: { + description: 'My custom test assertion', + version: '1.0.0', + }, + }, + ], + }, + { vendor: 'cai' }, +); + +await sign(asset, manifest); +``` + From 0ec851f39cd82f56ad3d9e6041c106c7356dcf45 Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Tue, 14 Jan 2025 15:57:13 -0800 Subject: [PATCH 13/35] Consolidate tasks, add cpp examples --- .../js-sdk/examples/quickstart/rollup-main.ts | 26 ++ docs/js-sdk/examples/quickstart/vite-main.ts | 27 ++ .../examples/quickstart/webpack-main.ts | 30 ++ docs/js-sdk/examples/view-manifest/index.html | 22 ++ docs/js-sdk/examples/view-manifest/main.ts | 59 ++++ docs/tasks/build.mdx | 201 ++++++++++++- docs/tasks/get-resources.mdx | 71 ++++- docs/tasks/index.md | 4 +- docs/tasks/sign.mdx | 278 ------------------ docs/tasks/write.mdx | 25 -- sidebars.js | 8 - 11 files changed, 433 insertions(+), 318 deletions(-) create mode 100644 docs/js-sdk/examples/quickstart/rollup-main.ts create mode 100644 docs/js-sdk/examples/quickstart/vite-main.ts create mode 100644 docs/js-sdk/examples/quickstart/webpack-main.ts create mode 100644 docs/js-sdk/examples/view-manifest/index.html create mode 100644 docs/js-sdk/examples/view-manifest/main.ts delete mode 100644 docs/tasks/sign.mdx delete mode 100644 docs/tasks/write.mdx diff --git a/docs/js-sdk/examples/quickstart/rollup-main.ts b/docs/js-sdk/examples/quickstart/rollup-main.ts new file mode 100644 index 00000000..f92207aa --- /dev/null +++ b/docs/js-sdk/examples/quickstart/rollup-main.ts @@ -0,0 +1,26 @@ +import { createC2pa } from 'c2pa'; +import wasmModule from 'c2pa/dist/assets/wasm/toolkit_bg.wasm'; + +const sampleImage = + 'https://raw.githubusercontent.com/contentauth/c2pa-js/main/tools/testing/fixtures/images/CAICAI.jpg'; + +(async () => { + // Initialize the c2pa-js SDK + const wasmSrc = await wasmModule(); + const c2pa = await createC2pa({ + wasmSrc, + workerSrc: 'c2pa.worker.min.js', + }); + + try { + // Read in our sample image and get a manifest store + const { manifestStore } = await c2pa.read(sampleImage); + console.log('manifestStore', manifestStore); + + // Get the active manifest + const activeManifest = manifestStore?.activeManifest; + console.log('activeManifest', activeManifest); + } catch (err) { + console.error('Error reading image:', err); + } +})(); diff --git a/docs/js-sdk/examples/quickstart/vite-main.ts b/docs/js-sdk/examples/quickstart/vite-main.ts new file mode 100644 index 00000000..e86371ea --- /dev/null +++ b/docs/js-sdk/examples/quickstart/vite-main.ts @@ -0,0 +1,27 @@ +const sampleImage = + 'https://raw.githubusercontent.com/contentauth/c2pa-js/main/tools/testing/fixtures/images/CAICAI.jpg'; + +(async () => { + // Information about where to fetch the library + const version = '0.17.2'; + const libraryUrl = `https://cdn.jsdelivr.net/npm/c2pa@${version}/+esm`; + + // Initialize the c2pa-js SDK + const { createC2pa } = await import(libraryUrl); + const c2pa = await createC2pa({ + wasmSrc: `https://cdn.jsdelivr.net/npm/c2pa@${version}/dist/assets/wasm/toolkit_bg.wasm`, + workerSrc: `https://cdn.jsdelivr.net/npm/c2pa@${version}/dist/c2pa.worker.min.js`, + }); + + // Read in our sample image and get a manifest store + try { + const { manifestStore } = await c2pa.read(sampleImage); + console.log('manifestStore', manifestStore); + + // Get the active manifest + const activeManifest = manifestStore?.activeManifest; + console.log('activeManifest', activeManifest); + } catch (err) { + console.error('Error reading image:', err); + } +})(); diff --git a/docs/js-sdk/examples/quickstart/webpack-main.ts b/docs/js-sdk/examples/quickstart/webpack-main.ts new file mode 100644 index 00000000..31a5898b --- /dev/null +++ b/docs/js-sdk/examples/quickstart/webpack-main.ts @@ -0,0 +1,30 @@ +import { createC2pa } from 'c2pa'; +import wasmSrc from 'c2pa/dist/assets/wasm/toolkit_bg.wasm?file'; +import workerSrc from 'c2pa/dist/c2pa.worker.min.js?file'; + +const element = document.createElement('div'); +element.innerHTML = `Please view the console`; +document.body.appendChild(element); + +const sampleImage = + 'https://raw.githubusercontent.com/contentauth/c2pa-js/main/tools/testing/fixtures/images/CAICAI.jpg'; + +(async () => { + // Initialize the c2pa-js SDK + const c2pa = await createC2pa({ + wasmSrc, + workerSrc, + }); + + try { + // Read in our sample image and get a manifest store + const { manifestStore } = await c2pa.read(sampleImage); + console.log('manifestStore', manifestStore); + + // Get the active manifest + const activeManifest = manifestStore?.activeManifest; + console.log('activeManifest', activeManifest); + } catch (err) { + console.error('Error reading image:', err); + } +})(); diff --git a/docs/js-sdk/examples/view-manifest/index.html b/docs/js-sdk/examples/view-manifest/index.html new file mode 100644 index 00000000..4e910bc7 --- /dev/null +++ b/docs/js-sdk/examples/view-manifest/index.html @@ -0,0 +1,22 @@ + + + + + + + + active-manifest + + + + + + + + + + +
PropertyValue
Loading…
+ + + diff --git a/docs/js-sdk/examples/view-manifest/main.ts b/docs/js-sdk/examples/view-manifest/main.ts new file mode 100644 index 00000000..94c8d745 --- /dev/null +++ b/docs/js-sdk/examples/view-manifest/main.ts @@ -0,0 +1,59 @@ +import { createC2pa, selectProducer } from 'c2pa'; +import wasmSrc from 'c2pa/dist/assets/wasm/toolkit_bg.wasm?url'; +import workerSrc from 'c2pa/dist/c2pa.worker.js?url'; +import { parseISO } from 'date-fns'; + +const sampleImage = + 'https://raw.githubusercontent.com/contentauth/c2pa-js/main/tools/testing/fixtures/images/CAICAI.jpg'; + +(async () => { + let output: string[] = []; + + const c2pa = await createC2pa({ + wasmSrc, + workerSrc, + }); + + const { manifestStore, source } = await c2pa.read(sampleImage); + const activeManifest = manifestStore?.activeManifest; + if (activeManifest) { + // Get thumbnail + // Note: You would normally call `dispose()` when working with a + // component-based UI library (e.g. on component un-mount) + // @ts-expect-error noUnusedLocals + const { url, dispose } = source.thumbnail.getUrl(); + + // Get properties + const properties: Record = { + title: activeManifest.title, + format: activeManifest.format, + claimGenerator: activeManifest.claimGenerator.split('(')[0]?.trim(), + producer: selectProducer(activeManifest)?.name ?? 'Unknown', + thumbnail: ``, + ingredients: (activeManifest.ingredients ?? []) + .map((i) => i.title) + .join(', '), + signatureIssuer: activeManifest.signatureInfo?.issuer, + signatureDate: activeManifest.signatureInfo?.time + ? parseISO(activeManifest.signatureInfo.time).toString() + : 'No date available', + }; + + output = Object.keys(properties).map((key) => { + return ` + + ${key} + ${properties[key]} + + `; + }); + } else { + output.push(` + + No provenance data found + + `); + } + + document.querySelector('#results tbody')!.innerHTML = output.join(''); +})(); diff --git a/docs/tasks/build.mdx b/docs/tasks/build.mdx index 166fadf2..734e7d92 100644 --- a/docs/tasks/build.mdx +++ b/docs/tasks/build.mdx @@ -1,6 +1,6 @@ --- id: build -title: Building a manifest +title: Attaching and signing a manifest hide_table_of_contents: true --- @@ -11,6 +11,8 @@ import TabItem from '@theme/TabItem'; +Use a `Builder` object to add a manifest to an asset. + ```python try: # Define a function to sign the claim bytes @@ -32,6 +34,10 @@ try: a_thumbnail_jpg_stream = open("tests/fixtures/A_thumbnail.jpg", "rb") builder.add_resource("image/jpeg", a_thumbnail_jpg_stream) + # Add the resource from a file + # The URI provided here, "thumbnail", must match an identifier in the manifest definition. + builder.add_resource_file("thumbnail", "tests/fixtures/A_thumbnail.jpg") + # Define an ingredient, in this case a parent ingredient named A.jpg, with a thumbnail ingredient_json = { "title": "A.jpg", @@ -64,6 +70,54 @@ except Exception as err: print(err) ``` +FROM SIGNING: + +```python +request_data = ... # This is the asset being signed +content_type = ... # MIME type of the asset + +manifest = json.dumps({ + "title": "image.jpg", + "format": "image/jpeg", + "claim_generator_info": [ + { + "name": "Documentation example", + "version": "0.0.1" + } + ], + "assertions": [ + { + "label": "c2pa.actions", + "data": { + "actions": [ + { + "action": "c2pa.edited", + "softwareAgent": { + "name": "C2PA Python Example", + "version": "0.1.0" + } + } + ] + } + } + ] +}) + +try: + builder = Builder(manifest) + + signer = create_signer(kms_sign, signing_alg, + cert_chain, timestamp_url) + + result = io.BytesIO(b"") + builder.sign(signer, content_type, io.BytesIO(request_data), result) + + return result.getvalue() +except Exception as e: + logging.error(e) + abort(500, description=e) +``` + @@ -95,6 +149,148 @@ const manifest = new ManifestBuilder({ }, ], }); +``` + +FROM SIGNING: + +Use the `c2pa.sign()` method to sign an ingredient, either locally if you have a signing certificate and key available, or by using a remote signing API. + +## Signing a stream + +If you have an asset file's data loaded into memory, you can sign the the asset using the loaded stream (buffer). + +**NOTE**: Signing using a stream is currently supported only for `image/jpeg` and `image/png` data. For all other file types, use the [file-based approach](#signing-files) . + +```ts +import { readFile } from 'node:fs/promises'; +import { createC2pa, createTestSigner } from 'c2pa-node'; + +// read an asset into a buffer +const buffer = await readFile('to-be-signed.jpg'); +const asset: Asset = { buffer, mimeType: 'image/jpeg' }; + +// build a manifest to use for signing +const manifest = new ManifestBuilder( + { + claim_generator: 'my-app/1.0.0', + format: 'image/jpeg', + title: 'buffer_signer.jpg', + assertions: [ + { + label: 'c2pa.actions', + data: { + actions: [ + { + action: 'c2pa.created', + }, + ], + }, + }, + { + label: 'com.custom.my-assertion', + data: { + description: 'My custom test assertion', + version: '1.0.0', + }, + }, + ], + }, + { vendor: 'cai' }, +); + +// create a signing function +async function sign(asset, manifest) { + const signer = await createTestSigner(); + const c2pa = createC2pa({ + signer, + }); + + const { signedAsset, signedManifest } = await c2pa.sign({ + asset, + manifest, + }); +} + +// sign +await sign(asset, manifest); +``` + +**Remote signing** + +If you have access to a web service that performs signing, you can use it to sign remotely; for example: + +```ts +import { readFile } from 'node:fs/promises'; +import { fetch, Headers } from 'node-fetch'; +import { createC2pa, SigningAlgorithm } from 'c2pa-node'; + +function createRemoteSigner() { + return { + type: 'remote', + async reserveSize() { + const url = `https://my.signing.service/box-size`; + const res = await fetch(url); + const data = (await res.json()) as { boxSize: number }; + return data.boxSize; + }, + async sign({ reserveSize, toBeSigned }) { + const url = `https://my.signing.service/sign?boxSize=${reserveSize}`; + const res = await fetch(url, { + method: 'POST', + headers: new Headers({ + 'Content-Type': 'application/octet-stream', + }), + body: toBeSigned, + }); + return res.buffer(); + }, + }; +} + +async function sign(asset, manifest) { + const signer = createRemoteSigner(); + const c2pa = createC2pa({ + signer, + }); + + const { signedAsset, signedManifest } = await c2pa.sign({ + asset, + manifest, + }); +} + +const buffer = await readFile('to-be-signed.jpg'); +const asset: Asset = { buffer, mimeType: 'image/jpeg' }; + +const manifest = new ManifestBuilder( + { + claim_generator: 'my-app/1.0.0', + format: 'image/jpeg', + title: 'buffer_signer.jpg', + assertions: [ + { + label: 'c2pa.actions', + data: { + actions: [ + { + action: 'c2pa.created', + }, + ], + }, + }, + { + label: 'com.custom.my-assertion', + data: { + description: 'My custom test assertion', + version: '1.0.0', + }, + }, + ], + }, + { vendor: 'cai' }, +); + +await sign(asset, manifest); ``` @@ -127,13 +323,12 @@ const std::string manifest_json = R"{ auto builder = Builder(manifest_json); - ```
- This is how to build a manifest using Rust. + This is how to attach and sign a manifest using Rust.
diff --git a/docs/tasks/get-resources.mdx b/docs/tasks/get-resources.mdx index 6570d788..3ef5425a 100644 --- a/docs/tasks/get-resources.mdx +++ b/docs/tasks/get-resources.mdx @@ -11,7 +11,7 @@ Manifest data can include binary resources such as thumbnail and icon images whi -```JavaScript +```js import { createC2pa, selectProducer } from 'c2pa'; import wasmSrc from 'c2pa/dist/assets/wasm/toolkit_bg.wasm?url'; import workerSrc from 'c2pa/dist/c2pa.worker.js?url'; @@ -106,6 +106,75 @@ except Exception as err: This is how to get resources from a manifest using C++. + +```cpp +fs::path manifest_path = current_dir / "../tests/fixtures/training.json"; +//fs::path certs_path = current_dir / "../tests/fixtures/es256_certs.pem"; +//fs::path image_path = current_dir / "../tests/fixtures/A.jpg"; +fs::path output_path = current_dir / "../target/example/training.jpg"; +fs::path thumbnail_path = current_dir / "../target/example/thumbnail.jpg"; + +string read_text_file(const fs::path &path) +{ + ifstream file(path); + if (!file.is_open()) + { + throw runtime_error("Could not open file " + string(path)); + } + string contents((istreambuf_iterator(file)), istreambuf_iterator()); + file.close(); + return contents.data(); +} + +try + { + // load the manifest, certs, and private key + /* Commenting out, because not part of resource reading + string manifest_json = read_text_file(manifest_path).data(); + + string certs = read_text_file(certs_path).data(); + + // create a signer + Signer signer = Signer(&test_signer, Es256, certs, "http://timestamp.digicert.com"); + + auto builder = Builder(manifest_json); + auto manifest_data = builder.sign(image_path, output_path, signer); + */ + + // read the new manifest and display the JSON + auto reader = Reader(output_path); + + auto manifest_store_json = reader.json(); + cout << "The new manifest is " << manifest_store_json << endl; + + // get the active manifest + json manifest_store = json::parse(manifest_store_json); + if (manifest_store.contains("active_manifest")) + { + string active_manifest = manifest_store["active_manifest"]; + json &manifest = manifest_store["manifests"][active_manifest]; + + string identifer = manifest["thumbnail"]["identifier"]; + + reader.get_resource(identifer, thumbnail_path); + + cout << "thumbnail written to" << thumbnail_path << endl; + } + } + catch (c2pa::Exception const &e) + { + cout << "C2PA Error: " << e.what() << endl; + } + catch (runtime_error const &e) + { + cout << "setup error" << e.what() << endl; + } + catch (json::parse_error const &e) + { + cout << "parse error " << e.what() << endl; + } +``` + diff --git a/docs/tasks/index.md b/docs/tasks/index.md index 2c830f2c..bedb3c62 100644 --- a/docs/tasks/index.md +++ b/docs/tasks/index.md @@ -9,6 +9,4 @@ The way you accomplish each task is specific to the language you're using, altho - [Preliminary setup](./setup.mdx) - [Reading manifest data](./read.mdx) - [Getting resources from a manifest](./get-resources.mdx) -- [Building a manifest](./build.mdx) -- [Writing a manifest](./write.mdx) -- [Signing a manifest](./sign.mdx) +- [Attaching a manifest to an asset and signing it](./build.mdx) diff --git a/docs/tasks/sign.mdx b/docs/tasks/sign.mdx deleted file mode 100644 index 925f9b76..00000000 --- a/docs/tasks/sign.mdx +++ /dev/null @@ -1,278 +0,0 @@ ---- -id: sign -title: Signing manifest data ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - - - - This is how to sign a manifest using Python. - -```python -request_data = ... # This is the asset being signed -content_type = ... # MIME type of the asset - -manifest = json.dumps({ - "title": "image.jpg", - "format": "image/jpeg", - "claim_generator_info": [ - { - "name": "Documentation example", - "version": "0.0.1" - } - ], - "assertions": [ - { - "label": "c2pa.actions", - "data": { - "actions": [ - { - "action": "c2pa.edited", - "softwareAgent": { - "name": "C2PA Python Example", - "version": "0.1.0" - } - } - ] - } - } - ] -}) - -try: - builder = Builder(manifest) - - signer = create_signer(kms_sign, signing_alg, - cert_chain, timestamp_url) - - result = io.BytesIO(b"") - builder.sign(signer, content_type, io.BytesIO(request_data), result) - - return result.getvalue() -except Exception as e: - logging.error(e) - abort(500, description=e) -``` - - - - - This is how to sign a manifest using Node.js. - -Use the `c2pa.sign()` method to sign an ingredient, either locally if you have a signing certificate and key available, or by using a remote signing API. - -## Signing buffers - -If you have an asset file's data loaded into memory, you can sign the the asset using the loaded buffer. - -**NOTE**: Signing using a buffer is currently supported only for `image/jpeg` and `image/png` data. For all other file types, use the [file-based approach](#signing-files) . - -```ts -import { readFile } from 'node:fs/promises'; -import { createC2pa, createTestSigner } from 'c2pa-node'; - -// read an asset into a buffer -const buffer = await readFile('to-be-signed.jpg'); -const asset: Asset = { buffer, mimeType: 'image/jpeg' }; - -// build a manifest to use for signing -const manifest = new ManifestBuilder( - { - claim_generator: 'my-app/1.0.0', - format: 'image/jpeg', - title: 'buffer_signer.jpg', - assertions: [ - { - label: 'c2pa.actions', - data: { - actions: [ - { - action: 'c2pa.created', - }, - ], - }, - }, - { - label: 'com.custom.my-assertion', - data: { - description: 'My custom test assertion', - version: '1.0.0', - }, - }, - ], - }, - { vendor: 'cai' }, -); - -// create a signing function -async function sign(asset, manifest) { - const signer = await createTestSigner(); - const c2pa = createC2pa({ - signer, - }); - - const { signedAsset, signedManifest } = await c2pa.sign({ - asset, - manifest, - }); -} - -// sign -await sign(asset, manifest); -``` - -## Signing files - -To avoid loading the entire asset into memory (or for file types other than JPEG and PNG that don't support in-memory signing), pass in the file path to the asset file to sign it; for example: - -```ts -import { resolve } from 'node:path'; -import { createC2pa, createTestSigner } from 'c2pa-node'; - -// get the asset full path -const asset = { - path: resolve('to-be-signed.jpg'), -}; -// define a location where to place the signed asset -const outputPath = resolve('signed.jpg'); - -// create a signing function -async function sign(asset, manifest) { - const signer = await createTestSigner(); - const c2pa = createC2pa({ - signer, - }); - - const { signedAsset, signedManifest } = await c2pa.sign({ - manifest, - asset, - options: { - outputPath, - }, - }); -} - -// build a manifest to use for signing -const manifest = new ManifestBuilder( - { - claim_generator: 'my-app/1.0.0', - format: 'image/jpeg', - title: 'buffer_signer.jpg', - assertions: [ - { - label: 'c2pa.actions', - data: { - actions: [ - { - action: 'c2pa.created', - }, - ], - }, - }, - { - label: 'com.custom.my-assertion', - data: { - description: 'My custom test assertion', - version: '1.0.0', - }, - }, - ], - }, - { vendor: 'cai' }, -); - -// sign -await sign(asset, manifest); -``` - -## Remote signing - -If you have access to a web service that performs signing, you can use it to sign remotely; for example: - -```ts -import { readFile } from 'node:fs/promises'; -import { fetch, Headers } from 'node-fetch'; -import { createC2pa, SigningAlgorithm } from 'c2pa-node'; - -function createRemoteSigner() { - return { - type: 'remote', - async reserveSize() { - const url = `https://my.signing.service/box-size`; - const res = await fetch(url); - const data = (await res.json()) as { boxSize: number }; - return data.boxSize; - }, - async sign({ reserveSize, toBeSigned }) { - const url = `https://my.signing.service/sign?boxSize=${reserveSize}`; - const res = await fetch(url, { - method: 'POST', - headers: new Headers({ - 'Content-Type': 'application/octet-stream', - }), - body: toBeSigned, - }); - return res.buffer(); - }, - }; -} - -async function sign(asset, manifest) { - const signer = createRemoteSigner(); - const c2pa = createC2pa({ - signer, - }); - - const { signedAsset, signedManifest } = await c2pa.sign({ - asset, - manifest, - }); -} - -const buffer = await readFile('to-be-signed.jpg'); -const asset: Asset = { buffer, mimeType: 'image/jpeg' }; - -const manifest = new ManifestBuilder( - { - claim_generator: 'my-app/1.0.0', - format: 'image/jpeg', - title: 'buffer_signer.jpg', - assertions: [ - { - label: 'c2pa.actions', - data: { - actions: [ - { - action: 'c2pa.created', - }, - ], - }, - }, - { - label: 'com.custom.my-assertion', - data: { - description: 'My custom test assertion', - version: '1.0.0', - }, - }, - ], - }, - { vendor: 'cai' }, -); - -await sign(asset, manifest); -``` - - - - - This is how to sign a manifest using C++. - - - - This is how to sign a manifest using Rust. - - - diff --git a/docs/tasks/write.mdx b/docs/tasks/write.mdx deleted file mode 100644 index 571fb175..00000000 --- a/docs/tasks/write.mdx +++ /dev/null @@ -1,25 +0,0 @@ ---- -id: write -title: Writing a manifest ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - - - - This is how to write a manifest using Python. - - - - This is how to write a manifest using Node.js. - - - - This is how to write a manifest using C++. - - - - This is how to write a manifest using Rust. - - diff --git a/sidebars.js b/sidebars.js index 8d0f593a..82e18580 100644 --- a/sidebars.js +++ b/sidebars.js @@ -83,14 +83,6 @@ const sidebars = { type: 'doc', id: 'tasks/build', }, - { - type: 'doc', - id: 'tasks/write', - }, - { - type: 'doc', - id: 'tasks/sign', - }, ], }, From 6774774255951926313334f5eb929af65f480148 Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Wed, 15 Jan 2025 13:29:59 -0800 Subject: [PATCH 14/35] Add Rust examples, fix syntax highlighting for Rust --- docs/tasks/build.mdx | 52 ++++++++++++++++++++++++++++++++++++ docs/tasks/get-resources.mdx | 31 +++++++++++++++++++++ docs/tasks/read.mdx | 4 +-- docs/tasks/setup.mdx | 2 +- docusaurus.config.js | 1 + 5 files changed, 87 insertions(+), 3 deletions(-) diff --git a/docs/tasks/build.mdx b/docs/tasks/build.mdx index 734e7d92..e1f596fb 100644 --- a/docs/tasks/build.mdx +++ b/docs/tasks/build.mdx @@ -329,6 +329,58 @@ auto builder = Builder(manifest_json); This is how to attach and sign a manifest using Rust. + +From [`c2pa-rs/sdk/examples/v2api.rs`](https://github.com/contentauth/c2pa-rs/blob/main/sdk/examples/v2api.rs#L88C5-L134C1): + +```rust +let json = manifest_def(title, format); + +let mut builder = Builder::from_json(&json)?; +builder.add_ingredient_from_stream( + json!({ + "title": parent_name, + "relationship": "parentOf" + }) + .to_string(), + format, + &mut source, +)?; + +let thumb_uri = builder + .definition + .thumbnail + .as_ref() + .map(|t| t.identifier.clone()); + +// add a manifest thumbnail ( just reuse the image for now ) +if let Some(uri) = thumb_uri { + if !uri.starts_with("self#jumbf") { + source.rewind()?; + builder.add_resource(&uri, &mut source)?; + } +} + +// write the manifest builder to a zipped stream +let mut zipped = Cursor::new(Vec::new()); +builder.to_archive(&mut zipped)?; + +// write the zipped stream to a file for debugging +//let debug_path = format!("{}/../target/test.zip", env!("CARGO_MANIFEST_DIR")); +// std::fs::write(debug_path, zipped.get_ref())?; + +// unzip the manifest builder from the zipped stream +zipped.rewind()?; + +let ed_signer = + |_context: *const (), data: &[u8]| CallbackSigner::ed25519_sign(data, PRIVATE_KEY); +let signer = CallbackSigner::new(ed_signer, SigningAlg::Ed25519, CERTS); + +let mut builder = Builder::from_archive(&mut zipped)?; +// sign the ManifestStoreBuilder and write it to the output stream +let mut dest = Cursor::new(Vec::new()); +builder.sign(&signer, format, &mut source, &mut dest)?; +``` + diff --git a/docs/tasks/get-resources.mdx b/docs/tasks/get-resources.mdx index 3ef5425a..d666d89c 100644 --- a/docs/tasks/get-resources.mdx +++ b/docs/tasks/get-resources.mdx @@ -179,6 +179,37 @@ try This is how to get resources using Rust. + +This is from [`resource_to_stream`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html#method.resource_to_stream) API doc: + +```rust +use c2pa::Reader; +let stream = std::io::Cursor::new(Vec::new()); +let reader = Reader::from_file("path/to/file.jpg").unwrap(); +let manifest = reader.active_manifest().unwrap(); +let uri = &manifest.thumbnail_ref().unwrap().identifier; +let bytes_written = reader.resource_to_stream(uri, stream).unwrap(); +``` + +This is from [`c2pa-rs/examples/v2api.rs`](https://github.com/contentauth/c2pa-rs/blob/main/sdk/examples/v2api.rs#L138): + +```rust +let reader = Reader::from_stream(format, &mut dest)?; + +// extract a thumbnail image from the ManifestStore +let mut thumbnail = Cursor::new(Vec::new()); +if let Some(manifest) = reader.active_manifest() { + if let Some(thumbnail_ref) = manifest.thumbnail_ref() { + reader.resource_to_stream(&thumbnail_ref.identifier, &mut thumbnail)?; + println!( + "wrote thumbnail {} of size {}", + thumbnail_ref.format, + thumbnail.get_ref().len() + ); + } +} +``` +
diff --git a/docs/tasks/read.mdx b/docs/tasks/read.mdx index e92eeb40..0a8adb99 100644 --- a/docs/tasks/read.mdx +++ b/docs/tasks/read.mdx @@ -114,7 +114,7 @@ Use the [`Reader`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html) struct t Use [`from_file`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html#method.from_file) to read manifest data from a file: -```rs +```rust use c2pa::Reader; let reader = Reader::from_file("path/to/file.jpg").unwrap(); ``` @@ -125,7 +125,7 @@ There is also an asynchronous version of this method, [`from_stream_async`](http Use [`from_stream`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html#method.from_stream) to read manifest data from a stream: -```rs +```rust use std::io::Cursor; use c2pa::Reader; diff --git a/docs/tasks/setup.mdx b/docs/tasks/setup.mdx index f37f3651..e6c6dcbb 100644 --- a/docs/tasks/setup.mdx +++ b/docs/tasks/setup.mdx @@ -91,7 +91,7 @@ using namespace c2pa; - This is how to setup the Rust library. + This is how to setup your code to use the Rust library. ```rust use std::{ diff --git a/docusaurus.config.js b/docusaurus.config.js index d60197ec..44f6af07 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -140,6 +140,7 @@ const config = { prism: { theme: lightCodeTheme, darkTheme: darkCodeTheme, + additionalLanguages: ['rust'], }, algolia: { // The application ID provided by Algolia From cb64e8d545d2924293e93efc9d07baca0df5c1c3 Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Wed, 15 Jan 2025 14:23:16 -0800 Subject: [PATCH 15/35] More example code --- docs/tasks/get-resources.mdx | 109 ++++++++++++++++++----------------- docs/tasks/read.mdx | 1 + 2 files changed, 57 insertions(+), 53 deletions(-) diff --git a/docs/tasks/get-resources.mdx b/docs/tasks/get-resources.mdx index d666d89c..e957461a 100644 --- a/docs/tasks/get-resources.mdx +++ b/docs/tasks/get-resources.mdx @@ -108,12 +108,6 @@ except Exception as err: This is how to get resources from a manifest using C++. ```cpp -fs::path manifest_path = current_dir / "../tests/fixtures/training.json"; -//fs::path certs_path = current_dir / "../tests/fixtures/es256_certs.pem"; -//fs::path image_path = current_dir / "../tests/fixtures/A.jpg"; -fs::path output_path = current_dir / "../target/example/training.jpg"; -fs::path thumbnail_path = current_dir / "../target/example/thumbnail.jpg"; - string read_text_file(const fs::path &path) { ifstream file(path); @@ -126,53 +120,62 @@ string read_text_file(const fs::path &path) return contents.data(); } -try - { - // load the manifest, certs, and private key - /* Commenting out, because not part of resource reading - string manifest_json = read_text_file(manifest_path).data(); - - string certs = read_text_file(certs_path).data(); - - // create a signer - Signer signer = Signer(&test_signer, Es256, certs, "http://timestamp.digicert.com"); - - auto builder = Builder(manifest_json); - auto manifest_data = builder.sign(image_path, output_path, signer); - */ - - // read the new manifest and display the JSON - auto reader = Reader(output_path); - - auto manifest_store_json = reader.json(); - cout << "The new manifest is " << manifest_store_json << endl; - - // get the active manifest - json manifest_store = json::parse(manifest_store_json); - if (manifest_store.contains("active_manifest")) - { - string active_manifest = manifest_store["active_manifest"]; - json &manifest = manifest_store["manifests"][active_manifest]; - - string identifer = manifest["thumbnail"]["identifier"]; - - reader.get_resource(identifer, thumbnail_path); - - cout << "thumbnail written to" << thumbnail_path << endl; - } - } - catch (c2pa::Exception const &e) - { - cout << "C2PA Error: " << e.what() << endl; - } - catch (runtime_error const &e) - { - cout << "setup error" << e.what() << endl; - } - catch (json::parse_error const &e) - { - cout << "parse error " << e.what() << endl; - } +int main() +{ + fs::path manifest_path = current_dir / "../tests/fixtures/training.json"; + //fs::path certs_path = current_dir / "../tests/fixtures/es256_certs.pem"; + //fs::path image_path = current_dir / "../tests/fixtures/A.jpg"; + fs::path output_path = current_dir / "../target/example/training.jpg"; + fs::path thumbnail_path = current_dir / "../target/example/thumbnail.jpg"; + + try + { + // load the manifest, certs, and private key + /* Commenting out, because not part of resource reading + string manifest_json = read_text_file(manifest_path).data(); + + string certs = read_text_file(certs_path).data(); + + // create a signer + Signer signer = Signer(&test_signer, Es256, certs, "http://timestamp.digicert.com"); + + auto builder = Builder(manifest_json); + auto manifest_data = builder.sign(image_path, output_path, signer); + */ + + // read the new manifest and display the JSON + auto reader = Reader(output_path); + + auto manifest_store_json = reader.json(); + cout << "The new manifest is " << manifest_store_json << endl; + + // get the active manifest + json manifest_store = json::parse(manifest_store_json); + if (manifest_store.contains("active_manifest")) + { + string active_manifest = manifest_store["active_manifest"]; + json &manifest = manifest_store["manifests"][active_manifest]; + + string identifer = manifest["thumbnail"]["identifier"]; + + reader.get_resource(identifer, thumbnail_path); + + cout << "thumbnail written to" << thumbnail_path << endl; + } + } + catch (c2pa::Exception const &e) + { + cout << "C2PA Error: " << e.what() << endl; + } + catch (runtime_error const &e) + { + cout << "setup error" << e.what() << endl; + } + catch (json::parse_error const &e) + { + cout << "parse error " << e.what() << endl; + } +} ``` diff --git a/docs/tasks/read.mdx b/docs/tasks/read.mdx index 0a8adb99..d53dfa36 100644 --- a/docs/tasks/read.mdx +++ b/docs/tasks/read.mdx @@ -78,6 +78,7 @@ async function read(path, mimeType) { } else { console.log('No claim found'); } + // If there are no validation errors, then validation_status will be an empty array } await read('my-c2pa-file.jpg', 'image/jpeg'); From b34aa0d616be670d30661f4c1803181f088a5d43 Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Wed, 12 Feb 2025 14:36:26 -0800 Subject: [PATCH 16/35] Move example task code into includes to make reviewing by developers easier. --- docs/tasks/build.mdx | 396 ++----------------- docs/tasks/get-resources.mdx | 214 +--------- docs/tasks/includes/_cpp-build.md | 28 ++ docs/tasks/includes/_cpp-get-resources.md | 72 ++++ docs/tasks/includes/_cpp-read.md | 17 + docs/tasks/includes/_cpp-setup.md | 21 + docs/tasks/includes/_js-get-resources.md | 61 +++ docs/tasks/includes/_js-read.md | 21 + docs/tasks/includes/_js-setup.md | 17 + docs/tasks/includes/_node-build.md | 171 ++++++++ docs/tasks/includes/_node-get-resources.md | 5 + docs/tasks/includes/_node-read.md | 24 ++ docs/tasks/includes/_node-setup.md | 8 + docs/tasks/includes/_python-build.md | 107 +++++ docs/tasks/includes/_python-get-resources.md | 21 + docs/tasks/includes/_python-read.md | 20 + docs/tasks/includes/_python-setup.md | 21 + docs/tasks/includes/_rust-build.md | 48 +++ docs/tasks/includes/_rust-get-resources.md | 31 ++ docs/tasks/includes/_rust-read.md | 28 ++ docs/tasks/includes/_rust-setup.md | 18 + docs/tasks/includes/touch.sh | 17 + docs/tasks/read.mdx | 133 +------ docs/tasks/setup.mdx | 114 +----- 24 files changed, 839 insertions(+), 774 deletions(-) create mode 100644 docs/tasks/includes/_cpp-build.md create mode 100644 docs/tasks/includes/_cpp-get-resources.md create mode 100644 docs/tasks/includes/_cpp-read.md create mode 100644 docs/tasks/includes/_cpp-setup.md create mode 100644 docs/tasks/includes/_js-get-resources.md create mode 100644 docs/tasks/includes/_js-read.md create mode 100644 docs/tasks/includes/_js-setup.md create mode 100644 docs/tasks/includes/_node-build.md create mode 100644 docs/tasks/includes/_node-get-resources.md create mode 100644 docs/tasks/includes/_node-read.md create mode 100644 docs/tasks/includes/_node-setup.md create mode 100644 docs/tasks/includes/_python-build.md create mode 100644 docs/tasks/includes/_python-get-resources.md create mode 100644 docs/tasks/includes/_python-read.md create mode 100644 docs/tasks/includes/_python-setup.md create mode 100644 docs/tasks/includes/_rust-build.md create mode 100644 docs/tasks/includes/_rust-get-resources.md create mode 100644 docs/tasks/includes/_rust-read.md create mode 100644 docs/tasks/includes/_rust-setup.md create mode 100755 docs/tasks/includes/touch.sh diff --git a/docs/tasks/build.mdx b/docs/tasks/build.mdx index e1f596fb..6c5776d4 100644 --- a/docs/tasks/build.mdx +++ b/docs/tasks/build.mdx @@ -7,380 +7,36 @@ hide_table_of_contents: true import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; - - - - -Use a `Builder` object to add a manifest to an asset. - -```python -try: - # Define a function to sign the claim bytes - # In this case we are using a pre-defined sign_ps256 method, passing in our private cert - # Normally this cert would be kept safe in some other location - def private_sign(data: bytes) -> bytes: - return sign_ps256(data, "tests/fixtures/ps256.pem") - - # read our public certs into memory - certs = open(data_dir + "ps256.pub", "rb").read() - - # Create a signer from the private signer, certs and a time stamp service url - signer = create_signer(private_sign, SigningAlg.PS256, certs, "http://timestamp.digicert.com") - - # Create a builder add a thumbnail resource and an ingredient file. - builder = Builder(manifest_json) - - # Add the resource from a stream - a_thumbnail_jpg_stream = open("tests/fixtures/A_thumbnail.jpg", "rb") - builder.add_resource("image/jpeg", a_thumbnail_jpg_stream) - - # Add the resource from a file - # The URI provided here, "thumbnail", must match an identifier in the manifest definition. - builder.add_resource_file("thumbnail", "tests/fixtures/A_thumbnail.jpg") - - # Define an ingredient, in this case a parent ingredient named A.jpg, with a thumbnail - ingredient_json = { - "title": "A.jpg", - "relationship": "parentOf", # "parentOf", "componentOf" or "inputTo" - "thumbnail": { - "identifier": "thumbnail", - "format": "image/jpeg" - } - } - - # Add the ingredient from a stream - a_jpg_stream = open("tests/fixtures/A.jpg", "rb") - builder.add_ingredient("image/jpeg", a_jpg_stream) - - # At this point we could archive or unarchive our Builder to continue later. - # In this example we use a bytearray for the archive stream. - # all ingredients and resources will be saved in the archive - archive = io.BytesIO(bytearray()) - builder.to_archive(archive) - archive.seek() - builder = builder.from_archive(archive) - - # Sign the builder with a stream and output it to a stream - # This returns the binary manifest data that could be uploaded to cloud storage. - input_stream = open("tests/fixtures/A.jpg", "rb") - output_stream = open("target/out.jpg", "wb") - c2pa_data = builder.sign(signer, "image/jpeg", input_stream, output_stream) - -except Exception as err: - print(err) -``` - -FROM SIGNING: - -```python -request_data = ... # This is the asset being signed -content_type = ... # MIME type of the asset - -manifest = json.dumps({ - "title": "image.jpg", - "format": "image/jpeg", - "claim_generator_info": [ - { - "name": "Documentation example", - "version": "0.0.1" - } - ], - "assertions": [ - { - "label": "c2pa.actions", - "data": { - "actions": [ - { - "action": "c2pa.edited", - "softwareAgent": { - "name": "C2PA Python Example", - "version": "0.1.0" - } - } - ] - } - } - ] -}) - -try: - builder = Builder(manifest) - - signer = create_signer(kms_sign, signing_alg, - cert_chain, timestamp_url) - - result = io.BytesIO(b"") - builder.sign(signer, content_type, io.BytesIO(request_data), result) - - return result.getvalue() -except Exception as e: - logging.error(e) - abort(500, description=e) -``` - - - - - -```ts -import { ManifestBuilder } from 'c2pa-node'; - -const manifest = new ManifestBuilder({ - claim_generator: 'my-app/1.0.0', - format: 'image/jpeg', - title: 'node_test_local_signer.jpg', - assertions: [ - { - label: 'c2pa.actions', - data: { - actions: [ - { - action: 'c2pa.created', - }, - ], - }, - }, - { - label: 'com.custom.my-assertion', - data: { - description: 'My custom test assertion', - version: '1.0.0', - }, - }, - ], -}); -``` - -FROM SIGNING: - -Use the `c2pa.sign()` method to sign an ingredient, either locally if you have a signing certificate and key available, or by using a remote signing API. - -## Signing a stream - -If you have an asset file's data loaded into memory, you can sign the the asset using the loaded stream (buffer). +import PythonBuild from './includes/_python-build.md'; +import NodeBuild from './includes/_node-build.md'; +import CppBuild from './includes/_cpp-build.md'; +import RustBuild from './includes/_rust-build.md'; -**NOTE**: Signing using a stream is currently supported only for `image/jpeg` and `image/png` data. For all other file types, use the [file-based approach](#signing-files) . - -```ts -import { readFile } from 'node:fs/promises'; -import { createC2pa, createTestSigner } from 'c2pa-node'; - -// read an asset into a buffer -const buffer = await readFile('to-be-signed.jpg'); -const asset: Asset = { buffer, mimeType: 'image/jpeg' }; - -// build a manifest to use for signing -const manifest = new ManifestBuilder( - { - claim_generator: 'my-app/1.0.0', - format: 'image/jpeg', - title: 'buffer_signer.jpg', - assertions: [ - { - label: 'c2pa.actions', - data: { - actions: [ - { - action: 'c2pa.created', - }, - ], - }, - }, - { - label: 'com.custom.my-assertion', - data: { - description: 'My custom test assertion', - version: '1.0.0', - }, - }, - ], - }, - { vendor: 'cai' }, -); - -// create a signing function -async function sign(asset, manifest) { - const signer = await createTestSigner(); - const c2pa = createC2pa({ - signer, - }); - - const { signedAsset, signedManifest } = await c2pa.sign({ - asset, - manifest, - }); -} - -// sign -await sign(asset, manifest); -``` - -**Remote signing** - -If you have access to a web service that performs signing, you can use it to sign remotely; for example: - -```ts -import { readFile } from 'node:fs/promises'; -import { fetch, Headers } from 'node-fetch'; -import { createC2pa, SigningAlgorithm } from 'c2pa-node'; - -function createRemoteSigner() { - return { - type: 'remote', - async reserveSize() { - const url = `https://my.signing.service/box-size`; - const res = await fetch(url); - const data = (await res.json()) as { boxSize: number }; - return data.boxSize; - }, - async sign({ reserveSize, toBeSigned }) { - const url = `https://my.signing.service/sign?boxSize=${reserveSize}`; - const res = await fetch(url, { - method: 'POST', - headers: new Headers({ - 'Content-Type': 'application/octet-stream', - }), - body: toBeSigned, - }); - return res.buffer(); - }, - }; -} - -async function sign(asset, manifest) { - const signer = createRemoteSigner(); - const c2pa = createC2pa({ - signer, - }); - - const { signedAsset, signedManifest } = await c2pa.sign({ - asset, - manifest, - }); -} - -const buffer = await readFile('to-be-signed.jpg'); -const asset: Asset = { buffer, mimeType: 'image/jpeg' }; - -const manifest = new ManifestBuilder( - { - claim_generator: 'my-app/1.0.0', - format: 'image/jpeg', - title: 'buffer_signer.jpg', - assertions: [ - { - label: 'c2pa.actions', - data: { - actions: [ - { - action: 'c2pa.created', - }, - ], - }, - }, - { - label: 'com.custom.my-assertion', - data: { - description: 'My custom test assertion', - version: '1.0.0', - }, - }, - ], - }, - { vendor: 'cai' }, -); - -await sign(asset, manifest); -``` - - - - - -```cpp -const std::string manifest_json = R"{ - "claim_generator": "c2pa_c_test/0.1", - "claim_generator_info": [ - { - "name": "c2pa-c test", - "version": "0.1" - } - ], - "assertions": [ - { - "label": "c2pa.training-mining", - "data": { - "entries": { - "c2pa.ai_generative_training": { "use": "notAllowed" }, - "c2pa.ai_inference": { "use": "notAllowed" }, - "c2pa.ai_training": { "use": "notAllowed" }, - "c2pa.data_mining": { "use": "notAllowed" } - } - } - } - ] - }; - -auto builder = Builder(manifest_json); - -``` - - - - - This is how to attach and sign a manifest using Rust. - -From [`c2pa-rs/sdk/examples/v2api.rs`](https://github.com/contentauth/c2pa-rs/blob/main/sdk/examples/v2api.rs#L88C5-L134C1): - -```rust -let json = manifest_def(title, format); - -let mut builder = Builder::from_json(&json)?; -builder.add_ingredient_from_stream( - json!({ - "title": parent_name, - "relationship": "parentOf" - }) - .to_string(), - format, - &mut source, -)?; - -let thumb_uri = builder - .definition - .thumbnail - .as_ref() - .map(|t| t.identifier.clone()); - -// add a manifest thumbnail ( just reuse the image for now ) -if let Some(uri) = thumb_uri { - if !uri.starts_with("self#jumbf") { - source.rewind()?; - builder.add_resource(&uri, &mut source)?; - } -} + -// write the manifest builder to a zipped stream -let mut zipped = Cursor::new(Vec::new()); -builder.to_archive(&mut zipped)?; +{' '} + + You can't currently attach and sign a manifest using the JavaScript library. + -// write the zipped stream to a file for debugging -//let debug_path = format!("{}/../target/test.zip", env!("CARGO_MANIFEST_DIR")); -// std::fs::write(debug_path, zipped.get_ref())?; +{' '} + + + -// unzip the manifest builder from the zipped stream -zipped.rewind()?; +{' '} + + + -let ed_signer = - |_context: *const (), data: &[u8]| CallbackSigner::ed25519_sign(data, PRIVATE_KEY); -let signer = CallbackSigner::new(ed_signer, SigningAlg::Ed25519, CERTS); +{' '} + + + -let mut builder = Builder::from_archive(&mut zipped)?; -// sign the ManifestStoreBuilder and write it to the output stream -let mut dest = Cursor::new(Vec::new()); -builder.sign(&signer, format, &mut source, &mut dest)?; -``` +{' '} + + + - - - + diff --git a/docs/tasks/get-resources.mdx b/docs/tasks/get-resources.mdx index e957461a..8a274370 100644 --- a/docs/tasks/get-resources.mdx +++ b/docs/tasks/get-resources.mdx @@ -6,213 +6,39 @@ title: Getting resources from a manifest import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -Manifest data can include binary resources such as thumbnail and icon images which are referenced by JUMBF URIs in manifest data. - - - - -```js -import { createC2pa, selectProducer } from 'c2pa'; -import wasmSrc from 'c2pa/dist/assets/wasm/toolkit_bg.wasm?url'; -import workerSrc from 'c2pa/dist/c2pa.worker.js?url'; -import { parseISO } from 'date-fns'; - -const sampleImage = - 'https://raw.githubusercontent.com/contentauth/c2pa-js/main/tools/testing/fixtures/images/CAICAI.jpg'; - -(async () => { - let output: string[] = []; - - const c2pa = await createC2pa({ - wasmSrc, - workerSrc, - }); - - const { manifestStore, source } = await c2pa.read(sampleImage); - const activeManifest = manifestStore?.activeManifest; - if (activeManifest) { - // Get thumbnail - // Note: You would normally call `dispose()` when working with a - // component-based UI library (e.g. on component un-mount) - // @ts-expect-error noUnusedLocals - const { url, dispose } = source.thumbnail.getUrl(); - - // Get properties - const properties: Record = { - title: activeManifest.title, - format: activeManifest.format, - claimGenerator: activeManifest.claimGenerator.split('(')[0]?.trim(), - producer: selectProducer(activeManifest)?.name ?? 'Unknown', - thumbnail: ``, - ingredients: (activeManifest.ingredients ?? []) - .map((i) => i.title) - .join(', '), - signatureIssuer: activeManifest.signatureInfo?.issuer, - signatureDate: activeManifest.signatureInfo?.time - ? parseISO(activeManifest.signatureInfo.time).toString() - : 'No date available', - }; - - output = Object.keys(properties).map((key) => { - return ` - - ${key} - ${properties[key]} - - `; - }); - } else { - output.push(` - - No provenance data found - - `); - } +import JSGetResources from './includes/_js-get-resources.md'; +import PythonGetResources from './includes/_python-get-resources.md'; +import NodeGetResources from './includes/_node-get-resources.md'; +import CppGetResources from './includes/_cpp-get-resources.md'; +import RustGetResources from './includes/_rust-get-resources.md'; - document.querySelector('#results tbody')!.innerHTML = output.join(''); -})(); -``` - - - - - -Retrieve binary resources such as thumbnails from the manifest data, use the `resource_to_stream` or `resource_to_file` methods using the associated `identifier` field values and a `uri`. - -NOTE: Need to add example of using `reader.resource_to_stream()`. - -```python -try: -# Create a reader from a file path -reader = c2pa.Reader.from_file("path/to/media_file.jpg") - -# Get the active manifest. -manifest = reader.get_active_manifest() -if manifest != None: +Manifest data can include binary resources such as thumbnail and icon images which are referenced by JUMBF URIs in manifest data. - # get the uri to the manifest's thumbnail and write it to a file - uri = manifest["thumbnail"]["identifier"] - reader.resource_to_file(uri, "thumbnail_v2.jpg") + -except Exception as err: - print(err) -``` +{' '} + + + +{' '} + + +{' '} - This is how to get resources from a manifest using Node.js. + +{' '} - This is how to get resources from a manifest using C++. - -```cpp -string read_text_file(const fs::path &path) -{ - ifstream file(path); - if (!file.is_open()) - { - throw runtime_error("Could not open file " + string(path)); - } - string contents((istreambuf_iterator(file)), istreambuf_iterator()); - file.close(); - return contents.data(); -} - -int main() -{ - fs::path manifest_path = current_dir / "../tests/fixtures/training.json"; - //fs::path certs_path = current_dir / "../tests/fixtures/es256_certs.pem"; - //fs::path image_path = current_dir / "../tests/fixtures/A.jpg"; - fs::path output_path = current_dir / "../target/example/training.jpg"; - fs::path thumbnail_path = current_dir / "../target/example/thumbnail.jpg"; - - try - { - // load the manifest, certs, and private key - /* Commenting out, because not part of resource reading - string manifest_json = read_text_file(manifest_path).data(); - - string certs = read_text_file(certs_path).data(); - - // create a signer - Signer signer = Signer(&test_signer, Es256, certs, "http://timestamp.digicert.com"); - - auto builder = Builder(manifest_json); - auto manifest_data = builder.sign(image_path, output_path, signer); - */ - - // read the new manifest and display the JSON - auto reader = Reader(output_path); - - auto manifest_store_json = reader.json(); - cout << "The new manifest is " << manifest_store_json << endl; - - // get the active manifest - json manifest_store = json::parse(manifest_store_json); - if (manifest_store.contains("active_manifest")) - { - string active_manifest = manifest_store["active_manifest"]; - json &manifest = manifest_store["manifests"][active_manifest]; - - string identifer = manifest["thumbnail"]["identifier"]; - - reader.get_resource(identifer, thumbnail_path); - - cout << "thumbnail written to" << thumbnail_path << endl; - } - } - catch (c2pa::Exception const &e) - { - cout << "C2PA Error: " << e.what() << endl; - } - catch (runtime_error const &e) - { - cout << "setup error" << e.what() << endl; - } - catch (json::parse_error const &e) - { - cout << "parse error " << e.what() << endl; - } -} -``` - + +{' '} - This is how to get resources using Rust. - -This is from [`resource_to_stream`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html#method.resource_to_stream) API doc: - -```rust -use c2pa::Reader; -let stream = std::io::Cursor::new(Vec::new()); -let reader = Reader::from_file("path/to/file.jpg").unwrap(); -let manifest = reader.active_manifest().unwrap(); -let uri = &manifest.thumbnail_ref().unwrap().identifier; -let bytes_written = reader.resource_to_stream(uri, stream).unwrap(); -``` - -This is from [`c2pa-rs/examples/v2api.rs`](https://github.com/contentauth/c2pa-rs/blob/main/sdk/examples/v2api.rs#L138): - -```rust -let reader = Reader::from_stream(format, &mut dest)?; - -// extract a thumbnail image from the ManifestStore -let mut thumbnail = Cursor::new(Vec::new()); -if let Some(manifest) = reader.active_manifest() { - if let Some(thumbnail_ref) = manifest.thumbnail_ref() { - reader.resource_to_stream(&thumbnail_ref.identifier, &mut thumbnail)?; - println!( - "wrote thumbnail {} of size {}", - thumbnail_ref.format, - thumbnail.get_ref().len() - ); - } -} -``` - + diff --git a/docs/tasks/includes/_cpp-build.md b/docs/tasks/includes/_cpp-build.md new file mode 100644 index 00000000..b3d82de8 --- /dev/null +++ b/docs/tasks/includes/_cpp-build.md @@ -0,0 +1,28 @@ + +```cpp +const std::string manifest_json = R"{ + "claim_generator": "c2pa_c_test/0.1", + "claim_generator_info": [ + { + "name": "c2pa-c test", + "version": "0.1" + } + ], + "assertions": [ + { + "label": "c2pa.training-mining", + "data": { + "entries": { + "c2pa.ai_generative_training": { "use": "notAllowed" }, + "c2pa.ai_inference": { "use": "notAllowed" }, + "c2pa.ai_training": { "use": "notAllowed" }, + "c2pa.data_mining": { "use": "notAllowed" } + } + } + } + ] + }; + +auto builder = Builder(manifest_json); + +``` \ No newline at end of file diff --git a/docs/tasks/includes/_cpp-get-resources.md b/docs/tasks/includes/_cpp-get-resources.md new file mode 100644 index 00000000..85fa94ed --- /dev/null +++ b/docs/tasks/includes/_cpp-get-resources.md @@ -0,0 +1,72 @@ +This is how to get resources from a manifest using C++. + +```cpp +string read_text_file(const fs::path &path) +{ + ifstream file(path); + if (!file.is_open()) + { + throw runtime_error("Could not open file " + string(path)); + } + string contents((istreambuf_iterator(file)), istreambuf_iterator()); + file.close(); + return contents.data(); +} + +int main() +{ + fs::path manifest_path = current_dir / "../tests/fixtures/training.json"; + //fs::path certs_path = current_dir / "../tests/fixtures/es256_certs.pem"; + //fs::path image_path = current_dir / "../tests/fixtures/A.jpg"; + fs::path output_path = current_dir / "../target/example/training.jpg"; + fs::path thumbnail_path = current_dir / "../target/example/thumbnail.jpg"; + + try + { + // load the manifest, certs, and private key + /* Commenting out, because not part of resource reading + string manifest_json = read_text_file(manifest_path).data(); + + string certs = read_text_file(certs_path).data(); + + // create a signer + Signer signer = Signer(&test_signer, Es256, certs, "http://timestamp.digicert.com"); + + auto builder = Builder(manifest_json); + auto manifest_data = builder.sign(image_path, output_path, signer); + */ + + // read the new manifest and display the JSON + auto reader = Reader(output_path); + + auto manifest_store_json = reader.json(); + cout << "The new manifest is " << manifest_store_json << endl; + + // get the active manifest + json manifest_store = json::parse(manifest_store_json); + if (manifest_store.contains("active_manifest")) + { + string active_manifest = manifest_store["active_manifest"]; + json &manifest = manifest_store["manifests"][active_manifest]; + + string identifer = manifest["thumbnail"]["identifier"]; + + reader.get_resource(identifer, thumbnail_path); + + cout << "thumbnail written to" << thumbnail_path << endl; + } + } + catch (c2pa::Exception const &e) + { + cout << "C2PA Error: " << e.what() << endl; + } + catch (runtime_error const &e) + { + cout << "setup error" << e.what() << endl; + } + catch (json::parse_error const &e) + { + cout << "parse error " << e.what() << endl; + } +} +``` \ No newline at end of file diff --git a/docs/tasks/includes/_cpp-read.md b/docs/tasks/includes/_cpp-read.md new file mode 100644 index 00000000..95dac314 --- /dev/null +++ b/docs/tasks/includes/_cpp-read.md @@ -0,0 +1,17 @@ + +Use the `read_file` function to read C2PA data from the specified file. This function examines the specified asset file for C2PA data and returns a JSON report if it finds any; it throws exceptions on errors. If there are validation errors, the report includes a `validation_status` field. + +```cpp +auto json_store = C2pa::read_file("", "") +``` + +Where: + +- ``- The asset file to read. +- `` - Optional path to data output directory; If provided, the function extracts any binary resources, such as thumbnails, icons, and C2PA data into that directory. These files are referenced by the identifier fields in the manifest store report. + +For example: + +```cpp +auto json_store = C2pa::read_file("work/media_file.jpg", "output/data_dir") +``` \ No newline at end of file diff --git a/docs/tasks/includes/_cpp-setup.md b/docs/tasks/includes/_cpp-setup.md new file mode 100644 index 00000000..ccc92326 --- /dev/null +++ b/docs/tasks/includes/_cpp-setup.md @@ -0,0 +1,21 @@ +To setup the C++ library: + +```cpp +#include +#include +#include +#include +#include +#include +#include +#include +#include "c2pa.hpp" +#include "test_signer.hpp" +#include + +// this example uses nlohmann json for parsing the manifest +using json = nlohmann::json; +using namespace std; +namespace fs = std::filesystem; +using namespace c2pa; +``` \ No newline at end of file diff --git a/docs/tasks/includes/_js-get-resources.md b/docs/tasks/includes/_js-get-resources.md new file mode 100644 index 00000000..12b561df --- /dev/null +++ b/docs/tasks/includes/_js-get-resources.md @@ -0,0 +1,61 @@ +```js +import { createC2pa, selectProducer } from 'c2pa'; +import wasmSrc from 'c2pa/dist/assets/wasm/toolkit_bg.wasm?url'; +import workerSrc from 'c2pa/dist/c2pa.worker.js?url'; +import { parseISO } from 'date-fns'; + +const sampleImage = + 'https://raw.githubusercontent.com/contentauth/c2pa-js/main/tools/testing/fixtures/images/CAICAI.jpg'; + +(async () => { + let output: string[] = []; + + const c2pa = await createC2pa({ + wasmSrc, + workerSrc, + }); + + const { manifestStore, source } = await c2pa.read(sampleImage); + const activeManifest = manifestStore?.activeManifest; + if (activeManifest) { + // Get thumbnail + // Note: You would normally call `dispose()` when working with a + // component-based UI library (e.g. on component un-mount) + // @ts-expect-error noUnusedLocals + const { url, dispose } = source.thumbnail.getUrl(); + + // Get properties + const properties: Record = { + title: activeManifest.title, + format: activeManifest.format, + claimGenerator: activeManifest.claimGenerator.split('(')[0]?.trim(), + producer: selectProducer(activeManifest)?.name ?? 'Unknown', + thumbnail: ``, + ingredients: (activeManifest.ingredients ?? []) + .map((i) => i.title) + .join(', '), + signatureIssuer: activeManifest.signatureInfo?.issuer, + signatureDate: activeManifest.signatureInfo?.time + ? parseISO(activeManifest.signatureInfo.time).toString() + : 'No date available', + }; + + output = Object.keys(properties).map((key) => { + return ` + + ${key} + ${properties[key]} + + `; + }); + } else { + output.push(` + + No provenance data found + + `); + } + + document.querySelector('#results tbody')!.innerHTML = output.join(''); +})(); +``` \ No newline at end of file diff --git a/docs/tasks/includes/_js-read.md b/docs/tasks/includes/_js-read.md new file mode 100644 index 00000000..e2d42ffd --- /dev/null +++ b/docs/tasks/includes/_js-read.md @@ -0,0 +1,21 @@ + +Use [`c2pa.read`](../../docs/js-sdk/api/c2pa.c2pa#methods) to read manifest data from an asset; if the asset has a C2PA manifest and was processed without errors, the returned [`c2paReadResult`](../../docs/js-sdk/api/c2pa.c2pareadresult) contains a [`manifestStore`](../../docs/js-sdk/api/c2pa.c2pareadresult.manifeststore) object with several useful properties: + +- **manifests**: An object containing all the asset's manifests ([`Manifest`](../../docs/js-sdk/api/c2pa.manifest) objects), keyed by UUID. +- **activeManifest**: A pointer to the latest [`manifest`](../../docs/js-sdk/api/c2pa.manifest) in the manifest store. Effectively the "parent" manifest, this is the likely starting point when inspecting an asset's C2PA data. +- **validationStatus**: A list of any validation errors encountered. See [Validation](../../docs/js-sdk/guides/validation) for more information. + +```js + try { + // Read in image and get a manifest store + const { manifestStore } = await c2pa.read(sampleImage); + console.log('manifestStore', manifestStore); + + // Get the active manifest + const activeManifest = manifestStore?.activeManifest; + console.log('activeManifest', activeManifest); + } catch (err) { + console.error('Error reading image:', err); + } +})(); +``` \ No newline at end of file diff --git a/docs/tasks/includes/_js-setup.md b/docs/tasks/includes/_js-setup.md new file mode 100644 index 00000000..6056f054 --- /dev/null +++ b/docs/tasks/includes/_js-setup.md @@ -0,0 +1,17 @@ +This is how to setup the JavaScript library: + +```js +const version = '0.24.2'; +const sampleImage = ''; + +import { createC2pa } from 'https://cdn.jsdelivr.net/npm/c2pa@${version}/+esm'; + +(async () => { + // Initialize the c2pa-js SDK + const c2pa = await createC2pa({ + wasmSrc: + 'https://cdn.jsdelivr.net/npm/c2pa@${version}/dist/assets/wasm/toolkit_bg.wasm', + workerSrc: + 'https://cdn.jsdelivr.net/npm/c2pa@${version}/dist/c2pa.worker.min.js', + }); +``` \ No newline at end of file diff --git a/docs/tasks/includes/_node-build.md b/docs/tasks/includes/_node-build.md new file mode 100644 index 00000000..e848396a --- /dev/null +++ b/docs/tasks/includes/_node-build.md @@ -0,0 +1,171 @@ + +```ts +import { ManifestBuilder } from 'c2pa-node'; + +const manifest = new ManifestBuilder({ + claim_generator: 'my-app/1.0.0', + format: 'image/jpeg', + title: 'node_test_local_signer.jpg', + assertions: [ + { + label: 'c2pa.actions', + data: { + actions: [ + { + action: 'c2pa.created', + }, + ], + }, + }, + { + label: 'com.custom.my-assertion', + data: { + description: 'My custom test assertion', + version: '1.0.0', + }, + }, + ], +}); +``` + +FROM SIGNING: + +Use the `c2pa.sign()` method to sign an ingredient, either locally if you have a signing certificate and key available, or by using a remote signing API. + +## Signing a stream + +If you have an asset file's data loaded into memory, you can sign the the asset using the loaded stream (buffer). + +**NOTE**: Signing using a stream is currently supported only for `image/jpeg` and `image/png` data. For all other file types, use the [file-based approach](#signing-files) . + +```ts +import { readFile } from 'node:fs/promises'; +import { createC2pa, createTestSigner } from 'c2pa-node'; + +// read an asset into a buffer +const buffer = await readFile('to-be-signed.jpg'); +const asset: Asset = { buffer, mimeType: 'image/jpeg' }; + +// build a manifest to use for signing +const manifest = new ManifestBuilder( + { + claim_generator: 'my-app/1.0.0', + format: 'image/jpeg', + title: 'buffer_signer.jpg', + assertions: [ + { + label: 'c2pa.actions', + data: { + actions: [ + { + action: 'c2pa.created', + }, + ], + }, + }, + { + label: 'com.custom.my-assertion', + data: { + description: 'My custom test assertion', + version: '1.0.0', + }, + }, + ], + }, + { vendor: 'cai' }, +); + +// create a signing function +async function sign(asset, manifest) { + const signer = await createTestSigner(); + const c2pa = createC2pa({ + signer, + }); + + const { signedAsset, signedManifest } = await c2pa.sign({ + asset, + manifest, + }); +} + +// sign +await sign(asset, manifest); +``` + +**Remote signing** + +If you have access to a web service that performs signing, you can use it to sign remotely; for example: + +```ts +import { readFile } from 'node:fs/promises'; +import { fetch, Headers } from 'node-fetch'; +import { createC2pa, SigningAlgorithm } from 'c2pa-node'; + +function createRemoteSigner() { + return { + type: 'remote', + async reserveSize() { + const url = `https://my.signing.service/box-size`; + const res = await fetch(url); + const data = (await res.json()) as { boxSize: number }; + return data.boxSize; + }, + async sign({ reserveSize, toBeSigned }) { + const url = `https://my.signing.service/sign?boxSize=${reserveSize}`; + const res = await fetch(url, { + method: 'POST', + headers: new Headers({ + 'Content-Type': 'application/octet-stream', + }), + body: toBeSigned, + }); + return res.buffer(); + }, + }; +} + +async function sign(asset, manifest) { + const signer = createRemoteSigner(); + const c2pa = createC2pa({ + signer, + }); + + const { signedAsset, signedManifest } = await c2pa.sign({ + asset, + manifest, + }); +} + +const buffer = await readFile('to-be-signed.jpg'); +const asset: Asset = { buffer, mimeType: 'image/jpeg' }; + +const manifest = new ManifestBuilder( + { + claim_generator: 'my-app/1.0.0', + format: 'image/jpeg', + title: 'buffer_signer.jpg', + assertions: [ + { + label: 'c2pa.actions', + data: { + actions: [ + { + action: 'c2pa.created', + }, + ], + }, + }, + { + label: 'com.custom.my-assertion', + data: { + description: 'My custom test assertion', + version: '1.0.0', + }, + }, + ], + }, + { vendor: 'cai' }, +); + +await sign(asset, manifest); +``` \ No newline at end of file diff --git a/docs/tasks/includes/_node-get-resources.md b/docs/tasks/includes/_node-get-resources.md new file mode 100644 index 00000000..109f33f0 --- /dev/null +++ b/docs/tasks/includes/_node-get-resources.md @@ -0,0 +1,5 @@ +This is how to get resources from a manifest using Node.js. + +```js +// TBD +``` \ No newline at end of file diff --git a/docs/tasks/includes/_node-read.md b/docs/tasks/includes/_node-read.md new file mode 100644 index 00000000..9c1a1973 --- /dev/null +++ b/docs/tasks/includes/_node-read.md @@ -0,0 +1,24 @@ + +Use the `c2pa.read()` function to read a manifest; for example: + +```ts +import { createC2pa } from 'c2pa-node'; +import { readFile } from 'node:fs/promises'; + +const c2pa = createC2pa(); + +async function read(path, mimeType) { + const buffer = await readFile(path); + const result = await c2pa.read({ buffer, mimeType }); + + if (result) { + const { active_manifest, manifests, validation_status } = result; + console.log(active_manifest); + } else { + console.log('No claim found'); + } + // If there are no validation errors, then validation_status will be an empty array +} + +await read('my-c2pa-file.jpg', 'image/jpeg'); +``` diff --git a/docs/tasks/includes/_node-setup.md b/docs/tasks/includes/_node-setup.md new file mode 100644 index 00000000..2c69ca83 --- /dev/null +++ b/docs/tasks/includes/_node-setup.md @@ -0,0 +1,8 @@ +This is how to setup the Node.js library. + +```js +import { createC2pa } from 'c2pa-node'; +import { readFile } from 'node:fs/promises'; + +const c2pa = createC2pa(); +``` \ No newline at end of file diff --git a/docs/tasks/includes/_python-build.md b/docs/tasks/includes/_python-build.md new file mode 100644 index 00000000..26045cdb --- /dev/null +++ b/docs/tasks/includes/_python-build.md @@ -0,0 +1,107 @@ + +Use a `Builder` object to add a manifest to an asset. + +```python +try: + # Define a function to sign the claim bytes + # In this case we are using a pre-defined sign_ps256 method, passing in our private cert + # Normally this cert would be kept safe in some other location + def private_sign(data: bytes) -> bytes: + return sign_ps256(data, "tests/fixtures/ps256.pem") + + # read our public certs into memory + certs = open(data_dir + "ps256.pub", "rb").read() + + # Create a signer from the private signer, certs and a time stamp service url + signer = create_signer(private_sign, SigningAlg.PS256, certs, "http://timestamp.digicert.com") + + # Create a builder add a thumbnail resource and an ingredient file. + builder = Builder(manifest_json) + + # Add the resource from a stream + a_thumbnail_jpg_stream = open("tests/fixtures/A_thumbnail.jpg", "rb") + builder.add_resource("image/jpeg", a_thumbnail_jpg_stream) + + # Add the resource from a file + # The URI provided here, "thumbnail", must match an identifier in the manifest definition. + builder.add_resource_file("thumbnail", "tests/fixtures/A_thumbnail.jpg") + + # Define an ingredient, in this case a parent ingredient named A.jpg, with a thumbnail + ingredient_json = { + "title": "A.jpg", + "relationship": "parentOf", # "parentOf", "componentOf" or "inputTo" + "thumbnail": { + "identifier": "thumbnail", + "format": "image/jpeg" + } + } + + # Add the ingredient from a stream + a_jpg_stream = open("tests/fixtures/A.jpg", "rb") + builder.add_ingredient("image/jpeg", a_jpg_stream) + + # At this point we could archive or unarchive our Builder to continue later. + # In this example we use a bytearray for the archive stream. + # all ingredients and resources will be saved in the archive + archive = io.BytesIO(bytearray()) + builder.to_archive(archive) + archive.seek() + builder = builder.from_archive(archive) + + # Sign the builder with a stream and output it to a stream + # This returns the binary manifest data that could be uploaded to cloud storage. + input_stream = open("tests/fixtures/A.jpg", "rb") + output_stream = open("target/out.jpg", "wb") + c2pa_data = builder.sign(signer, "image/jpeg", input_stream, output_stream) + +except Exception as err: + print(err) +``` + +FROM SIGNING: + +```python +request_data = ... # This is the asset being signed +content_type = ... # MIME type of the asset + +manifest = json.dumps({ + "title": "image.jpg", + "format": "image/jpeg", + "claim_generator_info": [ + { + "name": "Documentation example", + "version": "0.0.1" + } + ], + "assertions": [ + { + "label": "c2pa.actions", + "data": { + "actions": [ + { + "action": "c2pa.edited", + "softwareAgent": { + "name": "C2PA Python Example", + "version": "0.1.0" + } + } + ] + } + } + ] +}) + +try: + builder = Builder(manifest) + + signer = create_signer(kms_sign, signing_alg, + cert_chain, timestamp_url) + + result = io.BytesIO(b"") + builder.sign(signer, content_type, io.BytesIO(request_data), result) + + return result.getvalue() +except Exception as e: + logging.error(e) + abort(500, description=e) +``` \ No newline at end of file diff --git a/docs/tasks/includes/_python-get-resources.md b/docs/tasks/includes/_python-get-resources.md new file mode 100644 index 00000000..d8d9d925 --- /dev/null +++ b/docs/tasks/includes/_python-get-resources.md @@ -0,0 +1,21 @@ + +Retrieve binary resources such as thumbnails from the manifest data, use the `resource_to_stream` or `resource_to_file` methods using the associated `identifier` field values and a `uri`. + +NOTE: Need to add example of using `reader.resource_to_stream()`. + +```python +try: +# Create a reader from a file path +reader = c2pa.Reader.from_file("path/to/media_file.jpg") + +# Get the active manifest. +manifest = reader.get_active_manifest() +if manifest != None: + + # get the uri to the manifest's thumbnail and write it to a file + uri = manifest["thumbnail"]["identifier"] + reader.resource_to_file(uri, "thumbnail_v2.jpg") + +except Exception as err: + print(err) +``` \ No newline at end of file diff --git a/docs/tasks/includes/_python-read.md b/docs/tasks/includes/_python-read.md new file mode 100644 index 00000000..ea990120 --- /dev/null +++ b/docs/tasks/includes/_python-read.md @@ -0,0 +1,20 @@ + +Use the `Reader` object to read manifest data from a file or stream and perform validation on the manifest store. Use the `json()` method to return a JSON manifest report; If there are validation errors, the report includes a `validation_status` field. + +An asset file may contain many manifests in a manifest store. The most recent manifest is identified by the value of the `active_manifest` field in the manifests map. + +```py +try: + # Create a reader from a file path + reader = c2pa.Reader.from_file("path/to/media_file.jpg") + + # Alternatively, create a reader from a stream + stream = open("path/to/media_file.jpg", "rb") + reader = c2pa.Reader("image/jpeg", stream) + + # Print the JSON for a manifest. + print("manifest store:", reader.json()) + +except Exception as err: + print(err) +``` \ No newline at end of file diff --git a/docs/tasks/includes/_python-setup.md b/docs/tasks/includes/_python-setup.md new file mode 100644 index 00000000..fe695faf --- /dev/null +++ b/docs/tasks/includes/_python-setup.md @@ -0,0 +1,21 @@ +This is how to setup the Python library. + +```python +# Import the C2PA Python package +from c2pa import * + +# Import standard general-purpose packages +import os +import io +import logging +import json +import base64 + +# Import web packages used in example implementation +from flask import Flask, request, abort +from flask_cors import CORS +from waitress import serve + +# Import AWS SDK package (to use KMS) +import boto3 +``` diff --git a/docs/tasks/includes/_rust-build.md b/docs/tasks/includes/_rust-build.md new file mode 100644 index 00000000..4a2a0825 --- /dev/null +++ b/docs/tasks/includes/_rust-build.md @@ -0,0 +1,48 @@ +This is how to attach and sign a manifest using Rust. + +This example is from [`c2pa-rs/sdk/examples/v2api.rs`](https://github.com/contentauth/c2pa-rs/blob/main/sdk/examples/v2api.rs#L88C5-L134C1): + +```rust +let json = manifest_def(title, format); + +let mut builder = Builder::from_json(&json)?; +builder.add_ingredient_from_stream( + json!({ + "title": parent_name, + "relationship": "parentOf" + }) + .to_string(), + format, + &mut source, +)?; + +let thumb_uri = builder + .definition + .thumbnail + .as_ref() + .map(|t| t.identifier.clone()); + +// add a manifest thumbnail ( just reuse the image for now ) +if let Some(uri) = thumb_uri { + if !uri.starts_with("self#jumbf") { + source.rewind()?; + builder.add_resource(&uri, &mut source)?; + } +} + +// write the manifest builder to a zipped stream +let mut zipped = Cursor::new(Vec::new()); +builder.to_archive(&mut zipped)?; + +// unzip the manifest builder from the zipped stream +zipped.rewind()?; + +let ed_signer = + |_context: *const (), data: &[u8]| CallbackSigner::ed25519_sign(data, PRIVATE_KEY); +let signer = CallbackSigner::new(ed_signer, SigningAlg::Ed25519, CERTS); + +let mut builder = Builder::from_archive(&mut zipped)?; +// sign the ManifestStoreBuilder and write it to the output stream +let mut dest = Cursor::new(Vec::new()); +builder.sign(&signer, format, &mut source, &mut dest)?; +``` diff --git a/docs/tasks/includes/_rust-get-resources.md b/docs/tasks/includes/_rust-get-resources.md new file mode 100644 index 00000000..145d15b4 --- /dev/null +++ b/docs/tasks/includes/_rust-get-resources.md @@ -0,0 +1,31 @@ +This is how to get resources using Rust. + +This is from [`resource_to_stream`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html#method.resource_to_stream) API doc: + +```rust +use c2pa::Reader; +let stream = std::io::Cursor::new(Vec::new()); +let reader = Reader::from_file("path/to/file.jpg").unwrap(); +let manifest = reader.active_manifest().unwrap(); +let uri = &manifest.thumbnail_ref().unwrap().identifier; +let bytes_written = reader.resource_to_stream(uri, stream).unwrap(); +``` + +This is from [`c2pa-rs/examples/v2api.rs`](https://github.com/contentauth/c2pa-rs/blob/main/sdk/examples/v2api.rs#L138): + +```rust +let reader = Reader::from_stream(format, &mut dest)?; + +// extract a thumbnail image from the ManifestStore +let mut thumbnail = Cursor::new(Vec::new()); +if let Some(manifest) = reader.active_manifest() { + if let Some(thumbnail_ref) = manifest.thumbnail_ref() { + reader.resource_to_stream(&thumbnail_ref.identifier, &mut thumbnail)?; + println!( + "wrote thumbnail {} of size {}", + thumbnail_ref.format, + thumbnail.get_ref().len() + ); + } +} +``` \ No newline at end of file diff --git a/docs/tasks/includes/_rust-read.md b/docs/tasks/includes/_rust-read.md new file mode 100644 index 00000000..f164268a --- /dev/null +++ b/docs/tasks/includes/_rust-read.md @@ -0,0 +1,28 @@ + +Use the [`Reader`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html) struct to read manifest data from a file or stream. + +### Reading from a file + +Use [`from_file`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html#method.from_file) to read manifest data from a file: + +```rust +use c2pa::Reader; +let reader = Reader::from_file("path/to/file.jpg").unwrap(); +``` + +There is also an asynchronous version of this method, [`from_stream_async`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html#method.from_file_async). + +### Reading from a stream + +Use [`from_stream`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html#method.from_stream) to read manifest data from a stream: + +```rust +use std::io::Cursor; + +use c2pa::Reader; +let mut stream = Cursor::new(include_bytes!("../tests/fixtures/CA.jpg")); +let reader = Reader::from_stream("image/jpeg", stream).unwrap(); +println!("{}", reader.json()); +``` + +There is also an asynchronous version of this method, [`from_stream_async`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html#method.from_stream_async). diff --git a/docs/tasks/includes/_rust-setup.md b/docs/tasks/includes/_rust-setup.md new file mode 100644 index 00000000..f0e8e9cd --- /dev/null +++ b/docs/tasks/includes/_rust-setup.md @@ -0,0 +1,18 @@ +This is how to setup your code to use the Rust library. + +```rust +use std::{ + io::{Cursor, Write}, + process::{Command, Stdio}, +}; + +use anyhow::Result; +use c2pa::{settings::load_settings_from_str, Builder, CallbackSigner, Reader}; + +use c2pa_crypto::raw_signature::SigningAlg; +use serde_json::json; + +const TEST_IMAGE: &[u8] = include_bytes!("../tests/fixtures/CA.jpg"); +const CERTS: &[u8] = include_bytes!("../tests/fixtures/certs/ed25519.pub"); +const PRIVATE_KEY: &[u8] = include_bytes!("../tests/fixtures/certs/ed25519.pem"); +``` \ No newline at end of file diff --git a/docs/tasks/includes/touch.sh b/docs/tasks/includes/touch.sh new file mode 100755 index 00000000..41b5b530 --- /dev/null +++ b/docs/tasks/includes/touch.sh @@ -0,0 +1,17 @@ +touch _cpp-read.md +touch _node-read.md +touch _rust-read.md +touch _js-read.md +touch _python-read.md + +touch _cpp-get-resources.md +touch _node-get-resources.md +touch _rust-get-resources.md +touch _js-get-resources.md +touch _python-get-resources.md + +touch _cpp-sign.md +touch _node-sign.md +touch _rust-sign.md +touch _python-sign.md + diff --git a/docs/tasks/read.mdx b/docs/tasks/read.mdx index d53dfa36..6295e6a1 100644 --- a/docs/tasks/read.mdx +++ b/docs/tasks/read.mdx @@ -7,136 +7,37 @@ hide_table_of_contents: true import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; +import JSRead from './includes/_js-read.md'; +import PythonRead from './includes/_python-read.md'; +import NodeRead from './includes/_node-read.md'; +import CppRead from './includes/_cpp-read.md'; +import RustRead from './includes/_rust-read.md'; + +{' '} - -Use [`c2pa.read`](../../docs/js-sdk/api/c2pa.c2pa#methods) to read manifest data from an asset; if the asset has a C2PA manifest and was processed without errors, the returned [`c2paReadResult`](../../docs/js-sdk/api/c2pa.c2pareadresult) contains a [`manifestStore`](../../docs/js-sdk/api/c2pa.c2pareadresult.manifeststore) object with several useful properties: - -- **manifests**: An object containing all the asset's manifests ([`Manifest`](../../docs/js-sdk/api/c2pa.manifest) objects), keyed by UUID. -- **activeManifest**: A pointer to the latest [`manifest`](../../docs/js-sdk/api/c2pa.manifest) in the manifest store. Effectively the "parent" manifest, this is the likely starting point when inspecting an asset's C2PA data. -- **validationStatus**: A list of any validation errors encountered. See [Validation](../../docs/js-sdk/guides/validation) for more information. - -```js - try { - // Read in image and get a manifest store - const { manifestStore } = await c2pa.read(sampleImage); - console.log('manifestStore', manifestStore); - - // Get the active manifest - const activeManifest = manifestStore?.activeManifest; - console.log('activeManifest', activeManifest); - } catch (err) { - console.error('Error reading image:', err); - } -})(); -``` - + - - -Use the `Reader` object to read manifest data from a file or stream and perform validation on the manifest store. Use the `json()` method to return a JSON manifest report; If there are validation errors, the report includes a `validation_status` field. - -An asset file may contain many manifests in a manifest store. The most recent manifest is identified by the value of the `active_manifest` field in the manifests map. - -```py -try: - # Create a reader from a file path - reader = c2pa.Reader.from_file("path/to/media_file.jpg") - - # Alternatively, create a reader from a stream - stream = open("path/to/media_file.jpg", "rb") - reader = c2pa.Reader("image/jpeg", stream) - - # Print the JSON for a manifest. - print("manifest store:", reader.json()) - -except Exception as err: - print(err) -``` - +{' '} + + +{' '} - -Use the `c2pa.read()` function to read a manifest; for example: - -```ts -import { createC2pa } from 'c2pa-node'; -import { readFile } from 'node:fs/promises'; - -const c2pa = createC2pa(); - -async function read(path, mimeType) { - const buffer = await readFile(path); - const result = await c2pa.read({ buffer, mimeType }); - - if (result) { - const { active_manifest, manifests, validation_status } = result; - console.log(active_manifest); - } else { - console.log('No claim found'); - } - // If there are no validation errors, then validation_status will be an empty array -} - -await read('my-c2pa-file.jpg', 'image/jpeg'); -``` - + +{' '} - -Use the `read_file` function to read C2PA data from the specified file. This function examines the specified asset file for C2PA data and returns a JSON report if it finds any; it throws exceptions on errors. If there are validation errors, the report includes a `validation_status` field. - -```cpp -auto json_store = C2pa::read_file("", "") -``` - -Where: - -- ``- The asset file to read. -- `` - Optional path to data output directory; If provided, the function extracts any binary resources, such as thumbnails, icons, and C2PA data into that directory. These files are referenced by the identifier fields in the manifest store report. - -For example: - -```cpp -auto json_store = C2pa::read_file("work/media_file.jpg", "output/data_dir") -``` - + +{' '} - -Use the [`Reader`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html) struct to read manifest data from a file or stream. - -### Reading from a file - -Use [`from_file`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html#method.from_file) to read manifest data from a file: - -```rust -use c2pa::Reader; -let reader = Reader::from_file("path/to/file.jpg").unwrap(); -``` - -There is also an asynchronous version of this method, [`from_stream_async`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html#method.from_file_async). - -### Reading from a stream - -Use [`from_stream`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html#method.from_stream) to read manifest data from a stream: - -```rust -use std::io::Cursor; - -use c2pa::Reader; -let mut stream = Cursor::new(include_bytes!("../tests/fixtures/CA.jpg")); -let reader = Reader::from_stream("image/jpeg", stream).unwrap(); -println!("{}", reader.json()); -``` - -There is also an asynchronous version of this method, [`from_stream_async`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html#method.from_stream_async). - + diff --git a/docs/tasks/setup.mdx b/docs/tasks/setup.mdx index e6c6dcbb..a751cf56 100644 --- a/docs/tasks/setup.mdx +++ b/docs/tasks/setup.mdx @@ -6,110 +6,36 @@ hide_table_of_contents: true import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; +import JSSetup from './includes/_js-setup.md'; +import PythonSetup from './includes/_python-setup.md'; +import NodeSetup from './includes/_node-setup.md'; +import CppSetup from './includes/_cpp-setup.md'; +import RustSetup from './includes/_rust-setup.md'; +{' '} - -```js -const version = '0.24.2'; -const sampleImage = ''; - -import { createC2pa } from 'https://cdn.jsdelivr.net/npm/c2pa@${version}/+esm'; - -(async () => { - // Initialize the c2pa-js SDK - const c2pa = await createC2pa({ - wasmSrc: - 'https://cdn.jsdelivr.net/npm/c2pa@${version}/dist/assets/wasm/toolkit_bg.wasm', - workerSrc: - 'https://cdn.jsdelivr.net/npm/c2pa@${version}/dist/c2pa.worker.min.js', - }); -``` - + - - This is how to setup the Python library. - -```python -# Import the C2PA Python package -from c2pa import * - -# Import standard general-purpose packages -import os -import io -import logging -import json -import base64 - -# Import web packages used in example implementation -from flask import Flask, request, abort -from flask_cors import CORS -from waitress import serve - -# Import AWS SDK package (to use KMS) -import boto3 -``` - - - - - This is how to setup the Node.js library. - -```js -import { createC2pa } from 'c2pa-node'; -import { readFile } from 'node:fs/promises'; - -const c2pa = createC2pa(); -``` - - - - - -```cpp -#include -#include -#include -#include -#include -#include -#include -#include -#include "c2pa.hpp" -#include "test_signer.hpp" -#include +{' '} + + + -// this example uses nlohmann json for parsing the manifest -using json = nlohmann::json; -using namespace std; -namespace fs = std::filesystem; -using namespace c2pa; -``` +{' '} + + + - +{' '} + + + - This is how to setup your code to use the Rust library. - -```rust -use std::{ - io::{Cursor, Write}, - process::{Command, Stdio}, -}; - -use anyhow::Result; -use c2pa::{settings::load_settings_from_str, Builder, CallbackSigner, Reader}; - -use c2pa_crypto::raw_signature::SigningAlg; -use serde_json::json; - -const TEST_IMAGE: &[u8] = include_bytes!("../tests/fixtures/CA.jpg"); -const CERTS: &[u8] = include_bytes!("../tests/fixtures/certs/ed25519.pub"); -const PRIVATE_KEY: &[u8] = include_bytes!("../tests/fixtures/certs/ed25519.pem"); -``` - + From 234b2d33ae7574db541bffb25a16ff864e353caa Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Wed, 12 Feb 2025 14:47:35 -0800 Subject: [PATCH 17/35] Fix spacing to avoid linting weirdness --- docs/tasks/build.mdx | 31 ++++++++++++++++++++----------- docs/tasks/get-resources.mdx | 27 ++++++++++++++++++--------- docs/tasks/read.mdx | 27 ++++++++++++++++++--------- docs/tasks/setup.mdx | 26 ++++++++++++++++++-------- 4 files changed, 74 insertions(+), 37 deletions(-) diff --git a/docs/tasks/build.mdx b/docs/tasks/build.mdx index 6c5776d4..3818263f 100644 --- a/docs/tasks/build.mdx +++ b/docs/tasks/build.mdx @@ -14,29 +14,38 @@ import RustBuild from './includes/_rust-build.md'; -{' '} - - You can't currently attach and sign a manifest using the JavaScript library. + + +You can't currently attach a manifest to an asset and sign the claim using the JavaScript library. + -{' '} - - {' '} - - + + + + {' '} - - + + + + {' '} + + + + - + +{' '} + + diff --git a/docs/tasks/get-resources.mdx b/docs/tasks/get-resources.mdx index 8a274370..1fa248a1 100644 --- a/docs/tasks/get-resources.mdx +++ b/docs/tasks/get-resources.mdx @@ -16,29 +16,38 @@ Manifest data can include binary resources such as thumbnail and icon images whi -{' '} - - {' '} - - + + + + {' '} - - + + + + {' '} - - + + + + {' '} + + + + + + diff --git a/docs/tasks/read.mdx b/docs/tasks/read.mdx index 6295e6a1..aed5139a 100644 --- a/docs/tasks/read.mdx +++ b/docs/tasks/read.mdx @@ -15,29 +15,38 @@ import RustRead from './includes/_rust-read.md'; -{' '} - - {' '} - - + + + + {' '} - - + + + + {' '} - - + + + + {' '} + + + + + + diff --git a/docs/tasks/setup.mdx b/docs/tasks/setup.mdx index a751cf56..f631ec91 100644 --- a/docs/tasks/setup.mdx +++ b/docs/tasks/setup.mdx @@ -14,28 +14,38 @@ import RustSetup from './includes/_rust-setup.md'; -{' '} - - {' '} - - + + + + {' '} - - + + + + {' '} + + + + - + +{' '} + + + + From 71148645567a14a301582cf3c8b9fe823f9eaeec Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Wed, 12 Feb 2025 14:51:56 -0800 Subject: [PATCH 18/35] Remove script committed by accident --- docs/tasks/includes/touch.sh | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100755 docs/tasks/includes/touch.sh diff --git a/docs/tasks/includes/touch.sh b/docs/tasks/includes/touch.sh deleted file mode 100755 index 41b5b530..00000000 --- a/docs/tasks/includes/touch.sh +++ /dev/null @@ -1,17 +0,0 @@ -touch _cpp-read.md -touch _node-read.md -touch _rust-read.md -touch _js-read.md -touch _python-read.md - -touch _cpp-get-resources.md -touch _node-get-resources.md -touch _rust-get-resources.md -touch _js-get-resources.md -touch _python-get-resources.md - -touch _cpp-sign.md -touch _node-sign.md -touch _rust-sign.md -touch _python-sign.md - From 0e9ccd1f77425dea0f5ec0e0c164dce3878b8a3c Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Wed, 12 Feb 2025 15:00:59 -0800 Subject: [PATCH 19/35] Remove files from JS example repo --- .../js-sdk/examples/quickstart/rollup-main.ts | 26 -------- docs/js-sdk/examples/quickstart/vite-main.ts | 27 --------- .../examples/quickstart/webpack-main.ts | 30 ---------- docs/js-sdk/examples/view-manifest/index.html | 22 ------- docs/js-sdk/examples/view-manifest/main.ts | 59 ------------------- 5 files changed, 164 deletions(-) delete mode 100644 docs/js-sdk/examples/quickstart/rollup-main.ts delete mode 100644 docs/js-sdk/examples/quickstart/vite-main.ts delete mode 100644 docs/js-sdk/examples/quickstart/webpack-main.ts delete mode 100644 docs/js-sdk/examples/view-manifest/index.html delete mode 100644 docs/js-sdk/examples/view-manifest/main.ts diff --git a/docs/js-sdk/examples/quickstart/rollup-main.ts b/docs/js-sdk/examples/quickstart/rollup-main.ts deleted file mode 100644 index f92207aa..00000000 --- a/docs/js-sdk/examples/quickstart/rollup-main.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { createC2pa } from 'c2pa'; -import wasmModule from 'c2pa/dist/assets/wasm/toolkit_bg.wasm'; - -const sampleImage = - 'https://raw.githubusercontent.com/contentauth/c2pa-js/main/tools/testing/fixtures/images/CAICAI.jpg'; - -(async () => { - // Initialize the c2pa-js SDK - const wasmSrc = await wasmModule(); - const c2pa = await createC2pa({ - wasmSrc, - workerSrc: 'c2pa.worker.min.js', - }); - - try { - // Read in our sample image and get a manifest store - const { manifestStore } = await c2pa.read(sampleImage); - console.log('manifestStore', manifestStore); - - // Get the active manifest - const activeManifest = manifestStore?.activeManifest; - console.log('activeManifest', activeManifest); - } catch (err) { - console.error('Error reading image:', err); - } -})(); diff --git a/docs/js-sdk/examples/quickstart/vite-main.ts b/docs/js-sdk/examples/quickstart/vite-main.ts deleted file mode 100644 index e86371ea..00000000 --- a/docs/js-sdk/examples/quickstart/vite-main.ts +++ /dev/null @@ -1,27 +0,0 @@ -const sampleImage = - 'https://raw.githubusercontent.com/contentauth/c2pa-js/main/tools/testing/fixtures/images/CAICAI.jpg'; - -(async () => { - // Information about where to fetch the library - const version = '0.17.2'; - const libraryUrl = `https://cdn.jsdelivr.net/npm/c2pa@${version}/+esm`; - - // Initialize the c2pa-js SDK - const { createC2pa } = await import(libraryUrl); - const c2pa = await createC2pa({ - wasmSrc: `https://cdn.jsdelivr.net/npm/c2pa@${version}/dist/assets/wasm/toolkit_bg.wasm`, - workerSrc: `https://cdn.jsdelivr.net/npm/c2pa@${version}/dist/c2pa.worker.min.js`, - }); - - // Read in our sample image and get a manifest store - try { - const { manifestStore } = await c2pa.read(sampleImage); - console.log('manifestStore', manifestStore); - - // Get the active manifest - const activeManifest = manifestStore?.activeManifest; - console.log('activeManifest', activeManifest); - } catch (err) { - console.error('Error reading image:', err); - } -})(); diff --git a/docs/js-sdk/examples/quickstart/webpack-main.ts b/docs/js-sdk/examples/quickstart/webpack-main.ts deleted file mode 100644 index 31a5898b..00000000 --- a/docs/js-sdk/examples/quickstart/webpack-main.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { createC2pa } from 'c2pa'; -import wasmSrc from 'c2pa/dist/assets/wasm/toolkit_bg.wasm?file'; -import workerSrc from 'c2pa/dist/c2pa.worker.min.js?file'; - -const element = document.createElement('div'); -element.innerHTML = `Please view the console`; -document.body.appendChild(element); - -const sampleImage = - 'https://raw.githubusercontent.com/contentauth/c2pa-js/main/tools/testing/fixtures/images/CAICAI.jpg'; - -(async () => { - // Initialize the c2pa-js SDK - const c2pa = await createC2pa({ - wasmSrc, - workerSrc, - }); - - try { - // Read in our sample image and get a manifest store - const { manifestStore } = await c2pa.read(sampleImage); - console.log('manifestStore', manifestStore); - - // Get the active manifest - const activeManifest = manifestStore?.activeManifest; - console.log('activeManifest', activeManifest); - } catch (err) { - console.error('Error reading image:', err); - } -})(); diff --git a/docs/js-sdk/examples/view-manifest/index.html b/docs/js-sdk/examples/view-manifest/index.html deleted file mode 100644 index 4e910bc7..00000000 --- a/docs/js-sdk/examples/view-manifest/index.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - active-manifest - - - - - - - - - - -
PropertyValue
Loading…
- - - diff --git a/docs/js-sdk/examples/view-manifest/main.ts b/docs/js-sdk/examples/view-manifest/main.ts deleted file mode 100644 index 94c8d745..00000000 --- a/docs/js-sdk/examples/view-manifest/main.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { createC2pa, selectProducer } from 'c2pa'; -import wasmSrc from 'c2pa/dist/assets/wasm/toolkit_bg.wasm?url'; -import workerSrc from 'c2pa/dist/c2pa.worker.js?url'; -import { parseISO } from 'date-fns'; - -const sampleImage = - 'https://raw.githubusercontent.com/contentauth/c2pa-js/main/tools/testing/fixtures/images/CAICAI.jpg'; - -(async () => { - let output: string[] = []; - - const c2pa = await createC2pa({ - wasmSrc, - workerSrc, - }); - - const { manifestStore, source } = await c2pa.read(sampleImage); - const activeManifest = manifestStore?.activeManifest; - if (activeManifest) { - // Get thumbnail - // Note: You would normally call `dispose()` when working with a - // component-based UI library (e.g. on component un-mount) - // @ts-expect-error noUnusedLocals - const { url, dispose } = source.thumbnail.getUrl(); - - // Get properties - const properties: Record = { - title: activeManifest.title, - format: activeManifest.format, - claimGenerator: activeManifest.claimGenerator.split('(')[0]?.trim(), - producer: selectProducer(activeManifest)?.name ?? 'Unknown', - thumbnail: ``, - ingredients: (activeManifest.ingredients ?? []) - .map((i) => i.title) - .join(', '), - signatureIssuer: activeManifest.signatureInfo?.issuer, - signatureDate: activeManifest.signatureInfo?.time - ? parseISO(activeManifest.signatureInfo.time).toString() - : 'No date available', - }; - - output = Object.keys(properties).map((key) => { - return ` - - ${key} - ${properties[key]} - - `; - }); - } else { - output.push(` - - No provenance data found - - `); - } - - document.querySelector('#results tbody')!.innerHTML = output.join(''); -})(); From 3b136025d0b93fe6fe50270146bb3c8fefdb1190 Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Wed, 12 Feb 2025 15:02:06 -0800 Subject: [PATCH 20/35] Add examples to gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index d07e58e0..492f5261 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,8 @@ .docusaurus .cache-loader /docs/js-sdk/api +/docs/examples/quickstart/* +/docs/examples/view-manifest/* /docs/c2patool/*.md /docs/c2patool/docs/*.md /docs/c2pa-node/*.md From 171b336af66fce1e2f3e32cd7f6d4a13023d5619 Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Wed, 12 Feb 2025 15:07:51 -0800 Subject: [PATCH 21/35] Try again to remove spurious {' '} characters --- docs/tasks/build.mdx | 4 ---- docs/tasks/get-resources.mdx | 4 ---- docs/tasks/read.mdx | 4 ---- docs/tasks/setup.mdx | 10 +++------- 4 files changed, 3 insertions(+), 19 deletions(-) diff --git a/docs/tasks/build.mdx b/docs/tasks/build.mdx index 3818263f..e63016f2 100644 --- a/docs/tasks/build.mdx +++ b/docs/tasks/build.mdx @@ -22,28 +22,24 @@ You can't currently attach a manifest to an asset and sign the claim using the J -{' '} -{' '} -{' '} -{' '} diff --git a/docs/tasks/get-resources.mdx b/docs/tasks/get-resources.mdx index 1fa248a1..94e2bcbc 100644 --- a/docs/tasks/get-resources.mdx +++ b/docs/tasks/get-resources.mdx @@ -18,28 +18,24 @@ Manifest data can include binary resources such as thumbnail and icon images whi -{' '} -{' '} -{' '} -{' '} diff --git a/docs/tasks/read.mdx b/docs/tasks/read.mdx index aed5139a..97ba381a 100644 --- a/docs/tasks/read.mdx +++ b/docs/tasks/read.mdx @@ -17,28 +17,24 @@ import RustRead from './includes/_rust-read.md'; -{' '} -{' '} -{' '} -{' '} diff --git a/docs/tasks/setup.mdx b/docs/tasks/setup.mdx index f631ec91..ceb38d0d 100644 --- a/docs/tasks/setup.mdx +++ b/docs/tasks/setup.mdx @@ -16,36 +16,32 @@ import RustSetup from './includes/_rust-setup.md'; -{' '} -{' '} -{' '} -{' '} - + - + - +
From 6cd10724ba97ae67647d96d9b14199d127f4a754 Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Fri, 14 Feb 2025 10:25:09 -0800 Subject: [PATCH 22/35] Update gitignore to properly ignore external examples pulled down by fetch-readme script --- .gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 492f5261..6fabebf6 100644 --- a/.gitignore +++ b/.gitignore @@ -8,8 +8,8 @@ .docusaurus .cache-loader /docs/js-sdk/api -/docs/examples/quickstart/* -/docs/examples/view-manifest/* +/docs/js-sdk/examples/* +/docs/js-sdk/examples/* /docs/c2patool/*.md /docs/c2patool/docs/*.md /docs/c2pa-node/*.md From f346293174cf77641d30299324863fce8770fe89 Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Fri, 14 Feb 2025 10:48:30 -0800 Subject: [PATCH 23/35] Add query param, other small edits --- docs/tasks/build.mdx | 4 ++-- docs/tasks/get-resources.mdx | 2 +- docs/tasks/includes/_cpp-build.md | 1 + docs/tasks/includes/_cpp-setup.md | 2 +- docs/tasks/includes/_js-get-resources.md | 2 ++ docs/tasks/includes/_js-setup.md | 2 +- docs/tasks/includes/_node-build.md | 1 + docs/tasks/includes/_node-get-resources.md | 2 +- docs/tasks/includes/_node-setup.md | 2 +- docs/tasks/includes/_python-build.md | 2 ++ docs/tasks/includes/_python-get-resources.md | 4 +++- docs/tasks/includes/_python-setup.md | 2 +- docs/tasks/includes/_rust-build.md | 2 +- docs/tasks/includes/_rust-get-resources.md | 4 +++- docs/tasks/read.mdx | 2 +- docs/tasks/setup.mdx | 2 +- 16 files changed, 23 insertions(+), 13 deletions(-) diff --git a/docs/tasks/build.mdx b/docs/tasks/build.mdx index e63016f2..4095a3e4 100644 --- a/docs/tasks/build.mdx +++ b/docs/tasks/build.mdx @@ -12,11 +12,11 @@ import NodeBuild from './includes/_node-build.md'; import CppBuild from './includes/_cpp-build.md'; import RustBuild from './includes/_rust-build.md'; - + -You can't currently attach a manifest to an asset and sign the claim using the JavaScript library. +You can't currently attach a manifest to an asset and sign the claim using the JavaScript library. You need to use a language that runs on the "back-end," such as Python, Node.js, C++, or Rust. diff --git a/docs/tasks/get-resources.mdx b/docs/tasks/get-resources.mdx index 94e2bcbc..37c7547e 100644 --- a/docs/tasks/get-resources.mdx +++ b/docs/tasks/get-resources.mdx @@ -14,7 +14,7 @@ import RustGetResources from './includes/_rust-get-resources.md'; Manifest data can include binary resources such as thumbnail and icon images which are referenced by JUMBF URIs in manifest data. - + diff --git a/docs/tasks/includes/_cpp-build.md b/docs/tasks/includes/_cpp-build.md index b3d82de8..04f27f6e 100644 --- a/docs/tasks/includes/_cpp-build.md +++ b/docs/tasks/includes/_cpp-build.md @@ -1,3 +1,4 @@ +This is an example of how to assign a manifest to an asset and sign the claim using C++: ```cpp const std::string manifest_json = R"{ diff --git a/docs/tasks/includes/_cpp-setup.md b/docs/tasks/includes/_cpp-setup.md index ccc92326..c0c6853a 100644 --- a/docs/tasks/includes/_cpp-setup.md +++ b/docs/tasks/includes/_cpp-setup.md @@ -1,4 +1,4 @@ -To setup the C++ library: +This is how to set up your code to use the C++ library: ```cpp #include diff --git a/docs/tasks/includes/_js-get-resources.md b/docs/tasks/includes/_js-get-resources.md index 12b561df..4c5d7162 100644 --- a/docs/tasks/includes/_js-get-resources.md +++ b/docs/tasks/includes/_js-get-resources.md @@ -1,3 +1,5 @@ +The example below shows how to get resources from manifest data using the JavaScript library. + ```js import { createC2pa, selectProducer } from 'c2pa'; import wasmSrc from 'c2pa/dist/assets/wasm/toolkit_bg.wasm?url'; diff --git a/docs/tasks/includes/_js-setup.md b/docs/tasks/includes/_js-setup.md index 6056f054..850db022 100644 --- a/docs/tasks/includes/_js-setup.md +++ b/docs/tasks/includes/_js-setup.md @@ -1,4 +1,4 @@ -This is how to setup the JavaScript library: +This is how to set up your code to use the JavaScript library: ```js const version = '0.24.2'; diff --git a/docs/tasks/includes/_node-build.md b/docs/tasks/includes/_node-build.md index e848396a..14fa6e5e 100644 --- a/docs/tasks/includes/_node-build.md +++ b/docs/tasks/includes/_node-build.md @@ -1,3 +1,4 @@ +This is an example of how to assign a manifest to an asset and sign the claim using Node.js: ```ts import { ManifestBuilder } from 'c2pa-node'; diff --git a/docs/tasks/includes/_node-get-resources.md b/docs/tasks/includes/_node-get-resources.md index 109f33f0..422062ba 100644 --- a/docs/tasks/includes/_node-get-resources.md +++ b/docs/tasks/includes/_node-get-resources.md @@ -1,4 +1,4 @@ -This is how to get resources from a manifest using Node.js. +The example below shows how to get resources from manifest data using the Node.js library. ```js // TBD diff --git a/docs/tasks/includes/_node-setup.md b/docs/tasks/includes/_node-setup.md index 2c69ca83..85f8c9d4 100644 --- a/docs/tasks/includes/_node-setup.md +++ b/docs/tasks/includes/_node-setup.md @@ -1,4 +1,4 @@ -This is how to setup the Node.js library. +This is how to set up your code to use the Node.js library. ```js import { createC2pa } from 'c2pa-node'; diff --git a/docs/tasks/includes/_python-build.md b/docs/tasks/includes/_python-build.md index 26045cdb..d3569fd3 100644 --- a/docs/tasks/includes/_python-build.md +++ b/docs/tasks/includes/_python-build.md @@ -1,4 +1,6 @@ +This is an example of how to assign a manifest to an asset and sign the claim using Python. + Use a `Builder` object to add a manifest to an asset. ```python diff --git a/docs/tasks/includes/_python-get-resources.md b/docs/tasks/includes/_python-get-resources.md index d8d9d925..1fe26259 100644 --- a/docs/tasks/includes/_python-get-resources.md +++ b/docs/tasks/includes/_python-get-resources.md @@ -1,7 +1,9 @@ +The example below shows how to get resources from manifest data using the Python library. + Retrieve binary resources such as thumbnails from the manifest data, use the `resource_to_stream` or `resource_to_file` methods using the associated `identifier` field values and a `uri`. -NOTE: Need to add example of using `reader.resource_to_stream()`. +_NOTE: Need to add example of using `reader.resource_to_stream()`._ ```python try: diff --git a/docs/tasks/includes/_python-setup.md b/docs/tasks/includes/_python-setup.md index fe695faf..54b0a6d1 100644 --- a/docs/tasks/includes/_python-setup.md +++ b/docs/tasks/includes/_python-setup.md @@ -1,4 +1,4 @@ -This is how to setup the Python library. +This is how to set up your code to use the Python library. ```python # Import the C2PA Python package diff --git a/docs/tasks/includes/_rust-build.md b/docs/tasks/includes/_rust-build.md index 4a2a0825..4a569420 100644 --- a/docs/tasks/includes/_rust-build.md +++ b/docs/tasks/includes/_rust-build.md @@ -1,4 +1,4 @@ -This is how to attach and sign a manifest using Rust. +This is an example of how to assign a manifest to an asset and sign the claim using Rust. This example is from [`c2pa-rs/sdk/examples/v2api.rs`](https://github.com/contentauth/c2pa-rs/blob/main/sdk/examples/v2api.rs#L88C5-L134C1): diff --git a/docs/tasks/includes/_rust-get-resources.md b/docs/tasks/includes/_rust-get-resources.md index 145d15b4..52347b94 100644 --- a/docs/tasks/includes/_rust-get-resources.md +++ b/docs/tasks/includes/_rust-get-resources.md @@ -1,4 +1,6 @@ -This is how to get resources using Rust. +The example below shows how to get resources from manifest data using the Rust library. + +_NOTE: Need to clarify if/how these two code examples work together._ This is from [`resource_to_stream`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html#method.resource_to_stream) API doc: diff --git a/docs/tasks/read.mdx b/docs/tasks/read.mdx index 97ba381a..deb955d2 100644 --- a/docs/tasks/read.mdx +++ b/docs/tasks/read.mdx @@ -13,7 +13,7 @@ import NodeRead from './includes/_node-read.md'; import CppRead from './includes/_cpp-read.md'; import RustRead from './includes/_rust-read.md'; - + diff --git a/docs/tasks/setup.mdx b/docs/tasks/setup.mdx index ceb38d0d..9ca14193 100644 --- a/docs/tasks/setup.mdx +++ b/docs/tasks/setup.mdx @@ -12,7 +12,7 @@ import NodeSetup from './includes/_node-setup.md'; import CppSetup from './includes/_cpp-setup.md'; import RustSetup from './includes/_rust-setup.md'; - + From 561ac54f2c36268aa29deb21746b3715df60c8be Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Fri, 14 Feb 2025 14:42:47 -0800 Subject: [PATCH 24/35] Comments from Tania --- docs/tasks/includes/_python-build.md | 30 ++++++++++---------- docs/tasks/includes/_python-get-resources.md | 20 ++++++------- docs/tasks/includes/_python-read.md | 4 ++- 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/docs/tasks/includes/_python-build.md b/docs/tasks/includes/_python-build.md index d3569fd3..6342fd14 100644 --- a/docs/tasks/includes/_python-build.md +++ b/docs/tasks/includes/_python-build.md @@ -5,30 +5,30 @@ Use a `Builder` object to add a manifest to an asset. ```python try: - # Define a function to sign the claim bytes - # In this case we are using a pre-defined sign_ps256 method, passing in our private cert - # Normally this cert would be kept safe in some other location + # Define a function to sign the claim bytes. + # In this case we are using a pre-defined sign_ps256 method, passing in our private cert. + # Normally this cert would be kept safe in some other location. def private_sign(data: bytes) -> bytes: return sign_ps256(data, "tests/fixtures/ps256.pem") - # read our public certs into memory + # Read our public certs into memory. certs = open(data_dir + "ps256.pub", "rb").read() - # Create a signer from the private signer, certs and a time stamp service url + # Create a signer from the private signer, certs and a time stamp service URL. signer = create_signer(private_sign, SigningAlg.PS256, certs, "http://timestamp.digicert.com") # Create a builder add a thumbnail resource and an ingredient file. builder = Builder(manifest_json) - # Add the resource from a stream + # Add the resource from a stream. a_thumbnail_jpg_stream = open("tests/fixtures/A_thumbnail.jpg", "rb") builder.add_resource("image/jpeg", a_thumbnail_jpg_stream) - # Add the resource from a file + # Add the resource from a file. # The URI provided here, "thumbnail", must match an identifier in the manifest definition. builder.add_resource_file("thumbnail", "tests/fixtures/A_thumbnail.jpg") - # Define an ingredient, in this case a parent ingredient named A.jpg, with a thumbnail + # Define an ingredient, in this case a parent ingredient named A.jpg, with a thumbnail. ingredient_json = { "title": "A.jpg", "relationship": "parentOf", # "parentOf", "componentOf" or "inputTo" @@ -38,19 +38,19 @@ try: } } - # Add the ingredient from a stream + # Add the ingredient from a stream. a_jpg_stream = open("tests/fixtures/A.jpg", "rb") builder.add_ingredient("image/jpeg", a_jpg_stream) - # At this point we could archive or unarchive our Builder to continue later. - # In this example we use a bytearray for the archive stream. - # all ingredients and resources will be saved in the archive + # At this point archive or unarchive Builder to continue later. + # This example uses a bytearray for the archive stream. + # All ingredients and resources are saved in the archive. archive = io.BytesIO(bytearray()) builder.to_archive(archive) archive.seek() builder = builder.from_archive(archive) - # Sign the builder with a stream and output it to a stream + # Sign the builder with a stream and output it to a stream. # This returns the binary manifest data that could be uploaded to cloud storage. input_stream = open("tests/fixtures/A.jpg", "rb") output_stream = open("target/out.jpg", "wb") @@ -63,8 +63,8 @@ except Exception as err: FROM SIGNING: ```python -request_data = ... # This is the asset being signed -content_type = ... # MIME type of the asset +request_data = ... # This is the asset being signed. +content_type = ... # MIME type of the asset. manifest = json.dumps({ "title": "image.jpg", diff --git a/docs/tasks/includes/_python-get-resources.md b/docs/tasks/includes/_python-get-resources.md index 1fe26259..773e05da 100644 --- a/docs/tasks/includes/_python-get-resources.md +++ b/docs/tasks/includes/_python-get-resources.md @@ -5,19 +5,19 @@ Retrieve binary resources such as thumbnails from the manifest data, use the `re _NOTE: Need to add example of using `reader.resource_to_stream()`._ -```python +```py try: -# Create a reader from a file path -reader = c2pa.Reader.from_file("path/to/media_file.jpg") + # Create a reader from a file path + reader = c2pa.Reader.from_file("path/to/media_file.jpg") -# Get the active manifest. -manifest = reader.get_active_manifest() -if manifest != None: + # Get the active manifest. + manifest = reader.get_active_manifest() + if manifest != None: - # get the uri to the manifest's thumbnail and write it to a file - uri = manifest["thumbnail"]["identifier"] - reader.resource_to_file(uri, "thumbnail_v2.jpg") + # get the uri to the manifest's thumbnail and write it to a file + uri = manifest["thumbnail"]["identifier"] + reader.resource_to_file(uri, "thumbnail_v2.jpg") except Exception as err: - print(err) + print(err) ``` \ No newline at end of file diff --git a/docs/tasks/includes/_python-read.md b/docs/tasks/includes/_python-read.md index ea990120..64c659f4 100644 --- a/docs/tasks/includes/_python-read.md +++ b/docs/tasks/includes/_python-read.md @@ -1,5 +1,7 @@ -Use the `Reader` object to read manifest data from a file or stream and perform validation on the manifest store. Use the `json()` method to return a JSON manifest report; If there are validation errors, the report includes a `validation_status` field. +Use the `Reader` object to read manifest data from a file or stream and perform validation on the manifest store. + +Use the `json()` method to return a JSON manifest report; If there are validation errors, the report includes a `validation_status` field. An asset file may contain many manifests in a manifest store. The most recent manifest is identified by the value of the `active_manifest` field in the manifests map. From 00c670282b26d40be11101812267101a1ab574b2 Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Fri, 14 Feb 2025 14:59:56 -0800 Subject: [PATCH 25/35] Comments from ahalle and tmathern --- docs/tasks/includes/_python-get-resources.md | 4 ++-- docs/tasks/includes/_python-read.md | 4 ++-- docs/tasks/includes/_python-setup.md | 8 ++++---- docs/tasks/includes/_rust-read.md | 2 +- docs/tasks/includes/_rust-setup.md | 5 ++++- 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/docs/tasks/includes/_python-get-resources.md b/docs/tasks/includes/_python-get-resources.md index 773e05da..f2200c4c 100644 --- a/docs/tasks/includes/_python-get-resources.md +++ b/docs/tasks/includes/_python-get-resources.md @@ -7,14 +7,14 @@ _NOTE: Need to add example of using `reader.resource_to_stream()`._ ```py try: - # Create a reader from a file path + # Create a reader from a file path. reader = c2pa.Reader.from_file("path/to/media_file.jpg") # Get the active manifest. manifest = reader.get_active_manifest() if manifest != None: - # get the uri to the manifest's thumbnail and write it to a file + # get the uri to the manifest's thumbnail and write it to a file. uri = manifest["thumbnail"]["identifier"] reader.resource_to_file(uri, "thumbnail_v2.jpg") diff --git a/docs/tasks/includes/_python-read.md b/docs/tasks/includes/_python-read.md index 64c659f4..7f7a416f 100644 --- a/docs/tasks/includes/_python-read.md +++ b/docs/tasks/includes/_python-read.md @@ -7,10 +7,10 @@ An asset file may contain many manifests in a manifest store. The most recent ma ```py try: - # Create a reader from a file path + # Create a reader from a file path. reader = c2pa.Reader.from_file("path/to/media_file.jpg") - # Alternatively, create a reader from a stream + # Alternatively, create a reader from a stream. stream = open("path/to/media_file.jpg", "rb") reader = c2pa.Reader("image/jpeg", stream) diff --git a/docs/tasks/includes/_python-setup.md b/docs/tasks/includes/_python-setup.md index 54b0a6d1..01388bfa 100644 --- a/docs/tasks/includes/_python-setup.md +++ b/docs/tasks/includes/_python-setup.md @@ -1,21 +1,21 @@ This is how to set up your code to use the Python library. ```python -# Import the C2PA Python package +# Import the C2PA Python package. from c2pa import * -# Import standard general-purpose packages +# Import standard general-purpose packages. import os import io import logging import json import base64 -# Import web packages used in example implementation +# Import web packages used in example implementation. from flask import Flask, request, abort from flask_cors import CORS from waitress import serve -# Import AWS SDK package (to use KMS) +# Import AWS SDK package (to use KMS). import boto3 ``` diff --git a/docs/tasks/includes/_rust-read.md b/docs/tasks/includes/_rust-read.md index f164268a..d44ea756 100644 --- a/docs/tasks/includes/_rust-read.md +++ b/docs/tasks/includes/_rust-read.md @@ -18,8 +18,8 @@ Use [`from_stream`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html#method.f ```rust use std::io::Cursor; - use c2pa::Reader; + let mut stream = Cursor::new(include_bytes!("../tests/fixtures/CA.jpg")); let reader = Reader::from_stream("image/jpeg", stream).unwrap(); println!("{}", reader.json()); diff --git a/docs/tasks/includes/_rust-setup.md b/docs/tasks/includes/_rust-setup.md index f0e8e9cd..017826cb 100644 --- a/docs/tasks/includes/_rust-setup.md +++ b/docs/tasks/includes/_rust-setup.md @@ -1,5 +1,9 @@ This is how to setup your code to use the Rust library. +:::tip +The files used in this example are in the C2PA Rust library [`sdk/tests/fixtures`](https://github.com/contentauth/c2pa-rs/tree/main/sdk/tests/fixtures) directory. +::: + ```rust use std::{ io::{Cursor, Write}, @@ -8,7 +12,6 @@ use std::{ use anyhow::Result; use c2pa::{settings::load_settings_from_str, Builder, CallbackSigner, Reader}; - use c2pa_crypto::raw_signature::SigningAlg; use serde_json::json; From 18d1c4b27fd37d302d34742d05ee9d792fb7243b Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Thu, 20 Feb 2025 16:02:29 -0800 Subject: [PATCH 26/35] Remove duplicate example --- docs/tasks/includes/_python-build.md | 48 ---------------------------- 1 file changed, 48 deletions(-) diff --git a/docs/tasks/includes/_python-build.md b/docs/tasks/includes/_python-build.md index 6342fd14..19338a0b 100644 --- a/docs/tasks/includes/_python-build.md +++ b/docs/tasks/includes/_python-build.md @@ -58,52 +58,4 @@ try: except Exception as err: print(err) -``` - -FROM SIGNING: - -```python -request_data = ... # This is the asset being signed. -content_type = ... # MIME type of the asset. - -manifest = json.dumps({ - "title": "image.jpg", - "format": "image/jpeg", - "claim_generator_info": [ - { - "name": "Documentation example", - "version": "0.0.1" - } - ], - "assertions": [ - { - "label": "c2pa.actions", - "data": { - "actions": [ - { - "action": "c2pa.edited", - "softwareAgent": { - "name": "C2PA Python Example", - "version": "0.1.0" - } - } - ] - } - } - ] -}) - -try: - builder = Builder(manifest) - - signer = create_signer(kms_sign, signing_alg, - cert_chain, timestamp_url) - - result = io.BytesIO(b"") - builder.sign(signer, content_type, io.BytesIO(request_data), result) - - return result.getvalue() -except Exception as e: - logging.error(e) - abort(500, description=e) ``` \ No newline at end of file From 832b7e9cd5828ae2e731d9c7a1a5683195afa4e1 Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Fri, 21 Feb 2025 12:33:56 -0800 Subject: [PATCH 27/35] Eli comments --- docs/tasks/includes/_js-setup.md | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/docs/tasks/includes/_js-setup.md b/docs/tasks/includes/_js-setup.md index 850db022..f3857486 100644 --- a/docs/tasks/includes/_js-setup.md +++ b/docs/tasks/includes/_js-setup.md @@ -1,7 +1,7 @@ This is how to set up your code to use the JavaScript library: ```js -const version = '0.24.2'; +const version = '0.27.1'; const sampleImage = ''; import { createC2pa } from 'https://cdn.jsdelivr.net/npm/c2pa@${version}/+esm'; @@ -14,4 +14,31 @@ import { createC2pa } from 'https://cdn.jsdelivr.net/npm/c2pa@${version}/+esm'; workerSrc: 'https://cdn.jsdelivr.net/npm/c2pa@${version}/dist/c2pa.worker.min.js', }); +``` + +If you installed the package locally, for example from npm, then its simply: + +```js +import { createC2pa } from 'c2pa'; +``` + +Additionally, the `wasmSrc` and `workerSrc` objects need to available as static assets that can be fetched at runtime. The best way to do that depends on the build system. For example, using [Vite](https://vite.dev/guide/assets#explicit-url-imports), as shown in the [minimal-ts-vite example](https://github.com/contentauth/c2pa-js/blob/main/examples/minimal-ts-vite/examples/active-manifest/main.ts): + + +```js +import wasmSrc from 'c2pa/dist/assets/wasm/toolkit_bg.wasm?url'; +import workerSrc from 'c2pa/dist/c2pa.worker.js?url'; +import { parseISO } from 'date-fns'; + +(async () => { + let output: string[] = []; + + const c2pa = await createC2pa({ + wasmSrc, + workerSrc, + }); + + ... + +})(); ``` \ No newline at end of file From 133d4ba9105370c6d5cb092db0dac05347836083 Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Fri, 21 Feb 2025 12:35:31 -0800 Subject: [PATCH 28/35] Fix code example --- docs/tasks/includes/_js-setup.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/tasks/includes/_js-setup.md b/docs/tasks/includes/_js-setup.md index f3857486..ced981ee 100644 --- a/docs/tasks/includes/_js-setup.md +++ b/docs/tasks/includes/_js-setup.md @@ -14,6 +14,10 @@ import { createC2pa } from 'https://cdn.jsdelivr.net/npm/c2pa@${version}/+esm'; workerSrc: 'https://cdn.jsdelivr.net/npm/c2pa@${version}/dist/c2pa.worker.min.js', }); + + ... + +})(); ``` If you installed the package locally, for example from npm, then its simply: @@ -39,6 +43,6 @@ import { parseISO } from 'date-fns'; }); ... - + })(); ``` \ No newline at end of file From 404b6b961e3928803b93df5d256577207190ba5c Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Mon, 10 Mar 2025 14:07:24 -0700 Subject: [PATCH 29/35] Remove content for Node.js and replace with WIP note --- docs/tasks/build.mdx | 5 ++++- docs/tasks/get-resources.mdx | 5 ++++- docs/tasks/includes/_node-wip.md | 3 +++ docs/tasks/read.mdx | 5 ++++- docs/tasks/setup.mdx | 5 ++++- 5 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 docs/tasks/includes/_node-wip.md diff --git a/docs/tasks/build.mdx b/docs/tasks/build.mdx index 4095a3e4..2d4d0e3c 100644 --- a/docs/tasks/build.mdx +++ b/docs/tasks/build.mdx @@ -8,7 +8,10 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import PythonBuild from './includes/_python-build.md'; +/* import NodeBuild from './includes/_node-build.md'; +*/ +import NodeWIP from './includes/_node-wip.md'; import CppBuild from './includes/_cpp-build.md'; import RustBuild from './includes/_rust-build.md'; @@ -28,7 +31,7 @@ You can't currently attach a manifest to an asset and sign the claim using the J - + diff --git a/docs/tasks/get-resources.mdx b/docs/tasks/get-resources.mdx index 37c7547e..cb55f1f8 100644 --- a/docs/tasks/get-resources.mdx +++ b/docs/tasks/get-resources.mdx @@ -8,7 +8,10 @@ import TabItem from '@theme/TabItem'; import JSGetResources from './includes/_js-get-resources.md'; import PythonGetResources from './includes/_python-get-resources.md'; +/* import NodeGetResources from './includes/_node-get-resources.md'; +*/ +import NodeWIP from './includes/_node-wip.md'; import CppGetResources from './includes/_cpp-get-resources.md'; import RustGetResources from './includes/_rust-get-resources.md'; @@ -30,7 +33,7 @@ Manifest data can include binary resources such as thumbnail and icon images whi - + diff --git a/docs/tasks/includes/_node-wip.md b/docs/tasks/includes/_node-wip.md new file mode 100644 index 00000000..ca2cd717 --- /dev/null +++ b/docs/tasks/includes/_node-wip.md @@ -0,0 +1,3 @@ +:::note +The Node.js library is being revised. The documentation will be updated as soon as possible with the latest changes. +::: \ No newline at end of file diff --git a/docs/tasks/read.mdx b/docs/tasks/read.mdx index deb955d2..9c20c7bf 100644 --- a/docs/tasks/read.mdx +++ b/docs/tasks/read.mdx @@ -9,7 +9,10 @@ import TabItem from '@theme/TabItem'; import JSRead from './includes/_js-read.md'; import PythonRead from './includes/_python-read.md'; +/* import NodeRead from './includes/_node-read.md'; +*/ +import NodeWIP from './includes/_node-wip.md'; import CppRead from './includes/_cpp-read.md'; import RustRead from './includes/_rust-read.md'; @@ -29,7 +32,7 @@ import RustRead from './includes/_rust-read.md'; - + diff --git a/docs/tasks/setup.mdx b/docs/tasks/setup.mdx index 9ca14193..b272ae94 100644 --- a/docs/tasks/setup.mdx +++ b/docs/tasks/setup.mdx @@ -8,7 +8,10 @@ import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import JSSetup from './includes/_js-setup.md'; import PythonSetup from './includes/_python-setup.md'; +/* import NodeSetup from './includes/_node-setup.md'; +*/ +import NodeWIP from './includes/_node-wip.md'; import CppSetup from './includes/_cpp-setup.md'; import RustSetup from './includes/_rust-setup.md'; @@ -28,7 +31,7 @@ import RustSetup from './includes/_rust-setup.md'; - + From 9ec18a5c29ad4b6214e500e9f51933e2f71a7915 Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Mon, 10 Mar 2025 14:13:10 -0700 Subject: [PATCH 30/35] Add Tania's comments for future reference --- docs/tasks/includes/_node-build.md | 4 +--- docs/tasks/includes/_node-setup.md | 10 +++++++++- docs/tasks/includes/_python-read.md | 6 +++++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/docs/tasks/includes/_node-build.md b/docs/tasks/includes/_node-build.md index 14fa6e5e..72d1e2e5 100644 --- a/docs/tasks/includes/_node-build.md +++ b/docs/tasks/includes/_node-build.md @@ -29,13 +29,11 @@ const manifest = new ManifestBuilder({ }); ``` -FROM SIGNING: - Use the `c2pa.sign()` method to sign an ingredient, either locally if you have a signing certificate and key available, or by using a remote signing API. ## Signing a stream -If you have an asset file's data loaded into memory, you can sign the the asset using the loaded stream (buffer). +If you have an asset file's data loaded into a stream, you can use it to sign the asset **NOTE**: Signing using a stream is currently supported only for `image/jpeg` and `image/png` data. For all other file types, use the [file-based approach](#signing-files) . diff --git a/docs/tasks/includes/_node-setup.md b/docs/tasks/includes/_node-setup.md index 85f8c9d4..763985f6 100644 --- a/docs/tasks/includes/_node-setup.md +++ b/docs/tasks/includes/_node-setup.md @@ -5,4 +5,12 @@ import { createC2pa } from 'c2pa-node'; import { readFile } from 'node:fs/promises'; const c2pa = createC2pa(); -``` \ No newline at end of file +``` + + \ No newline at end of file diff --git a/docs/tasks/includes/_python-read.md b/docs/tasks/includes/_python-read.md index 7f7a416f..8f9b8055 100644 --- a/docs/tasks/includes/_python-read.md +++ b/docs/tasks/includes/_python-read.md @@ -19,4 +19,8 @@ try: except Exception as err: print(err) -``` \ No newline at end of file +``` + + \ No newline at end of file From 90333fedee9319e8ce4fdbada3889ee9d891a5d1 Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Mon, 10 Mar 2025 14:42:42 -0700 Subject: [PATCH 31/35] Comments from Eli --- docs/tasks/includes/_js-get-resources.md | 4 ++++ docs/tasks/includes/_js-read.md | 3 +++ docs/tasks/includes/_js-setup.md | 6 +++++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/tasks/includes/_js-get-resources.md b/docs/tasks/includes/_js-get-resources.md index 4c5d7162..4dd02de4 100644 --- a/docs/tasks/includes/_js-get-resources.md +++ b/docs/tasks/includes/_js-get-resources.md @@ -1,3 +1,7 @@ +:::note +That JavaScript library is being extensively revised so the APIs used here may change in the near future. +::: + The example below shows how to get resources from manifest data using the JavaScript library. ```js diff --git a/docs/tasks/includes/_js-read.md b/docs/tasks/includes/_js-read.md index e2d42ffd..7ddf4d98 100644 --- a/docs/tasks/includes/_js-read.md +++ b/docs/tasks/includes/_js-read.md @@ -1,3 +1,6 @@ +:::note +That JavaScript library is being extensively revised so the APIs used here may change in the near future. +::: Use [`c2pa.read`](../../docs/js-sdk/api/c2pa.c2pa#methods) to read manifest data from an asset; if the asset has a C2PA manifest and was processed without errors, the returned [`c2paReadResult`](../../docs/js-sdk/api/c2pa.c2pareadresult) contains a [`manifestStore`](../../docs/js-sdk/api/c2pa.c2pareadresult.manifeststore) object with several useful properties: diff --git a/docs/tasks/includes/_js-setup.md b/docs/tasks/includes/_js-setup.md index ced981ee..56885a08 100644 --- a/docs/tasks/includes/_js-setup.md +++ b/docs/tasks/includes/_js-setup.md @@ -1,3 +1,7 @@ +:::note +That JavaScript library is being extensively revised so the APIs used here may change in the near future. +::: + This is how to set up your code to use the JavaScript library: ```js @@ -20,7 +24,7 @@ import { createC2pa } from 'https://cdn.jsdelivr.net/npm/c2pa@${version}/+esm'; })(); ``` -If you installed the package locally, for example from npm, then its simply: +If you installed the package locally, for example from npm, then it's simply: ```js import { createC2pa } from 'c2pa'; From 92a823735b73c2f471b163b55a88c4f82c90678c Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Thu, 13 Mar 2025 14:50:00 -0700 Subject: [PATCH 32/35] Remove Python streaming example code, just use file i/o. --- docs/tasks/includes/_python-read.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/tasks/includes/_python-read.md b/docs/tasks/includes/_python-read.md index 8f9b8055..4020ef6e 100644 --- a/docs/tasks/includes/_python-read.md +++ b/docs/tasks/includes/_python-read.md @@ -10,10 +10,6 @@ try: # Create a reader from a file path. reader = c2pa.Reader.from_file("path/to/media_file.jpg") - # Alternatively, create a reader from a stream. - stream = open("path/to/media_file.jpg", "rb") - reader = c2pa.Reader("image/jpeg", stream) - # Print the JSON for a manifest. print("manifest store:", reader.json()) From ddd71f1ac644e7f38aa00de144cc76e9ee7f9598 Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Thu, 24 Jul 2025 14:44:51 -0700 Subject: [PATCH 33/35] Kick to try to get a doc build From 44d3f61fc234f20269dddd3ef902e0a682fab821 Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Thu, 18 Sep 2025 16:12:36 -0700 Subject: [PATCH 34/35] Switch around section titles --- docs/manifest/understanding.md | 11 +++++------ sidebars.js | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/manifest/understanding.md b/docs/manifest/understanding.md index 8831d335..0aed839a 100644 --- a/docs/manifest/understanding.md +++ b/docs/manifest/understanding.md @@ -1,13 +1,11 @@ --- id: understanding-manifest -title: Working with manifests +title: Understanding manifests --- -## Understanding manifests - The concept of a _manifest_ is central to how Content Credentials work. -A collection of manifests (known as a _manifest store_) is attached to an asset. Each manifest contains information about the provenance of the asset. Creating or editing an asset using a C2PA-compliant device or tool (for example Adobe Photoshop) adds a new manifest to the manifest store. +A collection of manifests (known as a _manifest store_) is attached to an asset. Each manifest contains information about the provenance of the asset. Creating or editing an asset using a C2PA-compliant device or tool (for example, Adobe Photoshop) adds a new manifest to the manifest store. The manifests in the manifest store are not ordered, but the most-recently added manifest is the _active manifest_. The active manifest has content bindings that can be validated with the asset—that is, it's hashed with the asset to ensure its validity. @@ -21,6 +19,7 @@ A manifest store can be either: In addition, an asset can have a [external manifest store](https://spec.c2pa.org/specifications/specifications/2.2/specs/C2PA_Specification.html#_external_manifests), linked from the asset's metadata, as detailed in the [C2PA specification](https://spec.c2pa.org/specifications/specifications/2.2/specs/C2PA_Specification.html#_embedding_a_reference_to_an_external_manifest). This is sometimes referred to as a _remote manifest_. To determine if an asset has Content Credentials, the SDK checks for the presence of a manifest store in this order: + 1. In the asset metadata 1. In a sidebar file. 1. In a remote manifest. @@ -28,10 +27,10 @@ To determine if an asset has Content Credentials, the SDK checks for the presenc So, for example to see if `foo.jpg` has Content Credentials, the SDK first checks if there's a manifest store in the file itself, then looks for a sidecar file (`foo.c2pa` in the same directory), and finally looks in the asset's metadata for a reference to a remote manifest store. :::info -Currently, only Adobe has implemented a Content Credentials cloud service to provide access to remote manifest stores, but in theory anyone could do so to provide a publicly-accessible network location for manifests. +Currently, only Adobe has implemented a Content Credentials cloud service to provide access to remote manifest stores, but in theory anyone could do so to provide a publicly-accessible network location for manifests. ::: -## Binary versus JSON manifest formats +## Binary versus JSON manifest The manifest as described in the C2PA specification is a binary structure in JPEG universal metadata box format ([JUMBF](https://www.iso.org/standard/84635.html)) that can include JSON and binary data for things like encryption keys and thumbnail images. diff --git a/sidebars.js b/sidebars.js index 57963a3e..5c945755 100644 --- a/sidebars.js +++ b/sidebars.js @@ -29,7 +29,7 @@ const sidebars = { }, { type: 'category', - label: 'Working with manifests', + label: 'Understanding manifests', link: { type: 'doc', id: 'manifest/understanding-manifest' }, collapsed: true, items: [ From c24e3f3ec2a0f623b35f794de253fe74003f4797 Mon Sep 17 00:00:00 2001 From: Rand McKinney Date: Thu, 18 Sep 2025 16:56:12 -0700 Subject: [PATCH 35/35] Remove setup page and include that code in each example instead --- docs/tasks/includes/_cpp-build.md | 28 +++++++++--- docs/tasks/includes/_cpp-get-resources.md | 34 +++++++++------ docs/tasks/includes/_cpp-read.md | 27 ++++++++---- docs/tasks/includes/_js-get-resources.md | 45 ++++++++++++++++++-- docs/tasks/includes/_js-read.md | 16 ++++++- docs/tasks/includes/_python-build.md | 10 +++++ docs/tasks/includes/_python-get-resources.md | 9 ++++ docs/tasks/includes/_python-read.md | 9 ++++ docs/tasks/includes/_rust-build.md | 11 ++++- docs/tasks/includes/_rust-get-resources.md | 22 +++++++--- docs/tasks/includes/_rust-read.md | 29 ++++++++++++- docs/tasks/index.md | 1 - sidebars.js | 4 -- 13 files changed, 199 insertions(+), 46 deletions(-) diff --git a/docs/tasks/includes/_cpp-build.md b/docs/tasks/includes/_cpp-build.md index 04f27f6e..ece961f5 100644 --- a/docs/tasks/includes/_cpp-build.md +++ b/docs/tasks/includes/_cpp-build.md @@ -1,23 +1,38 @@ This is an example of how to assign a manifest to an asset and sign the claim using C++: ```cpp +#include +#include +#include +#include +#include +#include +#include +#include +#include "c2pa.hpp" +#include "test_signer.hpp" + +using namespace std; +namespace fs = std::filesystem; +using namespace c2pa; + const std::string manifest_json = R"{ "claim_generator": "c2pa_c_test/0.1", "claim_generator_info": [ { - "name": "c2pa-c test", + "name": "c2pa-c example", "version": "0.1" } ], "assertions": [ { - "label": "c2pa.training-mining", + "label": "cawg.training-mining", "data": { "entries": { - "c2pa.ai_generative_training": { "use": "notAllowed" }, - "c2pa.ai_inference": { "use": "notAllowed" }, - "c2pa.ai_training": { "use": "notAllowed" }, - "c2pa.data_mining": { "use": "notAllowed" } + "cawg.ai_generative_training": { "use": "notAllowed" }, + "cawg.ai_inference": { "use": "notAllowed" }, + "cawg.ai_training": { "use": "notAllowed" }, + "cawg.data_mining": { "use": "notAllowed" } } } } @@ -25,5 +40,4 @@ const std::string manifest_json = R"{ }; auto builder = Builder(manifest_json); - ``` \ No newline at end of file diff --git a/docs/tasks/includes/_cpp-get-resources.md b/docs/tasks/includes/_cpp-get-resources.md index 85fa94ed..60bb1591 100644 --- a/docs/tasks/includes/_cpp-get-resources.md +++ b/docs/tasks/includes/_cpp-get-resources.md @@ -1,6 +1,24 @@ This is how to get resources from a manifest using C++. ```cpp +#include +#include +#include +#include +#include +#include +#include +#include +#include "c2pa.hpp" +#include "test_signer.hpp" +#include + +// this example uses nlohmann json for parsing the manifest +using json = nlohmann::json; +using namespace std; +namespace fs = std::filesystem; +using namespace c2pa; + string read_text_file(const fs::path &path) { ifstream file(path); @@ -23,19 +41,6 @@ int main() try { - // load the manifest, certs, and private key - /* Commenting out, because not part of resource reading - string manifest_json = read_text_file(manifest_path).data(); - - string certs = read_text_file(certs_path).data(); - - // create a signer - Signer signer = Signer(&test_signer, Es256, certs, "http://timestamp.digicert.com"); - - auto builder = Builder(manifest_json); - auto manifest_data = builder.sign(image_path, output_path, signer); - */ - // read the new manifest and display the JSON auto reader = Reader(output_path); @@ -56,14 +61,17 @@ int main() cout << "thumbnail written to" << thumbnail_path << endl; } } + catch (c2pa::Exception const &e) { cout << "C2PA Error: " << e.what() << endl; } + catch (runtime_error const &e) { cout << "setup error" << e.what() << endl; } + catch (json::parse_error const &e) { cout << "parse error " << e.what() << endl; diff --git a/docs/tasks/includes/_cpp-read.md b/docs/tasks/includes/_cpp-read.md index 95dac314..e48e63f2 100644 --- a/docs/tasks/includes/_cpp-read.md +++ b/docs/tasks/includes/_cpp-read.md @@ -2,16 +2,25 @@ Use the `read_file` function to read C2PA data from the specified file. This function examines the specified asset file for C2PA data and returns a JSON report if it finds any; it throws exceptions on errors. If there are validation errors, the report includes a `validation_status` field. ```cpp -auto json_store = C2pa::read_file("", "") -``` +#include +#include +#include +#include +#include +#include +#include +#include +#include "c2pa.hpp" +#include "test_signer.hpp" -Where: +using namespace std; +namespace fs = std::filesystem; +using namespace c2pa; -- ``- The asset file to read. -- `` - Optional path to data output directory; If provided, the function extracts any binary resources, such as thumbnails, icons, and C2PA data into that directory. These files are referenced by the identifier fields in the manifest store report. +auto json_store = C2pa::read_file("work/media_file.jpg", "output/data_dir") +``` -For example: +Where: -```cpp -auto json_store = C2pa::read_file("work/media_file.jpg", "output/data_dir") -``` \ No newline at end of file +- `work/media_file.jpg` is the asset file to read. +- `output/data_dir` is the optional path to data output directory; If provided, the function extracts any binary resources, such as thumbnails, icons, and C2PA data into that directory. These files are referenced by the identifier fields in the manifest store report. diff --git a/docs/tasks/includes/_js-get-resources.md b/docs/tasks/includes/_js-get-resources.md index 4dd02de4..56a6bfc8 100644 --- a/docs/tasks/includes/_js-get-resources.md +++ b/docs/tasks/includes/_js-get-resources.md @@ -5,15 +5,25 @@ That JavaScript library is being extensively revised so the APIs used here may c The example below shows how to get resources from manifest data using the JavaScript library. ```js +const version = '0.27.1'; +const sampleImage = 'my_image.jpg' + import { createC2pa, selectProducer } from 'c2pa'; import wasmSrc from 'c2pa/dist/assets/wasm/toolkit_bg.wasm?url'; import workerSrc from 'c2pa/dist/c2pa.worker.js?url'; import { parseISO } from 'date-fns'; - -const sampleImage = - 'https://raw.githubusercontent.com/contentauth/c2pa-js/main/tools/testing/fixtures/images/CAICAI.jpg'; +import { createC2pa } from 'https://cdn.jsdelivr.net/npm/c2pa@${version}/+esm'; (async () => { + // Initialize the c2pa-js SDK + const c2pa = await createC2pa({ + wasmSrc: + 'https://cdn.jsdelivr.net/npm/c2pa@${version}/dist/assets/wasm/toolkit_bg.wasm', + workerSrc: + 'https://cdn.jsdelivr.net/npm/c2pa@${version}/dist/c2pa.worker.min.js', + }); + + let output: string[] = []; const c2pa = await createC2pa({ @@ -54,6 +64,7 @@ const sampleImage = `; }); + } else { output.push(` @@ -63,5 +74,33 @@ const sampleImage = } document.querySelector('#results tbody')!.innerHTML = output.join(''); + })(); +``` + +With an HTML page like this: + +```html + + + + + + + + active-manifest + + + + + + + + + + +
PropertyValue
Loading…
+ + + ``` \ No newline at end of file diff --git a/docs/tasks/includes/_js-read.md b/docs/tasks/includes/_js-read.md index 7ddf4d98..30abbabf 100644 --- a/docs/tasks/includes/_js-read.md +++ b/docs/tasks/includes/_js-read.md @@ -9,6 +9,20 @@ Use [`c2pa.read`](../../docs/js-sdk/api/c2pa.c2pa#methods) to read manifest data - **validationStatus**: A list of any validation errors encountered. See [Validation](../../docs/js-sdk/guides/validation) for more information. ```js +const version = '0.30.14'; +const sampleImage = ''; + +import { createC2pa } from 'https://cdn.jsdelivr.net/npm/c2pa@${version}/+esm'; + +(async () => { + // Initialize the c2pa-js SDK + const c2pa = await createC2pa({ + wasmSrc: + 'https://cdn.jsdelivr.net/npm/c2pa@${version}/dist/assets/wasm/toolkit_bg.wasm', + workerSrc: + 'https://cdn.jsdelivr.net/npm/c2pa@${version}/dist/c2pa.worker.min.js', + }); + try { // Read in image and get a manifest store const { manifestStore } = await c2pa.read(sampleImage); @@ -21,4 +35,4 @@ Use [`c2pa.read`](../../docs/js-sdk/api/c2pa.c2pa#methods) to read manifest data console.error('Error reading image:', err); } })(); -``` \ No newline at end of file +``` diff --git a/docs/tasks/includes/_python-build.md b/docs/tasks/includes/_python-build.md index 19338a0b..b90cfb11 100644 --- a/docs/tasks/includes/_python-build.md +++ b/docs/tasks/includes/_python-build.md @@ -4,6 +4,16 @@ This is an example of how to assign a manifest to an asset and sign the claim us Use a `Builder` object to add a manifest to an asset. ```python +# Import the C2PA Python package. +from c2pa import * + +# Import standard general-purpose packages. +import os +import io +import logging +import json +import base64 + try: # Define a function to sign the claim bytes. # In this case we are using a pre-defined sign_ps256 method, passing in our private cert. diff --git a/docs/tasks/includes/_python-get-resources.md b/docs/tasks/includes/_python-get-resources.md index f2200c4c..833d8660 100644 --- a/docs/tasks/includes/_python-get-resources.md +++ b/docs/tasks/includes/_python-get-resources.md @@ -6,6 +6,15 @@ Retrieve binary resources such as thumbnails from the manifest data, use the `re _NOTE: Need to add example of using `reader.resource_to_stream()`._ ```py +# Import the C2PA Python package. +from c2pa import * + +# Import standard general-purpose packages. +import os +import io +import logging +import json + try: # Create a reader from a file path. reader = c2pa.Reader.from_file("path/to/media_file.jpg") diff --git a/docs/tasks/includes/_python-read.md b/docs/tasks/includes/_python-read.md index 4020ef6e..edf52a4e 100644 --- a/docs/tasks/includes/_python-read.md +++ b/docs/tasks/includes/_python-read.md @@ -6,6 +6,15 @@ Use the `json()` method to return a JSON manifest report; If there are validatio An asset file may contain many manifests in a manifest store. The most recent manifest is identified by the value of the `active_manifest` field in the manifests map. ```py +# Import the C2PA Python package. +from c2pa import * + +# Import standard general-purpose packages. +import os +import io +import logging +import json + try: # Create a reader from a file path. reader = c2pa.Reader.from_file("path/to/media_file.jpg") diff --git a/docs/tasks/includes/_rust-build.md b/docs/tasks/includes/_rust-build.md index 4a569420..6faadd69 100644 --- a/docs/tasks/includes/_rust-build.md +++ b/docs/tasks/includes/_rust-build.md @@ -3,9 +3,18 @@ This is an example of how to assign a manifest to an asset and sign the claim us This example is from [`c2pa-rs/sdk/examples/v2api.rs`](https://github.com/contentauth/c2pa-rs/blob/main/sdk/examples/v2api.rs#L88C5-L134C1): ```rust -let json = manifest_def(title, format); +use std::io::{Cursor, Seek}; + +use anyhow::Result; +use c2pa::{ + crypto::raw_signature::SigningAlg, settings::Settings, validation_results::ValidationState, + Builder, CallbackSigner, Reader, +}; +use serde_json::json; +let json = manifest_def(title, format); let mut builder = Builder::from_json(&json)?; + builder.add_ingredient_from_stream( json!({ "title": parent_name, diff --git a/docs/tasks/includes/_rust-get-resources.md b/docs/tasks/includes/_rust-get-resources.md index 52347b94..ade19f45 100644 --- a/docs/tasks/includes/_rust-get-resources.md +++ b/docs/tasks/includes/_rust-get-resources.md @@ -6,16 +6,28 @@ This is from [`resource_to_stream`](https://docs.rs/c2pa/latest/c2pa/struct.Read ```rust use c2pa::Reader; -let stream = std::io::Cursor::new(Vec::new()); -let reader = Reader::from_file("path/to/file.jpg").unwrap(); -let manifest = reader.active_manifest().unwrap(); -let uri = &manifest.thumbnail_ref().unwrap().identifier; -let bytes_written = reader.resource_to_stream(uri, stream).unwrap(); +#[cfg(feature = "file_io")] +{ + let stream = std::io::Cursor::new(Vec::new()); + let reader = Reader::from_file("path/to/file.jpg").unwrap(); + let manifest = reader.active_manifest().unwrap(); + let uri = &manifest.thumbnail_ref().unwrap().identifier; + let bytes_written = reader.resource_to_stream(uri, stream).unwrap(); +} ``` This is from [`c2pa-rs/examples/v2api.rs`](https://github.com/contentauth/c2pa-rs/blob/main/sdk/examples/v2api.rs#L138): ```rust +use std::io::{Cursor, Seek}; + +use anyhow::Result; +use c2pa::{ + crypto::raw_signature::SigningAlg, settings::Settings, validation_results::ValidationState, + Builder, CallbackSigner, Reader, +}; +use serde_json::json; + let reader = Reader::from_stream(format, &mut dest)?; // extract a thumbnail image from the ManifestStore diff --git a/docs/tasks/includes/_rust-read.md b/docs/tasks/includes/_rust-read.md index d44ea756..9521fd07 100644 --- a/docs/tasks/includes/_rust-read.md +++ b/docs/tasks/includes/_rust-read.md @@ -6,6 +6,20 @@ Use the [`Reader`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html) struct t Use [`from_file`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html#method.from_file) to read manifest data from a file: ```rust +use std::{ + io::{Cursor, Write}, + process::{Command, Stdio}, +}; + +use anyhow::Result; +use c2pa::{settings::load_settings_from_str, Builder, CallbackSigner, Reader}; +use c2pa_crypto::raw_signature::SigningAlg; +use serde_json::json; + +const TEST_IMAGE: &[u8] = include_bytes!("../tests/fixtures/CA.jpg"); +const CERTS: &[u8] = include_bytes!("../tests/fixtures/certs/ed25519.pub"); +const PRIVATE_KEY: &[u8] = include_bytes!("../tests/fixtures/certs/ed25519.pem"); + use c2pa::Reader; let reader = Reader::from_file("path/to/file.jpg").unwrap(); ``` @@ -17,8 +31,19 @@ There is also an asynchronous version of this method, [`from_stream_async`](http Use [`from_stream`](https://docs.rs/c2pa/latest/c2pa/struct.Reader.html#method.from_stream) to read manifest data from a stream: ```rust -use std::io::Cursor; -use c2pa::Reader; +use std::{ + io::{Cursor, Write}, + process::{Command, Stdio}, +}; + +use anyhow::Result; +use c2pa::{settings::load_settings_from_str, Builder, CallbackSigner, Reader}; +use c2pa_crypto::raw_signature::SigningAlg; +use serde_json::json; + +const TEST_IMAGE: &[u8] = include_bytes!("../tests/fixtures/CA.jpg"); +const CERTS: &[u8] = include_bytes!("../tests/fixtures/certs/ed25519.pub"); +const PRIVATE_KEY: &[u8] = include_bytes!("../tests/fixtures/certs/ed25519.pem"); let mut stream = Cursor::new(include_bytes!("../tests/fixtures/CA.jpg")); let reader = Reader::from_stream("image/jpeg", stream).unwrap(); diff --git a/docs/tasks/index.md b/docs/tasks/index.md index bedb3c62..0def1c21 100644 --- a/docs/tasks/index.md +++ b/docs/tasks/index.md @@ -6,7 +6,6 @@ title: Working with manifests There are a number of common tasks when working with manifests. The way you accomplish each task is specific to the language you're using, although at a high level the process is similar. -- [Preliminary setup](./setup.mdx) - [Reading manifest data](./read.mdx) - [Getting resources from a manifest](./get-resources.mdx) - [Attaching a manifest to an asset and signing it](./build.mdx) diff --git a/sidebars.js b/sidebars.js index 5c945755..cf1336ca 100644 --- a/sidebars.js +++ b/sidebars.js @@ -94,10 +94,6 @@ const sidebars = { link: { type: 'doc', id: 'tasks/working-manifests' }, collapsed: true, items: [ - { - type: 'doc', - id: 'tasks/setup', - }, { type: 'doc', id: 'tasks/read',