diff --git a/pyproject.toml b/pyproject.toml index 57d4368a..00965e12 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "c2pa-python" -version = "0.16.0" +version = "0.17.0" requires-python = ">=3.10" description = "Python bindings for the C2PA Content Authenticity Initiative (CAI) library" readme = { file = "README.md", content-type = "text/markdown" } diff --git a/src/c2pa/c2pa.py b/src/c2pa/c2pa.py index aeb1427c..87396b12 100644 --- a/src/c2pa/c2pa.py +++ b/src/c2pa/c2pa.py @@ -654,6 +654,33 @@ def load_settings(settings: str, format: str = "json") -> None: return result +def _get_mime_type_from_path(path: Union[str, Path]) -> str: + """Attempt to guess the MIME type from a file path (with extension). + + Args: + path: File path as string or Path object + + Returns: + MIME type string + + Raises: + C2paError.NotSupported: If MIME type cannot be determined + """ + path_obj = Path(path) + file_extension = path_obj.suffix.lower() if path_obj.suffix else "" + + if file_extension == ".dng": + # mimetypes guesses the wrong type for dng, + # so we bypass it and set the correct type + return "image/dng" + else: + mime_type = mimetypes.guess_type(str(path))[0] + if not mime_type: + raise C2paError.NotSupported( + f"Could not determine MIME type for file: {path}") + return mime_type + + def read_ingredient_file( path: Union[str, Path], data_dir: Union[str, Path]) -> str: """Read a file as C2PA ingredient. @@ -1214,7 +1241,11 @@ def __init__(self, """Create a new Reader. Args: - format_or_path: The format or path to read from + format_or_path: The format or path to read from. + The stream API (params format and an open stream) is + the recommended way to use the Reader. For paths, we + will attempt to guess the mimetype of the source + file based on the extension. stream: Optional stream to read from (Python stream-like object) manifest_data: Optional manifest data in bytes @@ -1231,12 +1262,8 @@ def __init__(self, # If we don't get a stream as param: # Create a stream from the file path in format_or_path path = str(format_or_path) - mime_type = mimetypes.guess_type( - path)[0] - if not mime_type: - raise C2paError.NotSupported( - f"Could not determine MIME type for file: {path}") + mime_type = _get_mime_type_from_path(path) if mime_type not in Reader.get_supported_mime_types(): raise C2paError.NotSupported( @@ -2269,7 +2296,9 @@ def sign_file(self, """Sign a file and write the signed data to an output file. Args: - source_path: Path to the source file + source_path: Path to the source file. We will attempt + to guess the mimetype of the source file based on + the extension. dest_path: Path to write the signed file to signer: The signer to use @@ -2279,11 +2308,8 @@ def sign_file(self, Raises: C2paError: If there was an error during signing """ - # Get the MIME type from the file extension - mime_type = mimetypes.guess_type(str(source_path))[0] - if not mime_type: - raise C2paError.NotSupported( - f"Could not determine MIME type for file: {source_path}") + + mime_type = _get_mime_type_from_path(source_path) try: # Open source file and destination file, then use the sign method diff --git a/tests/fixtures/C.dng b/tests/fixtures/C.dng new file mode 100644 index 00000000..361a7a0f Binary files /dev/null and b/tests/fixtures/C.dng differ diff --git a/tests/test_unit_tests.py b/tests/test_unit_tests.py index 7540d35f..22993938 100644 --- a/tests/test_unit_tests.py +++ b/tests/test_unit_tests.py @@ -126,9 +126,6 @@ def test_reader_streams_with_nested(self): def test_reader_close_cleanup(self): with open(self.testPath, "rb") as file: reader = Reader("image/jpeg", file) - # Store references to internal objects - reader_ref = reader._reader - stream_ref = reader._own_stream # Close the reader reader.close() # Verify all resources are cleaned up @@ -144,6 +141,23 @@ def test_resource_to_stream_on_closed_reader(self): with self.assertRaises(Error): reader.resource_to_stream("", io.BytesIO(bytearray())) + def test_read_dng_from_stream(self): + test_path = os.path.join(self.data_dir, "C.dng") + with open(test_path, "rb") as file: + file_content = file.read() + + with Reader("dng", io.BytesIO(file_content)) as reader: + # Just run and verify there is no crash + json.loads(reader.json()) + + def test_read_dng_file_from_path(self): + test_path = os.path.join(self.data_dir, "C.dng") + + # Create reader with the file content + with Reader(test_path) as reader: + # Just run and verify there is no crash + json.loads(reader.json()) + def test_read_all_files(self): """Test reading C2PA metadata from all files in the fixtures/files-for-reading-tests directory""" reading_dir = os.path.join(self.data_dir, "files-for-reading-tests")