|
7 | 7 | from typing import Optional, Union, Callable, Any |
8 | 8 | import time |
9 | 9 | from .lib import dynamically_load_library |
| 10 | +import mimetypes |
10 | 11 |
|
11 | 12 | # Define required function names |
12 | 13 | _REQUIRED_FUNCTIONS = [ |
|
16 | 17 | 'c2pa_load_settings', |
17 | 18 | 'c2pa_read_file', |
18 | 19 | 'c2pa_read_ingredient_file', |
19 | | - 'c2pa_sign_file', |
20 | 20 | 'c2pa_reader_from_stream', |
21 | 21 | 'c2pa_reader_from_manifest_data_and_stream', |
22 | 22 | 'c2pa_reader_free', |
@@ -247,13 +247,6 @@ def _setup_function(func, argtypes, restype=None): |
247 | 247 | _setup_function( |
248 | 248 | _lib.c2pa_read_ingredient_file, [ |
249 | 249 | ctypes.c_char_p, ctypes.c_char_p], ctypes.c_void_p) |
250 | | -_setup_function(_lib.c2pa_sign_file, |
251 | | - [ctypes.c_char_p, |
252 | | - ctypes.c_char_p, |
253 | | - ctypes.c_char_p, |
254 | | - ctypes.POINTER(C2paSignerInfo), |
255 | | - ctypes.c_char_p], |
256 | | - ctypes.c_void_p) |
257 | 250 |
|
258 | 251 | # Set up Reader and Builder function prototypes |
259 | 252 | _setup_function(_lib.c2pa_reader_from_stream, |
@@ -600,31 +593,61 @@ def sign_file( |
600 | 593 | data_dir: Optional directory to write binary resources to |
601 | 594 |
|
602 | 595 | Returns: |
603 | | - Result information as a JSON string |
| 596 | + The signed manifest as a JSON string |
604 | 597 |
|
605 | 598 | Raises: |
606 | 599 | C2paError: If there was an error signing the file |
607 | 600 | C2paError.Encoding: If any of the string inputs contain invalid UTF-8 characters |
| 601 | + C2paError.NotSupported: If the file type cannot be determined |
608 | 602 | """ |
609 | | - # Store encoded strings as attributes of signer_info to keep them alive |
610 | 603 | try: |
611 | | - signer_info._source_str = str(source_path).encode('utf-8') |
612 | | - signer_info._dest_str = str(dest_path).encode('utf-8') |
613 | | - signer_info._manifest_str = manifest.encode('utf-8') |
614 | | - signer_info._data_dir_str = str(data_dir).encode( |
615 | | - 'utf-8') if data_dir else None |
616 | | - except UnicodeError as e: |
617 | | - raise C2paError.Encoding( |
618 | | - f"Invalid UTF-8 characters in input strings: {str(e)}") |
619 | | - |
620 | | - result = _lib.c2pa_sign_file( |
621 | | - signer_info._source_str, |
622 | | - signer_info._dest_str, |
623 | | - signer_info._manifest_str, |
624 | | - ctypes.byref(signer_info), |
625 | | - signer_info._data_dir_str |
626 | | - ) |
627 | | - return _parse_operation_result_for_error(result) |
| 604 | + # Create a signer from the signer info |
| 605 | + signer = Signer.from_info(signer_info) |
| 606 | + |
| 607 | + # Create a builder from the manifest |
| 608 | + builder = Builder(manifest) |
| 609 | + |
| 610 | + # Open source and destination files |
| 611 | + with open(source_path, 'rb') as source_file, open(dest_path, 'wb') as dest_file: |
| 612 | + # Get the MIME type from the file extension |
| 613 | + mime_type = mimetypes.guess_type(str(source_path))[0] |
| 614 | + if not mime_type: |
| 615 | + raise C2paError.NotSupported(f"Could not determine MIME type for file: {source_path}") |
| 616 | + |
| 617 | + # Sign the file using the builder |
| 618 | + manifest_bytes = builder.sign( |
| 619 | + signer=signer, |
| 620 | + format=mime_type, |
| 621 | + source=source_file, |
| 622 | + dest=dest_file |
| 623 | + ) |
| 624 | + |
| 625 | + # If we have manifest bytes and a data directory, write them |
| 626 | + if manifest_bytes and data_dir: |
| 627 | + manifest_path = os.path.join(str(data_dir), 'manifest.json') |
| 628 | + with open(manifest_path, 'wb') as f: |
| 629 | + f.write(manifest_bytes) |
| 630 | + |
| 631 | + # Read the signed manifest from the destination file |
| 632 | + with Reader(dest_path) as reader: |
| 633 | + return reader.json() |
| 634 | + |
| 635 | + except Exception as e: |
| 636 | + # Clean up destination file if it exists and there was an error |
| 637 | + if os.path.exists(dest_path): |
| 638 | + try: |
| 639 | + os.remove(dest_path) |
| 640 | + except OSError: |
| 641 | + pass # Ignore cleanup errors |
| 642 | + |
| 643 | + # Re-raise the error |
| 644 | + raise C2paError(f"Error signing file: {str(e)}") |
| 645 | + finally: |
| 646 | + # Ensure resources are cleaned up |
| 647 | + if 'builder' in locals(): |
| 648 | + builder.close() |
| 649 | + if 'signer' in locals(): |
| 650 | + signer.close() |
628 | 651 |
|
629 | 652 |
|
630 | 653 | class Stream: |
@@ -958,7 +981,7 @@ def __init__(self, |
958 | 981 |
|
959 | 982 | path = str(format_or_path) |
960 | 983 | mime_type = mimetypes.guess_type( |
961 | | - path)[0] or 'application/octet-stream' |
| 984 | + path)[0] |
962 | 985 |
|
963 | 986 | # Keep mime_type string alive |
964 | 987 | try: |
|
0 commit comments