Skip to content

Commit ad877cb

Browse files
authored
feat: Bump to latest native libs version, enable PDF support (#140)
* fix: Add test to verify PDF support * fix: Update in progress * fix: Version bump * fix: Files handling in sign_file * fix: Update sign_file * fix: Update signing * fix: Last failure is in CAG data * fix: Threaded tests: update manifests * fix: Refactor * fix: Test API redirect * fix: Refactor * fix: Move tests * fix: Typings * fix: Initial fixes * fix: Increase test coverage, fix accordingly * fix: Some more changes * fix: Increase test coverage, fix accordingly * fix: Increase test coverage, fix accordingly 2 * fix: Refactor and docs * fix: Increase test coverage, fix accordingly 2 * fix: FIles * fix: WIP * fix: Udpate libs loading * fix: Make sure all test fixtures are here * fix: Add a test for video signing * fix: Reactivate cawg test * fix: Write buffer to file example in test * fix: Skip cawg test for now * fix: Retrigger pipeline * fix: Fix version issues for Linux * fix: Fix version issues for Linux for realx * fix: Format * fix: Be more surgical on deprecation check * fix: Claim versions * fix: Assertion structure * fix: Assertion structure * fix: Debug in progress * fix: One char stream fix * fix: Unignore some code lines * fix: Format * fix: Add one more test * fix: Add one more test 2 * fix: Update examples * fix: Docs * fix: Docs for streams * fix: Docs for streams * fix: Clean up test manifests
1 parent 6f7e8d3 commit ad877cb

29 files changed

+2060
-1224
lines changed

.github/workflows/build-wheel.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ jobs:
6666
C2PA_LIBS_PLATFORM=\"${{ format('{0}', inputs.architecture == 'aarch64' && 'aarch64-unknown-linux-gnu' || 'x86_64-unknown-linux-gnu') }}\" /opt/python/cp310-cp310/bin/python scripts/download_artifacts.py $C2PA_VERSION &&
6767
for PYBIN in /opt/python/cp3{10,11}-*/bin; do
6868
\${PYBIN}/pip install --upgrade pip wheel &&
69-
\${PYBIN}/pip install toml &&
69+
\${PYBIN}/pip install toml==0.10.2 &&
70+
\${PYBIN}/pip install setuptools==68.0.0 &&
7071
CFLAGS=\"-I/opt/python/cp310-cp310/include/python3.10\" LDFLAGS=\"-L/opt/python/cp310-cp310/lib\" \${PYBIN}/python setup.py bdist_wheel --plat-name $PLATFORM_TAG
7172
done &&
7273
rm -f /io/dist/*-linux_*.whl

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,3 +114,5 @@ target/
114114
*.dll
115115
*.so
116116
src/c2pa/libs/
117+
!tests/fixtures/*.pem
118+
!tests/fixtures/*.key

Makefile

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,18 @@ clean-c2pa-env: clean
2020
install-deps:
2121
python3 -m pip install -r requirements.txt
2222
python3 -m pip install -r requirements-dev.txt
23-
pip install -e .
2423

2524
# Installs the package in development mode
2625
build-python:
27-
pip install -e .
26+
python3 -m pip install -e .
2827

2928
# Performs a complete rebuild of the development environment
3029
rebuild: clean-c2pa-env install-deps download-native-artifacts build-python
3130
@echo "Development rebuild done"
3231

3332
run-examples:
3433
python3 ./examples/sign.py
34+
python3 ./examples/sign_info.py
3535
python3 ./examples/training.py
3636
rm -rf output/
3737

@@ -42,23 +42,23 @@ test:
4242

4343
# Runs benchmarks in the venv
4444
benchmark:
45-
python -m pytest tests/benchmark.py -v
45+
python3 -m pytest tests/benchmark.py -v
4646

4747
# Tests building and installing a local wheel package
4848
# Downloads required artifacts, builds the wheel, installs it, and verifies the installation
4949
test-local-wheel-build:
5050
# Clean any existing builds
5151
rm -rf build/ dist/
5252
# Download artifacts and place them where they should go
53-
python scripts/download_artifacts.py $(C2PA_VERSION)
53+
python3 scripts/download_artifacts.py $(C2PA_VERSION)
5454
# Install Python
5555
python3 -m pip install -r requirements.txt
5656
python3 -m pip install -r requirements-dev.txt
57-
python -m build --wheel
57+
python3 -m build --wheel
5858
# Install local build in venv
5959
pip install $$(ls dist/*.whl)
6060
# Verify installation in local venv
61-
python -c "import c2pa; print('C2PA package installed at:', c2pa.__file__)"
61+
python3 -c "import c2pa; print('C2PA package installed at:', c2pa.__file__)"
6262
# Verify wheel structure
6363
twine check dist/*
6464

@@ -68,33 +68,37 @@ test-local-sdist-build:
6868
# Clean any existing builds
6969
rm -rf build/ dist/
7070
# Download artifacts and place them where they should go
71-
python scripts/download_artifacts.py $(C2PA_VERSION)
71+
python3 scripts/download_artifacts.py $(C2PA_VERSION)
7272
# Install Python
7373
python3 -m pip install -r requirements.txt
7474
python3 -m pip install -r requirements-dev.txt
7575
# Build sdist package
76-
python setup.py sdist
76+
python3 setup.py sdist
7777
# Install local build in venv
7878
pip install $$(ls dist/*.tar.gz)
7979
# Verify installation in local venv
80-
python -c "import c2pa; print('C2PA package installed at:', c2pa.__file__)"
80+
python3 -c "import c2pa; print('C2PA package installed at:', c2pa.__file__)"
8181
# Verify sdist structure
8282
twine check dist/*
8383

8484
# Verifies the wheel build process and checks the built package and its metadata
8585
verify-wheel-build:
8686
rm -rf build/ dist/ src/*.egg-info/
87-
python -m build
87+
python3 -m build
8888
twine check dist/*
8989

9090
# Manually publishes the package to PyPI after creating a release
9191
publish: release
9292
python3 -m pip install twine
9393
python3 -m twine upload dist/*
9494

95+
# Code analysis
96+
check-format:
97+
flake8 src/c2pa/c2pa.py
98+
9599
# Formats Python source code using autopep8 with aggressive settings
96100
format:
97-
autopep8 --aggressive --aggressive --in-place src/c2pa/**/*.py
101+
autopep8 --aggressive --aggressive --in-place src/c2pa/*.py
98102

99103
# Downloads the required native artifacts for the specified version
100104
download-native-artifacts:

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# C2PA Python library
22

33
The [c2pa-python](https://github.com/contentauth/c2pa-python) repository provides a Python library that can:
4+
45
- Read and validate C2PA manifest data from media files in supported formats.
56
- Create and sign manifest data, and attach it to media files in supported formats.
67

c2pa-native-version.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
c2pa-v0.58.0
1+
c2pa-v0.59.1

examples/README.md

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ The [`examples/sign.py`](https://github.com/contentauth/c2pa-python/blob/main/ex
1111
The `examples/sign.py` script shows how to sign an asset with a C2PA manifest and verify it using a callback signer. Callback signers let you define signing logic, for example where to load keys from.
1212

1313
The `examples/sign_info.py` script shows how to sign an asset with a C2PA manifest and verify it using a "default" signer created with the needed signer information.
14+
1415
These statements create a `builder` object with the specified manifest JSON (omitted in the snippet below), call `builder.sign()` to sign and attach the manifest to the source file, `tests/fixtures/C.jpg`, and save the signed asset to the output file, `output/C_signed.jpg`:
1516

1617
```py
@@ -80,14 +81,6 @@ Run the "do not train" assertion example:
8081
python examples/training.py
8182
```
8283

83-
### Run the signing and verification example
84-
85-
In this example, `SignerInfo` creates a `Signer` object that signs the manifest.
86-
87-
```bash
88-
python examples/sign_info.py
89-
```
90-
9184
### Run the callback signing and verification example
9285

9386
In this example, a callback signer is the signer:
@@ -96,4 +89,10 @@ In this example, a callback signer is the signer:
9689
python examples/sign.py
9790
```
9891

92+
### Run the signing and verification example
93+
94+
In this example, `SignerInfo` creates a `Signer` object that signs the manifest.
9995

96+
```bash
97+
python examples/sign_info.py
98+
```

examples/sign.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
print(version)
3636

3737

38-
# Load certificates and private key (here from the test fixtures)
38+
# Load certificates and private key (here from the test fixtures).
3939
# This is OK for development, but in production you should use a
4040
# secure way to load the certificates and private key.
4141
certs = open(fixtures_dir + "es256_certs.pem", "rb").read()
@@ -63,15 +63,17 @@ def callback_signer_es256(data: bytes) -> bytes:
6363
tsa_url="http://timestamp.digicert.com"
6464
)
6565

66-
# Create a manifest definition as a dictionary
67-
# This manifest follows the V2 manifest format
66+
# Create a manifest definition as a dictionary.
67+
# This manifest follows the V2 manifest format.
6868
manifest_definition = {
6969
"claim_generator": "python_example",
7070
"claim_generator_info": [{
7171
"name": "python_example",
7272
"version": "0.0.1",
7373
}],
74-
"claim_version": 2,
74+
# Claims version 2 is the default, so the version
75+
# number can be omitted.
76+
# "claim_version": 2,
7577
"format": "image/jpeg",
7678
"title": "Python Example Image",
7779
"ingredients": [],
@@ -82,10 +84,7 @@ def callback_signer_es256(data: bytes) -> bytes:
8284
"actions": [
8385
{
8486
"action": "c2pa.created",
85-
"parameters": {
86-
# could hold additional information about this step
87-
# eg. model used, etc.
88-
}
87+
"digitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/digitalCreation"
8988
}
9089
]
9190
}
@@ -99,6 +98,7 @@ def callback_signer_es256(data: bytes) -> bytes:
9998
# Sign the image with the signer created above,
10099
# which will use the callback signer
101100
print("\nSigning the image file...")
101+
102102
builder.sign_file(
103103
source_path=fixtures_dir + "A.jpg",
104104
dest_path=output_dir + "A_signed.jpg",

examples/sign_info.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,16 @@
6161

6262
# Create a manifest definition as a dictionary
6363
# This examples signs using a V1 manifest
64+
# Note that this is a v1 spec manifest (legacy)
6465
manifest_definition = {
6566
"claim_generator": "python_example",
6667
"claim_generator_info": [{
6768
"name": "python_example",
6869
"version": "0.0.1",
6970
}],
71+
# This manifest uses v1 claims,
72+
# so the version 1 must be explicitly set.
73+
"claim_version": 1,
7074
"format": "image/jpeg",
7175
"title": "Python Example Image",
7276
"ingredients": [],
@@ -77,9 +81,7 @@
7781
"actions": [
7882
{
7983
"action": "c2pa.created",
80-
"parameters": {
81-
# could hold additional information about this step
82-
}
84+
"digitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/digitalCreation"
8385
}
8486
]
8587
}
@@ -93,7 +95,9 @@
9395
# Sign the image
9496
print("\nSigning the image...")
9597
with open(fixtures_dir + "C.jpg", "rb") as source:
96-
with open(output_dir + "C_signed.jpg", "wb") as dest:
98+
# File needs to be opened in write+read mode to be signed
99+
# and verified properly.
100+
with open(output_dir + "C_signed.jpg", "w+b") as dest:
97101
result = builder.sign(signer, "image/jpeg", source, dest)
98102

99103
# Read the signed image to verify

examples/training.py

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
import json
1717
import os
18-
import sys
1918

2019
# Example of using python crypto to sign data using openssl with Ps256
2120
from cryptography.hazmat.primitives import hashes, serialization
@@ -54,19 +53,32 @@ def getitem(d, key):
5453
"format": "image/jpeg",
5554
"identifier": "thumbnail"
5655
},
57-
"assertions": [{
58-
"label": "cawg.training-mining",
59-
"data": {
60-
"entries": {
61-
"cawg.ai_inference": {
62-
"use": "notAllowed"
63-
},
64-
"cawg.ai_generative_training": {
65-
"use": "notAllowed"
56+
"assertions": [
57+
{
58+
"label": "c2pa.actions",
59+
"data": {
60+
"actions": [
61+
{
62+
"action": "c2pa.created",
63+
"digitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/digitalCreation"
64+
}
65+
]
66+
}
67+
},
68+
{
69+
"label": "cawg.training-mining",
70+
"data": {
71+
"entries": {
72+
"cawg.ai_inference": {
73+
"use": "notAllowed"
74+
},
75+
"cawg.ai_generative_training": {
76+
"use": "notAllowed"
77+
}
6678
}
6779
}
6880
}
69-
}]
81+
]
7082
}
7183

7284
ingredient_json = {
@@ -109,37 +121,48 @@ def getitem(d, key):
109121

110122
# Sign the file using the stream-based sign method
111123
with open(testFile, "rb") as source_file:
112-
with open(testOutputFile, "wb") as dest_file:
124+
with open(testOutputFile, "w+b") as dest_file:
113125
result = builder.sign(signer, "image/jpeg", source_file, dest_file)
114126

115-
except Exception as err:
116-
sys.exit(err)
127+
# As an alternative, you can also use file paths directly during signing:
128+
# builder.sign_file(testFile, testOutputFile, signer)
117129

118-
print("V2: successfully added do not train manifest to file " + testOutputFile)
130+
# Clean up native resources (using a with statement works too!)
131+
signer.close()
132+
builder.close()
133+
134+
except Exception as err:
135+
print("Exception during signing: ", err)
119136

137+
print("\nSuccessfully added do not train manifest to file " + testOutputFile)
120138

121139
# now verify the asset and check the manifest for a do not train assertion...
122140

123141
allowed = True # opt out model, assume training is ok if the assertion doesn't exist
124142
try:
125-
# Create reader using the current API
143+
# Create reader using the Reader API
126144
reader = c2pa.Reader(testOutputFile)
145+
146+
# Retrieve the manifest store
127147
manifest_store = json.loads(reader.json())
128148

149+
# Look at data in the active manifest
129150
manifest = manifest_store["manifests"][manifest_store["active_manifest"]]
130-
131151
for assertion in manifest["assertions"]:
132152
if assertion["label"] == "cawg.training-mining":
133153
if getitem(assertion, ("data","entries","cawg.ai_generative_training","use")) == "notAllowed":
134154
allowed = False
135155

136-
# get the ingredient thumbnail and save it to a file using resource_to_stream
156+
# Get the ingredient thumbnail and save it to a file using resource_to_stream
137157
uri = getitem(manifest,("ingredients", 0, "thumbnail", "identifier"))
138158
with open(output_dir + "thumbnail_v2.jpg", "wb") as thumbnail_output:
139159
reader.resource_to_stream(uri, thumbnail_output)
140160

161+
# Clean up native resources (using a with statement works too!)
162+
reader.close()
163+
141164
except Exception as err:
142-
sys.exit(err)
165+
print("Exception during assertions reading: ", err)
143166

144167
if allowed:
145168
print("Training is allowed")

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "c2pa-python"
7-
version = "0.12.1"
7+
version = "0.13.0"
88
requires-python = ">=3.10"
99
description = "Python bindings for the C2PA Content Authenticity Initiative (CAI) library"
1010
readme = { file = "README.md", content-type = "text/markdown" }

0 commit comments

Comments
 (0)