from __future__ import annotations import json from core.exceptions import FileValidationError MAX_FILE_SIZE_MB = 100 SUPPORTED_VERSIONS = {'1.0', '2.0'} REQUIRED_FIELDS = {'document', 'styles', 'pages', 'fonts_used'} class ExportValidator: """Validates idconvert_export.json before any parsing occurs.""" def validate(self, content: bytes) -> dict: """Run all validation checks. Returns parsed dict on success. Raises FileValidationError on any failure. """ # 1. File size size_mb = len(content) / (1024 * 1024) if size_mb > MAX_FILE_SIZE_MB: raise FileValidationError( f'File exceeds {MAX_FILE_SIZE_MB}MB limit.', 'FILE_TOO_LARGE', ) # 2. Valid JSON try: data = json.loads(content) except (json.JSONDecodeError, UnicodeDecodeError): raise FileValidationError( 'This does not appear to be a valid IDexport file. ' 'Please run IDexport.jsx in InDesign and upload the ' 'idconvert_export.json file it produces.', 'INVALID_JSON', ) if not isinstance(data, dict): raise FileValidationError( 'IDexport file has an unexpected format.', 'INVALID_FORMAT', ) # 3. Generator signature if data.get('generator') != 'IDexport': raise FileValidationError( 'This JSON file was not produced by IDexport. ' 'Please run IDexport.jsx in InDesign to generate ' 'a compatible export file.', 'WRONG_GENERATOR', ) # 4. Version check if data.get('version') not in SUPPORTED_VERSIONS: raise FileValidationError( 'This IDexport file was created with an incompatible version. ' 'Please download the latest IDexport.jsx from IDconvert.', 'VERSION_MISMATCH', ) # 5. Required top-level fields for f in REQUIRED_FIELDS: if f not in data: raise FileValidationError( f'IDexport file is missing required field: {f}. ' 'Please re-run IDexport.jsx and try again.', 'MISSING_FIELD', ) # 6. Pages must be a non-empty list if not isinstance(data.get('pages'), list) or len(data['pages']) == 0: raise FileValidationError( 'IDexport file contains no pages.', 'NO_PAGES', ) return data