diff --git a/.gitignore b/.gitignore index 95fbd11a..acc58ef1 100644 --- a/.gitignore +++ b/.gitignore @@ -191,3 +191,7 @@ Screenshot*.png *.backup *.bak old/ +.venv/ +.venv-docs/ +.memory/ +.migration-backup/ diff --git a/.memory/audit/decisions-2025-10-09.jsonl b/.memory/audit/decisions-2025-10-09.jsonl deleted file mode 100644 index 9c52e235..00000000 --- a/.memory/audit/decisions-2025-10-09.jsonl +++ /dev/null @@ -1,6 +0,0 @@ -{"timestamp":"2025-10-09T23:32:13.911Z","sessionId":"production-deployment-test","action":"boundary_enforcement","rulesChecked":["inst_016","inst_017","inst_018"],"violations":[],"allowed":true,"metadata":{"boundary":"none","domain":"TECHNICAL","requirementType":"NONE","actionType":"deployment_test","enforcement_decision":"ALLOWED"}} -{"timestamp":"2025-10-09T23:39:11.351Z","sessionId":"session1-integration-test","action":"instruction_classification","rulesChecked":["inst_001","inst_002","inst_003","inst_004","inst_005","inst_006","inst_007","inst_008","inst_009","inst_010","inst_011","inst_012","inst_013","inst_014","inst_015","inst_016","inst_017","inst_018"],"violations":[],"allowed":true,"metadata":{"instruction_text":"Always check port 27027 for MongoDB connections","quadrant":"STRATEGIC","persistence":"HIGH","persistence_score":0.9,"explicitness":0.85,"verification":"MANDATORY","temporal_scope":"PERMANENT","source":"user","recency_weight":0.9999986111120757,"parameters":{"port":"27027"}}} -{"timestamp":"2025-10-09T23:39:11.354Z","sessionId":"session1-integration-test","action":"cross_reference_validation","rulesChecked":["instruction"],"violations":["Always check port 27027 for MongoDB connections"],"allowed":false,"metadata":{"action_description":"Connect to MongoDB on port 27017","validation_status":"REJECTED","conflicts_found":1,"critical_conflicts":1,"relevant_instructions":1,"validation_action":"REQUEST_CLARIFICATION","conflict_details":[{"parameter":"port","severity":"CRITICAL","action_value":"27017","instruction_value":"27027"}]}} -{"timestamp":"2025-10-09T23:48:44.373Z","sessionId":"session2-integration-test","action":"context_pressure_analysis","rulesChecked":["inst_001","inst_002","inst_003","inst_004","inst_005","inst_006","inst_007","inst_008","inst_009","inst_010","inst_011","inst_012","inst_013","inst_014","inst_015","inst_016","inst_017","inst_018"],"violations":[],"allowed":true,"metadata":{"overall_pressure":0.03,"pressure_level":"NORMAL","pressure_level_numeric":0,"action_required":"PROCEED","verification_multiplier":1,"metrics":{"token_usage":0,"conversation_length":0,"task_complexity":0.2,"error_frequency":0,"instruction_density":0},"top_metric":"taskComplexity","warnings_count":0,"recommendations_count":1}} -{"timestamp":"2025-10-09T23:48:44.373Z","sessionId":"session2-integration-test","action":"metacognitive_verification","rulesChecked":["inst_001","inst_002","inst_003","inst_004","inst_005","inst_006","inst_007","inst_008","inst_009","inst_010","inst_011","inst_012","inst_013","inst_014","inst_015","inst_016","inst_017","inst_018"],"violations":[],"allowed":true,"metadata":{"action_description":"Connect to MongoDB on port 27027","confidence":0.83,"original_confidence":0.83,"decision":"PROCEED","level":"PROCEED","pressure_level":"NORMAL","pressure_adjustment":0,"checks":{"alignment":true,"coherence":true,"completeness":true,"safety":true,"alternatives":false},"critical_failures":0,"failed_checks":["Alternatives"],"recommendations_count":2}} -{"timestamp":"2025-10-09T23:48:44.374Z","sessionId":"session2-integration-test","action":"context_pressure_analysis","rulesChecked":["inst_001","inst_002","inst_003","inst_004","inst_005","inst_006","inst_007","inst_008","inst_009","inst_010","inst_011","inst_012","inst_013","inst_014","inst_015","inst_016","inst_017","inst_018"],"violations":[],"allowed":true,"metadata":{"overall_pressure":0.245,"pressure_level":"NORMAL","pressure_level_numeric":0,"action_required":"PROCEED","verification_multiplier":1,"metrics":{"token_usage":0.35,"conversation_length":0.25,"task_complexity":0.4,"error_frequency":0,"instruction_density":0},"top_metric":"taskComplexity","warnings_count":0,"recommendations_count":1}} diff --git a/.migration-backup/audit/decisions-2025-10-09.jsonl b/.migration-backup/audit/decisions-2025-10-09.jsonl deleted file mode 100644 index 9c52e235..00000000 --- a/.migration-backup/audit/decisions-2025-10-09.jsonl +++ /dev/null @@ -1,6 +0,0 @@ -{"timestamp":"2025-10-09T23:32:13.911Z","sessionId":"production-deployment-test","action":"boundary_enforcement","rulesChecked":["inst_016","inst_017","inst_018"],"violations":[],"allowed":true,"metadata":{"boundary":"none","domain":"TECHNICAL","requirementType":"NONE","actionType":"deployment_test","enforcement_decision":"ALLOWED"}} -{"timestamp":"2025-10-09T23:39:11.351Z","sessionId":"session1-integration-test","action":"instruction_classification","rulesChecked":["inst_001","inst_002","inst_003","inst_004","inst_005","inst_006","inst_007","inst_008","inst_009","inst_010","inst_011","inst_012","inst_013","inst_014","inst_015","inst_016","inst_017","inst_018"],"violations":[],"allowed":true,"metadata":{"instruction_text":"Always check port 27027 for MongoDB connections","quadrant":"STRATEGIC","persistence":"HIGH","persistence_score":0.9,"explicitness":0.85,"verification":"MANDATORY","temporal_scope":"PERMANENT","source":"user","recency_weight":0.9999986111120757,"parameters":{"port":"27027"}}} -{"timestamp":"2025-10-09T23:39:11.354Z","sessionId":"session1-integration-test","action":"cross_reference_validation","rulesChecked":["instruction"],"violations":["Always check port 27027 for MongoDB connections"],"allowed":false,"metadata":{"action_description":"Connect to MongoDB on port 27017","validation_status":"REJECTED","conflicts_found":1,"critical_conflicts":1,"relevant_instructions":1,"validation_action":"REQUEST_CLARIFICATION","conflict_details":[{"parameter":"port","severity":"CRITICAL","action_value":"27017","instruction_value":"27027"}]}} -{"timestamp":"2025-10-09T23:48:44.373Z","sessionId":"session2-integration-test","action":"context_pressure_analysis","rulesChecked":["inst_001","inst_002","inst_003","inst_004","inst_005","inst_006","inst_007","inst_008","inst_009","inst_010","inst_011","inst_012","inst_013","inst_014","inst_015","inst_016","inst_017","inst_018"],"violations":[],"allowed":true,"metadata":{"overall_pressure":0.03,"pressure_level":"NORMAL","pressure_level_numeric":0,"action_required":"PROCEED","verification_multiplier":1,"metrics":{"token_usage":0,"conversation_length":0,"task_complexity":0.2,"error_frequency":0,"instruction_density":0},"top_metric":"taskComplexity","warnings_count":0,"recommendations_count":1}} -{"timestamp":"2025-10-09T23:48:44.373Z","sessionId":"session2-integration-test","action":"metacognitive_verification","rulesChecked":["inst_001","inst_002","inst_003","inst_004","inst_005","inst_006","inst_007","inst_008","inst_009","inst_010","inst_011","inst_012","inst_013","inst_014","inst_015","inst_016","inst_017","inst_018"],"violations":[],"allowed":true,"metadata":{"action_description":"Connect to MongoDB on port 27027","confidence":0.83,"original_confidence":0.83,"decision":"PROCEED","level":"PROCEED","pressure_level":"NORMAL","pressure_adjustment":0,"checks":{"alignment":true,"coherence":true,"completeness":true,"safety":true,"alternatives":false},"critical_failures":0,"failed_checks":["Alternatives"],"recommendations_count":2}} -{"timestamp":"2025-10-09T23:48:44.374Z","sessionId":"session2-integration-test","action":"context_pressure_analysis","rulesChecked":["inst_001","inst_002","inst_003","inst_004","inst_005","inst_006","inst_007","inst_008","inst_009","inst_010","inst_011","inst_012","inst_013","inst_014","inst_015","inst_016","inst_017","inst_018"],"violations":[],"allowed":true,"metadata":{"overall_pressure":0.245,"pressure_level":"NORMAL","pressure_level_numeric":0,"action_required":"PROCEED","verification_multiplier":1,"metrics":{"token_usage":0.35,"conversation_length":0.25,"task_complexity":0.4,"error_frequency":0,"instruction_density":0},"top_metric":"taskComplexity","warnings_count":0,"recommendations_count":1}} diff --git a/.migration-backup/instruction-history.json b/.migration-backup/instruction-history.json deleted file mode 100644 index 632659d3..00000000 --- a/.migration-backup/instruction-history.json +++ /dev/null @@ -1,354 +0,0 @@ -{ - "version": "1.0", - "last_updated": "2025-10-07T19:30:00Z", - "description": "Persistent instruction database for Tractatus framework governance", - "instructions": [ - { - "id": "inst_001", - "text": "MongoDB runs on port 27017 for tractatus_dev database", - "timestamp": "2025-10-06T14:00:00Z", - "quadrant": "SYSTEM", - "persistence": "HIGH", - "temporal_scope": "PROJECT", - "verification_required": "MANDATORY", - "explicitness": 0.90, - "source": "user", - "session_id": "2025-10-06-initial-setup", - "parameters": { - "port": "27017", - "database": "tractatus_dev", - "service": "mongodb" - }, - "active": true, - "notes": "Infrastructure decision from project initialization" - }, - { - "id": "inst_002", - "text": "Application runs on port 9000", - "timestamp": "2025-10-06T14:00:00Z", - "quadrant": "SYSTEM", - "persistence": "HIGH", - "temporal_scope": "PROJECT", - "verification_required": "MANDATORY", - "explicitness": 0.90, - "source": "user", - "session_id": "2025-10-06-initial-setup", - "parameters": { - "port": "9000", - "service": "tractatus-web" - }, - "active": true, - "notes": "Infrastructure decision from project initialization" - }, - { - "id": "inst_003", - "text": "This is a separate project from family-history and sydigital - no shared code or data", - "timestamp": "2025-10-06T14:00:00Z", - "quadrant": "STRATEGIC", - "persistence": "HIGH", - "temporal_scope": "PERMANENT", - "verification_required": "MANDATORY", - "explicitness": 0.95, - "source": "user", - "session_id": "2025-10-06-initial-setup", - "parameters": {}, - "active": true, - "notes": "Critical project isolation requirement" - }, - { - "id": "inst_004", - "text": "No shortcuts, no fake data, world-class quality", - "timestamp": "2025-10-06T14:00:00Z", - "quadrant": "STRATEGIC", - "persistence": "HIGH", - "temporal_scope": "PERMANENT", - "verification_required": "MANDATORY", - "explicitness": 0.88, - "source": "user", - "session_id": "2025-10-06-initial-setup", - "parameters": {}, - "active": true, - "notes": "Quality standard for all work" - }, - { - "id": "inst_005", - "text": "Human approval required for major decisions, architectural changes, values-sensitive content", - "timestamp": "2025-10-06T14:00:00Z", - "quadrant": "STRATEGIC", - "persistence": "HIGH", - "temporal_scope": "PERMANENT", - "verification_required": "MANDATORY", - "explicitness": 0.92, - "source": "user", - "session_id": "2025-10-06-initial-setup", - "parameters": {}, - "active": true, - "notes": "Governance requirement - aligns with BoundaryEnforcer" - }, - { - "id": "inst_006", - "text": "Use ContextPressureMonitor to manage sessions and create handoff when pressure is CRITICAL", - "timestamp": "2025-10-07T09:00:00Z", - "quadrant": "OPERATIONAL", - "persistence": "HIGH", - "temporal_scope": "PROJECT", - "verification_required": "REQUIRED", - "explicitness": 0.85, - "source": "user", - "session_id": "2025-10-07-part2", - "parameters": {}, - "active": true, - "notes": "Session management protocol established" - }, - { - "id": "inst_007", - "text": "Use Tractatus governance framework actively in all sessions", - "timestamp": "2025-10-07T09:15:00Z", - "quadrant": "OPERATIONAL", - "persistence": "HIGH", - "temporal_scope": "PROJECT", - "verification_required": "MANDATORY", - "explicitness": 0.98, - "source": "user", - "session_id": "2025-10-07-part2", - "parameters": { - "components": ["pressure_monitor", "classifier", "cross_reference", "boundary_enforcer"], - "verbosity": "summary" - }, - "active": true, - "notes": "Framework activation - THIS IS THE NEW NORMAL" - }, - { - "id": "inst_008", - "text": "ALWAYS comply with Content Security Policy (CSP) - no inline event handlers, no inline scripts", - "timestamp": "2025-10-07T19:30:00Z", - "quadrant": "SYSTEM", - "persistence": "HIGH", - "temporal_scope": "PERMANENT", - "verification_required": "MANDATORY", - "explicitness": 1.0, - "source": "user", - "session_id": "2025-10-07-docs-audit", - "parameters": { - "csp_policy": "script-src 'self'", - "violations_forbidden": ["onclick", "onload", "inline-script", "javascript:"], - "alternatives_required": ["addEventListener", "external-scripts"] - }, - "active": true, - "notes": "CRITICAL SECURITY REQUIREMENT - Framework should have caught CSP violation before deployment" - }, - { - "id": "inst_009", - "text": "Defer email services and Stripe activation to future sessions", - "timestamp": "2025-10-08T00:00:00Z", - "quadrant": "TACTICAL", - "persistence": "MEDIUM", - "temporal_scope": "SESSION", - "verification_required": "OPTIONAL", - "explicitness": 0.95, - "source": "user", - "session_id": "2025-10-08-phase-4", - "parameters": { - "deferred_tasks": ["email_service", "stripe_activation"] - }, - "active": true, - "notes": "Prioritization directive - focus on UI and documentation first" - }, - { - "id": "inst_010", - "text": "Ensure all production UI links are working correctly", - "timestamp": "2025-10-08T00:00:00Z", - "quadrant": "OPERATIONAL", - "persistence": "HIGH", - "temporal_scope": "PROJECT", - "verification_required": "REQUIRED", - "explicitness": 0.92, - "source": "user", - "session_id": "2025-10-08-phase-4", - "parameters": { - "scope": "production_ui", - "quality_standard": "all_links_functional" - }, - "active": true, - "notes": "Quality requirement for production deployment" - }, - { - "id": "inst_011", - "text": "Implement clear differentiation between technical documentation (for developers/implementers) and general documentation (for general audience)", - "timestamp": "2025-10-08T00:00:00Z", - "quadrant": "OPERATIONAL", - "persistence": "HIGH", - "temporal_scope": "PROJECT", - "verification_required": "REQUIRED", - "explicitness": 0.90, - "source": "user", - "session_id": "2025-10-08-phase-4", - "parameters": { - "technical_docs_examples": ["claude-code-framework-enforcement.md"], - "api_endpoint": "/api/documents", - "filter_requirement": "audience_type" - }, - "active": true, - "notes": "Content organization requirement - technical docs should be selectable separately from general docs" - }, - { - "id": "inst_012", - "text": "NEVER deploy documents marked 'internal' or 'confidential' to public production without explicit human approval. Documents containing credentials, security vulnerabilities, financial information, or infrastructure details MUST NOT be publicly accessible.", - "timestamp": "2025-10-08T01:00:00Z", - "quadrant": "SYSTEM", - "persistence": "HIGH", - "temporal_scope": "PERMANENT", - "verification_required": "MANDATORY", - "explicitness": 1.0, - "source": "system", - "session_id": "2025-10-08-phase-4-security", - "parameters": { - "visibility_levels": ["public", "internal", "confidential"], - "public_requires": "visibility: 'public' AND security validation passed", - "blocked_content": ["credentials", "api_keys", "secrets", "vulnerabilities", "security_audits", "payment_setup", "deployment_guides"], - "validation_script": "scripts/validate-document-security.js" - }, - "active": true, - "notes": "CRITICAL SECURITY REQUIREMENT - Prevents accidental exposure of sensitive internal documentation. Learned from incident where Security Audit Report, Koha Stripe Setup, and Koha Deployment guides were incorrectly marked for public import." - }, - { - "id": "inst_013", - "text": "Public API endpoints MUST NOT expose sensitive runtime data (memory usage, heap sizes, exact uptime, environment details, service architecture) that could aid attackers. Use minimal health checks for public endpoints. Sensitive monitoring data requires authentication.", - "timestamp": "2025-10-08T02:00:00Z", - "quadrant": "SYSTEM", - "persistence": "HIGH", - "temporal_scope": "PERMANENT", - "verification_required": "MANDATORY", - "explicitness": 1.0, - "source": "user", - "session_id": "2025-10-08-phase-4-security", - "parameters": { - "public_endpoints": ["/health", "/api/koha/transparency"], - "authenticated_endpoints": ["/api/governance", "/api/governance/status"], - "blocked_from_public": ["memory_usage", "heap_sizes", "uptime", "environment", "service_names", "internal_architecture"], - "allowed_public": ["status: ok", "timestamp", "public_metrics_only"], - "rate_limiting": "100 requests per 15 minutes per IP" - }, - "active": true, - "notes": "CRITICAL SECURITY REQUIREMENT - Prevents reconnaissance attacks. /api/governance exposed memory usage (95MB heap), exact uptime, service architecture to public. Now requires admin authentication. /health simplified to status + timestamp only." - }, - { - "id": "inst_014", - "text": "Do NOT expose API endpoint listings or attack surface maps to public users. Demo pages should showcase framework CONCEPTS (classification, boundaries, pressure), not production API infrastructure. API documentation requires authentication or should be deferred to GitHub SDK/samples.", - "timestamp": "2025-10-08T02:30:00Z", - "quadrant": "SYSTEM", - "persistence": "HIGH", - "temporal_scope": "PERMANENT", - "verification_required": "MANDATORY", - "explicitness": 1.0, - "source": "user", - "session_id": "2025-10-08-phase-4-security", - "parameters": { - "removed_sections": ["Live API Demo from tractatus-demo.html"], - "exposed_data_removed": ["all endpoint names", "admin capabilities", "authentication system", "webhook endpoints", "submission forms", "internal features"], - "replacement": "Resources section with links to docs, researcher, implementer, about pages", - "future_approach": "GitHub SDK/samples when ready, or authenticated developer portal" - }, - "active": true, - "notes": "SECURITY DECISION - Removed Live API Demo section that exposed complete API attack surface (auth, documents, blog, media, cases, admin, governance, koha endpoints). Provided zero value to legitimate users but gave attackers enumeration targets. Replaced with Resources section linking to static documentation." - }, - { - "id": "inst_015", - "text": "NEVER deploy internal development documents to public downloads directory. Session handoffs, phase planning docs, testing checklists, cost estimates, infrastructure plans, progress reports, and cover letters are CONFIDENTIAL. Only deploy documents explicitly approved for public consumption.", - "timestamp": "2025-10-08T03:00:00Z", - "quadrant": "SYSTEM", - "persistence": "HIGH", - "temporal_scope": "PERMANENT", - "verification_required": "MANDATORY", - "explicitness": 1.0, - "source": "user", - "session_id": "2025-10-08-phase-4-security", - "parameters": { - "blocked_patterns": ["session-handoff-*.pdf", "phase-2-*.pdf", "ai-features-*.pdf", "*-test-suite-*.pdf", "*-testing-*.pdf", "*-progress-report.pdf", "*-blog-post-*.pdf", "cover-letter-*.pdf"], - "public_directory": "/public/downloads/", - "approved_public_docs": ["framework documentation", "implementation guides", "glossary", "case studies", "core concepts", "executive briefs"], - "requires_explicit_approval": true - }, - "active": true, - "notes": "CRITICAL SECURITY INCIDENT - 20 internal documents were publicly accessible in downloads directory, exposing: session debugging, infrastructure plans, cost estimates, testing methodologies, development processes. Removed from production. Public downloads must be whitelisted." - }, - { - "id": "inst_016", - "text": "NEVER fabricate statistics, cite non-existent data, or make claims without verifiable evidence. ALL statistics, ROI figures, performance metrics, and quantitative claims MUST either cite sources OR be marked [NEEDS VERIFICATION] for human review. Marketing goals do NOT override factual accuracy requirements.", - "timestamp": "2025-10-09T00:00:00Z", - "quadrant": "STRATEGIC", - "persistence": "HIGH", - "temporal_scope": "PERMANENT", - "verification_required": "MANDATORY", - "explicitness": 1.0, - "source": "user", - "session_id": "2025-10-07-001-continued", - "parameters": { - "prohibited_actions": ["fabricating_statistics", "inventing_data", "citing_non_existent_sources", "making_unverifiable_claims"], - "required_for_statistics": ["source_citation", "verification_flag", "human_approval"], - "applies_to": ["marketing_content", "public_pages", "documentation", "presentations", "all_public_claims"], - "boundary_enforcer_trigger": "ANY statistic or quantitative claim", - "failure_mode": "Values violation - honesty and transparency" - }, - "active": true, - "notes": "CRITICAL FRAMEWORK FAILURE 2025-10-09 - Claude fabricated statistics on leader.html (1,315% ROI, $3.77M savings, 14mo payback, 80% risk reduction, etc.) without triggering BoundaryEnforcer. This directly violates Tractatus core values of honesty and transparency. All public claims must be factually grounded." - }, - { - "id": "inst_017", - "text": "NEVER use prohibited absolute assurance terms: 'guarantee', 'guaranteed', 'ensures 100%', 'eliminates all', 'completely prevents', 'never fails'. Use evidence-based language: 'designed to reduce', 'helps mitigate', 'reduces risk of', 'supports prevention of'. Any absolute claim requires BoundaryEnforcer check and human approval.", - "timestamp": "2025-10-09T00:00:00Z", - "quadrant": "STRATEGIC", - "persistence": "HIGH", - "temporal_scope": "PERMANENT", - "verification_required": "MANDATORY", - "explicitness": 1.0, - "source": "user", - "session_id": "2025-10-07-001-continued", - "parameters": { - "prohibited_terms": ["guarantee", "guaranteed", "ensures 100%", "eliminates all", "completely prevents", "never fails", "always works", "perfect protection"], - "approved_alternatives": ["designed to reduce", "helps mitigate", "reduces risk of", "supports prevention of", "intended to minimize", "architected to limit"], - "boundary_enforcer_trigger": "ANY absolute assurance language", - "replacement_required": true - }, - "active": true, - "notes": "CRITICAL FRAMEWORK FAILURE 2025-10-09 - Claude used term 'architectural guarantees' on leader.html. No AI safety framework can guarantee outcomes. This violates Tractatus principles of honesty and realistic expectations. Absolute assurances undermine credibility and set false expectations." - }, - { - "id": "inst_018", - "text": "NEVER claim Tractatus is 'production-ready', 'in production use', or has existing customers/deployments without explicit evidence. Current accurate status: 'Development framework', 'Proof-of-concept', 'Research prototype'. Do NOT imply adoption, market validation, or customer base that doesn't exist. Aspirational claims require human approval and clear labeling.", - "timestamp": "2025-10-09T00:00:00Z", - "quadrant": "STRATEGIC", - "persistence": "HIGH", - "temporal_scope": "PROJECT", - "verification_required": "MANDATORY", - "explicitness": 1.0, - "source": "user", - "session_id": "2025-10-07-001-continued", - "parameters": { - "prohibited_claims": ["production-ready", "in production", "deployed at scale", "existing customers", "proven in enterprise", "market leader", "widely adopted"], - "current_accurate_status": ["development framework", "proof-of-concept", "research prototype", "early-stage development"], - "requires_evidence": ["customer testimonials", "deployment statistics", "adoption metrics", "case studies"], - "boundary_enforcer_trigger": "ANY claim about production use or customers" - }, - "active": true, - "notes": "CRITICAL FRAMEWORK FAILURE 2025-10-09 - Claude claimed 'World's First Production-Ready AI Safety Framework' on leader.html without evidence. Tractatus is development/research stage. False market positioning undermines credibility and violates honesty principle. Status claims must match reality." - } - ], - "stats": { - "total_instructions": 18, - "active_instructions": 18, - "by_quadrant": { - "STRATEGIC": 6, - "OPERATIONAL": 4, - "TACTICAL": 1, - "SYSTEM": 7, - "STOCHASTIC": 0 - }, - "by_persistence": { - "HIGH": 16, - "MEDIUM": 2, - "LOW": 0, - "VARIABLE": 0 - } - } -} diff --git a/.venv-docs/bin/Activate.ps1 b/.venv-docs/bin/Activate.ps1 deleted file mode 100644 index b49d77ba..00000000 --- a/.venv-docs/bin/Activate.ps1 +++ /dev/null @@ -1,247 +0,0 @@ -<# -.Synopsis -Activate a Python virtual environment for the current PowerShell session. - -.Description -Pushes the python executable for a virtual environment to the front of the -$Env:PATH environment variable and sets the prompt to signify that you are -in a Python virtual environment. Makes use of the command line switches as -well as the `pyvenv.cfg` file values present in the virtual environment. - -.Parameter VenvDir -Path to the directory that contains the virtual environment to activate. The -default value for this is the parent of the directory that the Activate.ps1 -script is located within. - -.Parameter Prompt -The prompt prefix to display when this virtual environment is activated. By -default, this prompt is the name of the virtual environment folder (VenvDir) -surrounded by parentheses and followed by a single space (ie. '(.venv) '). - -.Example -Activate.ps1 -Activates the Python virtual environment that contains the Activate.ps1 script. - -.Example -Activate.ps1 -Verbose -Activates the Python virtual environment that contains the Activate.ps1 script, -and shows extra information about the activation as it executes. - -.Example -Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv -Activates the Python virtual environment located in the specified location. - -.Example -Activate.ps1 -Prompt "MyPython" -Activates the Python virtual environment that contains the Activate.ps1 script, -and prefixes the current prompt with the specified string (surrounded in -parentheses) while the virtual environment is active. - -.Notes -On Windows, it may be required to enable this Activate.ps1 script by setting the -execution policy for the user. You can do this by issuing the following PowerShell -command: - -PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser - -For more information on Execution Policies: -https://go.microsoft.com/fwlink/?LinkID=135170 - -#> -Param( - [Parameter(Mandatory = $false)] - [String] - $VenvDir, - [Parameter(Mandatory = $false)] - [String] - $Prompt -) - -<# Function declarations --------------------------------------------------- #> - -<# -.Synopsis -Remove all shell session elements added by the Activate script, including the -addition of the virtual environment's Python executable from the beginning of -the PATH variable. - -.Parameter NonDestructive -If present, do not remove this function from the global namespace for the -session. - -#> -function global:deactivate ([switch]$NonDestructive) { - # Revert to original values - - # The prior prompt: - if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) { - Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt - Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT - } - - # The prior PYTHONHOME: - if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) { - Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME - Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME - } - - # The prior PATH: - if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) { - Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH - Remove-Item -Path Env:_OLD_VIRTUAL_PATH - } - - # Just remove the VIRTUAL_ENV altogether: - if (Test-Path -Path Env:VIRTUAL_ENV) { - Remove-Item -Path env:VIRTUAL_ENV - } - - # Just remove VIRTUAL_ENV_PROMPT altogether. - if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) { - Remove-Item -Path env:VIRTUAL_ENV_PROMPT - } - - # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether: - if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) { - Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force - } - - # Leave deactivate function in the global namespace if requested: - if (-not $NonDestructive) { - Remove-Item -Path function:deactivate - } -} - -<# -.Description -Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the -given folder, and returns them in a map. - -For each line in the pyvenv.cfg file, if that line can be parsed into exactly -two strings separated by `=` (with any amount of whitespace surrounding the =) -then it is considered a `key = value` line. The left hand string is the key, -the right hand is the value. - -If the value starts with a `'` or a `"` then the first and last character is -stripped from the value before being captured. - -.Parameter ConfigDir -Path to the directory that contains the `pyvenv.cfg` file. -#> -function Get-PyVenvConfig( - [String] - $ConfigDir -) { - Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg" - - # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue). - $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue - - # An empty map will be returned if no config file is found. - $pyvenvConfig = @{ } - - if ($pyvenvConfigPath) { - - Write-Verbose "File exists, parse `key = value` lines" - $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath - - $pyvenvConfigContent | ForEach-Object { - $keyval = $PSItem -split "\s*=\s*", 2 - if ($keyval[0] -and $keyval[1]) { - $val = $keyval[1] - - # Remove extraneous quotations around a string value. - if ("'""".Contains($val.Substring(0, 1))) { - $val = $val.Substring(1, $val.Length - 2) - } - - $pyvenvConfig[$keyval[0]] = $val - Write-Verbose "Adding Key: '$($keyval[0])'='$val'" - } - } - } - return $pyvenvConfig -} - - -<# Begin Activate script --------------------------------------------------- #> - -# Determine the containing directory of this script -$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition -$VenvExecDir = Get-Item -Path $VenvExecPath - -Write-Verbose "Activation script is located in path: '$VenvExecPath'" -Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)" -Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)" - -# Set values required in priority: CmdLine, ConfigFile, Default -# First, get the location of the virtual environment, it might not be -# VenvExecDir if specified on the command line. -if ($VenvDir) { - Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values" -} -else { - Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir." - $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/") - Write-Verbose "VenvDir=$VenvDir" -} - -# Next, read the `pyvenv.cfg` file to determine any required value such -# as `prompt`. -$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir - -# Next, set the prompt from the command line, or the config file, or -# just use the name of the virtual environment folder. -if ($Prompt) { - Write-Verbose "Prompt specified as argument, using '$Prompt'" -} -else { - Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value" - if ($pyvenvCfg -and $pyvenvCfg['prompt']) { - Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'" - $Prompt = $pyvenvCfg['prompt']; - } - else { - Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)" - Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'" - $Prompt = Split-Path -Path $venvDir -Leaf - } -} - -Write-Verbose "Prompt = '$Prompt'" -Write-Verbose "VenvDir='$VenvDir'" - -# Deactivate any currently active virtual environment, but leave the -# deactivate function in place. -deactivate -nondestructive - -# Now set the environment variable VIRTUAL_ENV, used by many tools to determine -# that there is an activated venv. -$env:VIRTUAL_ENV = $VenvDir - -if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) { - - Write-Verbose "Setting prompt to '$Prompt'" - - # Set the prompt to include the env name - # Make sure _OLD_VIRTUAL_PROMPT is global - function global:_OLD_VIRTUAL_PROMPT { "" } - Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT - New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt - - function global:prompt { - Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) " - _OLD_VIRTUAL_PROMPT - } - $env:VIRTUAL_ENV_PROMPT = $Prompt -} - -# Clear PYTHONHOME -if (Test-Path -Path Env:PYTHONHOME) { - Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME - Remove-Item -Path Env:PYTHONHOME -} - -# Add the venv to the PATH -Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH -$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH" diff --git a/.venv-docs/bin/activate b/.venv-docs/bin/activate deleted file mode 100644 index 32d7b1c6..00000000 --- a/.venv-docs/bin/activate +++ /dev/null @@ -1,70 +0,0 @@ -# This file must be used with "source bin/activate" *from bash* -# You cannot run it directly - -deactivate () { - # reset old environment variables - if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then - PATH="${_OLD_VIRTUAL_PATH:-}" - export PATH - unset _OLD_VIRTUAL_PATH - fi - if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then - PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}" - export PYTHONHOME - unset _OLD_VIRTUAL_PYTHONHOME - fi - - # Call hash to forget past commands. Without forgetting - # past commands the $PATH changes we made may not be respected - hash -r 2> /dev/null - - if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then - PS1="${_OLD_VIRTUAL_PS1:-}" - export PS1 - unset _OLD_VIRTUAL_PS1 - fi - - unset VIRTUAL_ENV - unset VIRTUAL_ENV_PROMPT - if [ ! "${1:-}" = "nondestructive" ] ; then - # Self destruct! - unset -f deactivate - fi -} - -# unset irrelevant variables -deactivate nondestructive - -# on Windows, a path can contain colons and backslashes and has to be converted: -if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then - # transform D:\path\to\venv to /d/path/to/venv on MSYS - # and to /cygdrive/d/path/to/venv on Cygwin - export VIRTUAL_ENV=$(cygpath /home/theflow/projects/tractatus/.venv-docs) -else - # use the path as-is - export VIRTUAL_ENV=/home/theflow/projects/tractatus/.venv-docs -fi - -_OLD_VIRTUAL_PATH="$PATH" -PATH="$VIRTUAL_ENV/"bin":$PATH" -export PATH - -# unset PYTHONHOME if set -# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) -# could use `if (set -u; : $PYTHONHOME) ;` in bash -if [ -n "${PYTHONHOME:-}" ] ; then - _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" - unset PYTHONHOME -fi - -if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then - _OLD_VIRTUAL_PS1="${PS1:-}" - PS1='(.venv-docs) '"${PS1:-}" - export PS1 - VIRTUAL_ENV_PROMPT='(.venv-docs) ' - export VIRTUAL_ENV_PROMPT -fi - -# Call hash to forget past commands. Without forgetting -# past commands the $PATH changes we made may not be respected -hash -r 2> /dev/null diff --git a/.venv-docs/bin/activate.csh b/.venv-docs/bin/activate.csh deleted file mode 100644 index a2fff424..00000000 --- a/.venv-docs/bin/activate.csh +++ /dev/null @@ -1,27 +0,0 @@ -# This file must be used with "source bin/activate.csh" *from csh*. -# You cannot run it directly. - -# Created by Davide Di Blasi . -# Ported to Python 3.3 venv by Andrew Svetlov - -alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate' - -# Unset irrelevant variables. -deactivate nondestructive - -setenv VIRTUAL_ENV /home/theflow/projects/tractatus/.venv-docs - -set _OLD_VIRTUAL_PATH="$PATH" -setenv PATH "$VIRTUAL_ENV/"bin":$PATH" - - -set _OLD_VIRTUAL_PROMPT="$prompt" - -if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then - set prompt = '(.venv-docs) '"$prompt" - setenv VIRTUAL_ENV_PROMPT '(.venv-docs) ' -endif - -alias pydoc python -m pydoc - -rehash diff --git a/.venv-docs/bin/activate.fish b/.venv-docs/bin/activate.fish deleted file mode 100644 index 41a25b43..00000000 --- a/.venv-docs/bin/activate.fish +++ /dev/null @@ -1,69 +0,0 @@ -# This file must be used with "source /bin/activate.fish" *from fish* -# (https://fishshell.com/). You cannot run it directly. - -function deactivate -d "Exit virtual environment and return to normal shell environment" - # reset old environment variables - if test -n "$_OLD_VIRTUAL_PATH" - set -gx PATH $_OLD_VIRTUAL_PATH - set -e _OLD_VIRTUAL_PATH - end - if test -n "$_OLD_VIRTUAL_PYTHONHOME" - set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME - set -e _OLD_VIRTUAL_PYTHONHOME - end - - if test -n "$_OLD_FISH_PROMPT_OVERRIDE" - set -e _OLD_FISH_PROMPT_OVERRIDE - # prevents error when using nested fish instances (Issue #93858) - if functions -q _old_fish_prompt - functions -e fish_prompt - functions -c _old_fish_prompt fish_prompt - functions -e _old_fish_prompt - end - end - - set -e VIRTUAL_ENV - set -e VIRTUAL_ENV_PROMPT - if test "$argv[1]" != "nondestructive" - # Self-destruct! - functions -e deactivate - end -end - -# Unset irrelevant variables. -deactivate nondestructive - -set -gx VIRTUAL_ENV /home/theflow/projects/tractatus/.venv-docs - -set -gx _OLD_VIRTUAL_PATH $PATH -set -gx PATH "$VIRTUAL_ENV/"bin $PATH - -# Unset PYTHONHOME if set. -if set -q PYTHONHOME - set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME - set -e PYTHONHOME -end - -if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" - # fish uses a function instead of an env var to generate the prompt. - - # Save the current fish_prompt function as the function _old_fish_prompt. - functions -c fish_prompt _old_fish_prompt - - # With the original prompt function renamed, we can override with our own. - function fish_prompt - # Save the return status of the last command. - set -l old_status $status - - # Output the venv prompt; color taken from the blue of the Python logo. - printf "%s%s%s" (set_color 4B8BBE) '(.venv-docs) ' (set_color normal) - - # Restore the return status of the previous command. - echo "exit $old_status" | . - # Output the original/"old" prompt. - _old_fish_prompt - end - - set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" - set -gx VIRTUAL_ENV_PROMPT '(.venv-docs) ' -end diff --git a/.venv-docs/bin/fonttools b/.venv-docs/bin/fonttools deleted file mode 100755 index 6e26c19e..00000000 --- a/.venv-docs/bin/fonttools +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/theflow/projects/tractatus/.venv-docs/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from fontTools.__main__ import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/.venv-docs/bin/markdown_py b/.venv-docs/bin/markdown_py deleted file mode 100755 index ee69639b..00000000 --- a/.venv-docs/bin/markdown_py +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/theflow/projects/tractatus/.venv-docs/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from markdown.__main__ import run -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(run()) diff --git a/.venv-docs/bin/pip b/.venv-docs/bin/pip deleted file mode 100755 index b3775e50..00000000 --- a/.venv-docs/bin/pip +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/theflow/projects/tractatus/.venv-docs/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from pip._internal.cli.main import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/.venv-docs/bin/pip3 b/.venv-docs/bin/pip3 deleted file mode 100755 index b3775e50..00000000 --- a/.venv-docs/bin/pip3 +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/theflow/projects/tractatus/.venv-docs/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from pip._internal.cli.main import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/.venv-docs/bin/pip3.12 b/.venv-docs/bin/pip3.12 deleted file mode 100755 index b3775e50..00000000 --- a/.venv-docs/bin/pip3.12 +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/theflow/projects/tractatus/.venv-docs/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from pip._internal.cli.main import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/.venv-docs/bin/pyftmerge b/.venv-docs/bin/pyftmerge deleted file mode 100755 index dc8391cf..00000000 --- a/.venv-docs/bin/pyftmerge +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/theflow/projects/tractatus/.venv-docs/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from fontTools.merge import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/.venv-docs/bin/pyftsubset b/.venv-docs/bin/pyftsubset deleted file mode 100755 index 4c526442..00000000 --- a/.venv-docs/bin/pyftsubset +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/theflow/projects/tractatus/.venv-docs/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from fontTools.subset import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/.venv-docs/bin/python b/.venv-docs/bin/python deleted file mode 120000 index b8a0adbb..00000000 --- a/.venv-docs/bin/python +++ /dev/null @@ -1 +0,0 @@ -python3 \ No newline at end of file diff --git a/.venv-docs/bin/python3 b/.venv-docs/bin/python3 deleted file mode 120000 index ae65fdaa..00000000 --- a/.venv-docs/bin/python3 +++ /dev/null @@ -1 +0,0 @@ -/usr/bin/python3 \ No newline at end of file diff --git a/.venv-docs/bin/python3.12 b/.venv-docs/bin/python3.12 deleted file mode 120000 index b8a0adbb..00000000 --- a/.venv-docs/bin/python3.12 +++ /dev/null @@ -1 +0,0 @@ -python3 \ No newline at end of file diff --git a/.venv-docs/bin/ttx b/.venv-docs/bin/ttx deleted file mode 100755 index 30e0f50e..00000000 --- a/.venv-docs/bin/ttx +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/theflow/projects/tractatus/.venv-docs/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from fontTools.ttx import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/.venv-docs/bin/weasyprint b/.venv-docs/bin/weasyprint deleted file mode 100755 index 36e88779..00000000 --- a/.venv-docs/bin/weasyprint +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/theflow/projects/tractatus/.venv-docs/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from weasyprint.__main__ import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/.venv-docs/lib/python3.12/site-packages/Brotli-1.1.0.dist-info/INSTALLER b/.venv-docs/lib/python3.12/site-packages/Brotli-1.1.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e3..00000000 --- a/.venv-docs/lib/python3.12/site-packages/Brotli-1.1.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv-docs/lib/python3.12/site-packages/Brotli-1.1.0.dist-info/LICENSE b/.venv-docs/lib/python3.12/site-packages/Brotli-1.1.0.dist-info/LICENSE deleted file mode 100644 index 33b7cdd2..00000000 --- a/.venv-docs/lib/python3.12/site-packages/Brotli-1.1.0.dist-info/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/.venv-docs/lib/python3.12/site-packages/Brotli-1.1.0.dist-info/METADATA b/.venv-docs/lib/python3.12/site-packages/Brotli-1.1.0.dist-info/METADATA deleted file mode 100644 index 0f9bd0cc..00000000 --- a/.venv-docs/lib/python3.12/site-packages/Brotli-1.1.0.dist-info/METADATA +++ /dev/null @@ -1,131 +0,0 @@ -Metadata-Version: 2.1 -Name: Brotli -Version: 1.1.0 -Summary: Python bindings for the Brotli compression library -Home-page: https://github.com/google/brotli -Author: The Brotli Authors -License: MIT -Platform: Posix -Platform: MacOS X -Platform: Windows -Classifier: Development Status :: 4 - Beta -Classifier: Environment :: Console -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Operating System :: MacOS :: MacOS X -Classifier: Operating System :: Microsoft :: Windows -Classifier: Operating System :: POSIX :: Linux -Classifier: Programming Language :: C -Classifier: Programming Language :: C++ -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.3 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Unix Shell -Classifier: Topic :: Software Development :: Libraries -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Topic :: System :: Archiving -Classifier: Topic :: System :: Archiving :: Compression -Classifier: Topic :: Text Processing :: Fonts -Classifier: Topic :: Utilities -Description-Content-Type: text/markdown -License-File: LICENSE - -

- GitHub Actions Build Status - Fuzzing Status -

-

Brotli

- -### Introduction - -Brotli is a generic-purpose lossless compression algorithm that compresses data -using a combination of a modern variant of the LZ77 algorithm, Huffman coding -and 2nd order context modeling, with a compression ratio comparable to the best -currently available general-purpose compression methods. It is similar in speed -with deflate but offers more dense compression. - -The specification of the Brotli Compressed Data Format is defined in [RFC 7932](https://tools.ietf.org/html/rfc7932). - -Brotli is open-sourced under the MIT License, see the LICENSE file. - -> **Please note:** brotli is a "stream" format; it does not contain -> meta-information, like checksums or uncompresssed data length. It is possible -> to modify "raw" ranges of the compressed stream and the decoder will not -> notice that. - -### Build instructions - -#### Vcpkg - -You can download and install brotli using the [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager: - - git clone https://github.com/Microsoft/vcpkg.git - cd vcpkg - ./bootstrap-vcpkg.sh - ./vcpkg integrate install - ./vcpkg install brotli - -The brotli port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. - -#### Bazel - -See [Bazel](http://www.bazel.build/) - -#### CMake - -The basic commands to build and install brotli are: - - $ mkdir out && cd out - $ cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=./installed .. - $ cmake --build . --config Release --target install - -You can use other [CMake](https://cmake.org/) configuration. - -#### Python - -To install the latest release of the Python module, run the following: - - $ pip install brotli - -To install the tip-of-the-tree version, run: - - $ pip install --upgrade git+https://github.com/google/brotli - -See the [Python readme](python/README.md) for more details on installing -from source, development, and testing. - -### Contributing - -We glad to answer/library related questions in -[brotli mailing list](https://groups.google.com/forum/#!forum/brotli). - -Regular issues / feature requests should be reported in -[issue tracker](https://github.com/google/brotli/issues). - -For reporting vulnerability please read [SECURITY](SECURITY.md). - -For contributing changes please read [CONTRIBUTING](CONTRIBUTING.md). - -### Benchmarks -* [Squash Compression Benchmark](https://quixdb.github.io/squash-benchmark/) / [Unstable Squash Compression Benchmark](https://quixdb.github.io/squash-benchmark/unstable/) -* [Large Text Compression Benchmark](http://mattmahoney.net/dc/text.html) -* [Lzturbo Benchmark](https://sites.google.com/site/powturbo/home/benchmark) - -### Related projects -> **Disclaimer:** Brotli authors take no responsibility for the third party projects mentioned in this section. - -Independent [decoder](https://github.com/madler/brotli) implementation by Mark Adler, based entirely on format specification. - -JavaScript port of brotli [decoder](https://github.com/devongovett/brotli.js). Could be used directly via `npm install brotli` - -Hand ported [decoder / encoder](https://github.com/dominikhlbg/BrotliHaxe) in haxe by Dominik Homberger. Output source code: JavaScript, PHP, Python, Java and C# - -7Zip [plugin](https://github.com/mcmilk/7-Zip-Zstd) - -Dart [native bindings](https://github.com/thosakwe/brotli) - -Dart compression framework with [fast FFI-based Brotli implementation](https://pub.dev/documentation/es_compression/latest/brotli/brotli-library.html) with ready-to-use prebuilt binaries for Win/Linux/Mac diff --git a/.venv-docs/lib/python3.12/site-packages/Brotli-1.1.0.dist-info/RECORD b/.venv-docs/lib/python3.12/site-packages/Brotli-1.1.0.dist-info/RECORD deleted file mode 100644 index d4965507..00000000 --- a/.venv-docs/lib/python3.12/site-packages/Brotli-1.1.0.dist-info/RECORD +++ /dev/null @@ -1,9 +0,0 @@ -Brotli-1.1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -Brotli-1.1.0.dist-info/LICENSE,sha256=PRgACONpIqTo2uwRw0x68mT-1ZYtB5JK6pKMOOhmPJQ,1084 -Brotli-1.1.0.dist-info/METADATA,sha256=LJosR9yLMqr7jATPCPS3pSzTdjqqD08KpM8lk9ZuUrs,5496 -Brotli-1.1.0.dist-info/RECORD,, -Brotli-1.1.0.dist-info/WHEEL,sha256=4ZiCdXIWMxJyEClivrQv1QAHZpQh8kVYU92_ZAVwaok,152 -Brotli-1.1.0.dist-info/top_level.txt,sha256=gsS54HrhO3ZveFxeMrKo_7qH4Sm4TbQ7jGLVBEqJ4NI,15 -__pycache__/brotli.cpython-312.pyc,, -_brotli.cpython-312-x86_64-linux-gnu.so,sha256=heG65RZKWcoNLgOOe4_gCQnMVwCaGOo8031-EQJQ-cc,7458584 -brotli.py,sha256=PnGIVmeFGFHSOwermGwohhd2Fyr44FhgejLQkkIIFiA,1866 diff --git a/.venv-docs/lib/python3.12/site-packages/Brotli-1.1.0.dist-info/WHEEL b/.venv-docs/lib/python3.12/site-packages/Brotli-1.1.0.dist-info/WHEEL deleted file mode 100644 index d1b3f1da..00000000 --- a/.venv-docs/lib/python3.12/site-packages/Brotli-1.1.0.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.41.2) -Root-Is-Purelib: false -Tag: cp312-cp312-manylinux_2_17_x86_64 -Tag: cp312-cp312-manylinux2014_x86_64 - diff --git a/.venv-docs/lib/python3.12/site-packages/Brotli-1.1.0.dist-info/top_level.txt b/.venv-docs/lib/python3.12/site-packages/Brotli-1.1.0.dist-info/top_level.txt deleted file mode 100644 index a111e9cc..00000000 --- a/.venv-docs/lib/python3.12/site-packages/Brotli-1.1.0.dist-info/top_level.txt +++ /dev/null @@ -1,2 +0,0 @@ -_brotli -brotli diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/AvifImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/AvifImagePlugin.py deleted file mode 100644 index 366e0c86..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/AvifImagePlugin.py +++ /dev/null @@ -1,291 +0,0 @@ -from __future__ import annotations - -import os -from io import BytesIO -from typing import IO - -from . import ExifTags, Image, ImageFile - -try: - from . import _avif - - SUPPORTED = True -except ImportError: - SUPPORTED = False - -# Decoder options as module globals, until there is a way to pass parameters -# to Image.open (see https://github.com/python-pillow/Pillow/issues/569) -DECODE_CODEC_CHOICE = "auto" -DEFAULT_MAX_THREADS = 0 - - -def get_codec_version(codec_name: str) -> str | None: - versions = _avif.codec_versions() - for version in versions.split(", "): - if version.split(" [")[0] == codec_name: - return version.split(":")[-1].split(" ")[0] - return None - - -def _accept(prefix: bytes) -> bool | str: - if prefix[4:8] != b"ftyp": - return False - major_brand = prefix[8:12] - if major_brand in ( - # coding brands - b"avif", - b"avis", - # We accept files with AVIF container brands; we can't yet know if - # the ftyp box has the correct compatible brands, but if it doesn't - # then the plugin will raise a SyntaxError which Pillow will catch - # before moving on to the next plugin that accepts the file. - # - # Also, because this file might not actually be an AVIF file, we - # don't raise an error if AVIF support isn't properly compiled. - b"mif1", - b"msf1", - ): - if not SUPPORTED: - return ( - "image file could not be identified because AVIF support not installed" - ) - return True - return False - - -def _get_default_max_threads() -> int: - if DEFAULT_MAX_THREADS: - return DEFAULT_MAX_THREADS - if hasattr(os, "sched_getaffinity"): - return len(os.sched_getaffinity(0)) - else: - return os.cpu_count() or 1 - - -class AvifImageFile(ImageFile.ImageFile): - format = "AVIF" - format_description = "AVIF image" - __frame = -1 - - def _open(self) -> None: - if not SUPPORTED: - msg = "image file could not be opened because AVIF support not installed" - raise SyntaxError(msg) - - if DECODE_CODEC_CHOICE != "auto" and not _avif.decoder_codec_available( - DECODE_CODEC_CHOICE - ): - msg = "Invalid opening codec" - raise ValueError(msg) - self._decoder = _avif.AvifDecoder( - self.fp.read(), - DECODE_CODEC_CHOICE, - _get_default_max_threads(), - ) - - # Get info from decoder - self._size, self.n_frames, self._mode, icc, exif, exif_orientation, xmp = ( - self._decoder.get_info() - ) - self.is_animated = self.n_frames > 1 - - if icc: - self.info["icc_profile"] = icc - if xmp: - self.info["xmp"] = xmp - - if exif_orientation != 1 or exif: - exif_data = Image.Exif() - if exif: - exif_data.load(exif) - original_orientation = exif_data.get(ExifTags.Base.Orientation, 1) - else: - original_orientation = 1 - if exif_orientation != original_orientation: - exif_data[ExifTags.Base.Orientation] = exif_orientation - exif = exif_data.tobytes() - if exif: - self.info["exif"] = exif - self.seek(0) - - def seek(self, frame: int) -> None: - if not self._seek_check(frame): - return - - # Set tile - self.__frame = frame - self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 0, self.mode)] - - def load(self) -> Image.core.PixelAccess | None: - if self.tile: - # We need to load the image data for this frame - data, timescale, pts_in_timescales, duration_in_timescales = ( - self._decoder.get_frame(self.__frame) - ) - self.info["timestamp"] = round(1000 * (pts_in_timescales / timescale)) - self.info["duration"] = round(1000 * (duration_in_timescales / timescale)) - - if self.fp and self._exclusive_fp: - self.fp.close() - self.fp = BytesIO(data) - - return super().load() - - def load_seek(self, pos: int) -> None: - pass - - def tell(self) -> int: - return self.__frame - - -def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - _save(im, fp, filename, save_all=True) - - -def _save( - im: Image.Image, fp: IO[bytes], filename: str | bytes, save_all: bool = False -) -> None: - info = im.encoderinfo.copy() - if save_all: - append_images = list(info.get("append_images", [])) - else: - append_images = [] - - total = 0 - for ims in [im] + append_images: - total += getattr(ims, "n_frames", 1) - - quality = info.get("quality", 75) - if not isinstance(quality, int) or quality < 0 or quality > 100: - msg = "Invalid quality setting" - raise ValueError(msg) - - duration = info.get("duration", 0) - subsampling = info.get("subsampling", "4:2:0") - speed = info.get("speed", 6) - max_threads = info.get("max_threads", _get_default_max_threads()) - codec = info.get("codec", "auto") - if codec != "auto" and not _avif.encoder_codec_available(codec): - msg = "Invalid saving codec" - raise ValueError(msg) - range_ = info.get("range", "full") - tile_rows_log2 = info.get("tile_rows", 0) - tile_cols_log2 = info.get("tile_cols", 0) - alpha_premultiplied = bool(info.get("alpha_premultiplied", False)) - autotiling = bool(info.get("autotiling", tile_rows_log2 == tile_cols_log2 == 0)) - - icc_profile = info.get("icc_profile", im.info.get("icc_profile")) - exif_orientation = 1 - if exif := info.get("exif"): - if isinstance(exif, Image.Exif): - exif_data = exif - else: - exif_data = Image.Exif() - exif_data.load(exif) - if ExifTags.Base.Orientation in exif_data: - exif_orientation = exif_data.pop(ExifTags.Base.Orientation) - exif = exif_data.tobytes() if exif_data else b"" - elif isinstance(exif, Image.Exif): - exif = exif_data.tobytes() - - xmp = info.get("xmp") - - if isinstance(xmp, str): - xmp = xmp.encode("utf-8") - - advanced = info.get("advanced") - if advanced is not None: - if isinstance(advanced, dict): - advanced = advanced.items() - try: - advanced = tuple(advanced) - except TypeError: - invalid = True - else: - invalid = any(not isinstance(v, tuple) or len(v) != 2 for v in advanced) - if invalid: - msg = ( - "advanced codec options must be a dict of key-value string " - "pairs or a series of key-value two-tuples" - ) - raise ValueError(msg) - - # Setup the AVIF encoder - enc = _avif.AvifEncoder( - im.size, - subsampling, - quality, - speed, - max_threads, - codec, - range_, - tile_rows_log2, - tile_cols_log2, - alpha_premultiplied, - autotiling, - icc_profile or b"", - exif or b"", - exif_orientation, - xmp or b"", - advanced, - ) - - # Add each frame - frame_idx = 0 - frame_duration = 0 - cur_idx = im.tell() - is_single_frame = total == 1 - try: - for ims in [im] + append_images: - # Get number of frames in this image - nfr = getattr(ims, "n_frames", 1) - - for idx in range(nfr): - ims.seek(idx) - - # Make sure image mode is supported - frame = ims - rawmode = ims.mode - if ims.mode not in {"RGB", "RGBA"}: - rawmode = "RGBA" if ims.has_transparency_data else "RGB" - frame = ims.convert(rawmode) - - # Update frame duration - if isinstance(duration, (list, tuple)): - frame_duration = duration[frame_idx] - else: - frame_duration = duration - - # Append the frame to the animation encoder - enc.add( - frame.tobytes("raw", rawmode), - frame_duration, - frame.size, - rawmode, - is_single_frame, - ) - - # Update frame index - frame_idx += 1 - - if not save_all: - break - - finally: - im.seek(cur_idx) - - # Get the final output from the encoder - data = enc.finish() - if data is None: - msg = "cannot write file as AVIF (encoder returned None)" - raise OSError(msg) - - fp.write(data) - - -Image.register_open(AvifImageFile.format, AvifImageFile, _accept) -if SUPPORTED: - Image.register_save(AvifImageFile.format, _save) - Image.register_save_all(AvifImageFile.format, _save_all) - Image.register_extensions(AvifImageFile.format, [".avif", ".avifs"]) - Image.register_mime(AvifImageFile.format, "image/avif") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/BdfFontFile.py b/.venv-docs/lib/python3.12/site-packages/PIL/BdfFontFile.py deleted file mode 100644 index f175e2f4..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/BdfFontFile.py +++ /dev/null @@ -1,122 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# bitmap distribution font (bdf) file parser -# -# history: -# 1996-05-16 fl created (as bdf2pil) -# 1997-08-25 fl converted to FontFile driver -# 2001-05-25 fl removed bogus __init__ call -# 2002-11-20 fl robustification (from Kevin Cazabon, Dmitry Vasiliev) -# 2003-04-22 fl more robustification (from Graham Dumpleton) -# -# Copyright (c) 1997-2003 by Secret Labs AB. -# Copyright (c) 1997-2003 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# - -""" -Parse X Bitmap Distribution Format (BDF) -""" -from __future__ import annotations - -from typing import BinaryIO - -from . import FontFile, Image - - -def bdf_char( - f: BinaryIO, -) -> ( - tuple[ - str, - int, - tuple[tuple[int, int], tuple[int, int, int, int], tuple[int, int, int, int]], - Image.Image, - ] - | None -): - # skip to STARTCHAR - while True: - s = f.readline() - if not s: - return None - if s.startswith(b"STARTCHAR"): - break - id = s[9:].strip().decode("ascii") - - # load symbol properties - props = {} - while True: - s = f.readline() - if not s or s.startswith(b"BITMAP"): - break - i = s.find(b" ") - props[s[:i].decode("ascii")] = s[i + 1 : -1].decode("ascii") - - # load bitmap - bitmap = bytearray() - while True: - s = f.readline() - if not s or s.startswith(b"ENDCHAR"): - break - bitmap += s[:-1] - - # The word BBX - # followed by the width in x (BBw), height in y (BBh), - # and x and y displacement (BBxoff0, BByoff0) - # of the lower left corner from the origin of the character. - width, height, x_disp, y_disp = (int(p) for p in props["BBX"].split()) - - # The word DWIDTH - # followed by the width in x and y of the character in device pixels. - dwx, dwy = (int(p) for p in props["DWIDTH"].split()) - - bbox = ( - (dwx, dwy), - (x_disp, -y_disp - height, width + x_disp, -y_disp), - (0, 0, width, height), - ) - - try: - im = Image.frombytes("1", (width, height), bitmap, "hex", "1") - except ValueError: - # deal with zero-width characters - im = Image.new("1", (width, height)) - - return id, int(props["ENCODING"]), bbox, im - - -class BdfFontFile(FontFile.FontFile): - """Font file plugin for the X11 BDF format.""" - - def __init__(self, fp: BinaryIO) -> None: - super().__init__() - - s = fp.readline() - if not s.startswith(b"STARTFONT 2.1"): - msg = "not a valid BDF file" - raise SyntaxError(msg) - - props = {} - comments = [] - - while True: - s = fp.readline() - if not s or s.startswith(b"ENDPROPERTIES"): - break - i = s.find(b" ") - props[s[:i].decode("ascii")] = s[i + 1 : -1].decode("ascii") - if s[:i] in [b"COMMENT", b"COPYRIGHT"]: - if s.find(b"LogicalFontDescription") < 0: - comments.append(s[i + 1 : -1].decode("ascii")) - - while True: - c = bdf_char(fp) - if not c: - break - id, ch, (xy, dst, src), im = c - if 0 <= ch < len(self.glyph): - self.glyph[ch] = xy, dst, src, im diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/BlpImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/BlpImagePlugin.py deleted file mode 100644 index f7be7746..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/BlpImagePlugin.py +++ /dev/null @@ -1,497 +0,0 @@ -""" -Blizzard Mipmap Format (.blp) -Jerome Leclanche - -The contents of this file are hereby released in the public domain (CC0) -Full text of the CC0 license: - https://creativecommons.org/publicdomain/zero/1.0/ - -BLP1 files, used mostly in Warcraft III, are not fully supported. -All types of BLP2 files used in World of Warcraft are supported. - -The BLP file structure consists of a header, up to 16 mipmaps of the -texture - -Texture sizes must be powers of two, though the two dimensions do -not have to be equal; 512x256 is valid, but 512x200 is not. -The first mipmap (mipmap #0) is the full size image; each subsequent -mipmap halves both dimensions. The final mipmap should be 1x1. - -BLP files come in many different flavours: -* JPEG-compressed (type == 0) - only supported for BLP1. -* RAW images (type == 1, encoding == 1). Each mipmap is stored as an - array of 8-bit values, one per pixel, left to right, top to bottom. - Each value is an index to the palette. -* DXT-compressed (type == 1, encoding == 2): -- DXT1 compression is used if alpha_encoding == 0. - - An additional alpha bit is used if alpha_depth == 1. - - DXT3 compression is used if alpha_encoding == 1. - - DXT5 compression is used if alpha_encoding == 7. -""" - -from __future__ import annotations - -import abc -import os -import struct -from enum import IntEnum -from io import BytesIO -from typing import IO - -from . import Image, ImageFile - - -class Format(IntEnum): - JPEG = 0 - - -class Encoding(IntEnum): - UNCOMPRESSED = 1 - DXT = 2 - UNCOMPRESSED_RAW_BGRA = 3 - - -class AlphaEncoding(IntEnum): - DXT1 = 0 - DXT3 = 1 - DXT5 = 7 - - -def unpack_565(i: int) -> tuple[int, int, int]: - return ((i >> 11) & 0x1F) << 3, ((i >> 5) & 0x3F) << 2, (i & 0x1F) << 3 - - -def decode_dxt1( - data: bytes, alpha: bool = False -) -> tuple[bytearray, bytearray, bytearray, bytearray]: - """ - input: one "row" of data (i.e. will produce 4*width pixels) - """ - - blocks = len(data) // 8 # number of blocks in row - ret = (bytearray(), bytearray(), bytearray(), bytearray()) - - for block_index in range(blocks): - # Decode next 8-byte block. - idx = block_index * 8 - color0, color1, bits = struct.unpack_from("> 2 - - a = 0xFF - if control == 0: - r, g, b = r0, g0, b0 - elif control == 1: - r, g, b = r1, g1, b1 - elif control == 2: - if color0 > color1: - r = (2 * r0 + r1) // 3 - g = (2 * g0 + g1) // 3 - b = (2 * b0 + b1) // 3 - else: - r = (r0 + r1) // 2 - g = (g0 + g1) // 2 - b = (b0 + b1) // 2 - elif control == 3: - if color0 > color1: - r = (2 * r1 + r0) // 3 - g = (2 * g1 + g0) // 3 - b = (2 * b1 + b0) // 3 - else: - r, g, b, a = 0, 0, 0, 0 - - if alpha: - ret[j].extend([r, g, b, a]) - else: - ret[j].extend([r, g, b]) - - return ret - - -def decode_dxt3(data: bytes) -> tuple[bytearray, bytearray, bytearray, bytearray]: - """ - input: one "row" of data (i.e. will produce 4*width pixels) - """ - - blocks = len(data) // 16 # number of blocks in row - ret = (bytearray(), bytearray(), bytearray(), bytearray()) - - for block_index in range(blocks): - idx = block_index * 16 - block = data[idx : idx + 16] - # Decode next 16-byte block. - bits = struct.unpack_from("<8B", block) - color0, color1 = struct.unpack_from(">= 4 - else: - high = True - a &= 0xF - a *= 17 # We get a value between 0 and 15 - - color_code = (code >> 2 * (4 * j + i)) & 0x03 - - if color_code == 0: - r, g, b = r0, g0, b0 - elif color_code == 1: - r, g, b = r1, g1, b1 - elif color_code == 2: - r = (2 * r0 + r1) // 3 - g = (2 * g0 + g1) // 3 - b = (2 * b0 + b1) // 3 - elif color_code == 3: - r = (2 * r1 + r0) // 3 - g = (2 * g1 + g0) // 3 - b = (2 * b1 + b0) // 3 - - ret[j].extend([r, g, b, a]) - - return ret - - -def decode_dxt5(data: bytes) -> tuple[bytearray, bytearray, bytearray, bytearray]: - """ - input: one "row" of data (i.e. will produce 4 * width pixels) - """ - - blocks = len(data) // 16 # number of blocks in row - ret = (bytearray(), bytearray(), bytearray(), bytearray()) - - for block_index in range(blocks): - idx = block_index * 16 - block = data[idx : idx + 16] - # Decode next 16-byte block. - a0, a1 = struct.unpack_from("> alphacode_index) & 0x07 - elif alphacode_index == 15: - alphacode = (alphacode2 >> 15) | ((alphacode1 << 1) & 0x06) - else: # alphacode_index >= 18 and alphacode_index <= 45 - alphacode = (alphacode1 >> (alphacode_index - 16)) & 0x07 - - if alphacode == 0: - a = a0 - elif alphacode == 1: - a = a1 - elif a0 > a1: - a = ((8 - alphacode) * a0 + (alphacode - 1) * a1) // 7 - elif alphacode == 6: - a = 0 - elif alphacode == 7: - a = 255 - else: - a = ((6 - alphacode) * a0 + (alphacode - 1) * a1) // 5 - - color_code = (code >> 2 * (4 * j + i)) & 0x03 - - if color_code == 0: - r, g, b = r0, g0, b0 - elif color_code == 1: - r, g, b = r1, g1, b1 - elif color_code == 2: - r = (2 * r0 + r1) // 3 - g = (2 * g0 + g1) // 3 - b = (2 * b0 + b1) // 3 - elif color_code == 3: - r = (2 * r1 + r0) // 3 - g = (2 * g1 + g0) // 3 - b = (2 * b1 + b0) // 3 - - ret[j].extend([r, g, b, a]) - - return ret - - -class BLPFormatError(NotImplementedError): - pass - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith((b"BLP1", b"BLP2")) - - -class BlpImageFile(ImageFile.ImageFile): - """ - Blizzard Mipmap Format - """ - - format = "BLP" - format_description = "Blizzard Mipmap Format" - - def _open(self) -> None: - self.magic = self.fp.read(4) - if not _accept(self.magic): - msg = f"Bad BLP magic {repr(self.magic)}" - raise BLPFormatError(msg) - - compression = struct.unpack(" tuple[int, int]: - try: - self._read_header() - self._load() - except struct.error as e: - msg = "Truncated BLP file" - raise OSError(msg) from e - return -1, 0 - - @abc.abstractmethod - def _load(self) -> None: - pass - - def _read_header(self) -> None: - self._offsets = struct.unpack("<16I", self._safe_read(16 * 4)) - self._lengths = struct.unpack("<16I", self._safe_read(16 * 4)) - - def _safe_read(self, length: int) -> bytes: - assert self.fd is not None - return ImageFile._safe_read(self.fd, length) - - def _read_palette(self) -> list[tuple[int, int, int, int]]: - ret = [] - for i in range(256): - try: - b, g, r, a = struct.unpack("<4B", self._safe_read(4)) - except struct.error: - break - ret.append((b, g, r, a)) - return ret - - def _read_bgra( - self, palette: list[tuple[int, int, int, int]], alpha: bool - ) -> bytearray: - data = bytearray() - _data = BytesIO(self._safe_read(self._lengths[0])) - while True: - try: - (offset,) = struct.unpack(" None: - self._compression, self._encoding, alpha = self.args - - if self._compression == Format.JPEG: - self._decode_jpeg_stream() - - elif self._compression == 1: - if self._encoding in (4, 5): - palette = self._read_palette() - data = self._read_bgra(palette, alpha) - self.set_as_raw(data) - else: - msg = f"Unsupported BLP encoding {repr(self._encoding)}" - raise BLPFormatError(msg) - else: - msg = f"Unsupported BLP compression {repr(self._encoding)}" - raise BLPFormatError(msg) - - def _decode_jpeg_stream(self) -> None: - from .JpegImagePlugin import JpegImageFile - - (jpeg_header_size,) = struct.unpack(" None: - self._compression, self._encoding, alpha, self._alpha_encoding = self.args - - palette = self._read_palette() - - assert self.fd is not None - self.fd.seek(self._offsets[0]) - - if self._compression == 1: - # Uncompressed or DirectX compression - - if self._encoding == Encoding.UNCOMPRESSED: - data = self._read_bgra(palette, alpha) - - elif self._encoding == Encoding.DXT: - data = bytearray() - if self._alpha_encoding == AlphaEncoding.DXT1: - linesize = (self.state.xsize + 3) // 4 * 8 - for yb in range((self.state.ysize + 3) // 4): - for d in decode_dxt1(self._safe_read(linesize), alpha): - data += d - - elif self._alpha_encoding == AlphaEncoding.DXT3: - linesize = (self.state.xsize + 3) // 4 * 16 - for yb in range((self.state.ysize + 3) // 4): - for d in decode_dxt3(self._safe_read(linesize)): - data += d - - elif self._alpha_encoding == AlphaEncoding.DXT5: - linesize = (self.state.xsize + 3) // 4 * 16 - for yb in range((self.state.ysize + 3) // 4): - for d in decode_dxt5(self._safe_read(linesize)): - data += d - else: - msg = f"Unsupported alpha encoding {repr(self._alpha_encoding)}" - raise BLPFormatError(msg) - else: - msg = f"Unknown BLP encoding {repr(self._encoding)}" - raise BLPFormatError(msg) - - else: - msg = f"Unknown BLP compression {repr(self._compression)}" - raise BLPFormatError(msg) - - self.set_as_raw(data) - - -class BLPEncoder(ImageFile.PyEncoder): - _pushes_fd = True - - def _write_palette(self) -> bytes: - data = b"" - assert self.im is not None - palette = self.im.getpalette("RGBA", "RGBA") - for i in range(len(palette) // 4): - r, g, b, a = palette[i * 4 : (i + 1) * 4] - data += struct.pack("<4B", b, g, r, a) - while len(data) < 256 * 4: - data += b"\x00" * 4 - return data - - def encode(self, bufsize: int) -> tuple[int, int, bytes]: - palette_data = self._write_palette() - - offset = 20 + 16 * 4 * 2 + len(palette_data) - data = struct.pack("<16I", offset, *((0,) * 15)) - - assert self.im is not None - w, h = self.im.size - data += struct.pack("<16I", w * h, *((0,) * 15)) - - data += palette_data - - for y in range(h): - for x in range(w): - data += struct.pack(" None: - if im.mode != "P": - msg = "Unsupported BLP image mode" - raise ValueError(msg) - - magic = b"BLP1" if im.encoderinfo.get("blp_version") == "BLP1" else b"BLP2" - fp.write(magic) - - assert im.palette is not None - fp.write(struct.pack(" mode, rawmode - 1: ("P", "P;1"), - 4: ("P", "P;4"), - 8: ("P", "P"), - 16: ("RGB", "BGR;15"), - 24: ("RGB", "BGR"), - 32: ("RGB", "BGRX"), -} - -USE_RAW_ALPHA = False - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(b"BM") - - -def _dib_accept(prefix: bytes) -> bool: - return i32(prefix) in [12, 40, 52, 56, 64, 108, 124] - - -# ============================================================================= -# Image plugin for the Windows BMP format. -# ============================================================================= -class BmpImageFile(ImageFile.ImageFile): - """Image plugin for the Windows Bitmap format (BMP)""" - - # ------------------------------------------------------------- Description - format_description = "Windows Bitmap" - format = "BMP" - - # -------------------------------------------------- BMP Compression values - COMPRESSIONS = {"RAW": 0, "RLE8": 1, "RLE4": 2, "BITFIELDS": 3, "JPEG": 4, "PNG": 5} - for k, v in COMPRESSIONS.items(): - vars()[k] = v - - def _bitmap(self, header: int = 0, offset: int = 0) -> None: - """Read relevant info about the BMP""" - read, seek = self.fp.read, self.fp.seek - if header: - seek(header) - # read bmp header size @offset 14 (this is part of the header size) - file_info: dict[str, bool | int | tuple[int, ...]] = { - "header_size": i32(read(4)), - "direction": -1, - } - - # -------------------- If requested, read header at a specific position - # read the rest of the bmp header, without its size - assert isinstance(file_info["header_size"], int) - header_data = ImageFile._safe_read(self.fp, file_info["header_size"] - 4) - - # ------------------------------- Windows Bitmap v2, IBM OS/2 Bitmap v1 - # ----- This format has different offsets because of width/height types - # 12: BITMAPCOREHEADER/OS21XBITMAPHEADER - if file_info["header_size"] == 12: - file_info["width"] = i16(header_data, 0) - file_info["height"] = i16(header_data, 2) - file_info["planes"] = i16(header_data, 4) - file_info["bits"] = i16(header_data, 6) - file_info["compression"] = self.COMPRESSIONS["RAW"] - file_info["palette_padding"] = 3 - - # --------------------------------------------- Windows Bitmap v3 to v5 - # 40: BITMAPINFOHEADER - # 52: BITMAPV2HEADER - # 56: BITMAPV3HEADER - # 64: BITMAPCOREHEADER2/OS22XBITMAPHEADER - # 108: BITMAPV4HEADER - # 124: BITMAPV5HEADER - elif file_info["header_size"] in (40, 52, 56, 64, 108, 124): - file_info["y_flip"] = header_data[7] == 0xFF - file_info["direction"] = 1 if file_info["y_flip"] else -1 - file_info["width"] = i32(header_data, 0) - file_info["height"] = ( - i32(header_data, 4) - if not file_info["y_flip"] - else 2**32 - i32(header_data, 4) - ) - file_info["planes"] = i16(header_data, 8) - file_info["bits"] = i16(header_data, 10) - file_info["compression"] = i32(header_data, 12) - # byte size of pixel data - file_info["data_size"] = i32(header_data, 16) - file_info["pixels_per_meter"] = ( - i32(header_data, 20), - i32(header_data, 24), - ) - file_info["colors"] = i32(header_data, 28) - file_info["palette_padding"] = 4 - assert isinstance(file_info["pixels_per_meter"], tuple) - self.info["dpi"] = tuple(x / 39.3701 for x in file_info["pixels_per_meter"]) - if file_info["compression"] == self.COMPRESSIONS["BITFIELDS"]: - masks = ["r_mask", "g_mask", "b_mask"] - if len(header_data) >= 48: - if len(header_data) >= 52: - masks.append("a_mask") - else: - file_info["a_mask"] = 0x0 - for idx, mask in enumerate(masks): - file_info[mask] = i32(header_data, 36 + idx * 4) - else: - # 40 byte headers only have the three components in the - # bitfields masks, ref: - # https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx - # See also - # https://github.com/python-pillow/Pillow/issues/1293 - # There is a 4th component in the RGBQuad, in the alpha - # location, but it is listed as a reserved component, - # and it is not generally an alpha channel - file_info["a_mask"] = 0x0 - for mask in masks: - file_info[mask] = i32(read(4)) - assert isinstance(file_info["r_mask"], int) - assert isinstance(file_info["g_mask"], int) - assert isinstance(file_info["b_mask"], int) - assert isinstance(file_info["a_mask"], int) - file_info["rgb_mask"] = ( - file_info["r_mask"], - file_info["g_mask"], - file_info["b_mask"], - ) - file_info["rgba_mask"] = ( - file_info["r_mask"], - file_info["g_mask"], - file_info["b_mask"], - file_info["a_mask"], - ) - else: - msg = f"Unsupported BMP header type ({file_info['header_size']})" - raise OSError(msg) - - # ------------------ Special case : header is reported 40, which - # ---------------------- is shorter than real size for bpp >= 16 - assert isinstance(file_info["width"], int) - assert isinstance(file_info["height"], int) - self._size = file_info["width"], file_info["height"] - - # ------- If color count was not found in the header, compute from bits - assert isinstance(file_info["bits"], int) - file_info["colors"] = ( - file_info["colors"] - if file_info.get("colors", 0) - else (1 << file_info["bits"]) - ) - assert isinstance(file_info["colors"], int) - if offset == 14 + file_info["header_size"] and file_info["bits"] <= 8: - offset += 4 * file_info["colors"] - - # ---------------------- Check bit depth for unusual unsupported values - self._mode, raw_mode = BIT2MODE.get(file_info["bits"], ("", "")) - if not self.mode: - msg = f"Unsupported BMP pixel depth ({file_info['bits']})" - raise OSError(msg) - - # ---------------- Process BMP with Bitfields compression (not palette) - decoder_name = "raw" - if file_info["compression"] == self.COMPRESSIONS["BITFIELDS"]: - SUPPORTED: dict[int, list[tuple[int, ...]]] = { - 32: [ - (0xFF0000, 0xFF00, 0xFF, 0x0), - (0xFF000000, 0xFF0000, 0xFF00, 0x0), - (0xFF000000, 0xFF00, 0xFF, 0x0), - (0xFF000000, 0xFF0000, 0xFF00, 0xFF), - (0xFF, 0xFF00, 0xFF0000, 0xFF000000), - (0xFF0000, 0xFF00, 0xFF, 0xFF000000), - (0xFF000000, 0xFF00, 0xFF, 0xFF0000), - (0x0, 0x0, 0x0, 0x0), - ], - 24: [(0xFF0000, 0xFF00, 0xFF)], - 16: [(0xF800, 0x7E0, 0x1F), (0x7C00, 0x3E0, 0x1F)], - } - MASK_MODES = { - (32, (0xFF0000, 0xFF00, 0xFF, 0x0)): "BGRX", - (32, (0xFF000000, 0xFF0000, 0xFF00, 0x0)): "XBGR", - (32, (0xFF000000, 0xFF00, 0xFF, 0x0)): "BGXR", - (32, (0xFF000000, 0xFF0000, 0xFF00, 0xFF)): "ABGR", - (32, (0xFF, 0xFF00, 0xFF0000, 0xFF000000)): "RGBA", - (32, (0xFF0000, 0xFF00, 0xFF, 0xFF000000)): "BGRA", - (32, (0xFF000000, 0xFF00, 0xFF, 0xFF0000)): "BGAR", - (32, (0x0, 0x0, 0x0, 0x0)): "BGRA", - (24, (0xFF0000, 0xFF00, 0xFF)): "BGR", - (16, (0xF800, 0x7E0, 0x1F)): "BGR;16", - (16, (0x7C00, 0x3E0, 0x1F)): "BGR;15", - } - if file_info["bits"] in SUPPORTED: - if ( - file_info["bits"] == 32 - and file_info["rgba_mask"] in SUPPORTED[file_info["bits"]] - ): - assert isinstance(file_info["rgba_mask"], tuple) - raw_mode = MASK_MODES[(file_info["bits"], file_info["rgba_mask"])] - self._mode = "RGBA" if "A" in raw_mode else self.mode - elif ( - file_info["bits"] in (24, 16) - and file_info["rgb_mask"] in SUPPORTED[file_info["bits"]] - ): - assert isinstance(file_info["rgb_mask"], tuple) - raw_mode = MASK_MODES[(file_info["bits"], file_info["rgb_mask"])] - else: - msg = "Unsupported BMP bitfields layout" - raise OSError(msg) - else: - msg = "Unsupported BMP bitfields layout" - raise OSError(msg) - elif file_info["compression"] == self.COMPRESSIONS["RAW"]: - if file_info["bits"] == 32 and ( - header == 22 or USE_RAW_ALPHA # 32-bit .cur offset - ): - raw_mode, self._mode = "BGRA", "RGBA" - elif file_info["compression"] in ( - self.COMPRESSIONS["RLE8"], - self.COMPRESSIONS["RLE4"], - ): - decoder_name = "bmp_rle" - else: - msg = f"Unsupported BMP compression ({file_info['compression']})" - raise OSError(msg) - - # --------------- Once the header is processed, process the palette/LUT - if self.mode == "P": # Paletted for 1, 4 and 8 bit images - # ---------------------------------------------------- 1-bit images - if not (0 < file_info["colors"] <= 65536): - msg = f"Unsupported BMP Palette size ({file_info['colors']})" - raise OSError(msg) - else: - assert isinstance(file_info["palette_padding"], int) - padding = file_info["palette_padding"] - palette = read(padding * file_info["colors"]) - grayscale = True - indices = ( - (0, 255) - if file_info["colors"] == 2 - else list(range(file_info["colors"])) - ) - - # ----------------- Check if grayscale and ignore palette if so - for ind, val in enumerate(indices): - rgb = palette[ind * padding : ind * padding + 3] - if rgb != o8(val) * 3: - grayscale = False - - # ------- If all colors are gray, white or black, ditch palette - if grayscale: - self._mode = "1" if file_info["colors"] == 2 else "L" - raw_mode = self.mode - else: - self._mode = "P" - self.palette = ImagePalette.raw( - "BGRX" if padding == 4 else "BGR", palette - ) - - # ---------------------------- Finally set the tile data for the plugin - self.info["compression"] = file_info["compression"] - args: list[Any] = [raw_mode] - if decoder_name == "bmp_rle": - args.append(file_info["compression"] == self.COMPRESSIONS["RLE4"]) - else: - assert isinstance(file_info["width"], int) - args.append(((file_info["width"] * file_info["bits"] + 31) >> 3) & (~3)) - args.append(file_info["direction"]) - self.tile = [ - ImageFile._Tile( - decoder_name, - (0, 0, file_info["width"], file_info["height"]), - offset or self.fp.tell(), - tuple(args), - ) - ] - - def _open(self) -> None: - """Open file, check magic number and read header""" - # read 14 bytes: magic number, filesize, reserved, header final offset - head_data = self.fp.read(14) - # choke if the file does not have the required magic bytes - if not _accept(head_data): - msg = "Not a BMP file" - raise SyntaxError(msg) - # read the start position of the BMP image data (u32) - offset = i32(head_data, 10) - # load bitmap information (offset=raster info) - self._bitmap(offset=offset) - - -class BmpRleDecoder(ImageFile.PyDecoder): - _pulls_fd = True - - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: - assert self.fd is not None - rle4 = self.args[1] - data = bytearray() - x = 0 - dest_length = self.state.xsize * self.state.ysize - while len(data) < dest_length: - pixels = self.fd.read(1) - byte = self.fd.read(1) - if not pixels or not byte: - break - num_pixels = pixels[0] - if num_pixels: - # encoded mode - if x + num_pixels > self.state.xsize: - # Too much data for row - num_pixels = max(0, self.state.xsize - x) - if rle4: - first_pixel = o8(byte[0] >> 4) - second_pixel = o8(byte[0] & 0x0F) - for index in range(num_pixels): - if index % 2 == 0: - data += first_pixel - else: - data += second_pixel - else: - data += byte * num_pixels - x += num_pixels - else: - if byte[0] == 0: - # end of line - while len(data) % self.state.xsize != 0: - data += b"\x00" - x = 0 - elif byte[0] == 1: - # end of bitmap - break - elif byte[0] == 2: - # delta - bytes_read = self.fd.read(2) - if len(bytes_read) < 2: - break - right, up = self.fd.read(2) - data += b"\x00" * (right + up * self.state.xsize) - x = len(data) % self.state.xsize - else: - # absolute mode - if rle4: - # 2 pixels per byte - byte_count = byte[0] // 2 - bytes_read = self.fd.read(byte_count) - for byte_read in bytes_read: - data += o8(byte_read >> 4) - data += o8(byte_read & 0x0F) - else: - byte_count = byte[0] - bytes_read = self.fd.read(byte_count) - data += bytes_read - if len(bytes_read) < byte_count: - break - x += byte[0] - - # align to 16-bit word boundary - if self.fd.tell() % 2 != 0: - self.fd.seek(1, os.SEEK_CUR) - rawmode = "L" if self.mode == "L" else "P" - self.set_as_raw(bytes(data), rawmode, (0, self.args[-1])) - return -1, 0 - - -# ============================================================================= -# Image plugin for the DIB format (BMP alias) -# ============================================================================= -class DibImageFile(BmpImageFile): - format = "DIB" - format_description = "Windows Bitmap" - - def _open(self) -> None: - self._bitmap() - - -# -# -------------------------------------------------------------------- -# Write BMP file - - -SAVE = { - "1": ("1", 1, 2), - "L": ("L", 8, 256), - "P": ("P", 8, 256), - "RGB": ("BGR", 24, 0), - "RGBA": ("BGRA", 32, 0), -} - - -def _dib_save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - _save(im, fp, filename, False) - - -def _save( - im: Image.Image, fp: IO[bytes], filename: str | bytes, bitmap_header: bool = True -) -> None: - try: - rawmode, bits, colors = SAVE[im.mode] - except KeyError as e: - msg = f"cannot write mode {im.mode} as BMP" - raise OSError(msg) from e - - info = im.encoderinfo - - dpi = info.get("dpi", (96, 96)) - - # 1 meter == 39.3701 inches - ppm = tuple(int(x * 39.3701 + 0.5) for x in dpi) - - stride = ((im.size[0] * bits + 7) // 8 + 3) & (~3) - header = 40 # or 64 for OS/2 version 2 - image = stride * im.size[1] - - if im.mode == "1": - palette = b"".join(o8(i) * 3 + b"\x00" for i in (0, 255)) - elif im.mode == "L": - palette = b"".join(o8(i) * 3 + b"\x00" for i in range(256)) - elif im.mode == "P": - palette = im.im.getpalette("RGB", "BGRX") - colors = len(palette) // 4 - else: - palette = None - - # bitmap header - if bitmap_header: - offset = 14 + header + colors * 4 - file_size = offset + image - if file_size > 2**32 - 1: - msg = "File size is too large for the BMP format" - raise ValueError(msg) - fp.write( - b"BM" # file type (magic) - + o32(file_size) # file size - + o32(0) # reserved - + o32(offset) # image data offset - ) - - # bitmap info header - fp.write( - o32(header) # info header size - + o32(im.size[0]) # width - + o32(im.size[1]) # height - + o16(1) # planes - + o16(bits) # depth - + o32(0) # compression (0=uncompressed) - + o32(image) # size of bitmap - + o32(ppm[0]) # resolution - + o32(ppm[1]) # resolution - + o32(colors) # colors used - + o32(colors) # colors important - ) - - fp.write(b"\0" * (header - 40)) # padding (for OS/2 format) - - if palette: - fp.write(palette) - - ImageFile._save( - im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 0, (rawmode, stride, -1))] - ) - - -# -# -------------------------------------------------------------------- -# Registry - - -Image.register_open(BmpImageFile.format, BmpImageFile, _accept) -Image.register_save(BmpImageFile.format, _save) - -Image.register_extension(BmpImageFile.format, ".bmp") - -Image.register_mime(BmpImageFile.format, "image/bmp") - -Image.register_decoder("bmp_rle", BmpRleDecoder) - -Image.register_open(DibImageFile.format, DibImageFile, _dib_accept) -Image.register_save(DibImageFile.format, _dib_save) - -Image.register_extension(DibImageFile.format, ".dib") - -Image.register_mime(DibImageFile.format, "image/bmp") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/BufrStubImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/BufrStubImagePlugin.py deleted file mode 100644 index 8c5da14f..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/BufrStubImagePlugin.py +++ /dev/null @@ -1,75 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# BUFR stub adapter -# -# Copyright (c) 1996-2003 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import os -from typing import IO - -from . import Image, ImageFile - -_handler = None - - -def register_handler(handler: ImageFile.StubHandler | None) -> None: - """ - Install application-specific BUFR image handler. - - :param handler: Handler object. - """ - global _handler - _handler = handler - - -# -------------------------------------------------------------------- -# Image adapter - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith((b"BUFR", b"ZCZC")) - - -class BufrStubImageFile(ImageFile.StubImageFile): - format = "BUFR" - format_description = "BUFR" - - def _open(self) -> None: - if not _accept(self.fp.read(4)): - msg = "Not a BUFR file" - raise SyntaxError(msg) - - self.fp.seek(-4, os.SEEK_CUR) - - # make something up - self._mode = "F" - self._size = 1, 1 - - loader = self._load() - if loader: - loader.open(self) - - def _load(self) -> ImageFile.StubHandler | None: - return _handler - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - if _handler is None or not hasattr(_handler, "save"): - msg = "BUFR save handler not installed" - raise OSError(msg) - _handler.save(im, fp, filename) - - -# -------------------------------------------------------------------- -# Registry - -Image.register_open(BufrStubImageFile.format, BufrStubImageFile, _accept) -Image.register_save(BufrStubImageFile.format, _save) - -Image.register_extension(BufrStubImageFile.format, ".bufr") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/ContainerIO.py b/.venv-docs/lib/python3.12/site-packages/PIL/ContainerIO.py deleted file mode 100644 index ec9e66c7..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/ContainerIO.py +++ /dev/null @@ -1,173 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# a class to read from a container file -# -# History: -# 1995-06-18 fl Created -# 1995-09-07 fl Added readline(), readlines() -# -# Copyright (c) 1997-2001 by Secret Labs AB -# Copyright (c) 1995 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io -from collections.abc import Iterable -from typing import IO, AnyStr, NoReturn - - -class ContainerIO(IO[AnyStr]): - """ - A file object that provides read access to a part of an existing - file (for example a TAR file). - """ - - def __init__(self, file: IO[AnyStr], offset: int, length: int) -> None: - """ - Create file object. - - :param file: Existing file. - :param offset: Start of region, in bytes. - :param length: Size of region, in bytes. - """ - self.fh: IO[AnyStr] = file - self.pos = 0 - self.offset = offset - self.length = length - self.fh.seek(offset) - - ## - # Always false. - - def isatty(self) -> bool: - return False - - def seekable(self) -> bool: - return True - - def seek(self, offset: int, mode: int = io.SEEK_SET) -> int: - """ - Move file pointer. - - :param offset: Offset in bytes. - :param mode: Starting position. Use 0 for beginning of region, 1 - for current offset, and 2 for end of region. You cannot move - the pointer outside the defined region. - :returns: Offset from start of region, in bytes. - """ - if mode == 1: - self.pos = self.pos + offset - elif mode == 2: - self.pos = self.length + offset - else: - self.pos = offset - # clamp - self.pos = max(0, min(self.pos, self.length)) - self.fh.seek(self.offset + self.pos) - return self.pos - - def tell(self) -> int: - """ - Get current file pointer. - - :returns: Offset from start of region, in bytes. - """ - return self.pos - - def readable(self) -> bool: - return True - - def read(self, n: int = -1) -> AnyStr: - """ - Read data. - - :param n: Number of bytes to read. If omitted, zero or negative, - read until end of region. - :returns: An 8-bit string. - """ - if n > 0: - n = min(n, self.length - self.pos) - else: - n = self.length - self.pos - if n <= 0: # EOF - return b"" if "b" in self.fh.mode else "" # type: ignore[return-value] - self.pos = self.pos + n - return self.fh.read(n) - - def readline(self, n: int = -1) -> AnyStr: - """ - Read a line of text. - - :param n: Number of bytes to read. If omitted, zero or negative, - read until end of line. - :returns: An 8-bit string. - """ - s: AnyStr = b"" if "b" in self.fh.mode else "" # type: ignore[assignment] - newline_character = b"\n" if "b" in self.fh.mode else "\n" - while True: - c = self.read(1) - if not c: - break - s = s + c - if c == newline_character or len(s) == n: - break - return s - - def readlines(self, n: int | None = -1) -> list[AnyStr]: - """ - Read multiple lines of text. - - :param n: Number of lines to read. If omitted, zero, negative or None, - read until end of region. - :returns: A list of 8-bit strings. - """ - lines = [] - while True: - s = self.readline() - if not s: - break - lines.append(s) - if len(lines) == n: - break - return lines - - def writable(self) -> bool: - return False - - def write(self, b: AnyStr) -> NoReturn: - raise NotImplementedError() - - def writelines(self, lines: Iterable[AnyStr]) -> NoReturn: - raise NotImplementedError() - - def truncate(self, size: int | None = None) -> int: - raise NotImplementedError() - - def __enter__(self) -> ContainerIO[AnyStr]: - return self - - def __exit__(self, *args: object) -> None: - self.close() - - def __iter__(self) -> ContainerIO[AnyStr]: - return self - - def __next__(self) -> AnyStr: - line = self.readline() - if not line: - msg = "end of region" - raise StopIteration(msg) - return line - - def fileno(self) -> int: - return self.fh.fileno() - - def flush(self) -> None: - self.fh.flush() - - def close(self) -> None: - self.fh.close() diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/CurImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/CurImagePlugin.py deleted file mode 100644 index 9c188e08..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/CurImagePlugin.py +++ /dev/null @@ -1,75 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# Windows Cursor support for PIL -# -# notes: -# uses BmpImagePlugin.py to read the bitmap data. -# -# history: -# 96-05-27 fl Created -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1996. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from . import BmpImagePlugin, Image -from ._binary import i16le as i16 -from ._binary import i32le as i32 - -# -# -------------------------------------------------------------------- - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(b"\0\0\2\0") - - -## -# Image plugin for Windows Cursor files. - - -class CurImageFile(BmpImagePlugin.BmpImageFile): - format = "CUR" - format_description = "Windows Cursor" - - def _open(self) -> None: - assert self.fp is not None - offset = self.fp.tell() - - # check magic - s = self.fp.read(6) - if not _accept(s): - msg = "not a CUR file" - raise SyntaxError(msg) - - # pick the largest cursor in the file - m = b"" - for i in range(i16(s, 4)): - s = self.fp.read(16) - if not m: - m = s - elif s[0] > m[0] and s[1] > m[1]: - m = s - if not m: - msg = "No cursors were found" - raise TypeError(msg) - - # load as bitmap - self._bitmap(i32(m, 12) + offset) - - # patch up the bitmap height - self._size = self.size[0], self.size[1] // 2 - self.tile = [self.tile[0]._replace(extents=(0, 0) + self.size)] - - -# -# -------------------------------------------------------------------- - -Image.register_open(CurImageFile.format, CurImageFile, _accept) - -Image.register_extension(CurImageFile.format, ".cur") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/DcxImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/DcxImagePlugin.py deleted file mode 100644 index aea661b9..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/DcxImagePlugin.py +++ /dev/null @@ -1,83 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# DCX file handling -# -# DCX is a container file format defined by Intel, commonly used -# for fax applications. Each DCX file consists of a directory -# (a list of file offsets) followed by a set of (usually 1-bit) -# PCX files. -# -# History: -# 1995-09-09 fl Created -# 1996-03-20 fl Properly derived from PcxImageFile. -# 1998-07-15 fl Renamed offset attribute to avoid name clash -# 2002-07-30 fl Fixed file handling -# -# Copyright (c) 1997-98 by Secret Labs AB. -# Copyright (c) 1995-96 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from . import Image -from ._binary import i32le as i32 -from ._util import DeferredError -from .PcxImagePlugin import PcxImageFile - -MAGIC = 0x3ADE68B1 # QUIZ: what's this value, then? - - -def _accept(prefix: bytes) -> bool: - return len(prefix) >= 4 and i32(prefix) == MAGIC - - -## -# Image plugin for the Intel DCX format. - - -class DcxImageFile(PcxImageFile): - format = "DCX" - format_description = "Intel DCX" - _close_exclusive_fp_after_loading = False - - def _open(self) -> None: - # Header - s = self.fp.read(4) - if not _accept(s): - msg = "not a DCX file" - raise SyntaxError(msg) - - # Component directory - self._offset = [] - for i in range(1024): - offset = i32(self.fp.read(4)) - if not offset: - break - self._offset.append(offset) - - self._fp = self.fp - self.frame = -1 - self.n_frames = len(self._offset) - self.is_animated = self.n_frames > 1 - self.seek(0) - - def seek(self, frame: int) -> None: - if not self._seek_check(frame): - return - if isinstance(self._fp, DeferredError): - raise self._fp.ex - self.frame = frame - self.fp = self._fp - self.fp.seek(self._offset[frame]) - PcxImageFile._open(self) - - def tell(self) -> int: - return self.frame - - -Image.register_open(DcxImageFile.format, DcxImageFile, _accept) - -Image.register_extension(DcxImageFile.format, ".dcx") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/DdsImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/DdsImagePlugin.py deleted file mode 100644 index f9ade18f..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/DdsImagePlugin.py +++ /dev/null @@ -1,624 +0,0 @@ -""" -A Pillow plugin for .dds files (S3TC-compressed aka DXTC) -Jerome Leclanche - -Documentation: -https://web.archive.org/web/20170802060935/http://oss.sgi.com/projects/ogl-sample/registry/EXT/texture_compression_s3tc.txt - -The contents of this file are hereby released in the public domain (CC0) -Full text of the CC0 license: -https://creativecommons.org/publicdomain/zero/1.0/ -""" - -from __future__ import annotations - -import io -import struct -import sys -from enum import IntEnum, IntFlag -from typing import IO - -from . import Image, ImageFile, ImagePalette -from ._binary import i32le as i32 -from ._binary import o8 -from ._binary import o32le as o32 - -# Magic ("DDS ") -DDS_MAGIC = 0x20534444 - - -# DDS flags -class DDSD(IntFlag): - CAPS = 0x1 - HEIGHT = 0x2 - WIDTH = 0x4 - PITCH = 0x8 - PIXELFORMAT = 0x1000 - MIPMAPCOUNT = 0x20000 - LINEARSIZE = 0x80000 - DEPTH = 0x800000 - - -# DDS caps -class DDSCAPS(IntFlag): - COMPLEX = 0x8 - TEXTURE = 0x1000 - MIPMAP = 0x400000 - - -class DDSCAPS2(IntFlag): - CUBEMAP = 0x200 - CUBEMAP_POSITIVEX = 0x400 - CUBEMAP_NEGATIVEX = 0x800 - CUBEMAP_POSITIVEY = 0x1000 - CUBEMAP_NEGATIVEY = 0x2000 - CUBEMAP_POSITIVEZ = 0x4000 - CUBEMAP_NEGATIVEZ = 0x8000 - VOLUME = 0x200000 - - -# Pixel Format -class DDPF(IntFlag): - ALPHAPIXELS = 0x1 - ALPHA = 0x2 - FOURCC = 0x4 - PALETTEINDEXED8 = 0x20 - RGB = 0x40 - LUMINANCE = 0x20000 - - -# dxgiformat.h -class DXGI_FORMAT(IntEnum): - UNKNOWN = 0 - R32G32B32A32_TYPELESS = 1 - R32G32B32A32_FLOAT = 2 - R32G32B32A32_UINT = 3 - R32G32B32A32_SINT = 4 - R32G32B32_TYPELESS = 5 - R32G32B32_FLOAT = 6 - R32G32B32_UINT = 7 - R32G32B32_SINT = 8 - R16G16B16A16_TYPELESS = 9 - R16G16B16A16_FLOAT = 10 - R16G16B16A16_UNORM = 11 - R16G16B16A16_UINT = 12 - R16G16B16A16_SNORM = 13 - R16G16B16A16_SINT = 14 - R32G32_TYPELESS = 15 - R32G32_FLOAT = 16 - R32G32_UINT = 17 - R32G32_SINT = 18 - R32G8X24_TYPELESS = 19 - D32_FLOAT_S8X24_UINT = 20 - R32_FLOAT_X8X24_TYPELESS = 21 - X32_TYPELESS_G8X24_UINT = 22 - R10G10B10A2_TYPELESS = 23 - R10G10B10A2_UNORM = 24 - R10G10B10A2_UINT = 25 - R11G11B10_FLOAT = 26 - R8G8B8A8_TYPELESS = 27 - R8G8B8A8_UNORM = 28 - R8G8B8A8_UNORM_SRGB = 29 - R8G8B8A8_UINT = 30 - R8G8B8A8_SNORM = 31 - R8G8B8A8_SINT = 32 - R16G16_TYPELESS = 33 - R16G16_FLOAT = 34 - R16G16_UNORM = 35 - R16G16_UINT = 36 - R16G16_SNORM = 37 - R16G16_SINT = 38 - R32_TYPELESS = 39 - D32_FLOAT = 40 - R32_FLOAT = 41 - R32_UINT = 42 - R32_SINT = 43 - R24G8_TYPELESS = 44 - D24_UNORM_S8_UINT = 45 - R24_UNORM_X8_TYPELESS = 46 - X24_TYPELESS_G8_UINT = 47 - R8G8_TYPELESS = 48 - R8G8_UNORM = 49 - R8G8_UINT = 50 - R8G8_SNORM = 51 - R8G8_SINT = 52 - R16_TYPELESS = 53 - R16_FLOAT = 54 - D16_UNORM = 55 - R16_UNORM = 56 - R16_UINT = 57 - R16_SNORM = 58 - R16_SINT = 59 - R8_TYPELESS = 60 - R8_UNORM = 61 - R8_UINT = 62 - R8_SNORM = 63 - R8_SINT = 64 - A8_UNORM = 65 - R1_UNORM = 66 - R9G9B9E5_SHAREDEXP = 67 - R8G8_B8G8_UNORM = 68 - G8R8_G8B8_UNORM = 69 - BC1_TYPELESS = 70 - BC1_UNORM = 71 - BC1_UNORM_SRGB = 72 - BC2_TYPELESS = 73 - BC2_UNORM = 74 - BC2_UNORM_SRGB = 75 - BC3_TYPELESS = 76 - BC3_UNORM = 77 - BC3_UNORM_SRGB = 78 - BC4_TYPELESS = 79 - BC4_UNORM = 80 - BC4_SNORM = 81 - BC5_TYPELESS = 82 - BC5_UNORM = 83 - BC5_SNORM = 84 - B5G6R5_UNORM = 85 - B5G5R5A1_UNORM = 86 - B8G8R8A8_UNORM = 87 - B8G8R8X8_UNORM = 88 - R10G10B10_XR_BIAS_A2_UNORM = 89 - B8G8R8A8_TYPELESS = 90 - B8G8R8A8_UNORM_SRGB = 91 - B8G8R8X8_TYPELESS = 92 - B8G8R8X8_UNORM_SRGB = 93 - BC6H_TYPELESS = 94 - BC6H_UF16 = 95 - BC6H_SF16 = 96 - BC7_TYPELESS = 97 - BC7_UNORM = 98 - BC7_UNORM_SRGB = 99 - AYUV = 100 - Y410 = 101 - Y416 = 102 - NV12 = 103 - P010 = 104 - P016 = 105 - OPAQUE_420 = 106 - YUY2 = 107 - Y210 = 108 - Y216 = 109 - NV11 = 110 - AI44 = 111 - IA44 = 112 - P8 = 113 - A8P8 = 114 - B4G4R4A4_UNORM = 115 - P208 = 130 - V208 = 131 - V408 = 132 - SAMPLER_FEEDBACK_MIN_MIP_OPAQUE = 189 - SAMPLER_FEEDBACK_MIP_REGION_USED_OPAQUE = 190 - - -class D3DFMT(IntEnum): - UNKNOWN = 0 - R8G8B8 = 20 - A8R8G8B8 = 21 - X8R8G8B8 = 22 - R5G6B5 = 23 - X1R5G5B5 = 24 - A1R5G5B5 = 25 - A4R4G4B4 = 26 - R3G3B2 = 27 - A8 = 28 - A8R3G3B2 = 29 - X4R4G4B4 = 30 - A2B10G10R10 = 31 - A8B8G8R8 = 32 - X8B8G8R8 = 33 - G16R16 = 34 - A2R10G10B10 = 35 - A16B16G16R16 = 36 - A8P8 = 40 - P8 = 41 - L8 = 50 - A8L8 = 51 - A4L4 = 52 - V8U8 = 60 - L6V5U5 = 61 - X8L8V8U8 = 62 - Q8W8V8U8 = 63 - V16U16 = 64 - A2W10V10U10 = 67 - D16_LOCKABLE = 70 - D32 = 71 - D15S1 = 73 - D24S8 = 75 - D24X8 = 77 - D24X4S4 = 79 - D16 = 80 - D32F_LOCKABLE = 82 - D24FS8 = 83 - D32_LOCKABLE = 84 - S8_LOCKABLE = 85 - L16 = 81 - VERTEXDATA = 100 - INDEX16 = 101 - INDEX32 = 102 - Q16W16V16U16 = 110 - R16F = 111 - G16R16F = 112 - A16B16G16R16F = 113 - R32F = 114 - G32R32F = 115 - A32B32G32R32F = 116 - CxV8U8 = 117 - A1 = 118 - A2B10G10R10_XR_BIAS = 119 - BINARYBUFFER = 199 - - UYVY = i32(b"UYVY") - R8G8_B8G8 = i32(b"RGBG") - YUY2 = i32(b"YUY2") - G8R8_G8B8 = i32(b"GRGB") - DXT1 = i32(b"DXT1") - DXT2 = i32(b"DXT2") - DXT3 = i32(b"DXT3") - DXT4 = i32(b"DXT4") - DXT5 = i32(b"DXT5") - DX10 = i32(b"DX10") - BC4S = i32(b"BC4S") - BC4U = i32(b"BC4U") - BC5S = i32(b"BC5S") - BC5U = i32(b"BC5U") - ATI1 = i32(b"ATI1") - ATI2 = i32(b"ATI2") - MULTI2_ARGB8 = i32(b"MET1") - - -# Backward compatibility layer -module = sys.modules[__name__] -for item in DDSD: - assert item.name is not None - setattr(module, f"DDSD_{item.name}", item.value) -for item1 in DDSCAPS: - assert item1.name is not None - setattr(module, f"DDSCAPS_{item1.name}", item1.value) -for item2 in DDSCAPS2: - assert item2.name is not None - setattr(module, f"DDSCAPS2_{item2.name}", item2.value) -for item3 in DDPF: - assert item3.name is not None - setattr(module, f"DDPF_{item3.name}", item3.value) - -DDS_FOURCC = DDPF.FOURCC -DDS_RGB = DDPF.RGB -DDS_RGBA = DDPF.RGB | DDPF.ALPHAPIXELS -DDS_LUMINANCE = DDPF.LUMINANCE -DDS_LUMINANCEA = DDPF.LUMINANCE | DDPF.ALPHAPIXELS -DDS_ALPHA = DDPF.ALPHA -DDS_PAL8 = DDPF.PALETTEINDEXED8 - -DDS_HEADER_FLAGS_TEXTURE = DDSD.CAPS | DDSD.HEIGHT | DDSD.WIDTH | DDSD.PIXELFORMAT -DDS_HEADER_FLAGS_MIPMAP = DDSD.MIPMAPCOUNT -DDS_HEADER_FLAGS_VOLUME = DDSD.DEPTH -DDS_HEADER_FLAGS_PITCH = DDSD.PITCH -DDS_HEADER_FLAGS_LINEARSIZE = DDSD.LINEARSIZE - -DDS_HEIGHT = DDSD.HEIGHT -DDS_WIDTH = DDSD.WIDTH - -DDS_SURFACE_FLAGS_TEXTURE = DDSCAPS.TEXTURE -DDS_SURFACE_FLAGS_MIPMAP = DDSCAPS.COMPLEX | DDSCAPS.MIPMAP -DDS_SURFACE_FLAGS_CUBEMAP = DDSCAPS.COMPLEX - -DDS_CUBEMAP_POSITIVEX = DDSCAPS2.CUBEMAP | DDSCAPS2.CUBEMAP_POSITIVEX -DDS_CUBEMAP_NEGATIVEX = DDSCAPS2.CUBEMAP | DDSCAPS2.CUBEMAP_NEGATIVEX -DDS_CUBEMAP_POSITIVEY = DDSCAPS2.CUBEMAP | DDSCAPS2.CUBEMAP_POSITIVEY -DDS_CUBEMAP_NEGATIVEY = DDSCAPS2.CUBEMAP | DDSCAPS2.CUBEMAP_NEGATIVEY -DDS_CUBEMAP_POSITIVEZ = DDSCAPS2.CUBEMAP | DDSCAPS2.CUBEMAP_POSITIVEZ -DDS_CUBEMAP_NEGATIVEZ = DDSCAPS2.CUBEMAP | DDSCAPS2.CUBEMAP_NEGATIVEZ - -DXT1_FOURCC = D3DFMT.DXT1 -DXT3_FOURCC = D3DFMT.DXT3 -DXT5_FOURCC = D3DFMT.DXT5 - -DXGI_FORMAT_R8G8B8A8_TYPELESS = DXGI_FORMAT.R8G8B8A8_TYPELESS -DXGI_FORMAT_R8G8B8A8_UNORM = DXGI_FORMAT.R8G8B8A8_UNORM -DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = DXGI_FORMAT.R8G8B8A8_UNORM_SRGB -DXGI_FORMAT_BC5_TYPELESS = DXGI_FORMAT.BC5_TYPELESS -DXGI_FORMAT_BC5_UNORM = DXGI_FORMAT.BC5_UNORM -DXGI_FORMAT_BC5_SNORM = DXGI_FORMAT.BC5_SNORM -DXGI_FORMAT_BC6H_UF16 = DXGI_FORMAT.BC6H_UF16 -DXGI_FORMAT_BC6H_SF16 = DXGI_FORMAT.BC6H_SF16 -DXGI_FORMAT_BC7_TYPELESS = DXGI_FORMAT.BC7_TYPELESS -DXGI_FORMAT_BC7_UNORM = DXGI_FORMAT.BC7_UNORM -DXGI_FORMAT_BC7_UNORM_SRGB = DXGI_FORMAT.BC7_UNORM_SRGB - - -class DdsImageFile(ImageFile.ImageFile): - format = "DDS" - format_description = "DirectDraw Surface" - - def _open(self) -> None: - if not _accept(self.fp.read(4)): - msg = "not a DDS file" - raise SyntaxError(msg) - (header_size,) = struct.unpack(" None: - pass - - -class DdsRgbDecoder(ImageFile.PyDecoder): - _pulls_fd = True - - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: - assert self.fd is not None - bitcount, masks = self.args - - # Some masks will be padded with zeros, e.g. R 0b11 G 0b1100 - # Calculate how many zeros each mask is padded with - mask_offsets = [] - # And the maximum value of each channel without the padding - mask_totals = [] - for mask in masks: - offset = 0 - if mask != 0: - while mask >> (offset + 1) << (offset + 1) == mask: - offset += 1 - mask_offsets.append(offset) - mask_totals.append(mask >> offset) - - data = bytearray() - bytecount = bitcount // 8 - dest_length = self.state.xsize * self.state.ysize * len(masks) - while len(data) < dest_length: - value = int.from_bytes(self.fd.read(bytecount), "little") - for i, mask in enumerate(masks): - masked_value = value & mask - # Remove the zero padding, and scale it to 8 bits - data += o8( - int(((masked_value >> mask_offsets[i]) / mask_totals[i]) * 255) - ) - self.set_as_raw(data) - return -1, 0 - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - if im.mode not in ("RGB", "RGBA", "L", "LA"): - msg = f"cannot write mode {im.mode} as DDS" - raise OSError(msg) - - flags = DDSD.CAPS | DDSD.HEIGHT | DDSD.WIDTH | DDSD.PIXELFORMAT - bitcount = len(im.getbands()) * 8 - pixel_format = im.encoderinfo.get("pixel_format") - args: tuple[int] | str - if pixel_format: - codec_name = "bcn" - flags |= DDSD.LINEARSIZE - pitch = (im.width + 3) * 4 - rgba_mask = [0, 0, 0, 0] - pixel_flags = DDPF.FOURCC - if pixel_format == "DXT1": - fourcc = D3DFMT.DXT1 - args = (1,) - elif pixel_format == "DXT3": - fourcc = D3DFMT.DXT3 - args = (2,) - elif pixel_format == "DXT5": - fourcc = D3DFMT.DXT5 - args = (3,) - else: - fourcc = D3DFMT.DX10 - if pixel_format == "BC2": - args = (2,) - dxgi_format = DXGI_FORMAT.BC2_TYPELESS - elif pixel_format == "BC3": - args = (3,) - dxgi_format = DXGI_FORMAT.BC3_TYPELESS - elif pixel_format == "BC5": - args = (5,) - dxgi_format = DXGI_FORMAT.BC5_TYPELESS - if im.mode != "RGB": - msg = "only RGB mode can be written as BC5" - raise OSError(msg) - else: - msg = f"cannot write pixel format {pixel_format}" - raise OSError(msg) - else: - codec_name = "raw" - flags |= DDSD.PITCH - pitch = (im.width * bitcount + 7) // 8 - - alpha = im.mode[-1] == "A" - if im.mode[0] == "L": - pixel_flags = DDPF.LUMINANCE - args = im.mode - if alpha: - rgba_mask = [0x000000FF, 0x000000FF, 0x000000FF] - else: - rgba_mask = [0xFF000000, 0xFF000000, 0xFF000000] - else: - pixel_flags = DDPF.RGB - args = im.mode[::-1] - rgba_mask = [0x00FF0000, 0x0000FF00, 0x000000FF] - - if alpha: - r, g, b, a = im.split() - im = Image.merge("RGBA", (a, r, g, b)) - if alpha: - pixel_flags |= DDPF.ALPHAPIXELS - rgba_mask.append(0xFF000000 if alpha else 0) - - fourcc = D3DFMT.UNKNOWN - fp.write( - o32(DDS_MAGIC) - + struct.pack( - "<7I", - 124, # header size - flags, # flags - im.height, - im.width, - pitch, - 0, # depth - 0, # mipmaps - ) - + struct.pack("11I", *((0,) * 11)) # reserved - # pfsize, pfflags, fourcc, bitcount - + struct.pack("<4I", 32, pixel_flags, fourcc, bitcount) - + struct.pack("<4I", *rgba_mask) # dwRGBABitMask - + struct.pack("<5I", DDSCAPS.TEXTURE, 0, 0, 0, 0) - ) - if fourcc == D3DFMT.DX10: - fp.write( - # dxgi_format, 2D resource, misc, array size, straight alpha - struct.pack("<5I", dxgi_format, 3, 0, 0, 1) - ) - ImageFile._save(im, fp, [ImageFile._Tile(codec_name, (0, 0) + im.size, 0, args)]) - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(b"DDS ") - - -Image.register_open(DdsImageFile.format, DdsImageFile, _accept) -Image.register_decoder("dds_rgb", DdsRgbDecoder) -Image.register_save(DdsImageFile.format, _save) -Image.register_extension(DdsImageFile.format, ".dds") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/EpsImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/EpsImagePlugin.py deleted file mode 100644 index 69f3062b..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/EpsImagePlugin.py +++ /dev/null @@ -1,479 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# EPS file handling -# -# History: -# 1995-09-01 fl Created (0.1) -# 1996-05-18 fl Don't choke on "atend" fields, Ghostscript interface (0.2) -# 1996-08-22 fl Don't choke on floating point BoundingBox values -# 1996-08-23 fl Handle files from Macintosh (0.3) -# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.4) -# 2003-09-07 fl Check gs.close status (from Federico Di Gregorio) (0.5) -# 2014-05-07 e Handling of EPS with binary preview and fixed resolution -# resizing -# -# Copyright (c) 1997-2003 by Secret Labs AB. -# Copyright (c) 1995-2003 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io -import os -import re -import subprocess -import sys -import tempfile -from typing import IO - -from . import Image, ImageFile -from ._binary import i32le as i32 - -# -------------------------------------------------------------------- - - -split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$") -field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$") - -gs_binary: str | bool | None = None -gs_windows_binary = None - - -def has_ghostscript() -> bool: - global gs_binary, gs_windows_binary - if gs_binary is None: - if sys.platform.startswith("win"): - if gs_windows_binary is None: - import shutil - - for binary in ("gswin32c", "gswin64c", "gs"): - if shutil.which(binary) is not None: - gs_windows_binary = binary - break - else: - gs_windows_binary = False - gs_binary = gs_windows_binary - else: - try: - subprocess.check_call(["gs", "--version"], stdout=subprocess.DEVNULL) - gs_binary = "gs" - except OSError: - gs_binary = False - return gs_binary is not False - - -def Ghostscript( - tile: list[ImageFile._Tile], - size: tuple[int, int], - fp: IO[bytes], - scale: int = 1, - transparency: bool = False, -) -> Image.core.ImagingCore: - """Render an image using Ghostscript""" - global gs_binary - if not has_ghostscript(): - msg = "Unable to locate Ghostscript on paths" - raise OSError(msg) - assert isinstance(gs_binary, str) - - # Unpack decoder tile - args = tile[0].args - assert isinstance(args, tuple) - length, bbox = args - - # Hack to support hi-res rendering - scale = int(scale) or 1 - width = size[0] * scale - height = size[1] * scale - # resolution is dependent on bbox and size - res_x = 72.0 * width / (bbox[2] - bbox[0]) - res_y = 72.0 * height / (bbox[3] - bbox[1]) - - out_fd, outfile = tempfile.mkstemp() - os.close(out_fd) - - infile_temp = None - if hasattr(fp, "name") and os.path.exists(fp.name): - infile = fp.name - else: - in_fd, infile_temp = tempfile.mkstemp() - os.close(in_fd) - infile = infile_temp - - # Ignore length and offset! - # Ghostscript can read it - # Copy whole file to read in Ghostscript - with open(infile_temp, "wb") as f: - # fetch length of fp - fp.seek(0, io.SEEK_END) - fsize = fp.tell() - # ensure start position - # go back - fp.seek(0) - lengthfile = fsize - while lengthfile > 0: - s = fp.read(min(lengthfile, 100 * 1024)) - if not s: - break - lengthfile -= len(s) - f.write(s) - - if transparency: - # "RGBA" - device = "pngalpha" - else: - # "pnmraw" automatically chooses between - # PBM ("1"), PGM ("L"), and PPM ("RGB"). - device = "pnmraw" - - # Build Ghostscript command - command = [ - gs_binary, - "-q", # quiet mode - f"-g{width:d}x{height:d}", # set output geometry (pixels) - f"-r{res_x:f}x{res_y:f}", # set input DPI (dots per inch) - "-dBATCH", # exit after processing - "-dNOPAUSE", # don't pause between pages - "-dSAFER", # safe mode - f"-sDEVICE={device}", - f"-sOutputFile={outfile}", # output file - # adjust for image origin - "-c", - f"{-bbox[0]} {-bbox[1]} translate", - "-f", - infile, # input file - # showpage (see https://bugs.ghostscript.com/show_bug.cgi?id=698272) - "-c", - "showpage", - ] - - # push data through Ghostscript - try: - startupinfo = None - if sys.platform.startswith("win"): - startupinfo = subprocess.STARTUPINFO() - startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW - subprocess.check_call(command, startupinfo=startupinfo) - with Image.open(outfile) as out_im: - out_im.load() - return out_im.im.copy() - finally: - try: - os.unlink(outfile) - if infile_temp: - os.unlink(infile_temp) - except OSError: - pass - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(b"%!PS") or ( - len(prefix) >= 4 and i32(prefix) == 0xC6D3D0C5 - ) - - -## -# Image plugin for Encapsulated PostScript. This plugin supports only -# a few variants of this format. - - -class EpsImageFile(ImageFile.ImageFile): - """EPS File Parser for the Python Imaging Library""" - - format = "EPS" - format_description = "Encapsulated Postscript" - - mode_map = {1: "L", 2: "LAB", 3: "RGB", 4: "CMYK"} - - def _open(self) -> None: - (length, offset) = self._find_offset(self.fp) - - # go to offset - start of "%!PS" - self.fp.seek(offset) - - self._mode = "RGB" - - # When reading header comments, the first comment is used. - # When reading trailer comments, the last comment is used. - bounding_box: list[int] | None = None - imagedata_size: tuple[int, int] | None = None - - byte_arr = bytearray(255) - bytes_mv = memoryview(byte_arr) - bytes_read = 0 - reading_header_comments = True - reading_trailer_comments = False - trailer_reached = False - - def check_required_header_comments() -> None: - """ - The EPS specification requires that some headers exist. - This should be checked when the header comments formally end, - when image data starts, or when the file ends, whichever comes first. - """ - if "PS-Adobe" not in self.info: - msg = 'EPS header missing "%!PS-Adobe" comment' - raise SyntaxError(msg) - if "BoundingBox" not in self.info: - msg = 'EPS header missing "%%BoundingBox" comment' - raise SyntaxError(msg) - - def read_comment(s: str) -> bool: - nonlocal bounding_box, reading_trailer_comments - try: - m = split.match(s) - except re.error as e: - msg = "not an EPS file" - raise SyntaxError(msg) from e - - if not m: - return False - - k, v = m.group(1, 2) - self.info[k] = v - if k == "BoundingBox": - if v == "(atend)": - reading_trailer_comments = True - elif not bounding_box or (trailer_reached and reading_trailer_comments): - try: - # Note: The DSC spec says that BoundingBox - # fields should be integers, but some drivers - # put floating point values there anyway. - bounding_box = [int(float(i)) for i in v.split()] - except Exception: - pass - return True - - while True: - byte = self.fp.read(1) - if byte == b"": - # if we didn't read a byte we must be at the end of the file - if bytes_read == 0: - if reading_header_comments: - check_required_header_comments() - break - elif byte in b"\r\n": - # if we read a line ending character, ignore it and parse what - # we have already read. if we haven't read any other characters, - # continue reading - if bytes_read == 0: - continue - else: - # ASCII/hexadecimal lines in an EPS file must not exceed - # 255 characters, not including line ending characters - if bytes_read >= 255: - # only enforce this for lines starting with a "%", - # otherwise assume it's binary data - if byte_arr[0] == ord("%"): - msg = "not an EPS file" - raise SyntaxError(msg) - else: - if reading_header_comments: - check_required_header_comments() - reading_header_comments = False - # reset bytes_read so we can keep reading - # data until the end of the line - bytes_read = 0 - byte_arr[bytes_read] = byte[0] - bytes_read += 1 - continue - - if reading_header_comments: - # Load EPS header - - # if this line doesn't start with a "%", - # or does start with "%%EndComments", - # then we've reached the end of the header/comments - if byte_arr[0] != ord("%") or bytes_mv[:13] == b"%%EndComments": - check_required_header_comments() - reading_header_comments = False - continue - - s = str(bytes_mv[:bytes_read], "latin-1") - if not read_comment(s): - m = field.match(s) - if m: - k = m.group(1) - if k.startswith("PS-Adobe"): - self.info["PS-Adobe"] = k[9:] - else: - self.info[k] = "" - elif s[0] == "%": - # handle non-DSC PostScript comments that some - # tools mistakenly put in the Comments section - pass - else: - msg = "bad EPS header" - raise OSError(msg) - elif bytes_mv[:11] == b"%ImageData:": - # Check for an "ImageData" descriptor - # https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577413_pgfId-1035096 - - # If we've already read an "ImageData" descriptor, - # don't read another one. - if imagedata_size: - bytes_read = 0 - continue - - # Values: - # columns - # rows - # bit depth (1 or 8) - # mode (1: L, 2: LAB, 3: RGB, 4: CMYK) - # number of padding channels - # block size (number of bytes per row per channel) - # binary/ascii (1: binary, 2: ascii) - # data start identifier (the image data follows after a single line - # consisting only of this quoted value) - image_data_values = byte_arr[11:bytes_read].split(None, 7) - columns, rows, bit_depth, mode_id = ( - int(value) for value in image_data_values[:4] - ) - - if bit_depth == 1: - self._mode = "1" - elif bit_depth == 8: - try: - self._mode = self.mode_map[mode_id] - except ValueError: - break - else: - break - - # Parse the columns and rows after checking the bit depth and mode - # in case the bit depth and/or mode are invalid. - imagedata_size = columns, rows - elif bytes_mv[:5] == b"%%EOF": - break - elif trailer_reached and reading_trailer_comments: - # Load EPS trailer - s = str(bytes_mv[:bytes_read], "latin-1") - read_comment(s) - elif bytes_mv[:9] == b"%%Trailer": - trailer_reached = True - elif bytes_mv[:14] == b"%%BeginBinary:": - bytecount = int(byte_arr[14:bytes_read]) - self.fp.seek(bytecount, os.SEEK_CUR) - bytes_read = 0 - - # A "BoundingBox" is always required, - # even if an "ImageData" descriptor size exists. - if not bounding_box: - msg = "cannot determine EPS bounding box" - raise OSError(msg) - - # An "ImageData" size takes precedence over the "BoundingBox". - self._size = imagedata_size or ( - bounding_box[2] - bounding_box[0], - bounding_box[3] - bounding_box[1], - ) - - self.tile = [ - ImageFile._Tile("eps", (0, 0) + self.size, offset, (length, bounding_box)) - ] - - def _find_offset(self, fp: IO[bytes]) -> tuple[int, int]: - s = fp.read(4) - - if s == b"%!PS": - # for HEAD without binary preview - fp.seek(0, io.SEEK_END) - length = fp.tell() - offset = 0 - elif i32(s) == 0xC6D3D0C5: - # FIX for: Some EPS file not handled correctly / issue #302 - # EPS can contain binary data - # or start directly with latin coding - # more info see: - # https://web.archive.org/web/20160528181353/http://partners.adobe.com/public/developer/en/ps/5002.EPSF_Spec.pdf - s = fp.read(8) - offset = i32(s) - length = i32(s, 4) - else: - msg = "not an EPS file" - raise SyntaxError(msg) - - return length, offset - - def load( - self, scale: int = 1, transparency: bool = False - ) -> Image.core.PixelAccess | None: - # Load EPS via Ghostscript - if self.tile: - self.im = Ghostscript(self.tile, self.size, self.fp, scale, transparency) - self._mode = self.im.mode - self._size = self.im.size - self.tile = [] - return Image.Image.load(self) - - def load_seek(self, pos: int) -> None: - # we can't incrementally load, so force ImageFile.parser to - # use our custom load method by defining this method. - pass - - -# -------------------------------------------------------------------- - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes, eps: int = 1) -> None: - """EPS Writer for the Python Imaging Library.""" - - # make sure image data is available - im.load() - - # determine PostScript image mode - if im.mode == "L": - operator = (8, 1, b"image") - elif im.mode == "RGB": - operator = (8, 3, b"false 3 colorimage") - elif im.mode == "CMYK": - operator = (8, 4, b"false 4 colorimage") - else: - msg = "image mode is not supported" - raise ValueError(msg) - - if eps: - # write EPS header - fp.write(b"%!PS-Adobe-3.0 EPSF-3.0\n") - fp.write(b"%%Creator: PIL 0.1 EpsEncode\n") - # fp.write("%%CreationDate: %s"...) - fp.write(b"%%%%BoundingBox: 0 0 %d %d\n" % im.size) - fp.write(b"%%Pages: 1\n") - fp.write(b"%%EndComments\n") - fp.write(b"%%Page: 1 1\n") - fp.write(b"%%ImageData: %d %d " % im.size) - fp.write(b'%d %d 0 1 1 "%s"\n' % operator) - - # image header - fp.write(b"gsave\n") - fp.write(b"10 dict begin\n") - fp.write(b"/buf %d string def\n" % (im.size[0] * operator[1])) - fp.write(b"%d %d scale\n" % im.size) - fp.write(b"%d %d 8\n" % im.size) # <= bits - fp.write(b"[%d 0 0 -%d 0 %d]\n" % (im.size[0], im.size[1], im.size[1])) - fp.write(b"{ currentfile buf readhexstring pop } bind\n") - fp.write(operator[2] + b"\n") - if hasattr(fp, "flush"): - fp.flush() - - ImageFile._save(im, fp, [ImageFile._Tile("eps", (0, 0) + im.size)]) - - fp.write(b"\n%%%%EndBinary\n") - fp.write(b"grestore end\n") - if hasattr(fp, "flush"): - fp.flush() - - -# -------------------------------------------------------------------- - - -Image.register_open(EpsImageFile.format, EpsImageFile, _accept) - -Image.register_save(EpsImageFile.format, _save) - -Image.register_extensions(EpsImageFile.format, [".ps", ".eps"]) - -Image.register_mime(EpsImageFile.format, "application/postscript") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/ExifTags.py b/.venv-docs/lib/python3.12/site-packages/PIL/ExifTags.py deleted file mode 100644 index 2280d5ce..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/ExifTags.py +++ /dev/null @@ -1,382 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# EXIF tags -# -# Copyright (c) 2003 by Secret Labs AB -# -# See the README file for information on usage and redistribution. -# - -""" -This module provides constants and clear-text names for various -well-known EXIF tags. -""" -from __future__ import annotations - -from enum import IntEnum - - -class Base(IntEnum): - # possibly incomplete - InteropIndex = 0x0001 - ProcessingSoftware = 0x000B - NewSubfileType = 0x00FE - SubfileType = 0x00FF - ImageWidth = 0x0100 - ImageLength = 0x0101 - BitsPerSample = 0x0102 - Compression = 0x0103 - PhotometricInterpretation = 0x0106 - Thresholding = 0x0107 - CellWidth = 0x0108 - CellLength = 0x0109 - FillOrder = 0x010A - DocumentName = 0x010D - ImageDescription = 0x010E - Make = 0x010F - Model = 0x0110 - StripOffsets = 0x0111 - Orientation = 0x0112 - SamplesPerPixel = 0x0115 - RowsPerStrip = 0x0116 - StripByteCounts = 0x0117 - MinSampleValue = 0x0118 - MaxSampleValue = 0x0119 - XResolution = 0x011A - YResolution = 0x011B - PlanarConfiguration = 0x011C - PageName = 0x011D - FreeOffsets = 0x0120 - FreeByteCounts = 0x0121 - GrayResponseUnit = 0x0122 - GrayResponseCurve = 0x0123 - T4Options = 0x0124 - T6Options = 0x0125 - ResolutionUnit = 0x0128 - PageNumber = 0x0129 - TransferFunction = 0x012D - Software = 0x0131 - DateTime = 0x0132 - Artist = 0x013B - HostComputer = 0x013C - Predictor = 0x013D - WhitePoint = 0x013E - PrimaryChromaticities = 0x013F - ColorMap = 0x0140 - HalftoneHints = 0x0141 - TileWidth = 0x0142 - TileLength = 0x0143 - TileOffsets = 0x0144 - TileByteCounts = 0x0145 - SubIFDs = 0x014A - InkSet = 0x014C - InkNames = 0x014D - NumberOfInks = 0x014E - DotRange = 0x0150 - TargetPrinter = 0x0151 - ExtraSamples = 0x0152 - SampleFormat = 0x0153 - SMinSampleValue = 0x0154 - SMaxSampleValue = 0x0155 - TransferRange = 0x0156 - ClipPath = 0x0157 - XClipPathUnits = 0x0158 - YClipPathUnits = 0x0159 - Indexed = 0x015A - JPEGTables = 0x015B - OPIProxy = 0x015F - JPEGProc = 0x0200 - JpegIFOffset = 0x0201 - JpegIFByteCount = 0x0202 - JpegRestartInterval = 0x0203 - JpegLosslessPredictors = 0x0205 - JpegPointTransforms = 0x0206 - JpegQTables = 0x0207 - JpegDCTables = 0x0208 - JpegACTables = 0x0209 - YCbCrCoefficients = 0x0211 - YCbCrSubSampling = 0x0212 - YCbCrPositioning = 0x0213 - ReferenceBlackWhite = 0x0214 - XMLPacket = 0x02BC - RelatedImageFileFormat = 0x1000 - RelatedImageWidth = 0x1001 - RelatedImageLength = 0x1002 - Rating = 0x4746 - RatingPercent = 0x4749 - ImageID = 0x800D - CFARepeatPatternDim = 0x828D - BatteryLevel = 0x828F - Copyright = 0x8298 - ExposureTime = 0x829A - FNumber = 0x829D - IPTCNAA = 0x83BB - ImageResources = 0x8649 - ExifOffset = 0x8769 - InterColorProfile = 0x8773 - ExposureProgram = 0x8822 - SpectralSensitivity = 0x8824 - GPSInfo = 0x8825 - ISOSpeedRatings = 0x8827 - OECF = 0x8828 - Interlace = 0x8829 - TimeZoneOffset = 0x882A - SelfTimerMode = 0x882B - SensitivityType = 0x8830 - StandardOutputSensitivity = 0x8831 - RecommendedExposureIndex = 0x8832 - ISOSpeed = 0x8833 - ISOSpeedLatitudeyyy = 0x8834 - ISOSpeedLatitudezzz = 0x8835 - ExifVersion = 0x9000 - DateTimeOriginal = 0x9003 - DateTimeDigitized = 0x9004 - OffsetTime = 0x9010 - OffsetTimeOriginal = 0x9011 - OffsetTimeDigitized = 0x9012 - ComponentsConfiguration = 0x9101 - CompressedBitsPerPixel = 0x9102 - ShutterSpeedValue = 0x9201 - ApertureValue = 0x9202 - BrightnessValue = 0x9203 - ExposureBiasValue = 0x9204 - MaxApertureValue = 0x9205 - SubjectDistance = 0x9206 - MeteringMode = 0x9207 - LightSource = 0x9208 - Flash = 0x9209 - FocalLength = 0x920A - Noise = 0x920D - ImageNumber = 0x9211 - SecurityClassification = 0x9212 - ImageHistory = 0x9213 - TIFFEPStandardID = 0x9216 - MakerNote = 0x927C - UserComment = 0x9286 - SubsecTime = 0x9290 - SubsecTimeOriginal = 0x9291 - SubsecTimeDigitized = 0x9292 - AmbientTemperature = 0x9400 - Humidity = 0x9401 - Pressure = 0x9402 - WaterDepth = 0x9403 - Acceleration = 0x9404 - CameraElevationAngle = 0x9405 - XPTitle = 0x9C9B - XPComment = 0x9C9C - XPAuthor = 0x9C9D - XPKeywords = 0x9C9E - XPSubject = 0x9C9F - FlashPixVersion = 0xA000 - ColorSpace = 0xA001 - ExifImageWidth = 0xA002 - ExifImageHeight = 0xA003 - RelatedSoundFile = 0xA004 - ExifInteroperabilityOffset = 0xA005 - FlashEnergy = 0xA20B - SpatialFrequencyResponse = 0xA20C - FocalPlaneXResolution = 0xA20E - FocalPlaneYResolution = 0xA20F - FocalPlaneResolutionUnit = 0xA210 - SubjectLocation = 0xA214 - ExposureIndex = 0xA215 - SensingMethod = 0xA217 - FileSource = 0xA300 - SceneType = 0xA301 - CFAPattern = 0xA302 - CustomRendered = 0xA401 - ExposureMode = 0xA402 - WhiteBalance = 0xA403 - DigitalZoomRatio = 0xA404 - FocalLengthIn35mmFilm = 0xA405 - SceneCaptureType = 0xA406 - GainControl = 0xA407 - Contrast = 0xA408 - Saturation = 0xA409 - Sharpness = 0xA40A - DeviceSettingDescription = 0xA40B - SubjectDistanceRange = 0xA40C - ImageUniqueID = 0xA420 - CameraOwnerName = 0xA430 - BodySerialNumber = 0xA431 - LensSpecification = 0xA432 - LensMake = 0xA433 - LensModel = 0xA434 - LensSerialNumber = 0xA435 - CompositeImage = 0xA460 - CompositeImageCount = 0xA461 - CompositeImageExposureTimes = 0xA462 - Gamma = 0xA500 - PrintImageMatching = 0xC4A5 - DNGVersion = 0xC612 - DNGBackwardVersion = 0xC613 - UniqueCameraModel = 0xC614 - LocalizedCameraModel = 0xC615 - CFAPlaneColor = 0xC616 - CFALayout = 0xC617 - LinearizationTable = 0xC618 - BlackLevelRepeatDim = 0xC619 - BlackLevel = 0xC61A - BlackLevelDeltaH = 0xC61B - BlackLevelDeltaV = 0xC61C - WhiteLevel = 0xC61D - DefaultScale = 0xC61E - DefaultCropOrigin = 0xC61F - DefaultCropSize = 0xC620 - ColorMatrix1 = 0xC621 - ColorMatrix2 = 0xC622 - CameraCalibration1 = 0xC623 - CameraCalibration2 = 0xC624 - ReductionMatrix1 = 0xC625 - ReductionMatrix2 = 0xC626 - AnalogBalance = 0xC627 - AsShotNeutral = 0xC628 - AsShotWhiteXY = 0xC629 - BaselineExposure = 0xC62A - BaselineNoise = 0xC62B - BaselineSharpness = 0xC62C - BayerGreenSplit = 0xC62D - LinearResponseLimit = 0xC62E - CameraSerialNumber = 0xC62F - LensInfo = 0xC630 - ChromaBlurRadius = 0xC631 - AntiAliasStrength = 0xC632 - ShadowScale = 0xC633 - DNGPrivateData = 0xC634 - MakerNoteSafety = 0xC635 - CalibrationIlluminant1 = 0xC65A - CalibrationIlluminant2 = 0xC65B - BestQualityScale = 0xC65C - RawDataUniqueID = 0xC65D - OriginalRawFileName = 0xC68B - OriginalRawFileData = 0xC68C - ActiveArea = 0xC68D - MaskedAreas = 0xC68E - AsShotICCProfile = 0xC68F - AsShotPreProfileMatrix = 0xC690 - CurrentICCProfile = 0xC691 - CurrentPreProfileMatrix = 0xC692 - ColorimetricReference = 0xC6BF - CameraCalibrationSignature = 0xC6F3 - ProfileCalibrationSignature = 0xC6F4 - AsShotProfileName = 0xC6F6 - NoiseReductionApplied = 0xC6F7 - ProfileName = 0xC6F8 - ProfileHueSatMapDims = 0xC6F9 - ProfileHueSatMapData1 = 0xC6FA - ProfileHueSatMapData2 = 0xC6FB - ProfileToneCurve = 0xC6FC - ProfileEmbedPolicy = 0xC6FD - ProfileCopyright = 0xC6FE - ForwardMatrix1 = 0xC714 - ForwardMatrix2 = 0xC715 - PreviewApplicationName = 0xC716 - PreviewApplicationVersion = 0xC717 - PreviewSettingsName = 0xC718 - PreviewSettingsDigest = 0xC719 - PreviewColorSpace = 0xC71A - PreviewDateTime = 0xC71B - RawImageDigest = 0xC71C - OriginalRawFileDigest = 0xC71D - SubTileBlockSize = 0xC71E - RowInterleaveFactor = 0xC71F - ProfileLookTableDims = 0xC725 - ProfileLookTableData = 0xC726 - OpcodeList1 = 0xC740 - OpcodeList2 = 0xC741 - OpcodeList3 = 0xC74E - NoiseProfile = 0xC761 - - -"""Maps EXIF tags to tag names.""" -TAGS = { - **{i.value: i.name for i in Base}, - 0x920C: "SpatialFrequencyResponse", - 0x9214: "SubjectLocation", - 0x9215: "ExposureIndex", - 0x828E: "CFAPattern", - 0x920B: "FlashEnergy", - 0x9216: "TIFF/EPStandardID", -} - - -class GPS(IntEnum): - GPSVersionID = 0x00 - GPSLatitudeRef = 0x01 - GPSLatitude = 0x02 - GPSLongitudeRef = 0x03 - GPSLongitude = 0x04 - GPSAltitudeRef = 0x05 - GPSAltitude = 0x06 - GPSTimeStamp = 0x07 - GPSSatellites = 0x08 - GPSStatus = 0x09 - GPSMeasureMode = 0x0A - GPSDOP = 0x0B - GPSSpeedRef = 0x0C - GPSSpeed = 0x0D - GPSTrackRef = 0x0E - GPSTrack = 0x0F - GPSImgDirectionRef = 0x10 - GPSImgDirection = 0x11 - GPSMapDatum = 0x12 - GPSDestLatitudeRef = 0x13 - GPSDestLatitude = 0x14 - GPSDestLongitudeRef = 0x15 - GPSDestLongitude = 0x16 - GPSDestBearingRef = 0x17 - GPSDestBearing = 0x18 - GPSDestDistanceRef = 0x19 - GPSDestDistance = 0x1A - GPSProcessingMethod = 0x1B - GPSAreaInformation = 0x1C - GPSDateStamp = 0x1D - GPSDifferential = 0x1E - GPSHPositioningError = 0x1F - - -"""Maps EXIF GPS tags to tag names.""" -GPSTAGS = {i.value: i.name for i in GPS} - - -class Interop(IntEnum): - InteropIndex = 0x0001 - InteropVersion = 0x0002 - RelatedImageFileFormat = 0x1000 - RelatedImageWidth = 0x1001 - RelatedImageHeight = 0x1002 - - -class IFD(IntEnum): - Exif = 0x8769 - GPSInfo = 0x8825 - MakerNote = 0x927C - Makernote = 0x927C # Deprecated - Interop = 0xA005 - IFD1 = -1 - - -class LightSource(IntEnum): - Unknown = 0x00 - Daylight = 0x01 - Fluorescent = 0x02 - Tungsten = 0x03 - Flash = 0x04 - Fine = 0x09 - Cloudy = 0x0A - Shade = 0x0B - DaylightFluorescent = 0x0C - DayWhiteFluorescent = 0x0D - CoolWhiteFluorescent = 0x0E - WhiteFluorescent = 0x0F - StandardLightA = 0x11 - StandardLightB = 0x12 - StandardLightC = 0x13 - D55 = 0x14 - D65 = 0x15 - D75 = 0x16 - D50 = 0x17 - ISO = 0x18 - Other = 0xFF diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/FitsImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/FitsImagePlugin.py deleted file mode 100644 index a3fdc0ef..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/FitsImagePlugin.py +++ /dev/null @@ -1,152 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# FITS file handling -# -# Copyright (c) 1998-2003 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import gzip -import math - -from . import Image, ImageFile - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(b"SIMPLE") - - -class FitsImageFile(ImageFile.ImageFile): - format = "FITS" - format_description = "FITS" - - def _open(self) -> None: - assert self.fp is not None - - headers: dict[bytes, bytes] = {} - header_in_progress = False - decoder_name = "" - while True: - header = self.fp.read(80) - if not header: - msg = "Truncated FITS file" - raise OSError(msg) - keyword = header[:8].strip() - if keyword in (b"SIMPLE", b"XTENSION"): - header_in_progress = True - elif headers and not header_in_progress: - # This is now a data unit - break - elif keyword == b"END": - # Seek to the end of the header unit - self.fp.seek(math.ceil(self.fp.tell() / 2880) * 2880) - if not decoder_name: - decoder_name, offset, args = self._parse_headers(headers) - - header_in_progress = False - continue - - if decoder_name: - # Keep going to read past the headers - continue - - value = header[8:].split(b"/")[0].strip() - if value.startswith(b"="): - value = value[1:].strip() - if not headers and (not _accept(keyword) or value != b"T"): - msg = "Not a FITS file" - raise SyntaxError(msg) - headers[keyword] = value - - if not decoder_name: - msg = "No image data" - raise ValueError(msg) - - offset += self.fp.tell() - 80 - self.tile = [ImageFile._Tile(decoder_name, (0, 0) + self.size, offset, args)] - - def _get_size( - self, headers: dict[bytes, bytes], prefix: bytes - ) -> tuple[int, int] | None: - naxis = int(headers[prefix + b"NAXIS"]) - if naxis == 0: - return None - - if naxis == 1: - return 1, int(headers[prefix + b"NAXIS1"]) - else: - return int(headers[prefix + b"NAXIS1"]), int(headers[prefix + b"NAXIS2"]) - - def _parse_headers( - self, headers: dict[bytes, bytes] - ) -> tuple[str, int, tuple[str | int, ...]]: - prefix = b"" - decoder_name = "raw" - offset = 0 - if ( - headers.get(b"XTENSION") == b"'BINTABLE'" - and headers.get(b"ZIMAGE") == b"T" - and headers[b"ZCMPTYPE"] == b"'GZIP_1 '" - ): - no_prefix_size = self._get_size(headers, prefix) or (0, 0) - number_of_bits = int(headers[b"BITPIX"]) - offset = no_prefix_size[0] * no_prefix_size[1] * (number_of_bits // 8) - - prefix = b"Z" - decoder_name = "fits_gzip" - - size = self._get_size(headers, prefix) - if not size: - return "", 0, () - - self._size = size - - number_of_bits = int(headers[prefix + b"BITPIX"]) - if number_of_bits == 8: - self._mode = "L" - elif number_of_bits == 16: - self._mode = "I;16" - elif number_of_bits == 32: - self._mode = "I" - elif number_of_bits in (-32, -64): - self._mode = "F" - - args: tuple[str | int, ...] - if decoder_name == "raw": - args = (self.mode, 0, -1) - else: - args = (number_of_bits,) - return decoder_name, offset, args - - -class FitsGzipDecoder(ImageFile.PyDecoder): - _pulls_fd = True - - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: - assert self.fd is not None - value = gzip.decompress(self.fd.read()) - - rows = [] - offset = 0 - number_of_bits = min(self.args[0] // 8, 4) - for y in range(self.state.ysize): - row = bytearray() - for x in range(self.state.xsize): - row += value[offset + (4 - number_of_bits) : offset + 4] - offset += 4 - rows.append(row) - self.set_as_raw(bytes([pixel for row in rows[::-1] for pixel in row])) - return -1, 0 - - -# -------------------------------------------------------------------- -# Registry - -Image.register_open(FitsImageFile.format, FitsImageFile, _accept) -Image.register_decoder("fits_gzip", FitsGzipDecoder) - -Image.register_extensions(FitsImageFile.format, [".fit", ".fits"]) diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/FliImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/FliImagePlugin.py deleted file mode 100644 index da1e8e95..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/FliImagePlugin.py +++ /dev/null @@ -1,184 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# FLI/FLC file handling. -# -# History: -# 95-09-01 fl Created -# 97-01-03 fl Fixed parser, setup decoder tile -# 98-07-15 fl Renamed offset attribute to avoid name clash -# -# Copyright (c) Secret Labs AB 1997-98. -# Copyright (c) Fredrik Lundh 1995-97. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import os - -from . import Image, ImageFile, ImagePalette -from ._binary import i16le as i16 -from ._binary import i32le as i32 -from ._binary import o8 -from ._util import DeferredError - -# -# decoder - - -def _accept(prefix: bytes) -> bool: - return ( - len(prefix) >= 16 - and i16(prefix, 4) in [0xAF11, 0xAF12] - and i16(prefix, 14) in [0, 3] # flags - ) - - -## -# Image plugin for the FLI/FLC animation format. Use the seek -# method to load individual frames. - - -class FliImageFile(ImageFile.ImageFile): - format = "FLI" - format_description = "Autodesk FLI/FLC Animation" - _close_exclusive_fp_after_loading = False - - def _open(self) -> None: - # HEAD - assert self.fp is not None - s = self.fp.read(128) - if not ( - _accept(s) - and s[20:22] == b"\x00" * 2 - and s[42:80] == b"\x00" * 38 - and s[88:] == b"\x00" * 40 - ): - msg = "not an FLI/FLC file" - raise SyntaxError(msg) - - # frames - self.n_frames = i16(s, 6) - self.is_animated = self.n_frames > 1 - - # image characteristics - self._mode = "P" - self._size = i16(s, 8), i16(s, 10) - - # animation speed - duration = i32(s, 16) - magic = i16(s, 4) - if magic == 0xAF11: - duration = (duration * 1000) // 70 - self.info["duration"] = duration - - # look for palette - palette = [(a, a, a) for a in range(256)] - - s = self.fp.read(16) - - self.__offset = 128 - - if i16(s, 4) == 0xF100: - # prefix chunk; ignore it - self.fp.seek(self.__offset + i32(s)) - s = self.fp.read(16) - - if i16(s, 4) == 0xF1FA: - # look for palette chunk - number_of_subchunks = i16(s, 6) - chunk_size: int | None = None - for _ in range(number_of_subchunks): - if chunk_size is not None: - self.fp.seek(chunk_size - 6, os.SEEK_CUR) - s = self.fp.read(6) - chunk_type = i16(s, 4) - if chunk_type in (4, 11): - self._palette(palette, 2 if chunk_type == 11 else 0) - break - chunk_size = i32(s) - if not chunk_size: - break - - self.palette = ImagePalette.raw( - "RGB", b"".join(o8(r) + o8(g) + o8(b) for (r, g, b) in palette) - ) - - # set things up to decode first frame - self.__frame = -1 - self._fp = self.fp - self.__rewind = self.fp.tell() - self.seek(0) - - def _palette(self, palette: list[tuple[int, int, int]], shift: int) -> None: - # load palette - - i = 0 - assert self.fp is not None - for e in range(i16(self.fp.read(2))): - s = self.fp.read(2) - i = i + s[0] - n = s[1] - if n == 0: - n = 256 - s = self.fp.read(n * 3) - for n in range(0, len(s), 3): - r = s[n] << shift - g = s[n + 1] << shift - b = s[n + 2] << shift - palette[i] = (r, g, b) - i += 1 - - def seek(self, frame: int) -> None: - if not self._seek_check(frame): - return - if frame < self.__frame: - self._seek(0) - - for f in range(self.__frame + 1, frame + 1): - self._seek(f) - - def _seek(self, frame: int) -> None: - if isinstance(self._fp, DeferredError): - raise self._fp.ex - if frame == 0: - self.__frame = -1 - self._fp.seek(self.__rewind) - self.__offset = 128 - else: - # ensure that the previous frame was loaded - self.load() - - if frame != self.__frame + 1: - msg = f"cannot seek to frame {frame}" - raise ValueError(msg) - self.__frame = frame - - # move to next frame - self.fp = self._fp - self.fp.seek(self.__offset) - - s = self.fp.read(4) - if not s: - msg = "missing frame size" - raise EOFError(msg) - - framesize = i32(s) - - self.decodermaxblock = framesize - self.tile = [ImageFile._Tile("fli", (0, 0) + self.size, self.__offset)] - - self.__offset += framesize - - def tell(self) -> int: - return self.__frame - - -# -# registry - -Image.register_open(FliImageFile.format, FliImageFile, _accept) - -Image.register_extensions(FliImageFile.format, [".fli", ".flc"]) diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/FontFile.py b/.venv-docs/lib/python3.12/site-packages/PIL/FontFile.py deleted file mode 100644 index 1e0c1c16..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/FontFile.py +++ /dev/null @@ -1,134 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# base class for raster font file parsers -# -# history: -# 1997-06-05 fl created -# 1997-08-19 fl restrict image width -# -# Copyright (c) 1997-1998 by Secret Labs AB -# Copyright (c) 1997-1998 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import os -from typing import BinaryIO - -from . import Image, _binary - -WIDTH = 800 - - -def puti16( - fp: BinaryIO, values: tuple[int, int, int, int, int, int, int, int, int, int] -) -> None: - """Write network order (big-endian) 16-bit sequence""" - for v in values: - if v < 0: - v += 65536 - fp.write(_binary.o16be(v)) - - -class FontFile: - """Base class for raster font file handlers.""" - - bitmap: Image.Image | None = None - - def __init__(self) -> None: - self.info: dict[bytes, bytes | int] = {} - self.glyph: list[ - tuple[ - tuple[int, int], - tuple[int, int, int, int], - tuple[int, int, int, int], - Image.Image, - ] - | None - ] = [None] * 256 - - def __getitem__(self, ix: int) -> ( - tuple[ - tuple[int, int], - tuple[int, int, int, int], - tuple[int, int, int, int], - Image.Image, - ] - | None - ): - return self.glyph[ix] - - def compile(self) -> None: - """Create metrics and bitmap""" - - if self.bitmap: - return - - # create bitmap large enough to hold all data - h = w = maxwidth = 0 - lines = 1 - for glyph in self.glyph: - if glyph: - d, dst, src, im = glyph - h = max(h, src[3] - src[1]) - w = w + (src[2] - src[0]) - if w > WIDTH: - lines += 1 - w = src[2] - src[0] - maxwidth = max(maxwidth, w) - - xsize = maxwidth - ysize = lines * h - - if xsize == 0 and ysize == 0: - return - - self.ysize = h - - # paste glyphs into bitmap - self.bitmap = Image.new("1", (xsize, ysize)) - self.metrics: list[ - tuple[tuple[int, int], tuple[int, int, int, int], tuple[int, int, int, int]] - | None - ] = [None] * 256 - x = y = 0 - for i in range(256): - glyph = self[i] - if glyph: - d, dst, src, im = glyph - xx = src[2] - src[0] - x0, y0 = x, y - x = x + xx - if x > WIDTH: - x, y = 0, y + h - x0, y0 = x, y - x = xx - s = src[0] + x0, src[1] + y0, src[2] + x0, src[3] + y0 - self.bitmap.paste(im.crop(src), s) - self.metrics[i] = d, dst, s - - def save(self, filename: str) -> None: - """Save font""" - - self.compile() - - # font data - if not self.bitmap: - msg = "No bitmap created" - raise ValueError(msg) - self.bitmap.save(os.path.splitext(filename)[0] + ".pbm", "PNG") - - # font metrics - with open(os.path.splitext(filename)[0] + ".pil", "wb") as fp: - fp.write(b"PILfont\n") - fp.write(f";;;;;;{self.ysize};\n".encode("ascii")) # HACK!!! - fp.write(b"DATA\n") - for id in range(256): - m = self.metrics[id] - if not m: - puti16(fp, (0,) * 10) - else: - puti16(fp, m[0] + m[1] + m[2]) diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/FpxImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/FpxImagePlugin.py deleted file mode 100644 index fd992cd9..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/FpxImagePlugin.py +++ /dev/null @@ -1,257 +0,0 @@ -# -# THIS IS WORK IN PROGRESS -# -# The Python Imaging Library. -# $Id$ -# -# FlashPix support for PIL -# -# History: -# 97-01-25 fl Created (reads uncompressed RGB images only) -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1997. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import olefile - -from . import Image, ImageFile -from ._binary import i32le as i32 - -# we map from colour field tuples to (mode, rawmode) descriptors -MODES = { - # opacity - (0x00007FFE,): ("A", "L"), - # monochrome - (0x00010000,): ("L", "L"), - (0x00018000, 0x00017FFE): ("RGBA", "LA"), - # photo YCC - (0x00020000, 0x00020001, 0x00020002): ("RGB", "YCC;P"), - (0x00028000, 0x00028001, 0x00028002, 0x00027FFE): ("RGBA", "YCCA;P"), - # standard RGB (NIFRGB) - (0x00030000, 0x00030001, 0x00030002): ("RGB", "RGB"), - (0x00038000, 0x00038001, 0x00038002, 0x00037FFE): ("RGBA", "RGBA"), -} - - -# -# -------------------------------------------------------------------- - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(olefile.MAGIC) - - -## -# Image plugin for the FlashPix images. - - -class FpxImageFile(ImageFile.ImageFile): - format = "FPX" - format_description = "FlashPix" - - def _open(self) -> None: - # - # read the OLE directory and see if this is a likely - # to be a FlashPix file - - try: - self.ole = olefile.OleFileIO(self.fp) - except OSError as e: - msg = "not an FPX file; invalid OLE file" - raise SyntaxError(msg) from e - - root = self.ole.root - if not root or root.clsid != "56616700-C154-11CE-8553-00AA00A1F95B": - msg = "not an FPX file; bad root CLSID" - raise SyntaxError(msg) - - self._open_index(1) - - def _open_index(self, index: int = 1) -> None: - # - # get the Image Contents Property Set - - prop = self.ole.getproperties( - [f"Data Object Store {index:06d}", "\005Image Contents"] - ) - - # size (highest resolution) - - assert isinstance(prop[0x1000002], int) - assert isinstance(prop[0x1000003], int) - self._size = prop[0x1000002], prop[0x1000003] - - size = max(self.size) - i = 1 - while size > 64: - size = size // 2 - i += 1 - self.maxid = i - 1 - - # mode. instead of using a single field for this, flashpix - # requires you to specify the mode for each channel in each - # resolution subimage, and leaves it to the decoder to make - # sure that they all match. for now, we'll cheat and assume - # that this is always the case. - - id = self.maxid << 16 - - s = prop[0x2000002 | id] - - if not isinstance(s, bytes) or (bands := i32(s, 4)) > 4: - msg = "Invalid number of bands" - raise OSError(msg) - - # note: for now, we ignore the "uncalibrated" flag - colors = tuple(i32(s, 8 + i * 4) & 0x7FFFFFFF for i in range(bands)) - - self._mode, self.rawmode = MODES[colors] - - # load JPEG tables, if any - self.jpeg = {} - for i in range(256): - id = 0x3000001 | (i << 16) - if id in prop: - self.jpeg[i] = prop[id] - - self._open_subimage(1, self.maxid) - - def _open_subimage(self, index: int = 1, subimage: int = 0) -> None: - # - # setup tile descriptors for a given subimage - - stream = [ - f"Data Object Store {index:06d}", - f"Resolution {subimage:04d}", - "Subimage 0000 Header", - ] - - fp = self.ole.openstream(stream) - - # skip prefix - fp.read(28) - - # header stream - s = fp.read(36) - - size = i32(s, 4), i32(s, 8) - # tilecount = i32(s, 12) - tilesize = i32(s, 16), i32(s, 20) - # channels = i32(s, 24) - offset = i32(s, 28) - length = i32(s, 32) - - if size != self.size: - msg = "subimage mismatch" - raise OSError(msg) - - # get tile descriptors - fp.seek(28 + offset) - s = fp.read(i32(s, 12) * length) - - x = y = 0 - xsize, ysize = size - xtile, ytile = tilesize - self.tile = [] - - for i in range(0, len(s), length): - x1 = min(xsize, x + xtile) - y1 = min(ysize, y + ytile) - - compression = i32(s, i + 8) - - if compression == 0: - self.tile.append( - ImageFile._Tile( - "raw", - (x, y, x1, y1), - i32(s, i) + 28, - self.rawmode, - ) - ) - - elif compression == 1: - # FIXME: the fill decoder is not implemented - self.tile.append( - ImageFile._Tile( - "fill", - (x, y, x1, y1), - i32(s, i) + 28, - (self.rawmode, s[12:16]), - ) - ) - - elif compression == 2: - internal_color_conversion = s[14] - jpeg_tables = s[15] - rawmode = self.rawmode - - if internal_color_conversion: - # The image is stored as usual (usually YCbCr). - if rawmode == "RGBA": - # For "RGBA", data is stored as YCbCrA based on - # negative RGB. The following trick works around - # this problem : - jpegmode, rawmode = "YCbCrK", "CMYK" - else: - jpegmode = None # let the decoder decide - - else: - # The image is stored as defined by rawmode - jpegmode = rawmode - - self.tile.append( - ImageFile._Tile( - "jpeg", - (x, y, x1, y1), - i32(s, i) + 28, - (rawmode, jpegmode), - ) - ) - - # FIXME: jpeg tables are tile dependent; the prefix - # data must be placed in the tile descriptor itself! - - if jpeg_tables: - self.tile_prefix = self.jpeg[jpeg_tables] - - else: - msg = "unknown/invalid compression" - raise OSError(msg) - - x = x + xtile - if x >= xsize: - x, y = 0, y + ytile - if y >= ysize: - break # isn't really required - - self.stream = stream - self._fp = self.fp - self.fp = None - - def load(self) -> Image.core.PixelAccess | None: - if not self.fp: - self.fp = self.ole.openstream(self.stream[:2] + ["Subimage 0000 Data"]) - - return ImageFile.ImageFile.load(self) - - def close(self) -> None: - self.ole.close() - super().close() - - def __exit__(self, *args: object) -> None: - self.ole.close() - super().__exit__() - - -# -# -------------------------------------------------------------------- - - -Image.register_open(FpxImageFile.format, FpxImageFile, _accept) - -Image.register_extension(FpxImageFile.format, ".fpx") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/FtexImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/FtexImagePlugin.py deleted file mode 100644 index d60e75bb..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/FtexImagePlugin.py +++ /dev/null @@ -1,114 +0,0 @@ -""" -A Pillow loader for .ftc and .ftu files (FTEX) -Jerome Leclanche - -The contents of this file are hereby released in the public domain (CC0) -Full text of the CC0 license: - https://creativecommons.org/publicdomain/zero/1.0/ - -Independence War 2: Edge Of Chaos - Texture File Format - 16 October 2001 - -The textures used for 3D objects in Independence War 2: Edge Of Chaos are in a -packed custom format called FTEX. This file format uses file extensions FTC -and FTU. -* FTC files are compressed textures (using standard texture compression). -* FTU files are not compressed. -Texture File Format -The FTC and FTU texture files both use the same format. This -has the following structure: -{header} -{format_directory} -{data} -Where: -{header} = { - u32:magic, - u32:version, - u32:width, - u32:height, - u32:mipmap_count, - u32:format_count -} - -* The "magic" number is "FTEX". -* "width" and "height" are the dimensions of the texture. -* "mipmap_count" is the number of mipmaps in the texture. -* "format_count" is the number of texture formats (different versions of the -same texture) in this file. - -{format_directory} = format_count * { u32:format, u32:where } - -The format value is 0 for DXT1 compressed textures and 1 for 24-bit RGB -uncompressed textures. -The texture data for a format starts at the position "where" in the file. - -Each set of texture data in the file has the following structure: -{data} = format_count * { u32:mipmap_size, mipmap_size * { u8 } } -* "mipmap_size" is the number of bytes in that mip level. For compressed -textures this is the size of the texture data compressed with DXT1. For 24 bit -uncompressed textures, this is 3 * width * height. Following this are the image -bytes for that mipmap level. - -Note: All data is stored in little-Endian (Intel) byte order. -""" - -from __future__ import annotations - -import struct -from enum import IntEnum -from io import BytesIO - -from . import Image, ImageFile - -MAGIC = b"FTEX" - - -class Format(IntEnum): - DXT1 = 0 - UNCOMPRESSED = 1 - - -class FtexImageFile(ImageFile.ImageFile): - format = "FTEX" - format_description = "Texture File Format (IW2:EOC)" - - def _open(self) -> None: - if not _accept(self.fp.read(4)): - msg = "not an FTEX file" - raise SyntaxError(msg) - struct.unpack(" None: - pass - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(MAGIC) - - -Image.register_open(FtexImageFile.format, FtexImageFile, _accept) -Image.register_extensions(FtexImageFile.format, [".ftc", ".ftu"]) diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/GbrImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/GbrImagePlugin.py deleted file mode 100644 index d6929536..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/GbrImagePlugin.py +++ /dev/null @@ -1,101 +0,0 @@ -# -# The Python Imaging Library -# -# load a GIMP brush file -# -# History: -# 96-03-14 fl Created -# 16-01-08 es Version 2 -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1996. -# Copyright (c) Eric Soroos 2016. -# -# See the README file for information on usage and redistribution. -# -# -# See https://github.com/GNOME/gimp/blob/mainline/devel-docs/gbr.txt for -# format documentation. -# -# This code Interprets version 1 and 2 .gbr files. -# Version 1 files are obsolete, and should not be used for new -# brushes. -# Version 2 files are saved by GIMP v2.8 (at least) -# Version 3 files have a format specifier of 18 for 16bit floats in -# the color depth field. This is currently unsupported by Pillow. -from __future__ import annotations - -from . import Image, ImageFile -from ._binary import i32be as i32 - - -def _accept(prefix: bytes) -> bool: - return len(prefix) >= 8 and i32(prefix, 0) >= 20 and i32(prefix, 4) in (1, 2) - - -## -# Image plugin for the GIMP brush format. - - -class GbrImageFile(ImageFile.ImageFile): - format = "GBR" - format_description = "GIMP brush file" - - def _open(self) -> None: - header_size = i32(self.fp.read(4)) - if header_size < 20: - msg = "not a GIMP brush" - raise SyntaxError(msg) - version = i32(self.fp.read(4)) - if version not in (1, 2): - msg = f"Unsupported GIMP brush version: {version}" - raise SyntaxError(msg) - - width = i32(self.fp.read(4)) - height = i32(self.fp.read(4)) - color_depth = i32(self.fp.read(4)) - if width == 0 or height == 0: - msg = "not a GIMP brush" - raise SyntaxError(msg) - if color_depth not in (1, 4): - msg = f"Unsupported GIMP brush color depth: {color_depth}" - raise SyntaxError(msg) - - if version == 1: - comment_length = header_size - 20 - else: - comment_length = header_size - 28 - magic_number = self.fp.read(4) - if magic_number != b"GIMP": - msg = "not a GIMP brush, bad magic number" - raise SyntaxError(msg) - self.info["spacing"] = i32(self.fp.read(4)) - - self.info["comment"] = self.fp.read(comment_length)[:-1] - - if color_depth == 1: - self._mode = "L" - else: - self._mode = "RGBA" - - self._size = width, height - - # Image might not be small - Image._decompression_bomb_check(self.size) - - # Data is an uncompressed block of w * h * bytes/pixel - self._data_size = width * height * color_depth - - def load(self) -> Image.core.PixelAccess | None: - if self._im is None: - self.im = Image.core.new(self.mode, self.size) - self.frombytes(self.fp.read(self._data_size)) - return Image.Image.load(self) - - -# -# registry - - -Image.register_open(GbrImageFile.format, GbrImageFile, _accept) -Image.register_extension(GbrImageFile.format, ".gbr") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/GdImageFile.py b/.venv-docs/lib/python3.12/site-packages/PIL/GdImageFile.py deleted file mode 100644 index 891225ce..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/GdImageFile.py +++ /dev/null @@ -1,102 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# GD file handling -# -# History: -# 1996-04-12 fl Created -# -# Copyright (c) 1997 by Secret Labs AB. -# Copyright (c) 1996 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# - - -""" -.. note:: - This format cannot be automatically recognized, so the - class is not registered for use with :py:func:`PIL.Image.open()`. To open a - gd file, use the :py:func:`PIL.GdImageFile.open()` function instead. - -.. warning:: - THE GD FORMAT IS NOT DESIGNED FOR DATA INTERCHANGE. This - implementation is provided for convenience and demonstrational - purposes only. -""" -from __future__ import annotations - -from typing import IO - -from . import ImageFile, ImagePalette, UnidentifiedImageError -from ._binary import i16be as i16 -from ._binary import i32be as i32 -from ._typing import StrOrBytesPath - - -class GdImageFile(ImageFile.ImageFile): - """ - Image plugin for the GD uncompressed format. Note that this format - is not supported by the standard :py:func:`PIL.Image.open()` function. To use - this plugin, you have to import the :py:mod:`PIL.GdImageFile` module and - use the :py:func:`PIL.GdImageFile.open()` function. - """ - - format = "GD" - format_description = "GD uncompressed images" - - def _open(self) -> None: - # Header - assert self.fp is not None - - s = self.fp.read(1037) - - if i16(s) not in [65534, 65535]: - msg = "Not a valid GD 2.x .gd file" - raise SyntaxError(msg) - - self._mode = "P" - self._size = i16(s, 2), i16(s, 4) - - true_color = s[6] - true_color_offset = 2 if true_color else 0 - - # transparency index - tindex = i32(s, 7 + true_color_offset) - if tindex < 256: - self.info["transparency"] = tindex - - self.palette = ImagePalette.raw( - "RGBX", s[7 + true_color_offset + 6 : 7 + true_color_offset + 6 + 256 * 4] - ) - - self.tile = [ - ImageFile._Tile( - "raw", - (0, 0) + self.size, - 7 + true_color_offset + 6 + 256 * 4, - "L", - ) - ] - - -def open(fp: StrOrBytesPath | IO[bytes], mode: str = "r") -> GdImageFile: - """ - Load texture from a GD image file. - - :param fp: GD file name, or an opened file handle. - :param mode: Optional mode. In this version, if the mode argument - is given, it must be "r". - :returns: An image instance. - :raises OSError: If the image could not be read. - """ - if mode != "r": - msg = "bad mode" - raise ValueError(msg) - - try: - return GdImageFile(fp) - except SyntaxError as e: - msg = "cannot identify this image file" - raise UnidentifiedImageError(msg) from e diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/GifImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/GifImagePlugin.py deleted file mode 100644 index 58c460ef..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/GifImagePlugin.py +++ /dev/null @@ -1,1215 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# GIF file handling -# -# History: -# 1995-09-01 fl Created -# 1996-12-14 fl Added interlace support -# 1996-12-30 fl Added animation support -# 1997-01-05 fl Added write support, fixed local colour map bug -# 1997-02-23 fl Make sure to load raster data in getdata() -# 1997-07-05 fl Support external decoder (0.4) -# 1998-07-09 fl Handle all modes when saving (0.5) -# 1998-07-15 fl Renamed offset attribute to avoid name clash -# 2001-04-16 fl Added rewind support (seek to frame 0) (0.6) -# 2001-04-17 fl Added palette optimization (0.7) -# 2002-06-06 fl Added transparency support for save (0.8) -# 2004-02-24 fl Disable interlacing for small images -# -# Copyright (c) 1997-2004 by Secret Labs AB -# Copyright (c) 1995-2004 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import itertools -import math -import os -import subprocess -from enum import IntEnum -from functools import cached_property -from typing import Any, NamedTuple, cast - -from . import ( - Image, - ImageChops, - ImageFile, - ImageMath, - ImageOps, - ImagePalette, - ImageSequence, -) -from ._binary import i16le as i16 -from ._binary import o8 -from ._binary import o16le as o16 -from ._util import DeferredError - -TYPE_CHECKING = False -if TYPE_CHECKING: - from typing import IO, Literal - - from . import _imaging - from ._typing import Buffer - - -class LoadingStrategy(IntEnum): - """.. versionadded:: 9.1.0""" - - RGB_AFTER_FIRST = 0 - RGB_AFTER_DIFFERENT_PALETTE_ONLY = 1 - RGB_ALWAYS = 2 - - -#: .. versionadded:: 9.1.0 -LOADING_STRATEGY = LoadingStrategy.RGB_AFTER_FIRST - -# -------------------------------------------------------------------- -# Identify/read GIF files - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith((b"GIF87a", b"GIF89a")) - - -## -# Image plugin for GIF images. This plugin supports both GIF87 and -# GIF89 images. - - -class GifImageFile(ImageFile.ImageFile): - format = "GIF" - format_description = "Compuserve GIF" - _close_exclusive_fp_after_loading = False - - global_palette = None - - def data(self) -> bytes | None: - s = self.fp.read(1) - if s and s[0]: - return self.fp.read(s[0]) - return None - - def _is_palette_needed(self, p: bytes) -> bool: - for i in range(0, len(p), 3): - if not (i // 3 == p[i] == p[i + 1] == p[i + 2]): - return True - return False - - def _open(self) -> None: - # Screen - s = self.fp.read(13) - if not _accept(s): - msg = "not a GIF file" - raise SyntaxError(msg) - - self.info["version"] = s[:6] - self._size = i16(s, 6), i16(s, 8) - flags = s[10] - bits = (flags & 7) + 1 - - if flags & 128: - # get global palette - self.info["background"] = s[11] - # check if palette contains colour indices - p = self.fp.read(3 << bits) - if self._is_palette_needed(p): - p = ImagePalette.raw("RGB", p) - self.global_palette = self.palette = p - - self._fp = self.fp # FIXME: hack - self.__rewind = self.fp.tell() - self._n_frames: int | None = None - self._seek(0) # get ready to read first frame - - @property - def n_frames(self) -> int: - if self._n_frames is None: - current = self.tell() - try: - while True: - self._seek(self.tell() + 1, False) - except EOFError: - self._n_frames = self.tell() + 1 - self.seek(current) - return self._n_frames - - @cached_property - def is_animated(self) -> bool: - if self._n_frames is not None: - return self._n_frames != 1 - - current = self.tell() - if current: - return True - - try: - self._seek(1, False) - is_animated = True - except EOFError: - is_animated = False - - self.seek(current) - return is_animated - - def seek(self, frame: int) -> None: - if not self._seek_check(frame): - return - if frame < self.__frame: - self._im = None - self._seek(0) - - last_frame = self.__frame - for f in range(self.__frame + 1, frame + 1): - try: - self._seek(f) - except EOFError as e: - self.seek(last_frame) - msg = "no more images in GIF file" - raise EOFError(msg) from e - - def _seek(self, frame: int, update_image: bool = True) -> None: - if isinstance(self._fp, DeferredError): - raise self._fp.ex - if frame == 0: - # rewind - self.__offset = 0 - self.dispose: _imaging.ImagingCore | None = None - self.__frame = -1 - self._fp.seek(self.__rewind) - self.disposal_method = 0 - if "comment" in self.info: - del self.info["comment"] - else: - # ensure that the previous frame was loaded - if self.tile and update_image: - self.load() - - if frame != self.__frame + 1: - msg = f"cannot seek to frame {frame}" - raise ValueError(msg) - - self.fp = self._fp - if self.__offset: - # backup to last frame - self.fp.seek(self.__offset) - while self.data(): - pass - self.__offset = 0 - - s = self.fp.read(1) - if not s or s == b";": - msg = "no more images in GIF file" - raise EOFError(msg) - - palette: ImagePalette.ImagePalette | Literal[False] | None = None - - info: dict[str, Any] = {} - frame_transparency = None - interlace = None - frame_dispose_extent = None - while True: - if not s: - s = self.fp.read(1) - if not s or s == b";": - break - - elif s == b"!": - # - # extensions - # - s = self.fp.read(1) - block = self.data() - if s[0] == 249 and block is not None: - # - # graphic control extension - # - flags = block[0] - if flags & 1: - frame_transparency = block[3] - info["duration"] = i16(block, 1) * 10 - - # disposal method - find the value of bits 4 - 6 - dispose_bits = 0b00011100 & flags - dispose_bits = dispose_bits >> 2 - if dispose_bits: - # only set the dispose if it is not - # unspecified. I'm not sure if this is - # correct, but it seems to prevent the last - # frame from looking odd for some animations - self.disposal_method = dispose_bits - elif s[0] == 254: - # - # comment extension - # - comment = b"" - - # Read this comment block - while block: - comment += block - block = self.data() - - if "comment" in info: - # If multiple comment blocks in frame, separate with \n - info["comment"] += b"\n" + comment - else: - info["comment"] = comment - s = None - continue - elif s[0] == 255 and frame == 0 and block is not None: - # - # application extension - # - info["extension"] = block, self.fp.tell() - if block.startswith(b"NETSCAPE2.0"): - block = self.data() - if block and len(block) >= 3 and block[0] == 1: - self.info["loop"] = i16(block, 1) - while self.data(): - pass - - elif s == b",": - # - # local image - # - s = self.fp.read(9) - - # extent - x0, y0 = i16(s, 0), i16(s, 2) - x1, y1 = x0 + i16(s, 4), y0 + i16(s, 6) - if (x1 > self.size[0] or y1 > self.size[1]) and update_image: - self._size = max(x1, self.size[0]), max(y1, self.size[1]) - Image._decompression_bomb_check(self._size) - frame_dispose_extent = x0, y0, x1, y1 - flags = s[8] - - interlace = (flags & 64) != 0 - - if flags & 128: - bits = (flags & 7) + 1 - p = self.fp.read(3 << bits) - if self._is_palette_needed(p): - palette = ImagePalette.raw("RGB", p) - else: - palette = False - - # image data - bits = self.fp.read(1)[0] - self.__offset = self.fp.tell() - break - s = None - - if interlace is None: - msg = "image not found in GIF frame" - raise EOFError(msg) - - self.__frame = frame - if not update_image: - return - - self.tile = [] - - if self.dispose: - self.im.paste(self.dispose, self.dispose_extent) - - self._frame_palette = palette if palette is not None else self.global_palette - self._frame_transparency = frame_transparency - if frame == 0: - if self._frame_palette: - if LOADING_STRATEGY == LoadingStrategy.RGB_ALWAYS: - self._mode = "RGBA" if frame_transparency is not None else "RGB" - else: - self._mode = "P" - else: - self._mode = "L" - - if palette: - self.palette = palette - elif self.global_palette: - from copy import copy - - self.palette = copy(self.global_palette) - else: - self.palette = None - else: - if self.mode == "P": - if ( - LOADING_STRATEGY != LoadingStrategy.RGB_AFTER_DIFFERENT_PALETTE_ONLY - or palette - ): - if "transparency" in self.info: - self.im.putpalettealpha(self.info["transparency"], 0) - self.im = self.im.convert("RGBA", Image.Dither.FLOYDSTEINBERG) - self._mode = "RGBA" - del self.info["transparency"] - else: - self._mode = "RGB" - self.im = self.im.convert("RGB", Image.Dither.FLOYDSTEINBERG) - - def _rgb(color: int) -> tuple[int, int, int]: - if self._frame_palette: - if color * 3 + 3 > len(self._frame_palette.palette): - color = 0 - return cast( - tuple[int, int, int], - tuple(self._frame_palette.palette[color * 3 : color * 3 + 3]), - ) - else: - return (color, color, color) - - self.dispose = None - self.dispose_extent: tuple[int, int, int, int] | None = frame_dispose_extent - if self.dispose_extent and self.disposal_method >= 2: - try: - if self.disposal_method == 2: - # replace with background colour - - # only dispose the extent in this frame - x0, y0, x1, y1 = self.dispose_extent - dispose_size = (x1 - x0, y1 - y0) - - Image._decompression_bomb_check(dispose_size) - - # by convention, attempt to use transparency first - dispose_mode = "P" - color = self.info.get("transparency", frame_transparency) - if color is not None: - if self.mode in ("RGB", "RGBA"): - dispose_mode = "RGBA" - color = _rgb(color) + (0,) - else: - color = self.info.get("background", 0) - if self.mode in ("RGB", "RGBA"): - dispose_mode = "RGB" - color = _rgb(color) - self.dispose = Image.core.fill(dispose_mode, dispose_size, color) - else: - # replace with previous contents - if self._im is not None: - # only dispose the extent in this frame - self.dispose = self._crop(self.im, self.dispose_extent) - elif frame_transparency is not None: - x0, y0, x1, y1 = self.dispose_extent - dispose_size = (x1 - x0, y1 - y0) - - Image._decompression_bomb_check(dispose_size) - dispose_mode = "P" - color = frame_transparency - if self.mode in ("RGB", "RGBA"): - dispose_mode = "RGBA" - color = _rgb(frame_transparency) + (0,) - self.dispose = Image.core.fill( - dispose_mode, dispose_size, color - ) - except AttributeError: - pass - - if interlace is not None: - transparency = -1 - if frame_transparency is not None: - if frame == 0: - if LOADING_STRATEGY != LoadingStrategy.RGB_ALWAYS: - self.info["transparency"] = frame_transparency - elif self.mode not in ("RGB", "RGBA"): - transparency = frame_transparency - self.tile = [ - ImageFile._Tile( - "gif", - (x0, y0, x1, y1), - self.__offset, - (bits, interlace, transparency), - ) - ] - - if info.get("comment"): - self.info["comment"] = info["comment"] - for k in ["duration", "extension"]: - if k in info: - self.info[k] = info[k] - elif k in self.info: - del self.info[k] - - def load_prepare(self) -> None: - temp_mode = "P" if self._frame_palette else "L" - self._prev_im = None - if self.__frame == 0: - if self._frame_transparency is not None: - self.im = Image.core.fill( - temp_mode, self.size, self._frame_transparency - ) - elif self.mode in ("RGB", "RGBA"): - self._prev_im = self.im - if self._frame_palette: - self.im = Image.core.fill("P", self.size, self._frame_transparency or 0) - self.im.putpalette("RGB", *self._frame_palette.getdata()) - else: - self._im = None - if not self._prev_im and self._im is not None and self.size != self.im.size: - expanded_im = Image.core.fill(self.im.mode, self.size) - if self._frame_palette: - expanded_im.putpalette("RGB", *self._frame_palette.getdata()) - expanded_im.paste(self.im, (0, 0) + self.im.size) - - self.im = expanded_im - self._mode = temp_mode - self._frame_palette = None - - super().load_prepare() - - def load_end(self) -> None: - if self.__frame == 0: - if self.mode == "P" and LOADING_STRATEGY == LoadingStrategy.RGB_ALWAYS: - if self._frame_transparency is not None: - self.im.putpalettealpha(self._frame_transparency, 0) - self._mode = "RGBA" - else: - self._mode = "RGB" - self.im = self.im.convert(self.mode, Image.Dither.FLOYDSTEINBERG) - return - if not self._prev_im: - return - if self.size != self._prev_im.size: - if self._frame_transparency is not None: - expanded_im = Image.core.fill("RGBA", self.size) - else: - expanded_im = Image.core.fill("P", self.size) - expanded_im.putpalette("RGB", "RGB", self.im.getpalette()) - expanded_im = expanded_im.convert("RGB") - expanded_im.paste(self._prev_im, (0, 0) + self._prev_im.size) - - self._prev_im = expanded_im - assert self._prev_im is not None - if self._frame_transparency is not None: - if self.mode == "L": - frame_im = self.im.convert_transparent("LA", self._frame_transparency) - else: - self.im.putpalettealpha(self._frame_transparency, 0) - frame_im = self.im.convert("RGBA") - else: - frame_im = self.im.convert("RGB") - - assert self.dispose_extent is not None - frame_im = self._crop(frame_im, self.dispose_extent) - - self.im = self._prev_im - self._mode = self.im.mode - if frame_im.mode in ("LA", "RGBA"): - self.im.paste(frame_im, self.dispose_extent, frame_im) - else: - self.im.paste(frame_im, self.dispose_extent) - - def tell(self) -> int: - return self.__frame - - -# -------------------------------------------------------------------- -# Write GIF files - - -RAWMODE = {"1": "L", "L": "L", "P": "P"} - - -def _normalize_mode(im: Image.Image) -> Image.Image: - """ - Takes an image (or frame), returns an image in a mode that is appropriate - for saving in a Gif. - - It may return the original image, or it may return an image converted to - palette or 'L' mode. - - :param im: Image object - :returns: Image object - """ - if im.mode in RAWMODE: - im.load() - return im - if Image.getmodebase(im.mode) == "RGB": - im = im.convert("P", palette=Image.Palette.ADAPTIVE) - assert im.palette is not None - if im.palette.mode == "RGBA": - for rgba in im.palette.colors: - if rgba[3] == 0: - im.info["transparency"] = im.palette.colors[rgba] - break - return im - return im.convert("L") - - -_Palette = bytes | bytearray | list[int] | ImagePalette.ImagePalette - - -def _normalize_palette( - im: Image.Image, palette: _Palette | None, info: dict[str, Any] -) -> Image.Image: - """ - Normalizes the palette for image. - - Sets the palette to the incoming palette, if provided. - - Ensures that there's a palette for L mode images - - Optimizes the palette if necessary/desired. - - :param im: Image object - :param palette: bytes object containing the source palette, or .... - :param info: encoderinfo - :returns: Image object - """ - source_palette = None - if palette: - # a bytes palette - if isinstance(palette, (bytes, bytearray, list)): - source_palette = bytearray(palette[:768]) - if isinstance(palette, ImagePalette.ImagePalette): - source_palette = bytearray(palette.palette) - - if im.mode == "P": - if not source_palette: - im_palette = im.getpalette(None) - assert im_palette is not None - source_palette = bytearray(im_palette) - else: # L-mode - if not source_palette: - source_palette = bytearray(i // 3 for i in range(768)) - im.palette = ImagePalette.ImagePalette("RGB", palette=source_palette) - assert source_palette is not None - - if palette: - used_palette_colors: list[int | None] = [] - assert im.palette is not None - for i in range(0, len(source_palette), 3): - source_color = tuple(source_palette[i : i + 3]) - index = im.palette.colors.get(source_color) - if index in used_palette_colors: - index = None - used_palette_colors.append(index) - for i, index in enumerate(used_palette_colors): - if index is None: - for j in range(len(used_palette_colors)): - if j not in used_palette_colors: - used_palette_colors[i] = j - break - dest_map: list[int] = [] - for index in used_palette_colors: - assert index is not None - dest_map.append(index) - im = im.remap_palette(dest_map) - else: - optimized_palette_colors = _get_optimize(im, info) - if optimized_palette_colors is not None: - im = im.remap_palette(optimized_palette_colors, source_palette) - if "transparency" in info: - try: - info["transparency"] = optimized_palette_colors.index( - info["transparency"] - ) - except ValueError: - del info["transparency"] - return im - - assert im.palette is not None - im.palette.palette = source_palette - return im - - -def _write_single_frame( - im: Image.Image, - fp: IO[bytes], - palette: _Palette | None, -) -> None: - im_out = _normalize_mode(im) - for k, v in im_out.info.items(): - if isinstance(k, str): - im.encoderinfo.setdefault(k, v) - im_out = _normalize_palette(im_out, palette, im.encoderinfo) - - for s in _get_global_header(im_out, im.encoderinfo): - fp.write(s) - - # local image header - flags = 0 - if get_interlace(im): - flags = flags | 64 - _write_local_header(fp, im, (0, 0), flags) - - im_out.encoderconfig = (8, get_interlace(im)) - ImageFile._save( - im_out, fp, [ImageFile._Tile("gif", (0, 0) + im.size, 0, RAWMODE[im_out.mode])] - ) - - fp.write(b"\0") # end of image data - - -def _getbbox( - base_im: Image.Image, im_frame: Image.Image -) -> tuple[Image.Image, tuple[int, int, int, int] | None]: - palette_bytes = [ - bytes(im.palette.palette) if im.palette else b"" for im in (base_im, im_frame) - ] - if palette_bytes[0] != palette_bytes[1]: - im_frame = im_frame.convert("RGBA") - base_im = base_im.convert("RGBA") - delta = ImageChops.subtract_modulo(im_frame, base_im) - return delta, delta.getbbox(alpha_only=False) - - -class _Frame(NamedTuple): - im: Image.Image - bbox: tuple[int, int, int, int] | None - encoderinfo: dict[str, Any] - - -def _write_multiple_frames( - im: Image.Image, fp: IO[bytes], palette: _Palette | None -) -> bool: - duration = im.encoderinfo.get("duration") - disposal = im.encoderinfo.get("disposal", im.info.get("disposal")) - - im_frames: list[_Frame] = [] - previous_im: Image.Image | None = None - frame_count = 0 - background_im = None - for imSequence in itertools.chain([im], im.encoderinfo.get("append_images", [])): - for im_frame in ImageSequence.Iterator(imSequence): - # a copy is required here since seek can still mutate the image - im_frame = _normalize_mode(im_frame.copy()) - if frame_count == 0: - for k, v in im_frame.info.items(): - if k == "transparency": - continue - if isinstance(k, str): - im.encoderinfo.setdefault(k, v) - - encoderinfo = im.encoderinfo.copy() - if "transparency" in im_frame.info: - encoderinfo.setdefault("transparency", im_frame.info["transparency"]) - im_frame = _normalize_palette(im_frame, palette, encoderinfo) - if isinstance(duration, (list, tuple)): - encoderinfo["duration"] = duration[frame_count] - elif duration is None and "duration" in im_frame.info: - encoderinfo["duration"] = im_frame.info["duration"] - if isinstance(disposal, (list, tuple)): - encoderinfo["disposal"] = disposal[frame_count] - frame_count += 1 - - diff_frame = None - if im_frames and previous_im: - # delta frame - delta, bbox = _getbbox(previous_im, im_frame) - if not bbox: - # This frame is identical to the previous frame - if encoderinfo.get("duration"): - im_frames[-1].encoderinfo["duration"] += encoderinfo["duration"] - continue - if im_frames[-1].encoderinfo.get("disposal") == 2: - # To appear correctly in viewers using a convention, - # only consider transparency, and not background color - color = im.encoderinfo.get( - "transparency", im.info.get("transparency") - ) - if color is not None: - if background_im is None: - background = _get_background(im_frame, color) - background_im = Image.new("P", im_frame.size, background) - first_palette = im_frames[0].im.palette - assert first_palette is not None - background_im.putpalette(first_palette, first_palette.mode) - bbox = _getbbox(background_im, im_frame)[1] - else: - bbox = (0, 0) + im_frame.size - elif encoderinfo.get("optimize") and im_frame.mode != "1": - if "transparency" not in encoderinfo: - assert im_frame.palette is not None - try: - encoderinfo["transparency"] = ( - im_frame.palette._new_color_index(im_frame) - ) - except ValueError: - pass - if "transparency" in encoderinfo: - # When the delta is zero, fill the image with transparency - diff_frame = im_frame.copy() - fill = Image.new("P", delta.size, encoderinfo["transparency"]) - if delta.mode == "RGBA": - r, g, b, a = delta.split() - mask = ImageMath.lambda_eval( - lambda args: args["convert"]( - args["max"]( - args["max"]( - args["max"](args["r"], args["g"]), args["b"] - ), - args["a"], - ) - * 255, - "1", - ), - r=r, - g=g, - b=b, - a=a, - ) - else: - if delta.mode == "P": - # Convert to L without considering palette - delta_l = Image.new("L", delta.size) - delta_l.putdata(delta.getdata()) - delta = delta_l - mask = ImageMath.lambda_eval( - lambda args: args["convert"](args["im"] * 255, "1"), - im=delta, - ) - diff_frame.paste(fill, mask=ImageOps.invert(mask)) - else: - bbox = None - previous_im = im_frame - im_frames.append(_Frame(diff_frame or im_frame, bbox, encoderinfo)) - - if len(im_frames) == 1: - if "duration" in im.encoderinfo: - # Since multiple frames will not be written, use the combined duration - im.encoderinfo["duration"] = im_frames[0].encoderinfo["duration"] - return False - - for frame_data in im_frames: - im_frame = frame_data.im - if not frame_data.bbox: - # global header - for s in _get_global_header(im_frame, frame_data.encoderinfo): - fp.write(s) - offset = (0, 0) - else: - # compress difference - if not palette: - frame_data.encoderinfo["include_color_table"] = True - - if frame_data.bbox != (0, 0) + im_frame.size: - im_frame = im_frame.crop(frame_data.bbox) - offset = frame_data.bbox[:2] - _write_frame_data(fp, im_frame, offset, frame_data.encoderinfo) - return True - - -def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - _save(im, fp, filename, save_all=True) - - -def _save( - im: Image.Image, fp: IO[bytes], filename: str | bytes, save_all: bool = False -) -> None: - # header - if "palette" in im.encoderinfo or "palette" in im.info: - palette = im.encoderinfo.get("palette", im.info.get("palette")) - else: - palette = None - im.encoderinfo.setdefault("optimize", True) - - if not save_all or not _write_multiple_frames(im, fp, palette): - _write_single_frame(im, fp, palette) - - fp.write(b";") # end of file - - if hasattr(fp, "flush"): - fp.flush() - - -def get_interlace(im: Image.Image) -> int: - interlace = im.encoderinfo.get("interlace", 1) - - # workaround for @PIL153 - if min(im.size) < 16: - interlace = 0 - - return interlace - - -def _write_local_header( - fp: IO[bytes], im: Image.Image, offset: tuple[int, int], flags: int -) -> None: - try: - transparency = im.encoderinfo["transparency"] - except KeyError: - transparency = None - - if "duration" in im.encoderinfo: - duration = int(im.encoderinfo["duration"] / 10) - else: - duration = 0 - - disposal = int(im.encoderinfo.get("disposal", 0)) - - if transparency is not None or duration != 0 or disposal: - packed_flag = 1 if transparency is not None else 0 - packed_flag |= disposal << 2 - - fp.write( - b"!" - + o8(249) # extension intro - + o8(4) # length - + o8(packed_flag) # packed fields - + o16(duration) # duration - + o8(transparency or 0) # transparency index - + o8(0) - ) - - include_color_table = im.encoderinfo.get("include_color_table") - if include_color_table: - palette_bytes = _get_palette_bytes(im) - color_table_size = _get_color_table_size(palette_bytes) - if color_table_size: - flags = flags | 128 # local color table flag - flags = flags | color_table_size - - fp.write( - b"," - + o16(offset[0]) # offset - + o16(offset[1]) - + o16(im.size[0]) # size - + o16(im.size[1]) - + o8(flags) # flags - ) - if include_color_table and color_table_size: - fp.write(_get_header_palette(palette_bytes)) - fp.write(o8(8)) # bits - - -def _save_netpbm(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - # Unused by default. - # To use, uncomment the register_save call at the end of the file. - # - # If you need real GIF compression and/or RGB quantization, you - # can use the external NETPBM/PBMPLUS utilities. See comments - # below for information on how to enable this. - tempfile = im._dump() - - try: - with open(filename, "wb") as f: - if im.mode != "RGB": - subprocess.check_call( - ["ppmtogif", tempfile], stdout=f, stderr=subprocess.DEVNULL - ) - else: - # Pipe ppmquant output into ppmtogif - # "ppmquant 256 %s | ppmtogif > %s" % (tempfile, filename) - quant_cmd = ["ppmquant", "256", tempfile] - togif_cmd = ["ppmtogif"] - quant_proc = subprocess.Popen( - quant_cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL - ) - togif_proc = subprocess.Popen( - togif_cmd, - stdin=quant_proc.stdout, - stdout=f, - stderr=subprocess.DEVNULL, - ) - - # Allow ppmquant to receive SIGPIPE if ppmtogif exits - assert quant_proc.stdout is not None - quant_proc.stdout.close() - - retcode = quant_proc.wait() - if retcode: - raise subprocess.CalledProcessError(retcode, quant_cmd) - - retcode = togif_proc.wait() - if retcode: - raise subprocess.CalledProcessError(retcode, togif_cmd) - finally: - try: - os.unlink(tempfile) - except OSError: - pass - - -# Force optimization so that we can test performance against -# cases where it took lots of memory and time previously. -_FORCE_OPTIMIZE = False - - -def _get_optimize(im: Image.Image, info: dict[str, Any]) -> list[int] | None: - """ - Palette optimization is a potentially expensive operation. - - This function determines if the palette should be optimized using - some heuristics, then returns the list of palette entries in use. - - :param im: Image object - :param info: encoderinfo - :returns: list of indexes of palette entries in use, or None - """ - if im.mode in ("P", "L") and info and info.get("optimize"): - # Potentially expensive operation. - - # The palette saves 3 bytes per color not used, but palette - # lengths are restricted to 3*(2**N) bytes. Max saving would - # be 768 -> 6 bytes if we went all the way down to 2 colors. - # * If we're over 128 colors, we can't save any space. - # * If there aren't any holes, it's not worth collapsing. - # * If we have a 'large' image, the palette is in the noise. - - # create the new palette if not every color is used - optimise = _FORCE_OPTIMIZE or im.mode == "L" - if optimise or im.width * im.height < 512 * 512: - # check which colors are used - used_palette_colors = [] - for i, count in enumerate(im.histogram()): - if count: - used_palette_colors.append(i) - - if optimise or max(used_palette_colors) >= len(used_palette_colors): - return used_palette_colors - - assert im.palette is not None - num_palette_colors = len(im.palette.palette) // Image.getmodebands( - im.palette.mode - ) - current_palette_size = 1 << (num_palette_colors - 1).bit_length() - if ( - # check that the palette would become smaller when saved - len(used_palette_colors) <= current_palette_size // 2 - # check that the palette is not already the smallest possible size - and current_palette_size > 2 - ): - return used_palette_colors - return None - - -def _get_color_table_size(palette_bytes: bytes) -> int: - # calculate the palette size for the header - if not palette_bytes: - return 0 - elif len(palette_bytes) < 9: - return 1 - else: - return math.ceil(math.log(len(palette_bytes) // 3, 2)) - 1 - - -def _get_header_palette(palette_bytes: bytes) -> bytes: - """ - Returns the palette, null padded to the next power of 2 (*3) bytes - suitable for direct inclusion in the GIF header - - :param palette_bytes: Unpadded palette bytes, in RGBRGB form - :returns: Null padded palette - """ - color_table_size = _get_color_table_size(palette_bytes) - - # add the missing amount of bytes - # the palette has to be 2< 0: - palette_bytes += o8(0) * 3 * actual_target_size_diff - return palette_bytes - - -def _get_palette_bytes(im: Image.Image) -> bytes: - """ - Gets the palette for inclusion in the gif header - - :param im: Image object - :returns: Bytes, len<=768 suitable for inclusion in gif header - """ - if not im.palette: - return b"" - - palette = bytes(im.palette.palette) - if im.palette.mode == "RGBA": - palette = b"".join(palette[i * 4 : i * 4 + 3] for i in range(len(palette) // 3)) - return palette - - -def _get_background( - im: Image.Image, - info_background: int | tuple[int, int, int] | tuple[int, int, int, int] | None, -) -> int: - background = 0 - if info_background: - if isinstance(info_background, tuple): - # WebPImagePlugin stores an RGBA value in info["background"] - # So it must be converted to the same format as GifImagePlugin's - # info["background"] - a global color table index - assert im.palette is not None - try: - background = im.palette.getcolor(info_background, im) - except ValueError as e: - if str(e) not in ( - # If all 256 colors are in use, - # then there is no need for the background color - "cannot allocate more than 256 colors", - # Ignore non-opaque WebP background - "cannot add non-opaque RGBA color to RGB palette", - ): - raise - else: - background = info_background - return background - - -def _get_global_header(im: Image.Image, info: dict[str, Any]) -> list[bytes]: - """Return a list of strings representing a GIF header""" - - # Header Block - # https://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp - - version = b"87a" - if im.info.get("version") == b"89a" or ( - info - and ( - "transparency" in info - or info.get("loop") is not None - or info.get("duration") - or info.get("comment") - ) - ): - version = b"89a" - - background = _get_background(im, info.get("background")) - - palette_bytes = _get_palette_bytes(im) - color_table_size = _get_color_table_size(palette_bytes) - - header = [ - b"GIF" # signature - + version # version - + o16(im.size[0]) # canvas width - + o16(im.size[1]), # canvas height - # Logical Screen Descriptor - # size of global color table + global color table flag - o8(color_table_size + 128), # packed fields - # background + reserved/aspect - o8(background) + o8(0), - # Global Color Table - _get_header_palette(palette_bytes), - ] - if info.get("loop") is not None: - header.append( - b"!" - + o8(255) # extension intro - + o8(11) - + b"NETSCAPE2.0" - + o8(3) - + o8(1) - + o16(info["loop"]) # number of loops - + o8(0) - ) - if info.get("comment"): - comment_block = b"!" + o8(254) # extension intro - - comment = info["comment"] - if isinstance(comment, str): - comment = comment.encode() - for i in range(0, len(comment), 255): - subblock = comment[i : i + 255] - comment_block += o8(len(subblock)) + subblock - - comment_block += o8(0) - header.append(comment_block) - return header - - -def _write_frame_data( - fp: IO[bytes], - im_frame: Image.Image, - offset: tuple[int, int], - params: dict[str, Any], -) -> None: - try: - im_frame.encoderinfo = params - - # local image header - _write_local_header(fp, im_frame, offset, 0) - - ImageFile._save( - im_frame, - fp, - [ImageFile._Tile("gif", (0, 0) + im_frame.size, 0, RAWMODE[im_frame.mode])], - ) - - fp.write(b"\0") # end of image data - finally: - del im_frame.encoderinfo - - -# -------------------------------------------------------------------- -# Legacy GIF utilities - - -def getheader( - im: Image.Image, palette: _Palette | None = None, info: dict[str, Any] | None = None -) -> tuple[list[bytes], list[int] | None]: - """ - Legacy Method to get Gif data from image. - - Warning:: May modify image data. - - :param im: Image object - :param palette: bytes object containing the source palette, or .... - :param info: encoderinfo - :returns: tuple of(list of header items, optimized palette) - - """ - if info is None: - info = {} - - used_palette_colors = _get_optimize(im, info) - - if "background" not in info and "background" in im.info: - info["background"] = im.info["background"] - - im_mod = _normalize_palette(im, palette, info) - im.palette = im_mod.palette - im.im = im_mod.im - header = _get_global_header(im, info) - - return header, used_palette_colors - - -def getdata( - im: Image.Image, offset: tuple[int, int] = (0, 0), **params: Any -) -> list[bytes]: - """ - Legacy Method - - Return a list of strings representing this image. - The first string is a local image header, the rest contains - encoded image data. - - To specify duration, add the time in milliseconds, - e.g. ``getdata(im_frame, duration=1000)`` - - :param im: Image object - :param offset: Tuple of (x, y) pixels. Defaults to (0, 0) - :param \\**params: e.g. duration or other encoder info parameters - :returns: List of bytes containing GIF encoded frame data - - """ - from io import BytesIO - - class Collector(BytesIO): - data = [] - - def write(self, data: Buffer) -> int: - self.data.append(data) - return len(data) - - im.load() # make sure raster data is available - - fp = Collector() - - _write_frame_data(fp, im, offset, params) - - return fp.data - - -# -------------------------------------------------------------------- -# Registry - -Image.register_open(GifImageFile.format, GifImageFile, _accept) -Image.register_save(GifImageFile.format, _save) -Image.register_save_all(GifImageFile.format, _save_all) -Image.register_extension(GifImageFile.format, ".gif") -Image.register_mime(GifImageFile.format, "image/gif") - -# -# Uncomment the following line if you wish to use NETPBM/PBMPLUS -# instead of the built-in "uncompressed" GIF encoder - -# Image.register_save(GifImageFile.format, _save_netpbm) diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/GimpGradientFile.py b/.venv-docs/lib/python3.12/site-packages/PIL/GimpGradientFile.py deleted file mode 100644 index 5f269188..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/GimpGradientFile.py +++ /dev/null @@ -1,153 +0,0 @@ -# -# Python Imaging Library -# $Id$ -# -# stuff to read (and render) GIMP gradient files -# -# History: -# 97-08-23 fl Created -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1997. -# -# See the README file for information on usage and redistribution. -# - -""" -Stuff to translate curve segments to palette values (derived from -the corresponding code in GIMP, written by Federico Mena Quintero. -See the GIMP distribution for more information.) -""" -from __future__ import annotations - -from math import log, pi, sin, sqrt - -from ._binary import o8 - -TYPE_CHECKING = False -if TYPE_CHECKING: - from collections.abc import Callable - from typing import IO - -EPSILON = 1e-10 -"""""" # Enable auto-doc for data member - - -def linear(middle: float, pos: float) -> float: - if pos <= middle: - if middle < EPSILON: - return 0.0 - else: - return 0.5 * pos / middle - else: - pos = pos - middle - middle = 1.0 - middle - if middle < EPSILON: - return 1.0 - else: - return 0.5 + 0.5 * pos / middle - - -def curved(middle: float, pos: float) -> float: - return pos ** (log(0.5) / log(max(middle, EPSILON))) - - -def sine(middle: float, pos: float) -> float: - return (sin((-pi / 2.0) + pi * linear(middle, pos)) + 1.0) / 2.0 - - -def sphere_increasing(middle: float, pos: float) -> float: - return sqrt(1.0 - (linear(middle, pos) - 1.0) ** 2) - - -def sphere_decreasing(middle: float, pos: float) -> float: - return 1.0 - sqrt(1.0 - linear(middle, pos) ** 2) - - -SEGMENTS = [linear, curved, sine, sphere_increasing, sphere_decreasing] -"""""" # Enable auto-doc for data member - - -class GradientFile: - gradient: ( - list[ - tuple[ - float, - float, - float, - list[float], - list[float], - Callable[[float, float], float], - ] - ] - | None - ) = None - - def getpalette(self, entries: int = 256) -> tuple[bytes, str]: - assert self.gradient is not None - palette = [] - - ix = 0 - x0, x1, xm, rgb0, rgb1, segment = self.gradient[ix] - - for i in range(entries): - x = i / (entries - 1) - - while x1 < x: - ix += 1 - x0, x1, xm, rgb0, rgb1, segment = self.gradient[ix] - - w = x1 - x0 - - if w < EPSILON: - scale = segment(0.5, 0.5) - else: - scale = segment((xm - x0) / w, (x - x0) / w) - - # expand to RGBA - r = o8(int(255 * ((rgb1[0] - rgb0[0]) * scale + rgb0[0]) + 0.5)) - g = o8(int(255 * ((rgb1[1] - rgb0[1]) * scale + rgb0[1]) + 0.5)) - b = o8(int(255 * ((rgb1[2] - rgb0[2]) * scale + rgb0[2]) + 0.5)) - a = o8(int(255 * ((rgb1[3] - rgb0[3]) * scale + rgb0[3]) + 0.5)) - - # add to palette - palette.append(r + g + b + a) - - return b"".join(palette), "RGBA" - - -class GimpGradientFile(GradientFile): - """File handler for GIMP's gradient format.""" - - def __init__(self, fp: IO[bytes]) -> None: - if not fp.readline().startswith(b"GIMP Gradient"): - msg = "not a GIMP gradient file" - raise SyntaxError(msg) - - line = fp.readline() - - # GIMP 1.2 gradient files don't contain a name, but GIMP 1.3 files do - if line.startswith(b"Name: "): - line = fp.readline().strip() - - count = int(line) - - self.gradient = [] - - for i in range(count): - s = fp.readline().split() - w = [float(x) for x in s[:11]] - - x0, x1 = w[0], w[2] - xm = w[1] - rgb0 = w[3:7] - rgb1 = w[7:11] - - segment = SEGMENTS[int(s[11])] - cspace = int(s[12]) - - if cspace != 0: - msg = "cannot handle HSV colour space" - raise OSError(msg) - - self.gradient.append((x0, x1, xm, rgb0, rgb1, segment)) diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/GimpPaletteFile.py b/.venv-docs/lib/python3.12/site-packages/PIL/GimpPaletteFile.py deleted file mode 100644 index 016257d3..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/GimpPaletteFile.py +++ /dev/null @@ -1,75 +0,0 @@ -# -# Python Imaging Library -# $Id$ -# -# stuff to read GIMP palette files -# -# History: -# 1997-08-23 fl Created -# 2004-09-07 fl Support GIMP 2.0 palette files. -# -# Copyright (c) Secret Labs AB 1997-2004. All rights reserved. -# Copyright (c) Fredrik Lundh 1997-2004. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import re -from io import BytesIO - -TYPE_CHECKING = False -if TYPE_CHECKING: - from typing import IO - - -class GimpPaletteFile: - """File handler for GIMP's palette format.""" - - rawmode = "RGB" - - def _read(self, fp: IO[bytes], limit: bool = True) -> None: - if not fp.readline().startswith(b"GIMP Palette"): - msg = "not a GIMP palette file" - raise SyntaxError(msg) - - palette: list[int] = [] - i = 0 - while True: - if limit and i == 256 + 3: - break - - i += 1 - s = fp.readline() - if not s: - break - - # skip fields and comment lines - if re.match(rb"\w+:|#", s): - continue - if limit and len(s) > 100: - msg = "bad palette file" - raise SyntaxError(msg) - - v = s.split(maxsplit=3) - if len(v) < 3: - msg = "bad palette entry" - raise ValueError(msg) - - palette += (int(v[i]) for i in range(3)) - if limit and len(palette) == 768: - break - - self.palette = bytes(palette) - - def __init__(self, fp: IO[bytes]) -> None: - self._read(fp) - - @classmethod - def frombytes(cls, data: bytes) -> GimpPaletteFile: - self = cls.__new__(cls) - self._read(BytesIO(data), False) - return self - - def getpalette(self) -> tuple[bytes, str]: - return self.palette, self.rawmode diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/GribStubImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/GribStubImagePlugin.py deleted file mode 100644 index dfa79889..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/GribStubImagePlugin.py +++ /dev/null @@ -1,75 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# GRIB stub adapter -# -# Copyright (c) 1996-2003 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import os -from typing import IO - -from . import Image, ImageFile - -_handler = None - - -def register_handler(handler: ImageFile.StubHandler | None) -> None: - """ - Install application-specific GRIB image handler. - - :param handler: Handler object. - """ - global _handler - _handler = handler - - -# -------------------------------------------------------------------- -# Image adapter - - -def _accept(prefix: bytes) -> bool: - return len(prefix) >= 8 and prefix.startswith(b"GRIB") and prefix[7] == 1 - - -class GribStubImageFile(ImageFile.StubImageFile): - format = "GRIB" - format_description = "GRIB" - - def _open(self) -> None: - if not _accept(self.fp.read(8)): - msg = "Not a GRIB file" - raise SyntaxError(msg) - - self.fp.seek(-8, os.SEEK_CUR) - - # make something up - self._mode = "F" - self._size = 1, 1 - - loader = self._load() - if loader: - loader.open(self) - - def _load(self) -> ImageFile.StubHandler | None: - return _handler - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - if _handler is None or not hasattr(_handler, "save"): - msg = "GRIB save handler not installed" - raise OSError(msg) - _handler.save(im, fp, filename) - - -# -------------------------------------------------------------------- -# Registry - -Image.register_open(GribStubImageFile.format, GribStubImageFile, _accept) -Image.register_save(GribStubImageFile.format, _save) - -Image.register_extension(GribStubImageFile.format, ".grib") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/Hdf5StubImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/Hdf5StubImagePlugin.py deleted file mode 100644 index 76e640f1..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/Hdf5StubImagePlugin.py +++ /dev/null @@ -1,75 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# HDF5 stub adapter -# -# Copyright (c) 2000-2003 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import os -from typing import IO - -from . import Image, ImageFile - -_handler = None - - -def register_handler(handler: ImageFile.StubHandler | None) -> None: - """ - Install application-specific HDF5 image handler. - - :param handler: Handler object. - """ - global _handler - _handler = handler - - -# -------------------------------------------------------------------- -# Image adapter - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(b"\x89HDF\r\n\x1a\n") - - -class HDF5StubImageFile(ImageFile.StubImageFile): - format = "HDF5" - format_description = "HDF5" - - def _open(self) -> None: - if not _accept(self.fp.read(8)): - msg = "Not an HDF file" - raise SyntaxError(msg) - - self.fp.seek(-8, os.SEEK_CUR) - - # make something up - self._mode = "F" - self._size = 1, 1 - - loader = self._load() - if loader: - loader.open(self) - - def _load(self) -> ImageFile.StubHandler | None: - return _handler - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - if _handler is None or not hasattr(_handler, "save"): - msg = "HDF5 save handler not installed" - raise OSError(msg) - _handler.save(im, fp, filename) - - -# -------------------------------------------------------------------- -# Registry - -Image.register_open(HDF5StubImageFile.format, HDF5StubImageFile, _accept) -Image.register_save(HDF5StubImageFile.format, _save) - -Image.register_extensions(HDF5StubImageFile.format, [".h5", ".hdf"]) diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/IcnsImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/IcnsImagePlugin.py deleted file mode 100644 index 197ea7a2..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/IcnsImagePlugin.py +++ /dev/null @@ -1,401 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# macOS icns file decoder, based on icns.py by Bob Ippolito. -# -# history: -# 2004-10-09 fl Turned into a PIL plugin; removed 2.3 dependencies. -# 2020-04-04 Allow saving on all operating systems. -# -# Copyright (c) 2004 by Bob Ippolito. -# Copyright (c) 2004 by Secret Labs. -# Copyright (c) 2004 by Fredrik Lundh. -# Copyright (c) 2014 by Alastair Houghton. -# Copyright (c) 2020 by Pan Jing. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io -import os -import struct -import sys -from typing import IO - -from . import Image, ImageFile, PngImagePlugin, features - -enable_jpeg2k = features.check_codec("jpg_2000") -if enable_jpeg2k: - from . import Jpeg2KImagePlugin - -MAGIC = b"icns" -HEADERSIZE = 8 - - -def nextheader(fobj: IO[bytes]) -> tuple[bytes, int]: - return struct.unpack(">4sI", fobj.read(HEADERSIZE)) - - -def read_32t( - fobj: IO[bytes], start_length: tuple[int, int], size: tuple[int, int, int] -) -> dict[str, Image.Image]: - # The 128x128 icon seems to have an extra header for some reason. - (start, length) = start_length - fobj.seek(start) - sig = fobj.read(4) - if sig != b"\x00\x00\x00\x00": - msg = "Unknown signature, expecting 0x00000000" - raise SyntaxError(msg) - return read_32(fobj, (start + 4, length - 4), size) - - -def read_32( - fobj: IO[bytes], start_length: tuple[int, int], size: tuple[int, int, int] -) -> dict[str, Image.Image]: - """ - Read a 32bit RGB icon resource. Seems to be either uncompressed or - an RLE packbits-like scheme. - """ - (start, length) = start_length - fobj.seek(start) - pixel_size = (size[0] * size[2], size[1] * size[2]) - sizesq = pixel_size[0] * pixel_size[1] - if length == sizesq * 3: - # uncompressed ("RGBRGBGB") - indata = fobj.read(length) - im = Image.frombuffer("RGB", pixel_size, indata, "raw", "RGB", 0, 1) - else: - # decode image - im = Image.new("RGB", pixel_size, None) - for band_ix in range(3): - data = [] - bytesleft = sizesq - while bytesleft > 0: - byte = fobj.read(1) - if not byte: - break - byte_int = byte[0] - if byte_int & 0x80: - blocksize = byte_int - 125 - byte = fobj.read(1) - for i in range(blocksize): - data.append(byte) - else: - blocksize = byte_int + 1 - data.append(fobj.read(blocksize)) - bytesleft -= blocksize - if bytesleft <= 0: - break - if bytesleft != 0: - msg = f"Error reading channel [{repr(bytesleft)} left]" - raise SyntaxError(msg) - band = Image.frombuffer("L", pixel_size, b"".join(data), "raw", "L", 0, 1) - im.im.putband(band.im, band_ix) - return {"RGB": im} - - -def read_mk( - fobj: IO[bytes], start_length: tuple[int, int], size: tuple[int, int, int] -) -> dict[str, Image.Image]: - # Alpha masks seem to be uncompressed - start = start_length[0] - fobj.seek(start) - pixel_size = (size[0] * size[2], size[1] * size[2]) - sizesq = pixel_size[0] * pixel_size[1] - band = Image.frombuffer("L", pixel_size, fobj.read(sizesq), "raw", "L", 0, 1) - return {"A": band} - - -def read_png_or_jpeg2000( - fobj: IO[bytes], start_length: tuple[int, int], size: tuple[int, int, int] -) -> dict[str, Image.Image]: - (start, length) = start_length - fobj.seek(start) - sig = fobj.read(12) - - im: Image.Image - if sig.startswith(b"\x89PNG\x0d\x0a\x1a\x0a"): - fobj.seek(start) - im = PngImagePlugin.PngImageFile(fobj) - Image._decompression_bomb_check(im.size) - return {"RGBA": im} - elif ( - sig.startswith((b"\xff\x4f\xff\x51", b"\x0d\x0a\x87\x0a")) - or sig == b"\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a" - ): - if not enable_jpeg2k: - msg = ( - "Unsupported icon subimage format (rebuild PIL " - "with JPEG 2000 support to fix this)" - ) - raise ValueError(msg) - # j2k, jpc or j2c - fobj.seek(start) - jp2kstream = fobj.read(length) - f = io.BytesIO(jp2kstream) - im = Jpeg2KImagePlugin.Jpeg2KImageFile(f) - Image._decompression_bomb_check(im.size) - if im.mode != "RGBA": - im = im.convert("RGBA") - return {"RGBA": im} - else: - msg = "Unsupported icon subimage format" - raise ValueError(msg) - - -class IcnsFile: - SIZES = { - (512, 512, 2): [(b"ic10", read_png_or_jpeg2000)], - (512, 512, 1): [(b"ic09", read_png_or_jpeg2000)], - (256, 256, 2): [(b"ic14", read_png_or_jpeg2000)], - (256, 256, 1): [(b"ic08", read_png_or_jpeg2000)], - (128, 128, 2): [(b"ic13", read_png_or_jpeg2000)], - (128, 128, 1): [ - (b"ic07", read_png_or_jpeg2000), - (b"it32", read_32t), - (b"t8mk", read_mk), - ], - (64, 64, 1): [(b"icp6", read_png_or_jpeg2000)], - (32, 32, 2): [(b"ic12", read_png_or_jpeg2000)], - (48, 48, 1): [(b"ih32", read_32), (b"h8mk", read_mk)], - (32, 32, 1): [ - (b"icp5", read_png_or_jpeg2000), - (b"il32", read_32), - (b"l8mk", read_mk), - ], - (16, 16, 2): [(b"ic11", read_png_or_jpeg2000)], - (16, 16, 1): [ - (b"icp4", read_png_or_jpeg2000), - (b"is32", read_32), - (b"s8mk", read_mk), - ], - } - - def __init__(self, fobj: IO[bytes]) -> None: - """ - fobj is a file-like object as an icns resource - """ - # signature : (start, length) - self.dct = {} - self.fobj = fobj - sig, filesize = nextheader(fobj) - if not _accept(sig): - msg = "not an icns file" - raise SyntaxError(msg) - i = HEADERSIZE - while i < filesize: - sig, blocksize = nextheader(fobj) - if blocksize <= 0: - msg = "invalid block header" - raise SyntaxError(msg) - i += HEADERSIZE - blocksize -= HEADERSIZE - self.dct[sig] = (i, blocksize) - fobj.seek(blocksize, io.SEEK_CUR) - i += blocksize - - def itersizes(self) -> list[tuple[int, int, int]]: - sizes = [] - for size, fmts in self.SIZES.items(): - for fmt, reader in fmts: - if fmt in self.dct: - sizes.append(size) - break - return sizes - - def bestsize(self) -> tuple[int, int, int]: - sizes = self.itersizes() - if not sizes: - msg = "No 32bit icon resources found" - raise SyntaxError(msg) - return max(sizes) - - def dataforsize(self, size: tuple[int, int, int]) -> dict[str, Image.Image]: - """ - Get an icon resource as {channel: array}. Note that - the arrays are bottom-up like windows bitmaps and will likely - need to be flipped or transposed in some way. - """ - dct = {} - for code, reader in self.SIZES[size]: - desc = self.dct.get(code) - if desc is not None: - dct.update(reader(self.fobj, desc, size)) - return dct - - def getimage( - self, size: tuple[int, int] | tuple[int, int, int] | None = None - ) -> Image.Image: - if size is None: - size = self.bestsize() - elif len(size) == 2: - size = (size[0], size[1], 1) - channels = self.dataforsize(size) - - im = channels.get("RGBA") - if im: - return im - - im = channels["RGB"].copy() - try: - im.putalpha(channels["A"]) - except KeyError: - pass - return im - - -## -# Image plugin for Mac OS icons. - - -class IcnsImageFile(ImageFile.ImageFile): - """ - PIL image support for Mac OS .icns files. - Chooses the best resolution, but will possibly load - a different size image if you mutate the size attribute - before calling 'load'. - - The info dictionary has a key 'sizes' that is a list - of sizes that the icns file has. - """ - - format = "ICNS" - format_description = "Mac OS icns resource" - - def _open(self) -> None: - self.icns = IcnsFile(self.fp) - self._mode = "RGBA" - self.info["sizes"] = self.icns.itersizes() - self.best_size = self.icns.bestsize() - self.size = ( - self.best_size[0] * self.best_size[2], - self.best_size[1] * self.best_size[2], - ) - - @property - def size(self) -> tuple[int, int]: - return self._size - - @size.setter - def size(self, value: tuple[int, int]) -> None: - # Check that a matching size exists, - # or that there is a scale that would create a size that matches - for size in self.info["sizes"]: - simple_size = size[0] * size[2], size[1] * size[2] - scale = simple_size[0] // value[0] - if simple_size[1] / value[1] == scale: - self._size = value - return - msg = "This is not one of the allowed sizes of this image" - raise ValueError(msg) - - def load(self, scale: int | None = None) -> Image.core.PixelAccess | None: - if scale is not None: - width, height = self.size[:2] - self.size = width * scale, height * scale - self.best_size = width, height, scale - - px = Image.Image.load(self) - if self._im is not None and self.im.size == self.size: - # Already loaded - return px - self.load_prepare() - # This is likely NOT the best way to do it, but whatever. - im = self.icns.getimage(self.best_size) - - # If this is a PNG or JPEG 2000, it won't be loaded yet - px = im.load() - - self.im = im.im - self._mode = im.mode - self.size = im.size - - return px - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - """ - Saves the image as a series of PNG files, - that are then combined into a .icns file. - """ - if hasattr(fp, "flush"): - fp.flush() - - sizes = { - b"ic07": 128, - b"ic08": 256, - b"ic09": 512, - b"ic10": 1024, - b"ic11": 32, - b"ic12": 64, - b"ic13": 256, - b"ic14": 512, - } - provided_images = {im.width: im for im in im.encoderinfo.get("append_images", [])} - size_streams = {} - for size in set(sizes.values()): - image = ( - provided_images[size] - if size in provided_images - else im.resize((size, size)) - ) - - temp = io.BytesIO() - image.save(temp, "png") - size_streams[size] = temp.getvalue() - - entries = [] - for type, size in sizes.items(): - stream = size_streams[size] - entries.append((type, HEADERSIZE + len(stream), stream)) - - # Header - fp.write(MAGIC) - file_length = HEADERSIZE # Header - file_length += HEADERSIZE + 8 * len(entries) # TOC - file_length += sum(entry[1] for entry in entries) - fp.write(struct.pack(">i", file_length)) - - # TOC - fp.write(b"TOC ") - fp.write(struct.pack(">i", HEADERSIZE + len(entries) * HEADERSIZE)) - for entry in entries: - fp.write(entry[0]) - fp.write(struct.pack(">i", entry[1])) - - # Data - for entry in entries: - fp.write(entry[0]) - fp.write(struct.pack(">i", entry[1])) - fp.write(entry[2]) - - if hasattr(fp, "flush"): - fp.flush() - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(MAGIC) - - -Image.register_open(IcnsImageFile.format, IcnsImageFile, _accept) -Image.register_extension(IcnsImageFile.format, ".icns") - -Image.register_save(IcnsImageFile.format, _save) -Image.register_mime(IcnsImageFile.format, "image/icns") - -if __name__ == "__main__": - if len(sys.argv) < 2: - print("Syntax: python3 IcnsImagePlugin.py [file]") - sys.exit() - - with open(sys.argv[1], "rb") as fp: - imf = IcnsImageFile(fp) - for size in imf.info["sizes"]: - width, height, scale = imf.size = size - imf.save(f"out-{width}-{height}-{scale}.png") - with Image.open(sys.argv[1]) as im: - im.save("out.png") - if sys.platform == "windows": - os.startfile("out.png") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/IcoImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/IcoImagePlugin.py deleted file mode 100644 index bd35ac89..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/IcoImagePlugin.py +++ /dev/null @@ -1,381 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# Windows Icon support for PIL -# -# History: -# 96-05-27 fl Created -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1996. -# -# See the README file for information on usage and redistribution. -# - -# This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis -# . -# https://code.google.com/archive/p/casadebender/wikis/Win32IconImagePlugin.wiki -# -# Icon format references: -# * https://en.wikipedia.org/wiki/ICO_(file_format) -# * https://msdn.microsoft.com/en-us/library/ms997538.aspx -from __future__ import annotations - -import warnings -from io import BytesIO -from math import ceil, log -from typing import IO, NamedTuple - -from . import BmpImagePlugin, Image, ImageFile, PngImagePlugin -from ._binary import i16le as i16 -from ._binary import i32le as i32 -from ._binary import o8 -from ._binary import o16le as o16 -from ._binary import o32le as o32 - -# -# -------------------------------------------------------------------- - -_MAGIC = b"\0\0\1\0" - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - fp.write(_MAGIC) # (2+2) - bmp = im.encoderinfo.get("bitmap_format") == "bmp" - sizes = im.encoderinfo.get( - "sizes", - [(16, 16), (24, 24), (32, 32), (48, 48), (64, 64), (128, 128), (256, 256)], - ) - frames = [] - provided_ims = [im] + im.encoderinfo.get("append_images", []) - width, height = im.size - for size in sorted(set(sizes)): - if size[0] > width or size[1] > height or size[0] > 256 or size[1] > 256: - continue - - for provided_im in provided_ims: - if provided_im.size != size: - continue - frames.append(provided_im) - if bmp: - bits = BmpImagePlugin.SAVE[provided_im.mode][1] - bits_used = [bits] - for other_im in provided_ims: - if other_im.size != size: - continue - bits = BmpImagePlugin.SAVE[other_im.mode][1] - if bits not in bits_used: - # Another image has been supplied for this size - # with a different bit depth - frames.append(other_im) - bits_used.append(bits) - break - else: - # TODO: invent a more convenient method for proportional scalings - frame = provided_im.copy() - frame.thumbnail(size, Image.Resampling.LANCZOS, reducing_gap=None) - frames.append(frame) - fp.write(o16(len(frames))) # idCount(2) - offset = fp.tell() + len(frames) * 16 - for frame in frames: - width, height = frame.size - # 0 means 256 - fp.write(o8(width if width < 256 else 0)) # bWidth(1) - fp.write(o8(height if height < 256 else 0)) # bHeight(1) - - bits, colors = BmpImagePlugin.SAVE[frame.mode][1:] if bmp else (32, 0) - fp.write(o8(colors)) # bColorCount(1) - fp.write(b"\0") # bReserved(1) - fp.write(b"\0\0") # wPlanes(2) - fp.write(o16(bits)) # wBitCount(2) - - image_io = BytesIO() - if bmp: - frame.save(image_io, "dib") - - if bits != 32: - and_mask = Image.new("1", size) - ImageFile._save( - and_mask, - image_io, - [ImageFile._Tile("raw", (0, 0) + size, 0, ("1", 0, -1))], - ) - else: - frame.save(image_io, "png") - image_io.seek(0) - image_bytes = image_io.read() - if bmp: - image_bytes = image_bytes[:8] + o32(height * 2) + image_bytes[12:] - bytes_len = len(image_bytes) - fp.write(o32(bytes_len)) # dwBytesInRes(4) - fp.write(o32(offset)) # dwImageOffset(4) - current = fp.tell() - fp.seek(offset) - fp.write(image_bytes) - offset = offset + bytes_len - fp.seek(current) - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(_MAGIC) - - -class IconHeader(NamedTuple): - width: int - height: int - nb_color: int - reserved: int - planes: int - bpp: int - size: int - offset: int - dim: tuple[int, int] - square: int - color_depth: int - - -class IcoFile: - def __init__(self, buf: IO[bytes]) -> None: - """ - Parse image from file-like object containing ico file data - """ - - # check magic - s = buf.read(6) - if not _accept(s): - msg = "not an ICO file" - raise SyntaxError(msg) - - self.buf = buf - self.entry = [] - - # Number of items in file - self.nb_items = i16(s, 4) - - # Get headers for each item - for i in range(self.nb_items): - s = buf.read(16) - - # See Wikipedia - width = s[0] or 256 - height = s[1] or 256 - - # No. of colors in image (0 if >=8bpp) - nb_color = s[2] - bpp = i16(s, 6) - icon_header = IconHeader( - width=width, - height=height, - nb_color=nb_color, - reserved=s[3], - planes=i16(s, 4), - bpp=i16(s, 6), - size=i32(s, 8), - offset=i32(s, 12), - dim=(width, height), - square=width * height, - # See Wikipedia notes about color depth. - # We need this just to differ images with equal sizes - color_depth=bpp or (nb_color != 0 and ceil(log(nb_color, 2))) or 256, - ) - - self.entry.append(icon_header) - - self.entry = sorted(self.entry, key=lambda x: x.color_depth) - # ICO images are usually squares - self.entry = sorted(self.entry, key=lambda x: x.square, reverse=True) - - def sizes(self) -> set[tuple[int, int]]: - """ - Get a set of all available icon sizes and color depths. - """ - return {(h.width, h.height) for h in self.entry} - - def getentryindex(self, size: tuple[int, int], bpp: int | bool = False) -> int: - for i, h in enumerate(self.entry): - if size == h.dim and (bpp is False or bpp == h.color_depth): - return i - return 0 - - def getimage(self, size: tuple[int, int], bpp: int | bool = False) -> Image.Image: - """ - Get an image from the icon - """ - return self.frame(self.getentryindex(size, bpp)) - - def frame(self, idx: int) -> Image.Image: - """ - Get an image from frame idx - """ - - header = self.entry[idx] - - self.buf.seek(header.offset) - data = self.buf.read(8) - self.buf.seek(header.offset) - - im: Image.Image - if data[:8] == PngImagePlugin._MAGIC: - # png frame - im = PngImagePlugin.PngImageFile(self.buf) - Image._decompression_bomb_check(im.size) - else: - # XOR + AND mask bmp frame - im = BmpImagePlugin.DibImageFile(self.buf) - Image._decompression_bomb_check(im.size) - - # change tile dimension to only encompass XOR image - im._size = (im.size[0], int(im.size[1] / 2)) - d, e, o, a = im.tile[0] - im.tile[0] = ImageFile._Tile(d, (0, 0) + im.size, o, a) - - # figure out where AND mask image starts - if header.bpp == 32: - # 32-bit color depth icon image allows semitransparent areas - # PIL's DIB format ignores transparency bits, recover them. - # The DIB is packed in BGRX byte order where X is the alpha - # channel. - - # Back up to start of bmp data - self.buf.seek(o) - # extract every 4th byte (eg. 3,7,11,15,...) - alpha_bytes = self.buf.read(im.size[0] * im.size[1] * 4)[3::4] - - # convert to an 8bpp grayscale image - try: - mask = Image.frombuffer( - "L", # 8bpp - im.size, # (w, h) - alpha_bytes, # source chars - "raw", # raw decoder - ("L", 0, -1), # 8bpp inverted, unpadded, reversed - ) - except ValueError: - if ImageFile.LOAD_TRUNCATED_IMAGES: - mask = None - else: - raise - else: - # get AND image from end of bitmap - w = im.size[0] - if (w % 32) > 0: - # bitmap row data is aligned to word boundaries - w += 32 - (im.size[0] % 32) - - # the total mask data is - # padded row size * height / bits per char - - total_bytes = int((w * im.size[1]) / 8) - and_mask_offset = header.offset + header.size - total_bytes - - self.buf.seek(and_mask_offset) - mask_data = self.buf.read(total_bytes) - - # convert raw data to image - try: - mask = Image.frombuffer( - "1", # 1 bpp - im.size, # (w, h) - mask_data, # source chars - "raw", # raw decoder - ("1;I", int(w / 8), -1), # 1bpp inverted, padded, reversed - ) - except ValueError: - if ImageFile.LOAD_TRUNCATED_IMAGES: - mask = None - else: - raise - - # now we have two images, im is XOR image and mask is AND image - - # apply mask image as alpha channel - if mask: - im = im.convert("RGBA") - im.putalpha(mask) - - return im - - -## -# Image plugin for Windows Icon files. - - -class IcoImageFile(ImageFile.ImageFile): - """ - PIL read-only image support for Microsoft Windows .ico files. - - By default the largest resolution image in the file will be loaded. This - can be changed by altering the 'size' attribute before calling 'load'. - - The info dictionary has a key 'sizes' that is a list of the sizes available - in the icon file. - - Handles classic, XP and Vista icon formats. - - When saving, PNG compression is used. Support for this was only added in - Windows Vista. If you are unable to view the icon in Windows, convert the - image to "RGBA" mode before saving. - - This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis - . - https://code.google.com/archive/p/casadebender/wikis/Win32IconImagePlugin.wiki - """ - - format = "ICO" - format_description = "Windows Icon" - - def _open(self) -> None: - self.ico = IcoFile(self.fp) - self.info["sizes"] = self.ico.sizes() - self.size = self.ico.entry[0].dim - self.load() - - @property - def size(self) -> tuple[int, int]: - return self._size - - @size.setter - def size(self, value: tuple[int, int]) -> None: - if value not in self.info["sizes"]: - msg = "This is not one of the allowed sizes of this image" - raise ValueError(msg) - self._size = value - - def load(self) -> Image.core.PixelAccess | None: - if self._im is not None and self.im.size == self.size: - # Already loaded - return Image.Image.load(self) - im = self.ico.getimage(self.size) - # if tile is PNG, it won't really be loaded yet - im.load() - self.im = im.im - self._mode = im.mode - if im.palette: - self.palette = im.palette - if im.size != self.size: - warnings.warn("Image was not the expected size") - - index = self.ico.getentryindex(self.size) - sizes = list(self.info["sizes"]) - sizes[index] = im.size - self.info["sizes"] = set(sizes) - - self.size = im.size - return Image.Image.load(self) - - def load_seek(self, pos: int) -> None: - # Flag the ImageFile.Parser so that it - # just does all the decode at the end. - pass - - -# -# -------------------------------------------------------------------- - - -Image.register_open(IcoImageFile.format, IcoImageFile, _accept) -Image.register_save(IcoImageFile.format, _save) -Image.register_extension(IcoImageFile.format, ".ico") - -Image.register_mime(IcoImageFile.format, "image/x-icon") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/ImImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/ImImagePlugin.py deleted file mode 100644 index 71b99967..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/ImImagePlugin.py +++ /dev/null @@ -1,389 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# IFUNC IM file handling for PIL -# -# history: -# 1995-09-01 fl Created. -# 1997-01-03 fl Save palette images -# 1997-01-08 fl Added sequence support -# 1997-01-23 fl Added P and RGB save support -# 1997-05-31 fl Read floating point images -# 1997-06-22 fl Save floating point images -# 1997-08-27 fl Read and save 1-bit images -# 1998-06-25 fl Added support for RGB+LUT images -# 1998-07-02 fl Added support for YCC images -# 1998-07-15 fl Renamed offset attribute to avoid name clash -# 1998-12-29 fl Added I;16 support -# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.7) -# 2003-09-26 fl Added LA/PA support -# -# Copyright (c) 1997-2003 by Secret Labs AB. -# Copyright (c) 1995-2001 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import os -import re -from typing import IO, Any - -from . import Image, ImageFile, ImagePalette -from ._util import DeferredError - -# -------------------------------------------------------------------- -# Standard tags - -COMMENT = "Comment" -DATE = "Date" -EQUIPMENT = "Digitalization equipment" -FRAMES = "File size (no of images)" -LUT = "Lut" -NAME = "Name" -SCALE = "Scale (x,y)" -SIZE = "Image size (x*y)" -MODE = "Image type" - -TAGS = { - COMMENT: 0, - DATE: 0, - EQUIPMENT: 0, - FRAMES: 0, - LUT: 0, - NAME: 0, - SCALE: 0, - SIZE: 0, - MODE: 0, -} - -OPEN = { - # ifunc93/p3cfunc formats - "0 1 image": ("1", "1"), - "L 1 image": ("1", "1"), - "Greyscale image": ("L", "L"), - "Grayscale image": ("L", "L"), - "RGB image": ("RGB", "RGB;L"), - "RLB image": ("RGB", "RLB"), - "RYB image": ("RGB", "RLB"), - "B1 image": ("1", "1"), - "B2 image": ("P", "P;2"), - "B4 image": ("P", "P;4"), - "X 24 image": ("RGB", "RGB"), - "L 32 S image": ("I", "I;32"), - "L 32 F image": ("F", "F;32"), - # old p3cfunc formats - "RGB3 image": ("RGB", "RGB;T"), - "RYB3 image": ("RGB", "RYB;T"), - # extensions - "LA image": ("LA", "LA;L"), - "PA image": ("LA", "PA;L"), - "RGBA image": ("RGBA", "RGBA;L"), - "RGBX image": ("RGB", "RGBX;L"), - "CMYK image": ("CMYK", "CMYK;L"), - "YCC image": ("YCbCr", "YCbCr;L"), -} - -# ifunc95 extensions -for i in ["8", "8S", "16", "16S", "32", "32F"]: - OPEN[f"L {i} image"] = ("F", f"F;{i}") - OPEN[f"L*{i} image"] = ("F", f"F;{i}") -for i in ["16", "16L", "16B"]: - OPEN[f"L {i} image"] = (f"I;{i}", f"I;{i}") - OPEN[f"L*{i} image"] = (f"I;{i}", f"I;{i}") -for i in ["32S"]: - OPEN[f"L {i} image"] = ("I", f"I;{i}") - OPEN[f"L*{i} image"] = ("I", f"I;{i}") -for j in range(2, 33): - OPEN[f"L*{j} image"] = ("F", f"F;{j}") - - -# -------------------------------------------------------------------- -# Read IM directory - -split = re.compile(rb"^([A-Za-z][^:]*):[ \t]*(.*)[ \t]*$") - - -def number(s: Any) -> float: - try: - return int(s) - except ValueError: - return float(s) - - -## -# Image plugin for the IFUNC IM file format. - - -class ImImageFile(ImageFile.ImageFile): - format = "IM" - format_description = "IFUNC Image Memory" - _close_exclusive_fp_after_loading = False - - def _open(self) -> None: - # Quick rejection: if there's not an LF among the first - # 100 bytes, this is (probably) not a text header. - - if b"\n" not in self.fp.read(100): - msg = "not an IM file" - raise SyntaxError(msg) - self.fp.seek(0) - - n = 0 - - # Default values - self.info[MODE] = "L" - self.info[SIZE] = (512, 512) - self.info[FRAMES] = 1 - - self.rawmode = "L" - - while True: - s = self.fp.read(1) - - # Some versions of IFUNC uses \n\r instead of \r\n... - if s == b"\r": - continue - - if not s or s == b"\0" or s == b"\x1a": - break - - # FIXME: this may read whole file if not a text file - s = s + self.fp.readline() - - if len(s) > 100: - msg = "not an IM file" - raise SyntaxError(msg) - - if s.endswith(b"\r\n"): - s = s[:-2] - elif s.endswith(b"\n"): - s = s[:-1] - - try: - m = split.match(s) - except re.error as e: - msg = "not an IM file" - raise SyntaxError(msg) from e - - if m: - k, v = m.group(1, 2) - - # Don't know if this is the correct encoding, - # but a decent guess (I guess) - k = k.decode("latin-1", "replace") - v = v.decode("latin-1", "replace") - - # Convert value as appropriate - if k in [FRAMES, SCALE, SIZE]: - v = v.replace("*", ",") - v = tuple(map(number, v.split(","))) - if len(v) == 1: - v = v[0] - elif k == MODE and v in OPEN: - v, self.rawmode = OPEN[v] - - # Add to dictionary. Note that COMMENT tags are - # combined into a list of strings. - if k == COMMENT: - if k in self.info: - self.info[k].append(v) - else: - self.info[k] = [v] - else: - self.info[k] = v - - if k in TAGS: - n += 1 - - else: - msg = f"Syntax error in IM header: {s.decode('ascii', 'replace')}" - raise SyntaxError(msg) - - if not n: - msg = "Not an IM file" - raise SyntaxError(msg) - - # Basic attributes - self._size = self.info[SIZE] - self._mode = self.info[MODE] - - # Skip forward to start of image data - while s and not s.startswith(b"\x1a"): - s = self.fp.read(1) - if not s: - msg = "File truncated" - raise SyntaxError(msg) - - if LUT in self.info: - # convert lookup table to palette or lut attribute - palette = self.fp.read(768) - greyscale = 1 # greyscale palette - linear = 1 # linear greyscale palette - for i in range(256): - if palette[i] == palette[i + 256] == palette[i + 512]: - if palette[i] != i: - linear = 0 - else: - greyscale = 0 - if self.mode in ["L", "LA", "P", "PA"]: - if greyscale: - if not linear: - self.lut = list(palette[:256]) - else: - if self.mode in ["L", "P"]: - self._mode = self.rawmode = "P" - elif self.mode in ["LA", "PA"]: - self._mode = "PA" - self.rawmode = "PA;L" - self.palette = ImagePalette.raw("RGB;L", palette) - elif self.mode == "RGB": - if not greyscale or not linear: - self.lut = list(palette) - - self.frame = 0 - - self.__offset = offs = self.fp.tell() - - self._fp = self.fp # FIXME: hack - - if self.rawmode.startswith("F;"): - # ifunc95 formats - try: - # use bit decoder (if necessary) - bits = int(self.rawmode[2:]) - if bits not in [8, 16, 32]: - self.tile = [ - ImageFile._Tile( - "bit", (0, 0) + self.size, offs, (bits, 8, 3, 0, -1) - ) - ] - return - except ValueError: - pass - - if self.rawmode in ["RGB;T", "RYB;T"]: - # Old LabEye/3PC files. Would be very surprised if anyone - # ever stumbled upon such a file ;-) - size = self.size[0] * self.size[1] - self.tile = [ - ImageFile._Tile("raw", (0, 0) + self.size, offs, ("G", 0, -1)), - ImageFile._Tile("raw", (0, 0) + self.size, offs + size, ("R", 0, -1)), - ImageFile._Tile( - "raw", (0, 0) + self.size, offs + 2 * size, ("B", 0, -1) - ), - ] - else: - # LabEye/IFUNC files - self.tile = [ - ImageFile._Tile("raw", (0, 0) + self.size, offs, (self.rawmode, 0, -1)) - ] - - @property - def n_frames(self) -> int: - return self.info[FRAMES] - - @property - def is_animated(self) -> bool: - return self.info[FRAMES] > 1 - - def seek(self, frame: int) -> None: - if not self._seek_check(frame): - return - if isinstance(self._fp, DeferredError): - raise self._fp.ex - - self.frame = frame - - if self.mode == "1": - bits = 1 - else: - bits = 8 * len(self.mode) - - size = ((self.size[0] * bits + 7) // 8) * self.size[1] - offs = self.__offset + frame * size - - self.fp = self._fp - - self.tile = [ - ImageFile._Tile("raw", (0, 0) + self.size, offs, (self.rawmode, 0, -1)) - ] - - def tell(self) -> int: - return self.frame - - -# -# -------------------------------------------------------------------- -# Save IM files - - -SAVE = { - # mode: (im type, raw mode) - "1": ("0 1", "1"), - "L": ("Greyscale", "L"), - "LA": ("LA", "LA;L"), - "P": ("Greyscale", "P"), - "PA": ("LA", "PA;L"), - "I": ("L 32S", "I;32S"), - "I;16": ("L 16", "I;16"), - "I;16L": ("L 16L", "I;16L"), - "I;16B": ("L 16B", "I;16B"), - "F": ("L 32F", "F;32F"), - "RGB": ("RGB", "RGB;L"), - "RGBA": ("RGBA", "RGBA;L"), - "RGBX": ("RGBX", "RGBX;L"), - "CMYK": ("CMYK", "CMYK;L"), - "YCbCr": ("YCC", "YCbCr;L"), -} - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - try: - image_type, rawmode = SAVE[im.mode] - except KeyError as e: - msg = f"Cannot save {im.mode} images as IM" - raise ValueError(msg) from e - - frames = im.encoderinfo.get("frames", 1) - - fp.write(f"Image type: {image_type} image\r\n".encode("ascii")) - if filename: - # Each line must be 100 characters or less, - # or: SyntaxError("not an IM file") - # 8 characters are used for "Name: " and "\r\n" - # Keep just the filename, ditch the potentially overlong path - if isinstance(filename, bytes): - filename = filename.decode("ascii") - name, ext = os.path.splitext(os.path.basename(filename)) - name = "".join([name[: 92 - len(ext)], ext]) - - fp.write(f"Name: {name}\r\n".encode("ascii")) - fp.write(f"Image size (x*y): {im.size[0]}*{im.size[1]}\r\n".encode("ascii")) - fp.write(f"File size (no of images): {frames}\r\n".encode("ascii")) - if im.mode in ["P", "PA"]: - fp.write(b"Lut: 1\r\n") - fp.write(b"\000" * (511 - fp.tell()) + b"\032") - if im.mode in ["P", "PA"]: - im_palette = im.im.getpalette("RGB", "RGB;L") - colors = len(im_palette) // 3 - palette = b"" - for i in range(3): - palette += im_palette[colors * i : colors * (i + 1)] - palette += b"\x00" * (256 - colors) - fp.write(palette) # 768 bytes - ImageFile._save( - im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 0, (rawmode, 0, -1))] - ) - - -# -# -------------------------------------------------------------------- -# Registry - - -Image.register_open(ImImageFile.format, ImImageFile) -Image.register_save(ImImageFile.format, _save) - -Image.register_extension(ImImageFile.format, ".im") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/Image.py b/.venv-docs/lib/python3.12/site-packages/PIL/Image.py deleted file mode 100644 index 9d50812e..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/Image.py +++ /dev/null @@ -1,4227 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# the Image class wrapper -# -# partial release history: -# 1995-09-09 fl Created -# 1996-03-11 fl PIL release 0.0 (proof of concept) -# 1996-04-30 fl PIL release 0.1b1 -# 1999-07-28 fl PIL release 1.0 final -# 2000-06-07 fl PIL release 1.1 -# 2000-10-20 fl PIL release 1.1.1 -# 2001-05-07 fl PIL release 1.1.2 -# 2002-03-15 fl PIL release 1.1.3 -# 2003-05-10 fl PIL release 1.1.4 -# 2005-03-28 fl PIL release 1.1.5 -# 2006-12-02 fl PIL release 1.1.6 -# 2009-11-15 fl PIL release 1.1.7 -# -# Copyright (c) 1997-2009 by Secret Labs AB. All rights reserved. -# Copyright (c) 1995-2009 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# - -from __future__ import annotations - -import abc -import atexit -import builtins -import io -import logging -import math -import os -import re -import struct -import sys -import tempfile -import warnings -from collections.abc import MutableMapping -from enum import IntEnum -from typing import IO, Protocol, cast - -# VERSION was removed in Pillow 6.0.0. -# PILLOW_VERSION was removed in Pillow 9.0.0. -# Use __version__ instead. -from . import ( - ExifTags, - ImageMode, - TiffTags, - UnidentifiedImageError, - __version__, - _plugins, -) -from ._binary import i32le, o32be, o32le -from ._deprecate import deprecate -from ._util import DeferredError, is_path - -ElementTree: ModuleType | None -try: - from defusedxml import ElementTree -except ImportError: - ElementTree = None - -TYPE_CHECKING = False -if TYPE_CHECKING: - from collections.abc import Callable, Iterator, Sequence - from types import ModuleType - from typing import Any, Literal - -logger = logging.getLogger(__name__) - - -class DecompressionBombWarning(RuntimeWarning): - pass - - -class DecompressionBombError(Exception): - pass - - -WARN_POSSIBLE_FORMATS: bool = False - -# Limit to around a quarter gigabyte for a 24-bit (3 bpp) image -MAX_IMAGE_PIXELS: int | None = int(1024 * 1024 * 1024 // 4 // 3) - - -try: - # If the _imaging C module is not present, Pillow will not load. - # Note that other modules should not refer to _imaging directly; - # import Image and use the Image.core variable instead. - # Also note that Image.core is not a publicly documented interface, - # and should be considered private and subject to change. - from . import _imaging as core - - if __version__ != getattr(core, "PILLOW_VERSION", None): - msg = ( - "The _imaging extension was built for another version of Pillow or PIL:\n" - f"Core version: {getattr(core, 'PILLOW_VERSION', None)}\n" - f"Pillow version: {__version__}" - ) - raise ImportError(msg) - -except ImportError as v: - # Explanations for ways that we know we might have an import error - if str(v).startswith("Module use of python"): - # The _imaging C module is present, but not compiled for - # the right version (windows only). Print a warning, if - # possible. - warnings.warn( - "The _imaging extension was built for another version of Python.", - RuntimeWarning, - ) - elif str(v).startswith("The _imaging extension"): - warnings.warn(str(v), RuntimeWarning) - # Fail here anyway. Don't let people run with a mostly broken Pillow. - # see docs/porting.rst - raise - - -# -# Constants - - -# transpose -class Transpose(IntEnum): - FLIP_LEFT_RIGHT = 0 - FLIP_TOP_BOTTOM = 1 - ROTATE_90 = 2 - ROTATE_180 = 3 - ROTATE_270 = 4 - TRANSPOSE = 5 - TRANSVERSE = 6 - - -# transforms (also defined in Imaging.h) -class Transform(IntEnum): - AFFINE = 0 - EXTENT = 1 - PERSPECTIVE = 2 - QUAD = 3 - MESH = 4 - - -# resampling filters (also defined in Imaging.h) -class Resampling(IntEnum): - NEAREST = 0 - BOX = 4 - BILINEAR = 2 - HAMMING = 5 - BICUBIC = 3 - LANCZOS = 1 - - -_filters_support = { - Resampling.BOX: 0.5, - Resampling.BILINEAR: 1.0, - Resampling.HAMMING: 1.0, - Resampling.BICUBIC: 2.0, - Resampling.LANCZOS: 3.0, -} - - -# dithers -class Dither(IntEnum): - NONE = 0 - ORDERED = 1 # Not yet implemented - RASTERIZE = 2 # Not yet implemented - FLOYDSTEINBERG = 3 # default - - -# palettes/quantizers -class Palette(IntEnum): - WEB = 0 - ADAPTIVE = 1 - - -class Quantize(IntEnum): - MEDIANCUT = 0 - MAXCOVERAGE = 1 - FASTOCTREE = 2 - LIBIMAGEQUANT = 3 - - -module = sys.modules[__name__] -for enum in (Transpose, Transform, Resampling, Dither, Palette, Quantize): - for item in enum: - setattr(module, item.name, item.value) - - -if hasattr(core, "DEFAULT_STRATEGY"): - DEFAULT_STRATEGY = core.DEFAULT_STRATEGY - FILTERED = core.FILTERED - HUFFMAN_ONLY = core.HUFFMAN_ONLY - RLE = core.RLE - FIXED = core.FIXED - - -# -------------------------------------------------------------------- -# Registries - -TYPE_CHECKING = False -if TYPE_CHECKING: - import mmap - from xml.etree.ElementTree import Element - - from IPython.lib.pretty import PrettyPrinter - - from . import ImageFile, ImageFilter, ImagePalette, ImageQt, TiffImagePlugin - from ._typing import CapsuleType, NumpyArray, StrOrBytesPath -ID: list[str] = [] -OPEN: dict[ - str, - tuple[ - Callable[[IO[bytes], str | bytes], ImageFile.ImageFile], - Callable[[bytes], bool | str] | None, - ], -] = {} -MIME: dict[str, str] = {} -SAVE: dict[str, Callable[[Image, IO[bytes], str | bytes], None]] = {} -SAVE_ALL: dict[str, Callable[[Image, IO[bytes], str | bytes], None]] = {} -EXTENSION: dict[str, str] = {} -DECODERS: dict[str, type[ImageFile.PyDecoder]] = {} -ENCODERS: dict[str, type[ImageFile.PyEncoder]] = {} - -# -------------------------------------------------------------------- -# Modes - -_ENDIAN = "<" if sys.byteorder == "little" else ">" - - -def _conv_type_shape(im: Image) -> tuple[tuple[int, ...], str]: - m = ImageMode.getmode(im.mode) - shape: tuple[int, ...] = (im.height, im.width) - extra = len(m.bands) - if extra != 1: - shape += (extra,) - return shape, m.typestr - - -MODES = [ - "1", - "CMYK", - "F", - "HSV", - "I", - "I;16", - "I;16B", - "I;16L", - "I;16N", - "L", - "LA", - "La", - "LAB", - "P", - "PA", - "RGB", - "RGBA", - "RGBa", - "RGBX", - "YCbCr", -] - -# raw modes that may be memory mapped. NOTE: if you change this, you -# may have to modify the stride calculation in map.c too! -_MAPMODES = ("L", "P", "RGBX", "RGBA", "CMYK", "I;16", "I;16L", "I;16B") - - -def getmodebase(mode: str) -> str: - """ - Gets the "base" mode for given mode. This function returns "L" for - images that contain grayscale data, and "RGB" for images that - contain color data. - - :param mode: Input mode. - :returns: "L" or "RGB". - :exception KeyError: If the input mode was not a standard mode. - """ - return ImageMode.getmode(mode).basemode - - -def getmodetype(mode: str) -> str: - """ - Gets the storage type mode. Given a mode, this function returns a - single-layer mode suitable for storing individual bands. - - :param mode: Input mode. - :returns: "L", "I", or "F". - :exception KeyError: If the input mode was not a standard mode. - """ - return ImageMode.getmode(mode).basetype - - -def getmodebandnames(mode: str) -> tuple[str, ...]: - """ - Gets a list of individual band names. Given a mode, this function returns - a tuple containing the names of individual bands (use - :py:method:`~PIL.Image.getmodetype` to get the mode used to store each - individual band. - - :param mode: Input mode. - :returns: A tuple containing band names. The length of the tuple - gives the number of bands in an image of the given mode. - :exception KeyError: If the input mode was not a standard mode. - """ - return ImageMode.getmode(mode).bands - - -def getmodebands(mode: str) -> int: - """ - Gets the number of individual bands for this mode. - - :param mode: Input mode. - :returns: The number of bands in this mode. - :exception KeyError: If the input mode was not a standard mode. - """ - return len(ImageMode.getmode(mode).bands) - - -# -------------------------------------------------------------------- -# Helpers - -_initialized = 0 - - -def preinit() -> None: - """ - Explicitly loads BMP, GIF, JPEG, PPM and PPM file format drivers. - - It is called when opening or saving images. - """ - - global _initialized - if _initialized >= 1: - return - - try: - from . import BmpImagePlugin - - assert BmpImagePlugin - except ImportError: - pass - try: - from . import GifImagePlugin - - assert GifImagePlugin - except ImportError: - pass - try: - from . import JpegImagePlugin - - assert JpegImagePlugin - except ImportError: - pass - try: - from . import PpmImagePlugin - - assert PpmImagePlugin - except ImportError: - pass - try: - from . import PngImagePlugin - - assert PngImagePlugin - except ImportError: - pass - - _initialized = 1 - - -def init() -> bool: - """ - Explicitly initializes the Python Imaging Library. This function - loads all available file format drivers. - - It is called when opening or saving images if :py:meth:`~preinit()` is - insufficient, and by :py:meth:`~PIL.features.pilinfo`. - """ - - global _initialized - if _initialized >= 2: - return False - - parent_name = __name__.rpartition(".")[0] - for plugin in _plugins: - try: - logger.debug("Importing %s", plugin) - __import__(f"{parent_name}.{plugin}", globals(), locals(), []) - except ImportError as e: - logger.debug("Image: failed to import %s: %s", plugin, e) - - if OPEN or SAVE: - _initialized = 2 - return True - return False - - -# -------------------------------------------------------------------- -# Codec factories (used by tobytes/frombytes and ImageFile.load) - - -def _getdecoder( - mode: str, decoder_name: str, args: Any, extra: tuple[Any, ...] = () -) -> core.ImagingDecoder | ImageFile.PyDecoder: - # tweak arguments - if args is None: - args = () - elif not isinstance(args, tuple): - args = (args,) - - try: - decoder = DECODERS[decoder_name] - except KeyError: - pass - else: - return decoder(mode, *args + extra) - - try: - # get decoder - decoder = getattr(core, f"{decoder_name}_decoder") - except AttributeError as e: - msg = f"decoder {decoder_name} not available" - raise OSError(msg) from e - return decoder(mode, *args + extra) - - -def _getencoder( - mode: str, encoder_name: str, args: Any, extra: tuple[Any, ...] = () -) -> core.ImagingEncoder | ImageFile.PyEncoder: - # tweak arguments - if args is None: - args = () - elif not isinstance(args, tuple): - args = (args,) - - try: - encoder = ENCODERS[encoder_name] - except KeyError: - pass - else: - return encoder(mode, *args + extra) - - try: - # get encoder - encoder = getattr(core, f"{encoder_name}_encoder") - except AttributeError as e: - msg = f"encoder {encoder_name} not available" - raise OSError(msg) from e - return encoder(mode, *args + extra) - - -# -------------------------------------------------------------------- -# Simple expression analyzer - - -class ImagePointTransform: - """ - Used with :py:meth:`~PIL.Image.Image.point` for single band images with more than - 8 bits, this represents an affine transformation, where the value is multiplied by - ``scale`` and ``offset`` is added. - """ - - def __init__(self, scale: float, offset: float) -> None: - self.scale = scale - self.offset = offset - - def __neg__(self) -> ImagePointTransform: - return ImagePointTransform(-self.scale, -self.offset) - - def __add__(self, other: ImagePointTransform | float) -> ImagePointTransform: - if isinstance(other, ImagePointTransform): - return ImagePointTransform( - self.scale + other.scale, self.offset + other.offset - ) - return ImagePointTransform(self.scale, self.offset + other) - - __radd__ = __add__ - - def __sub__(self, other: ImagePointTransform | float) -> ImagePointTransform: - return self + -other - - def __rsub__(self, other: ImagePointTransform | float) -> ImagePointTransform: - return other + -self - - def __mul__(self, other: ImagePointTransform | float) -> ImagePointTransform: - if isinstance(other, ImagePointTransform): - return NotImplemented - return ImagePointTransform(self.scale * other, self.offset * other) - - __rmul__ = __mul__ - - def __truediv__(self, other: ImagePointTransform | float) -> ImagePointTransform: - if isinstance(other, ImagePointTransform): - return NotImplemented - return ImagePointTransform(self.scale / other, self.offset / other) - - -def _getscaleoffset( - expr: Callable[[ImagePointTransform], ImagePointTransform | float], -) -> tuple[float, float]: - a = expr(ImagePointTransform(1, 0)) - return (a.scale, a.offset) if isinstance(a, ImagePointTransform) else (0, a) - - -# -------------------------------------------------------------------- -# Implementation wrapper - - -class SupportsGetData(Protocol): - def getdata( - self, - ) -> tuple[Transform, Sequence[int]]: ... - - -class Image: - """ - This class represents an image object. To create - :py:class:`~PIL.Image.Image` objects, use the appropriate factory - functions. There's hardly ever any reason to call the Image constructor - directly. - - * :py:func:`~PIL.Image.open` - * :py:func:`~PIL.Image.new` - * :py:func:`~PIL.Image.frombytes` - """ - - format: str | None = None - format_description: str | None = None - _close_exclusive_fp_after_loading = True - - def __init__(self) -> None: - # FIXME: take "new" parameters / other image? - self._im: core.ImagingCore | DeferredError | None = None - self._mode = "" - self._size = (0, 0) - self.palette: ImagePalette.ImagePalette | None = None - self.info: dict[str | tuple[int, int], Any] = {} - self.readonly = 0 - self._exif: Exif | None = None - - @property - def im(self) -> core.ImagingCore: - if isinstance(self._im, DeferredError): - raise self._im.ex - assert self._im is not None - return self._im - - @im.setter - def im(self, im: core.ImagingCore) -> None: - self._im = im - - @property - def width(self) -> int: - return self.size[0] - - @property - def height(self) -> int: - return self.size[1] - - @property - def size(self) -> tuple[int, int]: - return self._size - - @property - def mode(self) -> str: - return self._mode - - @property - def readonly(self) -> int: - return (self._im and self._im.readonly) or self._readonly - - @readonly.setter - def readonly(self, readonly: int) -> None: - self._readonly = readonly - - def _new(self, im: core.ImagingCore) -> Image: - new = Image() - new.im = im - new._mode = im.mode - new._size = im.size - if im.mode in ("P", "PA"): - if self.palette: - new.palette = self.palette.copy() - else: - from . import ImagePalette - - new.palette = ImagePalette.ImagePalette() - new.info = self.info.copy() - return new - - # Context manager support - def __enter__(self): - return self - - def __exit__(self, *args): - from . import ImageFile - - if isinstance(self, ImageFile.ImageFile): - if getattr(self, "_exclusive_fp", False): - self._close_fp() - self.fp = None - - def close(self) -> None: - """ - This operation will destroy the image core and release its memory. - The image data will be unusable afterward. - - This function is required to close images that have multiple frames or - have not had their file read and closed by the - :py:meth:`~PIL.Image.Image.load` method. See :ref:`file-handling` for - more information. - """ - if getattr(self, "map", None): - if sys.platform == "win32" and hasattr(sys, "pypy_version_info"): - self.map.close() - self.map: mmap.mmap | None = None - - # Instead of simply setting to None, we're setting up a - # deferred error that will better explain that the core image - # object is gone. - self._im = DeferredError(ValueError("Operation on closed image")) - - def _copy(self) -> None: - self.load() - self.im = self.im.copy() - self.readonly = 0 - - def _ensure_mutable(self) -> None: - if self.readonly: - self._copy() - else: - self.load() - - def _dump( - self, file: str | None = None, format: str | None = None, **options: Any - ) -> str: - suffix = "" - if format: - suffix = f".{format}" - - if not file: - f, filename = tempfile.mkstemp(suffix) - os.close(f) - else: - filename = file - if not filename.endswith(suffix): - filename = filename + suffix - - self.load() - - if not format or format == "PPM": - self.im.save_ppm(filename) - else: - self.save(filename, format, **options) - - return filename - - def __eq__(self, other: object) -> bool: - if self.__class__ is not other.__class__: - return False - assert isinstance(other, Image) - return ( - self.mode == other.mode - and self.size == other.size - and self.info == other.info - and self.getpalette() == other.getpalette() - and self.tobytes() == other.tobytes() - ) - - def __repr__(self) -> str: - return ( - f"<{self.__class__.__module__}.{self.__class__.__name__} " - f"image mode={self.mode} size={self.size[0]}x{self.size[1]} " - f"at 0x{id(self):X}>" - ) - - def _repr_pretty_(self, p: PrettyPrinter, cycle: bool) -> None: - """IPython plain text display support""" - - # Same as __repr__ but without unpredictable id(self), - # to keep Jupyter notebook `text/plain` output stable. - p.text( - f"<{self.__class__.__module__}.{self.__class__.__name__} " - f"image mode={self.mode} size={self.size[0]}x{self.size[1]}>" - ) - - def _repr_image(self, image_format: str, **kwargs: Any) -> bytes | None: - """Helper function for iPython display hook. - - :param image_format: Image format. - :returns: image as bytes, saved into the given format. - """ - b = io.BytesIO() - try: - self.save(b, image_format, **kwargs) - except Exception: - return None - return b.getvalue() - - def _repr_png_(self) -> bytes | None: - """iPython display hook support for PNG format. - - :returns: PNG version of the image as bytes - """ - return self._repr_image("PNG", compress_level=1) - - def _repr_jpeg_(self) -> bytes | None: - """iPython display hook support for JPEG format. - - :returns: JPEG version of the image as bytes - """ - return self._repr_image("JPEG") - - @property - def __array_interface__(self) -> dict[str, str | bytes | int | tuple[int, ...]]: - # numpy array interface support - new: dict[str, str | bytes | int | tuple[int, ...]] = {"version": 3} - if self.mode == "1": - # Binary images need to be extended from bits to bytes - # See: https://github.com/python-pillow/Pillow/issues/350 - new["data"] = self.tobytes("raw", "L") - else: - new["data"] = self.tobytes() - new["shape"], new["typestr"] = _conv_type_shape(self) - return new - - def __arrow_c_schema__(self) -> object: - self.load() - return self.im.__arrow_c_schema__() - - def __arrow_c_array__( - self, requested_schema: object | None = None - ) -> tuple[object, object]: - self.load() - return (self.im.__arrow_c_schema__(), self.im.__arrow_c_array__()) - - def __getstate__(self) -> list[Any]: - im_data = self.tobytes() # load image first - return [self.info, self.mode, self.size, self.getpalette(), im_data] - - def __setstate__(self, state: list[Any]) -> None: - Image.__init__(self) - info, mode, size, palette, data = state[:5] - self.info = info - self._mode = mode - self._size = size - self.im = core.new(mode, size) - if mode in ("L", "LA", "P", "PA") and palette: - self.putpalette(palette) - self.frombytes(data) - - def tobytes(self, encoder_name: str = "raw", *args: Any) -> bytes: - """ - Return image as a bytes object. - - .. warning:: - - This method returns raw image data derived from Pillow's internal - storage. For compressed image data (e.g. PNG, JPEG) use - :meth:`~.save`, with a BytesIO parameter for in-memory data. - - :param encoder_name: What encoder to use. - - The default is to use the standard "raw" encoder. - To see how this packs pixel data into the returned - bytes, see :file:`libImaging/Pack.c`. - - A list of C encoders can be seen under codecs - section of the function array in - :file:`_imaging.c`. Python encoders are registered - within the relevant plugins. - :param args: Extra arguments to the encoder. - :returns: A :py:class:`bytes` object. - """ - - encoder_args: Any = args - if len(encoder_args) == 1 and isinstance(encoder_args[0], tuple): - # may pass tuple instead of argument list - encoder_args = encoder_args[0] - - if encoder_name == "raw" and encoder_args == (): - encoder_args = self.mode - - self.load() - - if self.width == 0 or self.height == 0: - return b"" - - # unpack data - e = _getencoder(self.mode, encoder_name, encoder_args) - e.setimage(self.im) - - from . import ImageFile - - bufsize = max(ImageFile.MAXBLOCK, self.size[0] * 4) # see RawEncode.c - - output = [] - while True: - bytes_consumed, errcode, data = e.encode(bufsize) - output.append(data) - if errcode: - break - if errcode < 0: - msg = f"encoder error {errcode} in tobytes" - raise RuntimeError(msg) - - return b"".join(output) - - def tobitmap(self, name: str = "image") -> bytes: - """ - Returns the image converted to an X11 bitmap. - - .. note:: This method only works for mode "1" images. - - :param name: The name prefix to use for the bitmap variables. - :returns: A string containing an X11 bitmap. - :raises ValueError: If the mode is not "1" - """ - - self.load() - if self.mode != "1": - msg = "not a bitmap" - raise ValueError(msg) - data = self.tobytes("xbm") - return b"".join( - [ - f"#define {name}_width {self.size[0]}\n".encode("ascii"), - f"#define {name}_height {self.size[1]}\n".encode("ascii"), - f"static char {name}_bits[] = {{\n".encode("ascii"), - data, - b"};", - ] - ) - - def frombytes( - self, - data: bytes | bytearray | SupportsArrayInterface, - decoder_name: str = "raw", - *args: Any, - ) -> None: - """ - Loads this image with pixel data from a bytes object. - - This method is similar to the :py:func:`~PIL.Image.frombytes` function, - but loads data into this image instead of creating a new image object. - """ - - if self.width == 0 or self.height == 0: - return - - decoder_args: Any = args - if len(decoder_args) == 1 and isinstance(decoder_args[0], tuple): - # may pass tuple instead of argument list - decoder_args = decoder_args[0] - - # default format - if decoder_name == "raw" and decoder_args == (): - decoder_args = self.mode - - # unpack data - d = _getdecoder(self.mode, decoder_name, decoder_args) - d.setimage(self.im) - s = d.decode(data) - - if s[0] >= 0: - msg = "not enough image data" - raise ValueError(msg) - if s[1] != 0: - msg = "cannot decode image data" - raise ValueError(msg) - - def load(self) -> core.PixelAccess | None: - """ - Allocates storage for the image and loads the pixel data. In - normal cases, you don't need to call this method, since the - Image class automatically loads an opened image when it is - accessed for the first time. - - If the file associated with the image was opened by Pillow, then this - method will close it. The exception to this is if the image has - multiple frames, in which case the file will be left open for seek - operations. See :ref:`file-handling` for more information. - - :returns: An image access object. - :rtype: :py:class:`.PixelAccess` - """ - if self._im is not None and self.palette and self.palette.dirty: - # realize palette - mode, arr = self.palette.getdata() - self.im.putpalette(self.palette.mode, mode, arr) - self.palette.dirty = 0 - self.palette.rawmode = None - if "transparency" in self.info and mode in ("LA", "PA"): - if isinstance(self.info["transparency"], int): - self.im.putpalettealpha(self.info["transparency"], 0) - else: - self.im.putpalettealphas(self.info["transparency"]) - self.palette.mode = "RGBA" - else: - self.palette.palette = self.im.getpalette( - self.palette.mode, self.palette.mode - ) - - if self._im is not None: - return self.im.pixel_access(self.readonly) - return None - - def verify(self) -> None: - """ - Verifies the contents of a file. For data read from a file, this - method attempts to determine if the file is broken, without - actually decoding the image data. If this method finds any - problems, it raises suitable exceptions. If you need to load - the image after using this method, you must reopen the image - file. - """ - pass - - def convert( - self, - mode: str | None = None, - matrix: tuple[float, ...] | None = None, - dither: Dither | None = None, - palette: Palette = Palette.WEB, - colors: int = 256, - ) -> Image: - """ - Returns a converted copy of this image. For the "P" mode, this - method translates pixels through the palette. If mode is - omitted, a mode is chosen so that all information in the image - and the palette can be represented without a palette. - - This supports all possible conversions between "L", "RGB" and "CMYK". The - ``matrix`` argument only supports "L" and "RGB". - - When translating a color image to grayscale (mode "L"), - the library uses the ITU-R 601-2 luma transform:: - - L = R * 299/1000 + G * 587/1000 + B * 114/1000 - - The default method of converting a grayscale ("L") or "RGB" - image into a bilevel (mode "1") image uses Floyd-Steinberg - dither to approximate the original image luminosity levels. If - dither is ``None``, all values larger than 127 are set to 255 (white), - all other values to 0 (black). To use other thresholds, use the - :py:meth:`~PIL.Image.Image.point` method. - - When converting from "RGBA" to "P" without a ``matrix`` argument, - this passes the operation to :py:meth:`~PIL.Image.Image.quantize`, - and ``dither`` and ``palette`` are ignored. - - When converting from "PA", if an "RGBA" palette is present, the alpha - channel from the image will be used instead of the values from the palette. - - :param mode: The requested mode. See: :ref:`concept-modes`. - :param matrix: An optional conversion matrix. If given, this - should be 4- or 12-tuple containing floating point values. - :param dither: Dithering method, used when converting from - mode "RGB" to "P" or from "RGB" or "L" to "1". - Available methods are :data:`Dither.NONE` or :data:`Dither.FLOYDSTEINBERG` - (default). Note that this is not used when ``matrix`` is supplied. - :param palette: Palette to use when converting from mode "RGB" - to "P". Available palettes are :data:`Palette.WEB` or - :data:`Palette.ADAPTIVE`. - :param colors: Number of colors to use for the :data:`Palette.ADAPTIVE` - palette. Defaults to 256. - :rtype: :py:class:`~PIL.Image.Image` - :returns: An :py:class:`~PIL.Image.Image` object. - """ - - self.load() - - has_transparency = "transparency" in self.info - if not mode and self.mode == "P": - # determine default mode - if self.palette: - mode = self.palette.mode - else: - mode = "RGB" - if mode == "RGB" and has_transparency: - mode = "RGBA" - if not mode or (mode == self.mode and not matrix): - return self.copy() - - if matrix: - # matrix conversion - if mode not in ("L", "RGB"): - msg = "illegal conversion" - raise ValueError(msg) - im = self.im.convert_matrix(mode, matrix) - new_im = self._new(im) - if has_transparency and self.im.bands == 3: - transparency = new_im.info["transparency"] - - def convert_transparency( - m: tuple[float, ...], v: tuple[int, int, int] - ) -> int: - value = m[0] * v[0] + m[1] * v[1] + m[2] * v[2] + m[3] * 0.5 - return max(0, min(255, int(value))) - - if mode == "L": - transparency = convert_transparency(matrix, transparency) - elif len(mode) == 3: - transparency = tuple( - convert_transparency(matrix[i * 4 : i * 4 + 4], transparency) - for i in range(len(transparency)) - ) - new_im.info["transparency"] = transparency - return new_im - - if self.mode == "RGBA": - if mode == "P": - return self.quantize(colors) - elif mode == "PA": - r, g, b, a = self.split() - rgb = merge("RGB", (r, g, b)) - p = rgb.quantize(colors) - return merge("PA", (p, a)) - - trns = None - delete_trns = False - # transparency handling - if has_transparency: - if (self.mode in ("1", "L", "I", "I;16") and mode in ("LA", "RGBA")) or ( - self.mode == "RGB" and mode in ("La", "LA", "RGBa", "RGBA") - ): - # Use transparent conversion to promote from transparent - # color to an alpha channel. - new_im = self._new( - self.im.convert_transparent(mode, self.info["transparency"]) - ) - del new_im.info["transparency"] - return new_im - elif self.mode in ("L", "RGB", "P") and mode in ("L", "RGB", "P"): - t = self.info["transparency"] - if isinstance(t, bytes): - # Dragons. This can't be represented by a single color - warnings.warn( - "Palette images with Transparency expressed in bytes should be " - "converted to RGBA images" - ) - delete_trns = True - else: - # get the new transparency color. - # use existing conversions - trns_im = new(self.mode, (1, 1)) - if self.mode == "P": - assert self.palette is not None - trns_im.putpalette(self.palette, self.palette.mode) - if isinstance(t, tuple): - err = "Couldn't allocate a palette color for transparency" - assert trns_im.palette is not None - try: - t = trns_im.palette.getcolor(t, self) - except ValueError as e: - if str(e) == "cannot allocate more than 256 colors": - # If all 256 colors are in use, - # then there is no need for transparency - t = None - else: - raise ValueError(err) from e - if t is None: - trns = None - else: - trns_im.putpixel((0, 0), t) - - if mode in ("L", "RGB"): - trns_im = trns_im.convert(mode) - else: - # can't just retrieve the palette number, got to do it - # after quantization. - trns_im = trns_im.convert("RGB") - trns = trns_im.getpixel((0, 0)) - - elif self.mode == "P" and mode in ("LA", "PA", "RGBA"): - t = self.info["transparency"] - delete_trns = True - - if isinstance(t, bytes): - self.im.putpalettealphas(t) - elif isinstance(t, int): - self.im.putpalettealpha(t, 0) - else: - msg = "Transparency for P mode should be bytes or int" - raise ValueError(msg) - - if mode == "P" and palette == Palette.ADAPTIVE: - im = self.im.quantize(colors) - new_im = self._new(im) - from . import ImagePalette - - new_im.palette = ImagePalette.ImagePalette( - "RGB", new_im.im.getpalette("RGB") - ) - if delete_trns: - # This could possibly happen if we requantize to fewer colors. - # The transparency would be totally off in that case. - del new_im.info["transparency"] - if trns is not None: - try: - new_im.info["transparency"] = new_im.palette.getcolor( - cast(tuple[int, ...], trns), # trns was converted to RGB - new_im, - ) - except Exception: - # if we can't make a transparent color, don't leave the old - # transparency hanging around to mess us up. - del new_im.info["transparency"] - warnings.warn("Couldn't allocate palette entry for transparency") - return new_im - - if "LAB" in (self.mode, mode): - im = self - if mode == "LAB": - if im.mode not in ("RGB", "RGBA", "RGBX"): - im = im.convert("RGBA") - other_mode = im.mode - else: - other_mode = mode - if other_mode in ("RGB", "RGBA", "RGBX"): - from . import ImageCms - - srgb = ImageCms.createProfile("sRGB") - lab = ImageCms.createProfile("LAB") - profiles = [lab, srgb] if im.mode == "LAB" else [srgb, lab] - transform = ImageCms.buildTransform( - profiles[0], profiles[1], im.mode, mode - ) - return transform.apply(im) - - # colorspace conversion - if dither is None: - dither = Dither.FLOYDSTEINBERG - - try: - im = self.im.convert(mode, dither) - except ValueError: - try: - # normalize source image and try again - modebase = getmodebase(self.mode) - if modebase == self.mode: - raise - im = self.im.convert(modebase) - im = im.convert(mode, dither) - except KeyError as e: - msg = "illegal conversion" - raise ValueError(msg) from e - - new_im = self._new(im) - if mode in ("P", "PA") and palette != Palette.ADAPTIVE: - from . import ImagePalette - - new_im.palette = ImagePalette.ImagePalette("RGB", im.getpalette("RGB")) - if delete_trns: - # crash fail if we leave a bytes transparency in an rgb/l mode. - del new_im.info["transparency"] - if trns is not None: - if new_im.mode == "P" and new_im.palette: - try: - new_im.info["transparency"] = new_im.palette.getcolor( - cast(tuple[int, ...], trns), new_im # trns was converted to RGB - ) - except ValueError as e: - del new_im.info["transparency"] - if str(e) != "cannot allocate more than 256 colors": - # If all 256 colors are in use, - # then there is no need for transparency - warnings.warn( - "Couldn't allocate palette entry for transparency" - ) - else: - new_im.info["transparency"] = trns - return new_im - - def quantize( - self, - colors: int = 256, - method: int | None = None, - kmeans: int = 0, - palette: Image | None = None, - dither: Dither = Dither.FLOYDSTEINBERG, - ) -> Image: - """ - Convert the image to 'P' mode with the specified number - of colors. - - :param colors: The desired number of colors, <= 256 - :param method: :data:`Quantize.MEDIANCUT` (median cut), - :data:`Quantize.MAXCOVERAGE` (maximum coverage), - :data:`Quantize.FASTOCTREE` (fast octree), - :data:`Quantize.LIBIMAGEQUANT` (libimagequant; check support - using :py:func:`PIL.features.check_feature` with - ``feature="libimagequant"``). - - By default, :data:`Quantize.MEDIANCUT` will be used. - - The exception to this is RGBA images. :data:`Quantize.MEDIANCUT` - and :data:`Quantize.MAXCOVERAGE` do not support RGBA images, so - :data:`Quantize.FASTOCTREE` is used by default instead. - :param kmeans: Integer greater than or equal to zero. - :param palette: Quantize to the palette of given - :py:class:`PIL.Image.Image`. - :param dither: Dithering method, used when converting from - mode "RGB" to "P" or from "RGB" or "L" to "1". - Available methods are :data:`Dither.NONE` or :data:`Dither.FLOYDSTEINBERG` - (default). - :returns: A new image - """ - - self.load() - - if method is None: - # defaults: - method = Quantize.MEDIANCUT - if self.mode == "RGBA": - method = Quantize.FASTOCTREE - - if self.mode == "RGBA" and method not in ( - Quantize.FASTOCTREE, - Quantize.LIBIMAGEQUANT, - ): - # Caller specified an invalid mode. - msg = ( - "Fast Octree (method == 2) and libimagequant (method == 3) " - "are the only valid methods for quantizing RGBA images" - ) - raise ValueError(msg) - - if palette: - # use palette from reference image - palette.load() - if palette.mode != "P": - msg = "bad mode for palette image" - raise ValueError(msg) - if self.mode not in {"RGB", "L"}: - msg = "only RGB or L mode images can be quantized to a palette" - raise ValueError(msg) - im = self.im.convert("P", dither, palette.im) - new_im = self._new(im) - assert palette.palette is not None - new_im.palette = palette.palette.copy() - return new_im - - if kmeans < 0: - msg = "kmeans must not be negative" - raise ValueError(msg) - - im = self._new(self.im.quantize(colors, method, kmeans)) - - from . import ImagePalette - - mode = im.im.getpalettemode() - palette_data = im.im.getpalette(mode, mode)[: colors * len(mode)] - im.palette = ImagePalette.ImagePalette(mode, palette_data) - - return im - - def copy(self) -> Image: - """ - Copies this image. Use this method if you wish to paste things - into an image, but still retain the original. - - :rtype: :py:class:`~PIL.Image.Image` - :returns: An :py:class:`~PIL.Image.Image` object. - """ - self.load() - return self._new(self.im.copy()) - - __copy__ = copy - - def crop(self, box: tuple[float, float, float, float] | None = None) -> Image: - """ - Returns a rectangular region from this image. The box is a - 4-tuple defining the left, upper, right, and lower pixel - coordinate. See :ref:`coordinate-system`. - - Note: Prior to Pillow 3.4.0, this was a lazy operation. - - :param box: The crop rectangle, as a (left, upper, right, lower)-tuple. - :rtype: :py:class:`~PIL.Image.Image` - :returns: An :py:class:`~PIL.Image.Image` object. - """ - - if box is None: - return self.copy() - - if box[2] < box[0]: - msg = "Coordinate 'right' is less than 'left'" - raise ValueError(msg) - elif box[3] < box[1]: - msg = "Coordinate 'lower' is less than 'upper'" - raise ValueError(msg) - - self.load() - return self._new(self._crop(self.im, box)) - - def _crop( - self, im: core.ImagingCore, box: tuple[float, float, float, float] - ) -> core.ImagingCore: - """ - Returns a rectangular region from the core image object im. - - This is equivalent to calling im.crop((x0, y0, x1, y1)), but - includes additional sanity checks. - - :param im: a core image object - :param box: The crop rectangle, as a (left, upper, right, lower)-tuple. - :returns: A core image object. - """ - - x0, y0, x1, y1 = map(int, map(round, box)) - - absolute_values = (abs(x1 - x0), abs(y1 - y0)) - - _decompression_bomb_check(absolute_values) - - return im.crop((x0, y0, x1, y1)) - - def draft( - self, mode: str | None, size: tuple[int, int] | None - ) -> tuple[str, tuple[int, int, float, float]] | None: - """ - Configures the image file loader so it returns a version of the - image that as closely as possible matches the given mode and - size. For example, you can use this method to convert a color - JPEG to grayscale while loading it. - - If any changes are made, returns a tuple with the chosen ``mode`` and - ``box`` with coordinates of the original image within the altered one. - - Note that this method modifies the :py:class:`~PIL.Image.Image` object - in place. If the image has already been loaded, this method has no - effect. - - Note: This method is not implemented for most images. It is - currently implemented only for JPEG and MPO images. - - :param mode: The requested mode. - :param size: The requested size in pixels, as a 2-tuple: - (width, height). - """ - pass - - def filter(self, filter: ImageFilter.Filter | type[ImageFilter.Filter]) -> Image: - """ - Filters this image using the given filter. For a list of - available filters, see the :py:mod:`~PIL.ImageFilter` module. - - :param filter: Filter kernel. - :returns: An :py:class:`~PIL.Image.Image` object.""" - - from . import ImageFilter - - self.load() - - if callable(filter): - filter = filter() - if not hasattr(filter, "filter"): - msg = "filter argument should be ImageFilter.Filter instance or class" - raise TypeError(msg) - - multiband = isinstance(filter, ImageFilter.MultibandFilter) - if self.im.bands == 1 or multiband: - return self._new(filter.filter(self.im)) - - ims = [ - self._new(filter.filter(self.im.getband(c))) for c in range(self.im.bands) - ] - return merge(self.mode, ims) - - def getbands(self) -> tuple[str, ...]: - """ - Returns a tuple containing the name of each band in this image. - For example, ``getbands`` on an RGB image returns ("R", "G", "B"). - - :returns: A tuple containing band names. - :rtype: tuple - """ - return ImageMode.getmode(self.mode).bands - - def getbbox(self, *, alpha_only: bool = True) -> tuple[int, int, int, int] | None: - """ - Calculates the bounding box of the non-zero regions in the - image. - - :param alpha_only: Optional flag, defaulting to ``True``. - If ``True`` and the image has an alpha channel, trim transparent pixels. - Otherwise, trim pixels when all channels are zero. - Keyword-only argument. - :returns: The bounding box is returned as a 4-tuple defining the - left, upper, right, and lower pixel coordinate. See - :ref:`coordinate-system`. If the image is completely empty, this - method returns None. - - """ - - self.load() - return self.im.getbbox(alpha_only) - - def getcolors( - self, maxcolors: int = 256 - ) -> list[tuple[int, tuple[int, ...]]] | list[tuple[int, float]] | None: - """ - Returns a list of colors used in this image. - - The colors will be in the image's mode. For example, an RGB image will - return a tuple of (red, green, blue) color values, and a P image will - return the index of the color in the palette. - - :param maxcolors: Maximum number of colors. If this number is - exceeded, this method returns None. The default limit is - 256 colors. - :returns: An unsorted list of (count, pixel) values. - """ - - self.load() - if self.mode in ("1", "L", "P"): - h = self.im.histogram() - out: list[tuple[int, float]] = [(h[i], i) for i in range(256) if h[i]] - if len(out) > maxcolors: - return None - return out - return self.im.getcolors(maxcolors) - - def getdata(self, band: int | None = None) -> core.ImagingCore: - """ - Returns the contents of this image as a sequence object - containing pixel values. The sequence object is flattened, so - that values for line one follow directly after the values of - line zero, and so on. - - Note that the sequence object returned by this method is an - internal PIL data type, which only supports certain sequence - operations. To convert it to an ordinary sequence (e.g. for - printing), use ``list(im.getdata())``. - - :param band: What band to return. The default is to return - all bands. To return a single band, pass in the index - value (e.g. 0 to get the "R" band from an "RGB" image). - :returns: A sequence-like object. - """ - - self.load() - if band is not None: - return self.im.getband(band) - return self.im # could be abused - - def getextrema(self) -> tuple[float, float] | tuple[tuple[int, int], ...]: - """ - Gets the minimum and maximum pixel values for each band in - the image. - - :returns: For a single-band image, a 2-tuple containing the - minimum and maximum pixel value. For a multi-band image, - a tuple containing one 2-tuple for each band. - """ - - self.load() - if self.im.bands > 1: - return tuple(self.im.getband(i).getextrema() for i in range(self.im.bands)) - return self.im.getextrema() - - def getxmp(self) -> dict[str, Any]: - """ - Returns a dictionary containing the XMP tags. - Requires defusedxml to be installed. - - :returns: XMP tags in a dictionary. - """ - - def get_name(tag: str) -> str: - return re.sub("^{[^}]+}", "", tag) - - def get_value(element: Element) -> str | dict[str, Any] | None: - value: dict[str, Any] = {get_name(k): v for k, v in element.attrib.items()} - children = list(element) - if children: - for child in children: - name = get_name(child.tag) - child_value = get_value(child) - if name in value: - if not isinstance(value[name], list): - value[name] = [value[name]] - value[name].append(child_value) - else: - value[name] = child_value - elif value: - if element.text: - value["text"] = element.text - else: - return element.text - return value - - if ElementTree is None: - warnings.warn("XMP data cannot be read without defusedxml dependency") - return {} - if "xmp" not in self.info: - return {} - root = ElementTree.fromstring(self.info["xmp"].rstrip(b"\x00 ")) - return {get_name(root.tag): get_value(root)} - - def getexif(self) -> Exif: - """ - Gets EXIF data from the image. - - :returns: an :py:class:`~PIL.Image.Exif` object. - """ - if self._exif is None: - self._exif = Exif() - elif self._exif._loaded: - return self._exif - self._exif._loaded = True - - exif_info = self.info.get("exif") - if exif_info is None: - if "Raw profile type exif" in self.info: - exif_info = bytes.fromhex( - "".join(self.info["Raw profile type exif"].split("\n")[3:]) - ) - elif hasattr(self, "tag_v2"): - self._exif.bigtiff = self.tag_v2._bigtiff - self._exif.endian = self.tag_v2._endian - self._exif.load_from_fp(self.fp, self.tag_v2._offset) - if exif_info is not None: - self._exif.load(exif_info) - - # XMP tags - if ExifTags.Base.Orientation not in self._exif: - xmp_tags = self.info.get("XML:com.adobe.xmp") - pattern: str | bytes = r'tiff:Orientation(="|>)([0-9])' - if not xmp_tags and (xmp_tags := self.info.get("xmp")): - pattern = rb'tiff:Orientation(="|>)([0-9])' - if xmp_tags: - match = re.search(pattern, xmp_tags) - if match: - self._exif[ExifTags.Base.Orientation] = int(match[2]) - - return self._exif - - def _reload_exif(self) -> None: - if self._exif is None or not self._exif._loaded: - return - self._exif._loaded = False - self.getexif() - - def get_child_images(self) -> list[ImageFile.ImageFile]: - from . import ImageFile - - deprecate("Image.Image.get_child_images", 13) - return ImageFile.ImageFile.get_child_images(self) # type: ignore[arg-type] - - def getim(self) -> CapsuleType: - """ - Returns a capsule that points to the internal image memory. - - :returns: A capsule object. - """ - - self.load() - return self.im.ptr - - def getpalette(self, rawmode: str | None = "RGB") -> list[int] | None: - """ - Returns the image palette as a list. - - :param rawmode: The mode in which to return the palette. ``None`` will - return the palette in its current mode. - - .. versionadded:: 9.1.0 - - :returns: A list of color values [r, g, b, ...], or None if the - image has no palette. - """ - - self.load() - try: - mode = self.im.getpalettemode() - except ValueError: - return None # no palette - if rawmode is None: - rawmode = mode - return list(self.im.getpalette(mode, rawmode)) - - @property - def has_transparency_data(self) -> bool: - """ - Determine if an image has transparency data, whether in the form of an - alpha channel, a palette with an alpha channel, or a "transparency" key - in the info dictionary. - - Note the image might still appear solid, if all of the values shown - within are opaque. - - :returns: A boolean. - """ - if ( - self.mode in ("LA", "La", "PA", "RGBA", "RGBa") - or "transparency" in self.info - ): - return True - if self.mode == "P": - assert self.palette is not None - return self.palette.mode.endswith("A") - return False - - def apply_transparency(self) -> None: - """ - If a P mode image has a "transparency" key in the info dictionary, - remove the key and instead apply the transparency to the palette. - Otherwise, the image is unchanged. - """ - if self.mode != "P" or "transparency" not in self.info: - return - - from . import ImagePalette - - palette = self.getpalette("RGBA") - assert palette is not None - transparency = self.info["transparency"] - if isinstance(transparency, bytes): - for i, alpha in enumerate(transparency): - palette[i * 4 + 3] = alpha - else: - palette[transparency * 4 + 3] = 0 - self.palette = ImagePalette.ImagePalette("RGBA", bytes(palette)) - self.palette.dirty = 1 - - del self.info["transparency"] - - def getpixel( - self, xy: tuple[int, int] | list[int] - ) -> float | tuple[int, ...] | None: - """ - Returns the pixel value at a given position. - - :param xy: The coordinate, given as (x, y). See - :ref:`coordinate-system`. - :returns: The pixel value. If the image is a multi-layer image, - this method returns a tuple. - """ - - self.load() - return self.im.getpixel(tuple(xy)) - - def getprojection(self) -> tuple[list[int], list[int]]: - """ - Get projection to x and y axes - - :returns: Two sequences, indicating where there are non-zero - pixels along the X-axis and the Y-axis, respectively. - """ - - self.load() - x, y = self.im.getprojection() - return list(x), list(y) - - def histogram( - self, mask: Image | None = None, extrema: tuple[float, float] | None = None - ) -> list[int]: - """ - Returns a histogram for the image. The histogram is returned as a - list of pixel counts, one for each pixel value in the source - image. Counts are grouped into 256 bins for each band, even if - the image has more than 8 bits per band. If the image has more - than one band, the histograms for all bands are concatenated (for - example, the histogram for an "RGB" image contains 768 values). - - A bilevel image (mode "1") is treated as a grayscale ("L") image - by this method. - - If a mask is provided, the method returns a histogram for those - parts of the image where the mask image is non-zero. The mask - image must have the same size as the image, and be either a - bi-level image (mode "1") or a grayscale image ("L"). - - :param mask: An optional mask. - :param extrema: An optional tuple of manually-specified extrema. - :returns: A list containing pixel counts. - """ - self.load() - if mask: - mask.load() - return self.im.histogram((0, 0), mask.im) - if self.mode in ("I", "F"): - return self.im.histogram( - extrema if extrema is not None else self.getextrema() - ) - return self.im.histogram() - - def entropy( - self, mask: Image | None = None, extrema: tuple[float, float] | None = None - ) -> float: - """ - Calculates and returns the entropy for the image. - - A bilevel image (mode "1") is treated as a grayscale ("L") - image by this method. - - If a mask is provided, the method employs the histogram for - those parts of the image where the mask image is non-zero. - The mask image must have the same size as the image, and be - either a bi-level image (mode "1") or a grayscale image ("L"). - - :param mask: An optional mask. - :param extrema: An optional tuple of manually-specified extrema. - :returns: A float value representing the image entropy - """ - self.load() - if mask: - mask.load() - return self.im.entropy((0, 0), mask.im) - if self.mode in ("I", "F"): - return self.im.entropy( - extrema if extrema is not None else self.getextrema() - ) - return self.im.entropy() - - def paste( - self, - im: Image | str | float | tuple[float, ...], - box: Image | tuple[int, int, int, int] | tuple[int, int] | None = None, - mask: Image | None = None, - ) -> None: - """ - Pastes another image into this image. The box argument is either - a 2-tuple giving the upper left corner, a 4-tuple defining the - left, upper, right, and lower pixel coordinate, or None (same as - (0, 0)). See :ref:`coordinate-system`. If a 4-tuple is given, the size - of the pasted image must match the size of the region. - - If the modes don't match, the pasted image is converted to the mode of - this image (see the :py:meth:`~PIL.Image.Image.convert` method for - details). - - Instead of an image, the source can be a integer or tuple - containing pixel values. The method then fills the region - with the given color. When creating RGB images, you can - also use color strings as supported by the ImageColor module. See - :ref:`colors` for more information. - - If a mask is given, this method updates only the regions - indicated by the mask. You can use either "1", "L", "LA", "RGBA" - or "RGBa" images (if present, the alpha band is used as mask). - Where the mask is 255, the given image is copied as is. Where - the mask is 0, the current value is preserved. Intermediate - values will mix the two images together, including their alpha - channels if they have them. - - See :py:meth:`~PIL.Image.Image.alpha_composite` if you want to - combine images with respect to their alpha channels. - - :param im: Source image or pixel value (integer, float or tuple). - :param box: An optional 4-tuple giving the region to paste into. - If a 2-tuple is used instead, it's treated as the upper left - corner. If omitted or None, the source is pasted into the - upper left corner. - - If an image is given as the second argument and there is no - third, the box defaults to (0, 0), and the second argument - is interpreted as a mask image. - :param mask: An optional mask image. - """ - - if isinstance(box, Image): - if mask is not None: - msg = "If using second argument as mask, third argument must be None" - raise ValueError(msg) - # abbreviated paste(im, mask) syntax - mask = box - box = None - - if box is None: - box = (0, 0) - - if len(box) == 2: - # upper left corner given; get size from image or mask - if isinstance(im, Image): - size = im.size - elif isinstance(mask, Image): - size = mask.size - else: - # FIXME: use self.size here? - msg = "cannot determine region size; use 4-item box" - raise ValueError(msg) - box += (box[0] + size[0], box[1] + size[1]) - - source: core.ImagingCore | str | float | tuple[float, ...] - if isinstance(im, str): - from . import ImageColor - - source = ImageColor.getcolor(im, self.mode) - elif isinstance(im, Image): - im.load() - if self.mode != im.mode: - if self.mode != "RGB" or im.mode not in ("LA", "RGBA", "RGBa"): - # should use an adapter for this! - im = im.convert(self.mode) - source = im.im - else: - source = im - - self._ensure_mutable() - - if mask: - mask.load() - self.im.paste(source, box, mask.im) - else: - self.im.paste(source, box) - - def alpha_composite( - self, im: Image, dest: Sequence[int] = (0, 0), source: Sequence[int] = (0, 0) - ) -> None: - """'In-place' analog of Image.alpha_composite. Composites an image - onto this image. - - :param im: image to composite over this one - :param dest: Optional 2 tuple (left, top) specifying the upper - left corner in this (destination) image. - :param source: Optional 2 (left, top) tuple for the upper left - corner in the overlay source image, or 4 tuple (left, top, right, - bottom) for the bounds of the source rectangle - - Performance Note: Not currently implemented in-place in the core layer. - """ - - if not isinstance(source, (list, tuple)): - msg = "Source must be a list or tuple" - raise ValueError(msg) - if not isinstance(dest, (list, tuple)): - msg = "Destination must be a list or tuple" - raise ValueError(msg) - - if len(source) == 4: - overlay_crop_box = tuple(source) - elif len(source) == 2: - overlay_crop_box = tuple(source) + im.size - else: - msg = "Source must be a sequence of length 2 or 4" - raise ValueError(msg) - - if not len(dest) == 2: - msg = "Destination must be a sequence of length 2" - raise ValueError(msg) - if min(source) < 0: - msg = "Source must be non-negative" - raise ValueError(msg) - - # over image, crop if it's not the whole image. - if overlay_crop_box == (0, 0) + im.size: - overlay = im - else: - overlay = im.crop(overlay_crop_box) - - # target for the paste - box = tuple(dest) + (dest[0] + overlay.width, dest[1] + overlay.height) - - # destination image. don't copy if we're using the whole image. - if box == (0, 0) + self.size: - background = self - else: - background = self.crop(box) - - result = alpha_composite(background, overlay) - self.paste(result, box) - - def point( - self, - lut: ( - Sequence[float] - | NumpyArray - | Callable[[int], float] - | Callable[[ImagePointTransform], ImagePointTransform | float] - | ImagePointHandler - ), - mode: str | None = None, - ) -> Image: - """ - Maps this image through a lookup table or function. - - :param lut: A lookup table, containing 256 (or 65536 if - self.mode=="I" and mode == "L") values per band in the - image. A function can be used instead, it should take a - single argument. The function is called once for each - possible pixel value, and the resulting table is applied to - all bands of the image. - - It may also be an :py:class:`~PIL.Image.ImagePointHandler` - object:: - - class Example(Image.ImagePointHandler): - def point(self, im: Image) -> Image: - # Return result - :param mode: Output mode (default is same as input). This can only be used if - the source image has mode "L" or "P", and the output has mode "1" or the - source image mode is "I" and the output mode is "L". - :returns: An :py:class:`~PIL.Image.Image` object. - """ - - self.load() - - if isinstance(lut, ImagePointHandler): - return lut.point(self) - - if callable(lut): - # if it isn't a list, it should be a function - if self.mode in ("I", "I;16", "F"): - # check if the function can be used with point_transform - # UNDONE wiredfool -- I think this prevents us from ever doing - # a gamma function point transform on > 8bit images. - scale, offset = _getscaleoffset(lut) # type: ignore[arg-type] - return self._new(self.im.point_transform(scale, offset)) - # for other modes, convert the function to a table - flatLut = [lut(i) for i in range(256)] * self.im.bands # type: ignore[arg-type] - else: - flatLut = lut - - if self.mode == "F": - # FIXME: _imaging returns a confusing error message for this case - msg = "point operation not supported for this mode" - raise ValueError(msg) - - if mode != "F": - flatLut = [round(i) for i in flatLut] - return self._new(self.im.point(flatLut, mode)) - - def putalpha(self, alpha: Image | int) -> None: - """ - Adds or replaces the alpha layer in this image. If the image - does not have an alpha layer, it's converted to "LA" or "RGBA". - The new layer must be either "L" or "1". - - :param alpha: The new alpha layer. This can either be an "L" or "1" - image having the same size as this image, or an integer. - """ - - self._ensure_mutable() - - if self.mode not in ("LA", "PA", "RGBA"): - # attempt to promote self to a matching alpha mode - try: - mode = getmodebase(self.mode) + "A" - try: - self.im.setmode(mode) - except (AttributeError, ValueError) as e: - # do things the hard way - im = self.im.convert(mode) - if im.mode not in ("LA", "PA", "RGBA"): - msg = "alpha channel could not be added" - raise ValueError(msg) from e # sanity check - self.im = im - self._mode = self.im.mode - except KeyError as e: - msg = "illegal image mode" - raise ValueError(msg) from e - - if self.mode in ("LA", "PA"): - band = 1 - else: - band = 3 - - if isinstance(alpha, Image): - # alpha layer - if alpha.mode not in ("1", "L"): - msg = "illegal image mode" - raise ValueError(msg) - alpha.load() - if alpha.mode == "1": - alpha = alpha.convert("L") - else: - # constant alpha - try: - self.im.fillband(band, alpha) - except (AttributeError, ValueError): - # do things the hard way - alpha = new("L", self.size, alpha) - else: - return - - self.im.putband(alpha.im, band) - - def putdata( - self, - data: Sequence[float] | Sequence[Sequence[int]] | core.ImagingCore | NumpyArray, - scale: float = 1.0, - offset: float = 0.0, - ) -> None: - """ - Copies pixel data from a flattened sequence object into the image. The - values should start at the upper left corner (0, 0), continue to the - end of the line, followed directly by the first value of the second - line, and so on. Data will be read until either the image or the - sequence ends. The scale and offset values are used to adjust the - sequence values: **pixel = value*scale + offset**. - - :param data: A flattened sequence object. See :ref:`colors` for more - information about values. - :param scale: An optional scale value. The default is 1.0. - :param offset: An optional offset value. The default is 0.0. - """ - - self._ensure_mutable() - - self.im.putdata(data, scale, offset) - - def putpalette( - self, - data: ImagePalette.ImagePalette | bytes | Sequence[int], - rawmode: str = "RGB", - ) -> None: - """ - Attaches a palette to this image. The image must be a "P", "PA", "L" - or "LA" image. - - The palette sequence must contain at most 256 colors, made up of one - integer value for each channel in the raw mode. - For example, if the raw mode is "RGB", then it can contain at most 768 - values, made up of red, green and blue values for the corresponding pixel - index in the 256 colors. - If the raw mode is "RGBA", then it can contain at most 1024 values, - containing red, green, blue and alpha values. - - Alternatively, an 8-bit string may be used instead of an integer sequence. - - :param data: A palette sequence (either a list or a string). - :param rawmode: The raw mode of the palette. Either "RGB", "RGBA", or a mode - that can be transformed to "RGB" or "RGBA" (e.g. "R", "BGR;15", "RGBA;L"). - """ - from . import ImagePalette - - if self.mode not in ("L", "LA", "P", "PA"): - msg = "illegal image mode" - raise ValueError(msg) - if isinstance(data, ImagePalette.ImagePalette): - if data.rawmode is not None: - palette = ImagePalette.raw(data.rawmode, data.palette) - else: - palette = ImagePalette.ImagePalette(palette=data.palette) - palette.dirty = 1 - else: - if not isinstance(data, bytes): - data = bytes(data) - palette = ImagePalette.raw(rawmode, data) - self._mode = "PA" if "A" in self.mode else "P" - self.palette = palette - self.palette.mode = "RGBA" if "A" in rawmode else "RGB" - self.load() # install new palette - - def putpixel( - self, xy: tuple[int, int], value: float | tuple[int, ...] | list[int] - ) -> None: - """ - Modifies the pixel at the given position. The color is given as - a single numerical value for single-band images, and a tuple for - multi-band images. In addition to this, RGB and RGBA tuples are - accepted for P and PA images. See :ref:`colors` for more information. - - Note that this method is relatively slow. For more extensive changes, - use :py:meth:`~PIL.Image.Image.paste` or the :py:mod:`~PIL.ImageDraw` - module instead. - - See: - - * :py:meth:`~PIL.Image.Image.paste` - * :py:meth:`~PIL.Image.Image.putdata` - * :py:mod:`~PIL.ImageDraw` - - :param xy: The pixel coordinate, given as (x, y). See - :ref:`coordinate-system`. - :param value: The pixel value. - """ - - self._ensure_mutable() - - if ( - self.mode in ("P", "PA") - and isinstance(value, (list, tuple)) - and len(value) in [3, 4] - ): - # RGB or RGBA value for a P or PA image - if self.mode == "PA": - alpha = value[3] if len(value) == 4 else 255 - value = value[:3] - assert self.palette is not None - palette_index = self.palette.getcolor(tuple(value), self) - value = (palette_index, alpha) if self.mode == "PA" else palette_index - return self.im.putpixel(xy, value) - - def remap_palette( - self, dest_map: list[int], source_palette: bytes | bytearray | None = None - ) -> Image: - """ - Rewrites the image to reorder the palette. - - :param dest_map: A list of indexes into the original palette. - e.g. ``[1,0]`` would swap a two item palette, and ``list(range(256))`` - is the identity transform. - :param source_palette: Bytes or None. - :returns: An :py:class:`~PIL.Image.Image` object. - - """ - from . import ImagePalette - - if self.mode not in ("L", "P"): - msg = "illegal image mode" - raise ValueError(msg) - - bands = 3 - palette_mode = "RGB" - if source_palette is None: - if self.mode == "P": - self.load() - palette_mode = self.im.getpalettemode() - if palette_mode == "RGBA": - bands = 4 - source_palette = self.im.getpalette(palette_mode, palette_mode) - else: # L-mode - source_palette = bytearray(i // 3 for i in range(768)) - elif len(source_palette) > 768: - bands = 4 - palette_mode = "RGBA" - - palette_bytes = b"" - new_positions = [0] * 256 - - # pick only the used colors from the palette - for i, oldPosition in enumerate(dest_map): - palette_bytes += source_palette[ - oldPosition * bands : oldPosition * bands + bands - ] - new_positions[oldPosition] = i - - # replace the palette color id of all pixel with the new id - - # Palette images are [0..255], mapped through a 1 or 3 - # byte/color map. We need to remap the whole image - # from palette 1 to palette 2. New_positions is - # an array of indexes into palette 1. Palette 2 is - # palette 1 with any holes removed. - - # We're going to leverage the convert mechanism to use the - # C code to remap the image from palette 1 to palette 2, - # by forcing the source image into 'L' mode and adding a - # mapping 'L' mode palette, then converting back to 'L' - # sans palette thus converting the image bytes, then - # assigning the optimized RGB palette. - - # perf reference, 9500x4000 gif, w/~135 colors - # 14 sec prepatch, 1 sec postpatch with optimization forced. - - mapping_palette = bytearray(new_positions) - - m_im = self.copy() - m_im._mode = "P" - - m_im.palette = ImagePalette.ImagePalette( - palette_mode, palette=mapping_palette * bands - ) - # possibly set palette dirty, then - # m_im.putpalette(mapping_palette, 'L') # converts to 'P' - # or just force it. - # UNDONE -- this is part of the general issue with palettes - m_im.im.putpalette(palette_mode, palette_mode + ";L", m_im.palette.tobytes()) - - m_im = m_im.convert("L") - - m_im.putpalette(palette_bytes, palette_mode) - m_im.palette = ImagePalette.ImagePalette(palette_mode, palette=palette_bytes) - - if "transparency" in self.info: - try: - m_im.info["transparency"] = dest_map.index(self.info["transparency"]) - except ValueError: - if "transparency" in m_im.info: - del m_im.info["transparency"] - - return m_im - - def _get_safe_box( - self, - size: tuple[int, int], - resample: Resampling, - box: tuple[float, float, float, float], - ) -> tuple[int, int, int, int]: - """Expands the box so it includes adjacent pixels - that may be used by resampling with the given resampling filter. - """ - filter_support = _filters_support[resample] - 0.5 - scale_x = (box[2] - box[0]) / size[0] - scale_y = (box[3] - box[1]) / size[1] - support_x = filter_support * scale_x - support_y = filter_support * scale_y - - return ( - max(0, int(box[0] - support_x)), - max(0, int(box[1] - support_y)), - min(self.size[0], math.ceil(box[2] + support_x)), - min(self.size[1], math.ceil(box[3] + support_y)), - ) - - def resize( - self, - size: tuple[int, int] | list[int] | NumpyArray, - resample: int | None = None, - box: tuple[float, float, float, float] | None = None, - reducing_gap: float | None = None, - ) -> Image: - """ - Returns a resized copy of this image. - - :param size: The requested size in pixels, as a tuple or array: - (width, height). - :param resample: An optional resampling filter. This can be - one of :py:data:`Resampling.NEAREST`, :py:data:`Resampling.BOX`, - :py:data:`Resampling.BILINEAR`, :py:data:`Resampling.HAMMING`, - :py:data:`Resampling.BICUBIC` or :py:data:`Resampling.LANCZOS`. - If the image has mode "1" or "P", it is always set to - :py:data:`Resampling.NEAREST`. Otherwise, the default filter is - :py:data:`Resampling.BICUBIC`. See: :ref:`concept-filters`. - :param box: An optional 4-tuple of floats providing - the source image region to be scaled. - The values must be within (0, 0, width, height) rectangle. - If omitted or None, the entire source is used. - :param reducing_gap: Apply optimization by resizing the image - in two steps. First, reducing the image by integer times - using :py:meth:`~PIL.Image.Image.reduce`. - Second, resizing using regular resampling. The last step - changes size no less than by ``reducing_gap`` times. - ``reducing_gap`` may be None (no first step is performed) - or should be greater than 1.0. The bigger ``reducing_gap``, - the closer the result to the fair resampling. - The smaller ``reducing_gap``, the faster resizing. - With ``reducing_gap`` greater or equal to 3.0, the result is - indistinguishable from fair resampling in most cases. - The default value is None (no optimization). - :returns: An :py:class:`~PIL.Image.Image` object. - """ - - if resample is None: - resample = Resampling.BICUBIC - elif resample not in ( - Resampling.NEAREST, - Resampling.BILINEAR, - Resampling.BICUBIC, - Resampling.LANCZOS, - Resampling.BOX, - Resampling.HAMMING, - ): - msg = f"Unknown resampling filter ({resample})." - - filters = [ - f"{filter[1]} ({filter[0]})" - for filter in ( - (Resampling.NEAREST, "Image.Resampling.NEAREST"), - (Resampling.LANCZOS, "Image.Resampling.LANCZOS"), - (Resampling.BILINEAR, "Image.Resampling.BILINEAR"), - (Resampling.BICUBIC, "Image.Resampling.BICUBIC"), - (Resampling.BOX, "Image.Resampling.BOX"), - (Resampling.HAMMING, "Image.Resampling.HAMMING"), - ) - ] - msg += f" Use {', '.join(filters[:-1])} or {filters[-1]}" - raise ValueError(msg) - - if reducing_gap is not None and reducing_gap < 1.0: - msg = "reducing_gap must be 1.0 or greater" - raise ValueError(msg) - - if box is None: - box = (0, 0) + self.size - - size = tuple(size) - if self.size == size and box == (0, 0) + self.size: - return self.copy() - - if self.mode in ("1", "P"): - resample = Resampling.NEAREST - - if self.mode in ["LA", "RGBA"] and resample != Resampling.NEAREST: - im = self.convert({"LA": "La", "RGBA": "RGBa"}[self.mode]) - im = im.resize(size, resample, box) - return im.convert(self.mode) - - self.load() - - if reducing_gap is not None and resample != Resampling.NEAREST: - factor_x = int((box[2] - box[0]) / size[0] / reducing_gap) or 1 - factor_y = int((box[3] - box[1]) / size[1] / reducing_gap) or 1 - if factor_x > 1 or factor_y > 1: - reduce_box = self._get_safe_box(size, cast(Resampling, resample), box) - factor = (factor_x, factor_y) - self = ( - self.reduce(factor, box=reduce_box) - if callable(self.reduce) - else Image.reduce(self, factor, box=reduce_box) - ) - box = ( - (box[0] - reduce_box[0]) / factor_x, - (box[1] - reduce_box[1]) / factor_y, - (box[2] - reduce_box[0]) / factor_x, - (box[3] - reduce_box[1]) / factor_y, - ) - - return self._new(self.im.resize(size, resample, box)) - - def reduce( - self, - factor: int | tuple[int, int], - box: tuple[int, int, int, int] | None = None, - ) -> Image: - """ - Returns a copy of the image reduced ``factor`` times. - If the size of the image is not dividable by ``factor``, - the resulting size will be rounded up. - - :param factor: A greater than 0 integer or tuple of two integers - for width and height separately. - :param box: An optional 4-tuple of ints providing - the source image region to be reduced. - The values must be within ``(0, 0, width, height)`` rectangle. - If omitted or ``None``, the entire source is used. - """ - if not isinstance(factor, (list, tuple)): - factor = (factor, factor) - - if box is None: - box = (0, 0) + self.size - - if factor == (1, 1) and box == (0, 0) + self.size: - return self.copy() - - if self.mode in ["LA", "RGBA"]: - im = self.convert({"LA": "La", "RGBA": "RGBa"}[self.mode]) - im = im.reduce(factor, box) - return im.convert(self.mode) - - self.load() - - return self._new(self.im.reduce(factor, box)) - - def rotate( - self, - angle: float, - resample: Resampling = Resampling.NEAREST, - expand: int | bool = False, - center: tuple[float, float] | None = None, - translate: tuple[int, int] | None = None, - fillcolor: float | tuple[float, ...] | str | None = None, - ) -> Image: - """ - Returns a rotated copy of this image. This method returns a - copy of this image, rotated the given number of degrees counter - clockwise around its centre. - - :param angle: In degrees counter clockwise. - :param resample: An optional resampling filter. This can be - one of :py:data:`Resampling.NEAREST` (use nearest neighbour), - :py:data:`Resampling.BILINEAR` (linear interpolation in a 2x2 - environment), or :py:data:`Resampling.BICUBIC` (cubic spline - interpolation in a 4x4 environment). If omitted, or if the image has - mode "1" or "P", it is set to :py:data:`Resampling.NEAREST`. - See :ref:`concept-filters`. - :param expand: Optional expansion flag. If true, expands the output - image to make it large enough to hold the entire rotated image. - If false or omitted, make the output image the same size as the - input image. Note that the expand flag assumes rotation around - the center and no translation. - :param center: Optional center of rotation (a 2-tuple). Origin is - the upper left corner. Default is the center of the image. - :param translate: An optional post-rotate translation (a 2-tuple). - :param fillcolor: An optional color for area outside the rotated image. - :returns: An :py:class:`~PIL.Image.Image` object. - """ - - angle = angle % 360.0 - - # Fast paths regardless of filter, as long as we're not - # translating or changing the center. - if not (center or translate): - if angle == 0: - return self.copy() - if angle == 180: - return self.transpose(Transpose.ROTATE_180) - if angle in (90, 270) and (expand or self.width == self.height): - return self.transpose( - Transpose.ROTATE_90 if angle == 90 else Transpose.ROTATE_270 - ) - - # Calculate the affine matrix. Note that this is the reverse - # transformation (from destination image to source) because we - # want to interpolate the (discrete) destination pixel from - # the local area around the (floating) source pixel. - - # The matrix we actually want (note that it operates from the right): - # (1, 0, tx) (1, 0, cx) ( cos a, sin a, 0) (1, 0, -cx) - # (0, 1, ty) * (0, 1, cy) * (-sin a, cos a, 0) * (0, 1, -cy) - # (0, 0, 1) (0, 0, 1) ( 0, 0, 1) (0, 0, 1) - - # The reverse matrix is thus: - # (1, 0, cx) ( cos -a, sin -a, 0) (1, 0, -cx) (1, 0, -tx) - # (0, 1, cy) * (-sin -a, cos -a, 0) * (0, 1, -cy) * (0, 1, -ty) - # (0, 0, 1) ( 0, 0, 1) (0, 0, 1) (0, 0, 1) - - # In any case, the final translation may be updated at the end to - # compensate for the expand flag. - - w, h = self.size - - if translate is None: - post_trans = (0, 0) - else: - post_trans = translate - if center is None: - center = (w / 2, h / 2) - - angle = -math.radians(angle) - matrix = [ - round(math.cos(angle), 15), - round(math.sin(angle), 15), - 0.0, - round(-math.sin(angle), 15), - round(math.cos(angle), 15), - 0.0, - ] - - def transform(x: float, y: float, matrix: list[float]) -> tuple[float, float]: - (a, b, c, d, e, f) = matrix - return a * x + b * y + c, d * x + e * y + f - - matrix[2], matrix[5] = transform( - -center[0] - post_trans[0], -center[1] - post_trans[1], matrix - ) - matrix[2] += center[0] - matrix[5] += center[1] - - if expand: - # calculate output size - xx = [] - yy = [] - for x, y in ((0, 0), (w, 0), (w, h), (0, h)): - transformed_x, transformed_y = transform(x, y, matrix) - xx.append(transformed_x) - yy.append(transformed_y) - nw = math.ceil(max(xx)) - math.floor(min(xx)) - nh = math.ceil(max(yy)) - math.floor(min(yy)) - - # We multiply a translation matrix from the right. Because of its - # special form, this is the same as taking the image of the - # translation vector as new translation vector. - matrix[2], matrix[5] = transform(-(nw - w) / 2.0, -(nh - h) / 2.0, matrix) - w, h = nw, nh - - return self.transform( - (w, h), Transform.AFFINE, matrix, resample, fillcolor=fillcolor - ) - - def save( - self, fp: StrOrBytesPath | IO[bytes], format: str | None = None, **params: Any - ) -> None: - """ - Saves this image under the given filename. If no format is - specified, the format to use is determined from the filename - extension, if possible. - - Keyword options can be used to provide additional instructions - to the writer. If a writer doesn't recognise an option, it is - silently ignored. The available options are described in the - :doc:`image format documentation - <../handbook/image-file-formats>` for each writer. - - You can use a file object instead of a filename. In this case, - you must always specify the format. The file object must - implement the ``seek``, ``tell``, and ``write`` - methods, and be opened in binary mode. - - :param fp: A filename (string), os.PathLike object or file object. - :param format: Optional format override. If omitted, the - format to use is determined from the filename extension. - If a file object was used instead of a filename, this - parameter should always be used. - :param params: Extra parameters to the image writer. These can also be - set on the image itself through ``encoderinfo``. This is useful when - saving multiple images:: - - # Saving XMP data to a single image - from PIL import Image - red = Image.new("RGB", (1, 1), "#f00") - red.save("out.mpo", xmp=b"test") - - # Saving XMP data to the second frame of an image - from PIL import Image - black = Image.new("RGB", (1, 1)) - red = Image.new("RGB", (1, 1), "#f00") - red.encoderinfo = {"xmp": b"test"} - black.save("out.mpo", save_all=True, append_images=[red]) - :returns: None - :exception ValueError: If the output format could not be determined - from the file name. Use the format option to solve this. - :exception OSError: If the file could not be written. The file - may have been created, and may contain partial data. - """ - - filename: str | bytes = "" - open_fp = False - if is_path(fp): - filename = os.fspath(fp) - open_fp = True - elif fp == sys.stdout: - try: - fp = sys.stdout.buffer - except AttributeError: - pass - if not filename and hasattr(fp, "name") and is_path(fp.name): - # only set the name for metadata purposes - filename = os.fspath(fp.name) - - preinit() - - filename_ext = os.path.splitext(filename)[1].lower() - ext = filename_ext.decode() if isinstance(filename_ext, bytes) else filename_ext - - if not format: - if ext not in EXTENSION: - init() - try: - format = EXTENSION[ext] - except KeyError as e: - msg = f"unknown file extension: {ext}" - raise ValueError(msg) from e - - from . import ImageFile - - # may mutate self! - if isinstance(self, ImageFile.ImageFile) and os.path.abspath( - filename - ) == os.path.abspath(self.filename): - self._ensure_mutable() - else: - self.load() - - save_all = params.pop("save_all", None) - self._default_encoderinfo = params - encoderinfo = getattr(self, "encoderinfo", {}) - self._attach_default_encoderinfo(self) - self.encoderconfig: tuple[Any, ...] = () - - if format.upper() not in SAVE: - init() - if save_all or ( - save_all is None - and params.get("append_images") - and format.upper() in SAVE_ALL - ): - save_handler = SAVE_ALL[format.upper()] - else: - save_handler = SAVE[format.upper()] - - created = False - if open_fp: - created = not os.path.exists(filename) - if params.get("append", False): - # Open also for reading ("+"), because TIFF save_all - # writer needs to go back and edit the written data. - fp = builtins.open(filename, "r+b") - else: - fp = builtins.open(filename, "w+b") - else: - fp = cast(IO[bytes], fp) - - try: - save_handler(self, fp, filename) - except Exception: - if open_fp: - fp.close() - if created: - try: - os.remove(filename) - except PermissionError: - pass - raise - finally: - self.encoderinfo = encoderinfo - if open_fp: - fp.close() - - def _attach_default_encoderinfo(self, im: Image) -> dict[str, Any]: - encoderinfo = getattr(self, "encoderinfo", {}) - self.encoderinfo = {**im._default_encoderinfo, **encoderinfo} - return encoderinfo - - def seek(self, frame: int) -> None: - """ - Seeks to the given frame in this sequence file. If you seek - beyond the end of the sequence, the method raises an - ``EOFError`` exception. When a sequence file is opened, the - library automatically seeks to frame 0. - - See :py:meth:`~PIL.Image.Image.tell`. - - If defined, :attr:`~PIL.Image.Image.n_frames` refers to the - number of available frames. - - :param frame: Frame number, starting at 0. - :exception EOFError: If the call attempts to seek beyond the end - of the sequence. - """ - - # overridden by file handlers - if frame != 0: - msg = "no more images in file" - raise EOFError(msg) - - def show(self, title: str | None = None) -> None: - """ - Displays this image. This method is mainly intended for debugging purposes. - - This method calls :py:func:`PIL.ImageShow.show` internally. You can use - :py:func:`PIL.ImageShow.register` to override its default behaviour. - - The image is first saved to a temporary file. By default, it will be in - PNG format. - - On Unix, the image is then opened using the **xdg-open**, **display**, - **gm**, **eog** or **xv** utility, depending on which one can be found. - - On macOS, the image is opened with the native Preview application. - - On Windows, the image is opened with the standard PNG display utility. - - :param title: Optional title to use for the image window, where possible. - """ - - from . import ImageShow - - ImageShow.show(self, title) - - def split(self) -> tuple[Image, ...]: - """ - Split this image into individual bands. This method returns a - tuple of individual image bands from an image. For example, - splitting an "RGB" image creates three new images each - containing a copy of one of the original bands (red, green, - blue). - - If you need only one band, :py:meth:`~PIL.Image.Image.getchannel` - method can be more convenient and faster. - - :returns: A tuple containing bands. - """ - - self.load() - if self.im.bands == 1: - return (self.copy(),) - return tuple(map(self._new, self.im.split())) - - def getchannel(self, channel: int | str) -> Image: - """ - Returns an image containing a single channel of the source image. - - :param channel: What channel to return. Could be index - (0 for "R" channel of "RGB") or channel name - ("A" for alpha channel of "RGBA"). - :returns: An image in "L" mode. - - .. versionadded:: 4.3.0 - """ - self.load() - - if isinstance(channel, str): - try: - channel = self.getbands().index(channel) - except ValueError as e: - msg = f'The image has no channel "{channel}"' - raise ValueError(msg) from e - - return self._new(self.im.getband(channel)) - - def tell(self) -> int: - """ - Returns the current frame number. See :py:meth:`~PIL.Image.Image.seek`. - - If defined, :attr:`~PIL.Image.Image.n_frames` refers to the - number of available frames. - - :returns: Frame number, starting with 0. - """ - return 0 - - def thumbnail( - self, - size: tuple[float, float], - resample: Resampling = Resampling.BICUBIC, - reducing_gap: float | None = 2.0, - ) -> None: - """ - Make this image into a thumbnail. This method modifies the - image to contain a thumbnail version of itself, no larger than - the given size. This method calculates an appropriate thumbnail - size to preserve the aspect of the image, calls the - :py:meth:`~PIL.Image.Image.draft` method to configure the file reader - (where applicable), and finally resizes the image. - - Note that this function modifies the :py:class:`~PIL.Image.Image` - object in place. If you need to use the full resolution image as well, - apply this method to a :py:meth:`~PIL.Image.Image.copy` of the original - image. - - :param size: The requested size in pixels, as a 2-tuple: - (width, height). - :param resample: Optional resampling filter. This can be one - of :py:data:`Resampling.NEAREST`, :py:data:`Resampling.BOX`, - :py:data:`Resampling.BILINEAR`, :py:data:`Resampling.HAMMING`, - :py:data:`Resampling.BICUBIC` or :py:data:`Resampling.LANCZOS`. - If omitted, it defaults to :py:data:`Resampling.BICUBIC`. - (was :py:data:`Resampling.NEAREST` prior to version 2.5.0). - See: :ref:`concept-filters`. - :param reducing_gap: Apply optimization by resizing the image - in two steps. First, reducing the image by integer times - using :py:meth:`~PIL.Image.Image.reduce` or - :py:meth:`~PIL.Image.Image.draft` for JPEG images. - Second, resizing using regular resampling. The last step - changes size no less than by ``reducing_gap`` times. - ``reducing_gap`` may be None (no first step is performed) - or should be greater than 1.0. The bigger ``reducing_gap``, - the closer the result to the fair resampling. - The smaller ``reducing_gap``, the faster resizing. - With ``reducing_gap`` greater or equal to 3.0, the result is - indistinguishable from fair resampling in most cases. - The default value is 2.0 (very close to fair resampling - while still being faster in many cases). - :returns: None - """ - - provided_size = tuple(map(math.floor, size)) - - def preserve_aspect_ratio() -> tuple[int, int] | None: - def round_aspect(number: float, key: Callable[[int], float]) -> int: - return max(min(math.floor(number), math.ceil(number), key=key), 1) - - x, y = provided_size - if x >= self.width and y >= self.height: - return None - - aspect = self.width / self.height - if x / y >= aspect: - x = round_aspect(y * aspect, key=lambda n: abs(aspect - n / y)) - else: - y = round_aspect( - x / aspect, key=lambda n: 0 if n == 0 else abs(aspect - x / n) - ) - return x, y - - preserved_size = preserve_aspect_ratio() - if preserved_size is None: - return - final_size = preserved_size - - box = None - if reducing_gap is not None: - res = self.draft( - None, (int(size[0] * reducing_gap), int(size[1] * reducing_gap)) - ) - if res is not None: - box = res[1] - - if self.size != final_size: - im = self.resize(final_size, resample, box=box, reducing_gap=reducing_gap) - - self.im = im.im - self._size = final_size - self._mode = self.im.mode - - self.readonly = 0 - - # FIXME: the different transform methods need further explanation - # instead of bloating the method docs, add a separate chapter. - def transform( - self, - size: tuple[int, int], - method: Transform | ImageTransformHandler | SupportsGetData, - data: Sequence[Any] | None = None, - resample: int = Resampling.NEAREST, - fill: int = 1, - fillcolor: float | tuple[float, ...] | str | None = None, - ) -> Image: - """ - Transforms this image. This method creates a new image with the - given size, and the same mode as the original, and copies data - to the new image using the given transform. - - :param size: The output size in pixels, as a 2-tuple: - (width, height). - :param method: The transformation method. This is one of - :py:data:`Transform.EXTENT` (cut out a rectangular subregion), - :py:data:`Transform.AFFINE` (affine transform), - :py:data:`Transform.PERSPECTIVE` (perspective transform), - :py:data:`Transform.QUAD` (map a quadrilateral to a rectangle), or - :py:data:`Transform.MESH` (map a number of source quadrilaterals - in one operation). - - It may also be an :py:class:`~PIL.Image.ImageTransformHandler` - object:: - - class Example(Image.ImageTransformHandler): - def transform(self, size, data, resample, fill=1): - # Return result - - Implementations of :py:class:`~PIL.Image.ImageTransformHandler` - for some of the :py:class:`Transform` methods are provided - in :py:mod:`~PIL.ImageTransform`. - - It may also be an object with a ``method.getdata`` method - that returns a tuple supplying new ``method`` and ``data`` values:: - - class Example: - def getdata(self): - method = Image.Transform.EXTENT - data = (0, 0, 100, 100) - return method, data - :param data: Extra data to the transformation method. - :param resample: Optional resampling filter. It can be one of - :py:data:`Resampling.NEAREST` (use nearest neighbour), - :py:data:`Resampling.BILINEAR` (linear interpolation in a 2x2 - environment), or :py:data:`Resampling.BICUBIC` (cubic spline - interpolation in a 4x4 environment). If omitted, or if the image - has mode "1" or "P", it is set to :py:data:`Resampling.NEAREST`. - See: :ref:`concept-filters`. - :param fill: If ``method`` is an - :py:class:`~PIL.Image.ImageTransformHandler` object, this is one of - the arguments passed to it. Otherwise, it is unused. - :param fillcolor: Optional fill color for the area outside the - transform in the output image. - :returns: An :py:class:`~PIL.Image.Image` object. - """ - - if self.mode in ("LA", "RGBA") and resample != Resampling.NEAREST: - return ( - self.convert({"LA": "La", "RGBA": "RGBa"}[self.mode]) - .transform(size, method, data, resample, fill, fillcolor) - .convert(self.mode) - ) - - if isinstance(method, ImageTransformHandler): - return method.transform(size, self, resample=resample, fill=fill) - - if hasattr(method, "getdata"): - # compatibility w. old-style transform objects - method, data = method.getdata() - - if data is None: - msg = "missing method data" - raise ValueError(msg) - - im = new(self.mode, size, fillcolor) - if self.mode == "P" and self.palette: - im.palette = self.palette.copy() - im.info = self.info.copy() - if method == Transform.MESH: - # list of quads - for box, quad in data: - im.__transformer( - box, self, Transform.QUAD, quad, resample, fillcolor is None - ) - else: - im.__transformer( - (0, 0) + size, self, method, data, resample, fillcolor is None - ) - - return im - - def __transformer( - self, - box: tuple[int, int, int, int], - image: Image, - method: Transform, - data: Sequence[float], - resample: int = Resampling.NEAREST, - fill: bool = True, - ) -> None: - w = box[2] - box[0] - h = box[3] - box[1] - - if method == Transform.AFFINE: - data = data[:6] - - elif method == Transform.EXTENT: - # convert extent to an affine transform - x0, y0, x1, y1 = data - xs = (x1 - x0) / w - ys = (y1 - y0) / h - method = Transform.AFFINE - data = (xs, 0, x0, 0, ys, y0) - - elif method == Transform.PERSPECTIVE: - data = data[:8] - - elif method == Transform.QUAD: - # quadrilateral warp. data specifies the four corners - # given as NW, SW, SE, and NE. - nw = data[:2] - sw = data[2:4] - se = data[4:6] - ne = data[6:8] - x0, y0 = nw - As = 1.0 / w - At = 1.0 / h - data = ( - x0, - (ne[0] - x0) * As, - (sw[0] - x0) * At, - (se[0] - sw[0] - ne[0] + x0) * As * At, - y0, - (ne[1] - y0) * As, - (sw[1] - y0) * At, - (se[1] - sw[1] - ne[1] + y0) * As * At, - ) - - else: - msg = "unknown transformation method" - raise ValueError(msg) - - if resample not in ( - Resampling.NEAREST, - Resampling.BILINEAR, - Resampling.BICUBIC, - ): - if resample in (Resampling.BOX, Resampling.HAMMING, Resampling.LANCZOS): - unusable: dict[int, str] = { - Resampling.BOX: "Image.Resampling.BOX", - Resampling.HAMMING: "Image.Resampling.HAMMING", - Resampling.LANCZOS: "Image.Resampling.LANCZOS", - } - msg = unusable[resample] + f" ({resample}) cannot be used." - else: - msg = f"Unknown resampling filter ({resample})." - - filters = [ - f"{filter[1]} ({filter[0]})" - for filter in ( - (Resampling.NEAREST, "Image.Resampling.NEAREST"), - (Resampling.BILINEAR, "Image.Resampling.BILINEAR"), - (Resampling.BICUBIC, "Image.Resampling.BICUBIC"), - ) - ] - msg += f" Use {', '.join(filters[:-1])} or {filters[-1]}" - raise ValueError(msg) - - image.load() - - self.load() - - if image.mode in ("1", "P"): - resample = Resampling.NEAREST - - self.im.transform(box, image.im, method, data, resample, fill) - - def transpose(self, method: Transpose) -> Image: - """ - Transpose image (flip or rotate in 90 degree steps) - - :param method: One of :py:data:`Transpose.FLIP_LEFT_RIGHT`, - :py:data:`Transpose.FLIP_TOP_BOTTOM`, :py:data:`Transpose.ROTATE_90`, - :py:data:`Transpose.ROTATE_180`, :py:data:`Transpose.ROTATE_270`, - :py:data:`Transpose.TRANSPOSE` or :py:data:`Transpose.TRANSVERSE`. - :returns: Returns a flipped or rotated copy of this image. - """ - - self.load() - return self._new(self.im.transpose(method)) - - def effect_spread(self, distance: int) -> Image: - """ - Randomly spread pixels in an image. - - :param distance: Distance to spread pixels. - """ - self.load() - return self._new(self.im.effect_spread(distance)) - - def toqimage(self) -> ImageQt.ImageQt: - """Returns a QImage copy of this image""" - from . import ImageQt - - if not ImageQt.qt_is_installed: - msg = "Qt bindings are not installed" - raise ImportError(msg) - return ImageQt.toqimage(self) - - def toqpixmap(self) -> ImageQt.QPixmap: - """Returns a QPixmap copy of this image""" - from . import ImageQt - - if not ImageQt.qt_is_installed: - msg = "Qt bindings are not installed" - raise ImportError(msg) - return ImageQt.toqpixmap(self) - - -# -------------------------------------------------------------------- -# Abstract handlers. - - -class ImagePointHandler(abc.ABC): - """ - Used as a mixin by point transforms - (for use with :py:meth:`~PIL.Image.Image.point`) - """ - - @abc.abstractmethod - def point(self, im: Image) -> Image: - pass - - -class ImageTransformHandler(abc.ABC): - """ - Used as a mixin by geometry transforms - (for use with :py:meth:`~PIL.Image.Image.transform`) - """ - - @abc.abstractmethod - def transform( - self, - size: tuple[int, int], - image: Image, - **options: Any, - ) -> Image: - pass - - -# -------------------------------------------------------------------- -# Factories - - -def _check_size(size: Any) -> None: - """ - Common check to enforce type and sanity check on size tuples - - :param size: Should be a 2 tuple of (width, height) - :returns: None, or raises a ValueError - """ - - if not isinstance(size, (list, tuple)): - msg = "Size must be a list or tuple" - raise ValueError(msg) - if len(size) != 2: - msg = "Size must be a sequence of length 2" - raise ValueError(msg) - if size[0] < 0 or size[1] < 0: - msg = "Width and height must be >= 0" - raise ValueError(msg) - - -def new( - mode: str, - size: tuple[int, int] | list[int], - color: float | tuple[float, ...] | str | None = 0, -) -> Image: - """ - Creates a new image with the given mode and size. - - :param mode: The mode to use for the new image. See: - :ref:`concept-modes`. - :param size: A 2-tuple, containing (width, height) in pixels. - :param color: What color to use for the image. Default is black. If given, - this should be a single integer or floating point value for single-band - modes, and a tuple for multi-band modes (one value per band). When - creating RGB or HSV images, you can also use color strings as supported - by the ImageColor module. See :ref:`colors` for more information. If the - color is None, the image is not initialised. - :returns: An :py:class:`~PIL.Image.Image` object. - """ - - _check_size(size) - - if color is None: - # don't initialize - return Image()._new(core.new(mode, size)) - - if isinstance(color, str): - # css3-style specifier - - from . import ImageColor - - color = ImageColor.getcolor(color, mode) - - im = Image() - if ( - mode == "P" - and isinstance(color, (list, tuple)) - and all(isinstance(i, int) for i in color) - ): - color_ints: tuple[int, ...] = cast(tuple[int, ...], tuple(color)) - if len(color_ints) == 3 or len(color_ints) == 4: - # RGB or RGBA value for a P image - from . import ImagePalette - - im.palette = ImagePalette.ImagePalette() - color = im.palette.getcolor(color_ints) - return im._new(core.fill(mode, size, color)) - - -def frombytes( - mode: str, - size: tuple[int, int], - data: bytes | bytearray | SupportsArrayInterface, - decoder_name: str = "raw", - *args: Any, -) -> Image: - """ - Creates a copy of an image memory from pixel data in a buffer. - - In its simplest form, this function takes three arguments - (mode, size, and unpacked pixel data). - - You can also use any pixel decoder supported by PIL. For more - information on available decoders, see the section - :ref:`Writing Your Own File Codec `. - - Note that this function decodes pixel data only, not entire images. - If you have an entire image in a string, wrap it in a - :py:class:`~io.BytesIO` object, and use :py:func:`~PIL.Image.open` to load - it. - - :param mode: The image mode. See: :ref:`concept-modes`. - :param size: The image size. - :param data: A byte buffer containing raw data for the given mode. - :param decoder_name: What decoder to use. - :param args: Additional parameters for the given decoder. - :returns: An :py:class:`~PIL.Image.Image` object. - """ - - _check_size(size) - - im = new(mode, size) - if im.width != 0 and im.height != 0: - decoder_args: Any = args - if len(decoder_args) == 1 and isinstance(decoder_args[0], tuple): - # may pass tuple instead of argument list - decoder_args = decoder_args[0] - - if decoder_name == "raw" and decoder_args == (): - decoder_args = mode - - im.frombytes(data, decoder_name, decoder_args) - return im - - -def frombuffer( - mode: str, - size: tuple[int, int], - data: bytes | SupportsArrayInterface, - decoder_name: str = "raw", - *args: Any, -) -> Image: - """ - Creates an image memory referencing pixel data in a byte buffer. - - This function is similar to :py:func:`~PIL.Image.frombytes`, but uses data - in the byte buffer, where possible. This means that changes to the - original buffer object are reflected in this image). Not all modes can - share memory; supported modes include "L", "RGBX", "RGBA", and "CMYK". - - Note that this function decodes pixel data only, not entire images. - If you have an entire image file in a string, wrap it in a - :py:class:`~io.BytesIO` object, and use :py:func:`~PIL.Image.open` to load it. - - The default parameters used for the "raw" decoder differs from that used for - :py:func:`~PIL.Image.frombytes`. This is a bug, and will probably be fixed in a - future release. The current release issues a warning if you do this; to disable - the warning, you should provide the full set of parameters. See below for details. - - :param mode: The image mode. See: :ref:`concept-modes`. - :param size: The image size. - :param data: A bytes or other buffer object containing raw - data for the given mode. - :param decoder_name: What decoder to use. - :param args: Additional parameters for the given decoder. For the - default encoder ("raw"), it's recommended that you provide the - full set of parameters:: - - frombuffer(mode, size, data, "raw", mode, 0, 1) - - :returns: An :py:class:`~PIL.Image.Image` object. - - .. versionadded:: 1.1.4 - """ - - _check_size(size) - - # may pass tuple instead of argument list - if len(args) == 1 and isinstance(args[0], tuple): - args = args[0] - - if decoder_name == "raw": - if args == (): - args = mode, 0, 1 - if args[0] in _MAPMODES: - im = new(mode, (0, 0)) - im = im._new(core.map_buffer(data, size, decoder_name, 0, args)) - if mode == "P": - from . import ImagePalette - - im.palette = ImagePalette.ImagePalette("RGB", im.im.getpalette("RGB")) - im.readonly = 1 - return im - - return frombytes(mode, size, data, decoder_name, args) - - -class SupportsArrayInterface(Protocol): - """ - An object that has an ``__array_interface__`` dictionary. - """ - - @property - def __array_interface__(self) -> dict[str, Any]: - raise NotImplementedError() - - -class SupportsArrowArrayInterface(Protocol): - """ - An object that has an ``__arrow_c_array__`` method corresponding to the arrow c - data interface. - """ - - def __arrow_c_array__( - self, requested_schema: "PyCapsule" = None # type: ignore[name-defined] # noqa: F821, UP037 - ) -> tuple["PyCapsule", "PyCapsule"]: # type: ignore[name-defined] # noqa: F821, UP037 - raise NotImplementedError() - - -def fromarray(obj: SupportsArrayInterface, mode: str | None = None) -> Image: - """ - Creates an image memory from an object exporting the array interface - (using the buffer protocol):: - - from PIL import Image - import numpy as np - a = np.zeros((5, 5)) - im = Image.fromarray(a) - - If ``obj`` is not contiguous, then the ``tobytes`` method is called - and :py:func:`~PIL.Image.frombuffer` is used. - - In the case of NumPy, be aware that Pillow modes do not always correspond - to NumPy dtypes. Pillow modes only offer 1-bit pixels, 8-bit pixels, - 32-bit signed integer pixels, and 32-bit floating point pixels. - - Pillow images can also be converted to arrays:: - - from PIL import Image - import numpy as np - im = Image.open("hopper.jpg") - a = np.asarray(im) - - When converting Pillow images to arrays however, only pixel values are - transferred. This means that P and PA mode images will lose their palette. - - :param obj: Object with array interface - :param mode: Optional mode to use when reading ``obj``. Since pixel values do not - contain information about palettes or color spaces, this can be used to place - grayscale L mode data within a P mode image, or read RGB data as YCbCr for - example. - - See: :ref:`concept-modes` for general information about modes. - :returns: An image object. - - .. versionadded:: 1.1.6 - """ - arr = obj.__array_interface__ - shape = arr["shape"] - ndim = len(shape) - strides = arr.get("strides", None) - try: - typekey = (1, 1) + shape[2:], arr["typestr"] - except KeyError as e: - if mode is not None: - typekey = None - color_modes: list[str] = [] - else: - msg = "Cannot handle this data type" - raise TypeError(msg) from e - if typekey is not None: - try: - typemode, rawmode, color_modes = _fromarray_typemap[typekey] - except KeyError as e: - typekey_shape, typestr = typekey - msg = f"Cannot handle this data type: {typekey_shape}, {typestr}" - raise TypeError(msg) from e - if mode is not None: - if mode != typemode and mode not in color_modes: - deprecate("'mode' parameter for changing data types", 13) - rawmode = mode - else: - mode = typemode - if mode in ["1", "L", "I", "P", "F"]: - ndmax = 2 - elif mode == "RGB": - ndmax = 3 - else: - ndmax = 4 - if ndim > ndmax: - msg = f"Too many dimensions: {ndim} > {ndmax}." - raise ValueError(msg) - - size = 1 if ndim == 1 else shape[1], shape[0] - if strides is not None: - if hasattr(obj, "tobytes"): - obj = obj.tobytes() - elif hasattr(obj, "tostring"): - obj = obj.tostring() - else: - msg = "'strides' requires either tobytes() or tostring()" - raise ValueError(msg) - - return frombuffer(mode, size, obj, "raw", rawmode, 0, 1) - - -def fromarrow( - obj: SupportsArrowArrayInterface, mode: str, size: tuple[int, int] -) -> Image: - """Creates an image with zero-copy shared memory from an object exporting - the arrow_c_array interface protocol:: - - from PIL import Image - import pyarrow as pa - arr = pa.array([0]*(5*5*4), type=pa.uint8()) - im = Image.fromarrow(arr, 'RGBA', (5, 5)) - - If the data representation of the ``obj`` is not compatible with - Pillow internal storage, a ValueError is raised. - - Pillow images can also be converted to Arrow objects:: - - from PIL import Image - import pyarrow as pa - im = Image.open('hopper.jpg') - arr = pa.array(im) - - As with array support, when converting Pillow images to arrays, - only pixel values are transferred. This means that P and PA mode - images will lose their palette. - - :param obj: Object with an arrow_c_array interface - :param mode: Image mode. - :param size: Image size. This must match the storage of the arrow object. - :returns: An Image object - - Note that according to the Arrow spec, both the producer and the - consumer should consider the exported array to be immutable, as - unsynchronized updates will potentially cause inconsistent data. - - See: :ref:`arrow-support` for more detailed information - - .. versionadded:: 11.2.1 - - """ - if not hasattr(obj, "__arrow_c_array__"): - msg = "arrow_c_array interface not found" - raise ValueError(msg) - - (schema_capsule, array_capsule) = obj.__arrow_c_array__() - _im = core.new_arrow(mode, size, schema_capsule, array_capsule) - if _im: - return Image()._new(_im) - - msg = "new_arrow returned None without an exception" - raise ValueError(msg) - - -def fromqimage(im: ImageQt.QImage) -> ImageFile.ImageFile: - """Creates an image instance from a QImage image""" - from . import ImageQt - - if not ImageQt.qt_is_installed: - msg = "Qt bindings are not installed" - raise ImportError(msg) - return ImageQt.fromqimage(im) - - -def fromqpixmap(im: ImageQt.QPixmap) -> ImageFile.ImageFile: - """Creates an image instance from a QPixmap image""" - from . import ImageQt - - if not ImageQt.qt_is_installed: - msg = "Qt bindings are not installed" - raise ImportError(msg) - return ImageQt.fromqpixmap(im) - - -_fromarray_typemap = { - # (shape, typestr) => mode, rawmode, color modes - # first two members of shape are set to one - ((1, 1), "|b1"): ("1", "1;8", []), - ((1, 1), "|u1"): ("L", "L", ["P"]), - ((1, 1), "|i1"): ("I", "I;8", []), - ((1, 1), "u2"): ("I", "I;16B", []), - ((1, 1), "i2"): ("I", "I;16BS", []), - ((1, 1), "u4"): ("I", "I;32B", []), - ((1, 1), "i4"): ("I", "I;32BS", []), - ((1, 1), "f4"): ("F", "F;32BF", []), - ((1, 1), "f8"): ("F", "F;64BF", []), - ((1, 1, 2), "|u1"): ("LA", "LA", ["La", "PA"]), - ((1, 1, 3), "|u1"): ("RGB", "RGB", ["YCbCr", "LAB", "HSV"]), - ((1, 1, 4), "|u1"): ("RGBA", "RGBA", ["RGBa", "RGBX", "CMYK"]), - # shortcuts: - ((1, 1), f"{_ENDIAN}i4"): ("I", "I", []), - ((1, 1), f"{_ENDIAN}f4"): ("F", "F", []), -} - - -def _decompression_bomb_check(size: tuple[int, int]) -> None: - if MAX_IMAGE_PIXELS is None: - return - - pixels = max(1, size[0]) * max(1, size[1]) - - if pixels > 2 * MAX_IMAGE_PIXELS: - msg = ( - f"Image size ({pixels} pixels) exceeds limit of {2 * MAX_IMAGE_PIXELS} " - "pixels, could be decompression bomb DOS attack." - ) - raise DecompressionBombError(msg) - - if pixels > MAX_IMAGE_PIXELS: - warnings.warn( - f"Image size ({pixels} pixels) exceeds limit of {MAX_IMAGE_PIXELS} pixels, " - "could be decompression bomb DOS attack.", - DecompressionBombWarning, - ) - - -def open( - fp: StrOrBytesPath | IO[bytes], - mode: Literal["r"] = "r", - formats: list[str] | tuple[str, ...] | None = None, -) -> ImageFile.ImageFile: - """ - Opens and identifies the given image file. - - This is a lazy operation; this function identifies the file, but - the file remains open and the actual image data is not read from - the file until you try to process the data (or call the - :py:meth:`~PIL.Image.Image.load` method). See - :py:func:`~PIL.Image.new`. See :ref:`file-handling`. - - :param fp: A filename (string), os.PathLike object or a file object. - The file object must implement ``file.read``, - ``file.seek``, and ``file.tell`` methods, - and be opened in binary mode. The file object will also seek to zero - before reading. - :param mode: The mode. If given, this argument must be "r". - :param formats: A list or tuple of formats to attempt to load the file in. - This can be used to restrict the set of formats checked. - Pass ``None`` to try all supported formats. You can print the set of - available formats by running ``python3 -m PIL`` or using - the :py:func:`PIL.features.pilinfo` function. - :returns: An :py:class:`~PIL.Image.Image` object. - :exception FileNotFoundError: If the file cannot be found. - :exception PIL.UnidentifiedImageError: If the image cannot be opened and - identified. - :exception ValueError: If the ``mode`` is not "r", or if a ``StringIO`` - instance is used for ``fp``. - :exception TypeError: If ``formats`` is not ``None``, a list or a tuple. - """ - - if mode != "r": - msg = f"bad mode {repr(mode)}" # type: ignore[unreachable] - raise ValueError(msg) - elif isinstance(fp, io.StringIO): - msg = ( # type: ignore[unreachable] - "StringIO cannot be used to open an image. " - "Binary data must be used instead." - ) - raise ValueError(msg) - - if formats is None: - formats = ID - elif not isinstance(formats, (list, tuple)): - msg = "formats must be a list or tuple" # type: ignore[unreachable] - raise TypeError(msg) - - exclusive_fp = False - filename: str | bytes = "" - if is_path(fp): - filename = os.fspath(fp) - fp = builtins.open(filename, "rb") - exclusive_fp = True - else: - fp = cast(IO[bytes], fp) - - try: - fp.seek(0) - except (AttributeError, io.UnsupportedOperation): - fp = io.BytesIO(fp.read()) - exclusive_fp = True - - prefix = fp.read(16) - - preinit() - - warning_messages: list[str] = [] - - def _open_core( - fp: IO[bytes], - filename: str | bytes, - prefix: bytes, - formats: list[str] | tuple[str, ...], - ) -> ImageFile.ImageFile | None: - for i in formats: - i = i.upper() - if i not in OPEN: - init() - try: - factory, accept = OPEN[i] - result = not accept or accept(prefix) - if isinstance(result, str): - warning_messages.append(result) - elif result: - fp.seek(0) - im = factory(fp, filename) - _decompression_bomb_check(im.size) - return im - except (SyntaxError, IndexError, TypeError, struct.error) as e: - if WARN_POSSIBLE_FORMATS: - warning_messages.append(i + " opening failed. " + str(e)) - except BaseException: - if exclusive_fp: - fp.close() - raise - return None - - im = _open_core(fp, filename, prefix, formats) - - if im is None and formats is ID: - checked_formats = ID.copy() - if init(): - im = _open_core( - fp, - filename, - prefix, - tuple(format for format in formats if format not in checked_formats), - ) - - if im: - im._exclusive_fp = exclusive_fp - return im - - if exclusive_fp: - fp.close() - for message in warning_messages: - warnings.warn(message) - msg = "cannot identify image file %r" % (filename if filename else fp) - raise UnidentifiedImageError(msg) - - -# -# Image processing. - - -def alpha_composite(im1: Image, im2: Image) -> Image: - """ - Alpha composite im2 over im1. - - :param im1: The first image. Must have mode RGBA or LA. - :param im2: The second image. Must have the same mode and size as the first image. - :returns: An :py:class:`~PIL.Image.Image` object. - """ - - im1.load() - im2.load() - return im1._new(core.alpha_composite(im1.im, im2.im)) - - -def blend(im1: Image, im2: Image, alpha: float) -> Image: - """ - Creates a new image by interpolating between two input images, using - a constant alpha:: - - out = image1 * (1.0 - alpha) + image2 * alpha - - :param im1: The first image. - :param im2: The second image. Must have the same mode and size as - the first image. - :param alpha: The interpolation alpha factor. If alpha is 0.0, a - copy of the first image is returned. If alpha is 1.0, a copy of - the second image is returned. There are no restrictions on the - alpha value. If necessary, the result is clipped to fit into - the allowed output range. - :returns: An :py:class:`~PIL.Image.Image` object. - """ - - im1.load() - im2.load() - return im1._new(core.blend(im1.im, im2.im, alpha)) - - -def composite(image1: Image, image2: Image, mask: Image) -> Image: - """ - Create composite image by blending images using a transparency mask. - - :param image1: The first image. - :param image2: The second image. Must have the same mode and - size as the first image. - :param mask: A mask image. This image can have mode - "1", "L", or "RGBA", and must have the same size as the - other two images. - """ - - image = image2.copy() - image.paste(image1, None, mask) - return image - - -def eval(image: Image, *args: Callable[[int], float]) -> Image: - """ - Applies the function (which should take one argument) to each pixel - in the given image. If the image has more than one band, the same - function is applied to each band. Note that the function is - evaluated once for each possible pixel value, so you cannot use - random components or other generators. - - :param image: The input image. - :param function: A function object, taking one integer argument. - :returns: An :py:class:`~PIL.Image.Image` object. - """ - - return image.point(args[0]) - - -def merge(mode: str, bands: Sequence[Image]) -> Image: - """ - Merge a set of single band images into a new multiband image. - - :param mode: The mode to use for the output image. See: - :ref:`concept-modes`. - :param bands: A sequence containing one single-band image for - each band in the output image. All bands must have the - same size. - :returns: An :py:class:`~PIL.Image.Image` object. - """ - - if getmodebands(mode) != len(bands) or "*" in mode: - msg = "wrong number of bands" - raise ValueError(msg) - for band in bands[1:]: - if band.mode != getmodetype(mode): - msg = "mode mismatch" - raise ValueError(msg) - if band.size != bands[0].size: - msg = "size mismatch" - raise ValueError(msg) - for band in bands: - band.load() - return bands[0]._new(core.merge(mode, *[b.im for b in bands])) - - -# -------------------------------------------------------------------- -# Plugin registry - - -def register_open( - id: str, - factory: ( - Callable[[IO[bytes], str | bytes], ImageFile.ImageFile] - | type[ImageFile.ImageFile] - ), - accept: Callable[[bytes], bool | str] | None = None, -) -> None: - """ - Register an image file plugin. This function should not be used - in application code. - - :param id: An image format identifier. - :param factory: An image file factory method. - :param accept: An optional function that can be used to quickly - reject images having another format. - """ - id = id.upper() - if id not in ID: - ID.append(id) - OPEN[id] = factory, accept - - -def register_mime(id: str, mimetype: str) -> None: - """ - Registers an image MIME type by populating ``Image.MIME``. This function - should not be used in application code. - - ``Image.MIME`` provides a mapping from image format identifiers to mime - formats, but :py:meth:`~PIL.ImageFile.ImageFile.get_format_mimetype` can - provide a different result for specific images. - - :param id: An image format identifier. - :param mimetype: The image MIME type for this format. - """ - MIME[id.upper()] = mimetype - - -def register_save( - id: str, driver: Callable[[Image, IO[bytes], str | bytes], None] -) -> None: - """ - Registers an image save function. This function should not be - used in application code. - - :param id: An image format identifier. - :param driver: A function to save images in this format. - """ - SAVE[id.upper()] = driver - - -def register_save_all( - id: str, driver: Callable[[Image, IO[bytes], str | bytes], None] -) -> None: - """ - Registers an image function to save all the frames - of a multiframe format. This function should not be - used in application code. - - :param id: An image format identifier. - :param driver: A function to save images in this format. - """ - SAVE_ALL[id.upper()] = driver - - -def register_extension(id: str, extension: str) -> None: - """ - Registers an image extension. This function should not be - used in application code. - - :param id: An image format identifier. - :param extension: An extension used for this format. - """ - EXTENSION[extension.lower()] = id.upper() - - -def register_extensions(id: str, extensions: list[str]) -> None: - """ - Registers image extensions. This function should not be - used in application code. - - :param id: An image format identifier. - :param extensions: A list of extensions used for this format. - """ - for extension in extensions: - register_extension(id, extension) - - -def registered_extensions() -> dict[str, str]: - """ - Returns a dictionary containing all file extensions belonging - to registered plugins - """ - init() - return EXTENSION - - -def register_decoder(name: str, decoder: type[ImageFile.PyDecoder]) -> None: - """ - Registers an image decoder. This function should not be - used in application code. - - :param name: The name of the decoder - :param decoder: An ImageFile.PyDecoder object - - .. versionadded:: 4.1.0 - """ - DECODERS[name] = decoder - - -def register_encoder(name: str, encoder: type[ImageFile.PyEncoder]) -> None: - """ - Registers an image encoder. This function should not be - used in application code. - - :param name: The name of the encoder - :param encoder: An ImageFile.PyEncoder object - - .. versionadded:: 4.1.0 - """ - ENCODERS[name] = encoder - - -# -------------------------------------------------------------------- -# Simple display support. - - -def _show(image: Image, **options: Any) -> None: - from . import ImageShow - - deprecate("Image._show", 13, "ImageShow.show") - ImageShow.show(image, **options) - - -# -------------------------------------------------------------------- -# Effects - - -def effect_mandelbrot( - size: tuple[int, int], extent: tuple[float, float, float, float], quality: int -) -> Image: - """ - Generate a Mandelbrot set covering the given extent. - - :param size: The requested size in pixels, as a 2-tuple: - (width, height). - :param extent: The extent to cover, as a 4-tuple: - (x0, y0, x1, y1). - :param quality: Quality. - """ - return Image()._new(core.effect_mandelbrot(size, extent, quality)) - - -def effect_noise(size: tuple[int, int], sigma: float) -> Image: - """ - Generate Gaussian noise centered around 128. - - :param size: The requested size in pixels, as a 2-tuple: - (width, height). - :param sigma: Standard deviation of noise. - """ - return Image()._new(core.effect_noise(size, sigma)) - - -def linear_gradient(mode: str) -> Image: - """ - Generate 256x256 linear gradient from black to white, top to bottom. - - :param mode: Input mode. - """ - return Image()._new(core.linear_gradient(mode)) - - -def radial_gradient(mode: str) -> Image: - """ - Generate 256x256 radial gradient from black to white, centre to edge. - - :param mode: Input mode. - """ - return Image()._new(core.radial_gradient(mode)) - - -# -------------------------------------------------------------------- -# Resources - - -def _apply_env_variables(env: dict[str, str] | None = None) -> None: - env_dict = env if env is not None else os.environ - - for var_name, setter in [ - ("PILLOW_ALIGNMENT", core.set_alignment), - ("PILLOW_BLOCK_SIZE", core.set_block_size), - ("PILLOW_BLOCKS_MAX", core.set_blocks_max), - ]: - if var_name not in env_dict: - continue - - var = env_dict[var_name].lower() - - units = 1 - for postfix, mul in [("k", 1024), ("m", 1024 * 1024)]: - if var.endswith(postfix): - units = mul - var = var[: -len(postfix)] - - try: - var_int = int(var) * units - except ValueError: - warnings.warn(f"{var_name} is not int") - continue - - try: - setter(var_int) - except ValueError as e: - warnings.warn(f"{var_name}: {e}") - - -_apply_env_variables() -atexit.register(core.clear_cache) - - -if TYPE_CHECKING: - _ExifBase = MutableMapping[int, Any] -else: - _ExifBase = MutableMapping - - -class Exif(_ExifBase): - """ - This class provides read and write access to EXIF image data:: - - from PIL import Image - im = Image.open("exif.png") - exif = im.getexif() # Returns an instance of this class - - Information can be read and written, iterated over or deleted:: - - print(exif[274]) # 1 - exif[274] = 2 - for k, v in exif.items(): - print("Tag", k, "Value", v) # Tag 274 Value 2 - del exif[274] - - To access information beyond IFD0, :py:meth:`~PIL.Image.Exif.get_ifd` - returns a dictionary:: - - from PIL import ExifTags - im = Image.open("exif_gps.jpg") - exif = im.getexif() - gps_ifd = exif.get_ifd(ExifTags.IFD.GPSInfo) - print(gps_ifd) - - Other IFDs include ``ExifTags.IFD.Exif``, ``ExifTags.IFD.MakerNote``, - ``ExifTags.IFD.Interop`` and ``ExifTags.IFD.IFD1``. - - :py:mod:`~PIL.ExifTags` also has enum classes to provide names for data:: - - print(exif[ExifTags.Base.Software]) # PIL - print(gps_ifd[ExifTags.GPS.GPSDateStamp]) # 1999:99:99 99:99:99 - """ - - endian: str | None = None - bigtiff = False - _loaded = False - - def __init__(self) -> None: - self._data: dict[int, Any] = {} - self._hidden_data: dict[int, Any] = {} - self._ifds: dict[int, dict[int, Any]] = {} - self._info: TiffImagePlugin.ImageFileDirectory_v2 | None = None - self._loaded_exif: bytes | None = None - - def _fixup(self, value: Any) -> Any: - try: - if len(value) == 1 and isinstance(value, tuple): - return value[0] - except Exception: - pass - return value - - def _fixup_dict(self, src_dict: dict[int, Any]) -> dict[int, Any]: - # Helper function - # returns a dict with any single item tuples/lists as individual values - return {k: self._fixup(v) for k, v in src_dict.items()} - - def _get_ifd_dict( - self, offset: int, group: int | None = None - ) -> dict[int, Any] | None: - try: - # an offset pointer to the location of the nested embedded IFD. - # It should be a long, but may be corrupted. - self.fp.seek(offset) - except (KeyError, TypeError): - return None - else: - from . import TiffImagePlugin - - info = TiffImagePlugin.ImageFileDirectory_v2(self.head, group=group) - info.load(self.fp) - return self._fixup_dict(dict(info)) - - def _get_head(self) -> bytes: - version = b"\x2b" if self.bigtiff else b"\x2a" - if self.endian == "<": - head = b"II" + version + b"\x00" + o32le(8) - else: - head = b"MM\x00" + version + o32be(8) - if self.bigtiff: - head += o32le(8) if self.endian == "<" else o32be(8) - head += b"\x00\x00\x00\x00" - return head - - def load(self, data: bytes) -> None: - # Extract EXIF information. This is highly experimental, - # and is likely to be replaced with something better in a future - # version. - - # The EXIF record consists of a TIFF file embedded in a JPEG - # application marker (!). - if data == self._loaded_exif: - return - self._loaded_exif = data - self._data.clear() - self._hidden_data.clear() - self._ifds.clear() - while data and data.startswith(b"Exif\x00\x00"): - data = data[6:] - if not data: - self._info = None - return - - self.fp: IO[bytes] = io.BytesIO(data) - self.head = self.fp.read(8) - # process dictionary - from . import TiffImagePlugin - - self._info = TiffImagePlugin.ImageFileDirectory_v2(self.head) - self.endian = self._info._endian - self.fp.seek(self._info.next) - self._info.load(self.fp) - - def load_from_fp(self, fp: IO[bytes], offset: int | None = None) -> None: - self._loaded_exif = None - self._data.clear() - self._hidden_data.clear() - self._ifds.clear() - - # process dictionary - from . import TiffImagePlugin - - self.fp = fp - if offset is not None: - self.head = self._get_head() - else: - self.head = self.fp.read(8) - self._info = TiffImagePlugin.ImageFileDirectory_v2(self.head) - if self.endian is None: - self.endian = self._info._endian - if offset is None: - offset = self._info.next - self.fp.tell() - self.fp.seek(offset) - self._info.load(self.fp) - - def _get_merged_dict(self) -> dict[int, Any]: - merged_dict = dict(self) - - # get EXIF extension - if ExifTags.IFD.Exif in self: - ifd = self._get_ifd_dict(self[ExifTags.IFD.Exif], ExifTags.IFD.Exif) - if ifd: - merged_dict.update(ifd) - - # GPS - if ExifTags.IFD.GPSInfo in self: - merged_dict[ExifTags.IFD.GPSInfo] = self._get_ifd_dict( - self[ExifTags.IFD.GPSInfo], ExifTags.IFD.GPSInfo - ) - - return merged_dict - - def tobytes(self, offset: int = 8) -> bytes: - from . import TiffImagePlugin - - head = self._get_head() - ifd = TiffImagePlugin.ImageFileDirectory_v2(ifh=head) - for tag, ifd_dict in self._ifds.items(): - if tag not in self: - ifd[tag] = ifd_dict - for tag, value in self.items(): - if tag in [ - ExifTags.IFD.Exif, - ExifTags.IFD.GPSInfo, - ] and not isinstance(value, dict): - value = self.get_ifd(tag) - if ( - tag == ExifTags.IFD.Exif - and ExifTags.IFD.Interop in value - and not isinstance(value[ExifTags.IFD.Interop], dict) - ): - value = value.copy() - value[ExifTags.IFD.Interop] = self.get_ifd(ExifTags.IFD.Interop) - ifd[tag] = value - return b"Exif\x00\x00" + head + ifd.tobytes(offset) - - def get_ifd(self, tag: int) -> dict[int, Any]: - if tag not in self._ifds: - if tag == ExifTags.IFD.IFD1: - if self._info is not None and self._info.next != 0: - ifd = self._get_ifd_dict(self._info.next) - if ifd is not None: - self._ifds[tag] = ifd - elif tag in [ExifTags.IFD.Exif, ExifTags.IFD.GPSInfo]: - offset = self._hidden_data.get(tag, self.get(tag)) - if offset is not None: - ifd = self._get_ifd_dict(offset, tag) - if ifd is not None: - self._ifds[tag] = ifd - elif tag in [ExifTags.IFD.Interop, ExifTags.IFD.MakerNote]: - if ExifTags.IFD.Exif not in self._ifds: - self.get_ifd(ExifTags.IFD.Exif) - tag_data = self._ifds[ExifTags.IFD.Exif][tag] - if tag == ExifTags.IFD.MakerNote: - from .TiffImagePlugin import ImageFileDirectory_v2 - - if tag_data.startswith(b"FUJIFILM"): - ifd_offset = i32le(tag_data, 8) - ifd_data = tag_data[ifd_offset:] - - makernote = {} - for i in range(struct.unpack(" 4: - (offset,) = struct.unpack("H", tag_data[:2])[0]): - ifd_tag, typ, count, data = struct.unpack( - ">HHL4s", tag_data[i * 12 + 2 : (i + 1) * 12 + 2] - ) - if ifd_tag == 0x1101: - # CameraInfo - (offset,) = struct.unpack(">L", data) - self.fp.seek(offset) - - camerainfo: dict[str, int | bytes] = { - "ModelID": self.fp.read(4) - } - - self.fp.read(4) - # Seconds since 2000 - camerainfo["TimeStamp"] = i32le(self.fp.read(12)) - - self.fp.read(4) - camerainfo["InternalSerialNumber"] = self.fp.read(4) - - self.fp.read(12) - parallax = self.fp.read(4) - handler = ImageFileDirectory_v2._load_dispatch[ - TiffTags.FLOAT - ][1] - camerainfo["Parallax"] = handler( - ImageFileDirectory_v2(), parallax, False - )[0] - - self.fp.read(4) - camerainfo["Category"] = self.fp.read(2) - - makernote = {0x1101: camerainfo} - self._ifds[tag] = makernote - else: - # Interop - ifd = self._get_ifd_dict(tag_data, tag) - if ifd is not None: - self._ifds[tag] = ifd - ifd = self._ifds.setdefault(tag, {}) - if tag == ExifTags.IFD.Exif and self._hidden_data: - ifd = { - k: v - for (k, v) in ifd.items() - if k not in (ExifTags.IFD.Interop, ExifTags.IFD.MakerNote) - } - return ifd - - def hide_offsets(self) -> None: - for tag in (ExifTags.IFD.Exif, ExifTags.IFD.GPSInfo): - if tag in self: - self._hidden_data[tag] = self[tag] - del self[tag] - - def __str__(self) -> str: - if self._info is not None: - # Load all keys into self._data - for tag in self._info: - self[tag] - - return str(self._data) - - def __len__(self) -> int: - keys = set(self._data) - if self._info is not None: - keys.update(self._info) - return len(keys) - - def __getitem__(self, tag: int) -> Any: - if self._info is not None and tag not in self._data and tag in self._info: - self._data[tag] = self._fixup(self._info[tag]) - del self._info[tag] - return self._data[tag] - - def __contains__(self, tag: object) -> bool: - return tag in self._data or (self._info is not None and tag in self._info) - - def __setitem__(self, tag: int, value: Any) -> None: - if self._info is not None and tag in self._info: - del self._info[tag] - self._data[tag] = value - - def __delitem__(self, tag: int) -> None: - if self._info is not None and tag in self._info: - del self._info[tag] - else: - del self._data[tag] - if tag in self._ifds: - del self._ifds[tag] - - def __iter__(self) -> Iterator[int]: - keys = set(self._data) - if self._info is not None: - keys.update(self._info) - return iter(keys) diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/ImageChops.py b/.venv-docs/lib/python3.12/site-packages/PIL/ImageChops.py deleted file mode 100644 index 29a5c995..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/ImageChops.py +++ /dev/null @@ -1,311 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# standard channel operations -# -# History: -# 1996-03-24 fl Created -# 1996-08-13 fl Added logical operations (for "1" images) -# 2000-10-12 fl Added offset method (from Image.py) -# -# Copyright (c) 1997-2000 by Secret Labs AB -# Copyright (c) 1996-2000 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# - -from __future__ import annotations - -from . import Image - - -def constant(image: Image.Image, value: int) -> Image.Image: - """Fill a channel with a given gray level. - - :rtype: :py:class:`~PIL.Image.Image` - """ - - return Image.new("L", image.size, value) - - -def duplicate(image: Image.Image) -> Image.Image: - """Copy a channel. Alias for :py:meth:`PIL.Image.Image.copy`. - - :rtype: :py:class:`~PIL.Image.Image` - """ - - return image.copy() - - -def invert(image: Image.Image) -> Image.Image: - """ - Invert an image (channel). :: - - out = MAX - image - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image.load() - return image._new(image.im.chop_invert()) - - -def lighter(image1: Image.Image, image2: Image.Image) -> Image.Image: - """ - Compares the two images, pixel by pixel, and returns a new image containing - the lighter values. :: - - out = max(image1, image2) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_lighter(image2.im)) - - -def darker(image1: Image.Image, image2: Image.Image) -> Image.Image: - """ - Compares the two images, pixel by pixel, and returns a new image containing - the darker values. :: - - out = min(image1, image2) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_darker(image2.im)) - - -def difference(image1: Image.Image, image2: Image.Image) -> Image.Image: - """ - Returns the absolute value of the pixel-by-pixel difference between the two - images. :: - - out = abs(image1 - image2) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_difference(image2.im)) - - -def multiply(image1: Image.Image, image2: Image.Image) -> Image.Image: - """ - Superimposes two images on top of each other. - - If you multiply an image with a solid black image, the result is black. If - you multiply with a solid white image, the image is unaffected. :: - - out = image1 * image2 / MAX - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_multiply(image2.im)) - - -def screen(image1: Image.Image, image2: Image.Image) -> Image.Image: - """ - Superimposes two inverted images on top of each other. :: - - out = MAX - ((MAX - image1) * (MAX - image2) / MAX) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_screen(image2.im)) - - -def soft_light(image1: Image.Image, image2: Image.Image) -> Image.Image: - """ - Superimposes two images on top of each other using the Soft Light algorithm - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_soft_light(image2.im)) - - -def hard_light(image1: Image.Image, image2: Image.Image) -> Image.Image: - """ - Superimposes two images on top of each other using the Hard Light algorithm - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_hard_light(image2.im)) - - -def overlay(image1: Image.Image, image2: Image.Image) -> Image.Image: - """ - Superimposes two images on top of each other using the Overlay algorithm - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_overlay(image2.im)) - - -def add( - image1: Image.Image, image2: Image.Image, scale: float = 1.0, offset: float = 0 -) -> Image.Image: - """ - Adds two images, dividing the result by scale and adding the - offset. If omitted, scale defaults to 1.0, and offset to 0.0. :: - - out = ((image1 + image2) / scale + offset) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_add(image2.im, scale, offset)) - - -def subtract( - image1: Image.Image, image2: Image.Image, scale: float = 1.0, offset: float = 0 -) -> Image.Image: - """ - Subtracts two images, dividing the result by scale and adding the offset. - If omitted, scale defaults to 1.0, and offset to 0.0. :: - - out = ((image1 - image2) / scale + offset) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_subtract(image2.im, scale, offset)) - - -def add_modulo(image1: Image.Image, image2: Image.Image) -> Image.Image: - """Add two images, without clipping the result. :: - - out = ((image1 + image2) % MAX) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_add_modulo(image2.im)) - - -def subtract_modulo(image1: Image.Image, image2: Image.Image) -> Image.Image: - """Subtract two images, without clipping the result. :: - - out = ((image1 - image2) % MAX) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_subtract_modulo(image2.im)) - - -def logical_and(image1: Image.Image, image2: Image.Image) -> Image.Image: - """Logical AND between two images. - - Both of the images must have mode "1". If you would like to perform a - logical AND on an image with a mode other than "1", try - :py:meth:`~PIL.ImageChops.multiply` instead, using a black-and-white mask - as the second image. :: - - out = ((image1 and image2) % MAX) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_and(image2.im)) - - -def logical_or(image1: Image.Image, image2: Image.Image) -> Image.Image: - """Logical OR between two images. - - Both of the images must have mode "1". :: - - out = ((image1 or image2) % MAX) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_or(image2.im)) - - -def logical_xor(image1: Image.Image, image2: Image.Image) -> Image.Image: - """Logical XOR between two images. - - Both of the images must have mode "1". :: - - out = ((bool(image1) != bool(image2)) % MAX) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_xor(image2.im)) - - -def blend(image1: Image.Image, image2: Image.Image, alpha: float) -> Image.Image: - """Blend images using constant transparency weight. Alias for - :py:func:`PIL.Image.blend`. - - :rtype: :py:class:`~PIL.Image.Image` - """ - - return Image.blend(image1, image2, alpha) - - -def composite( - image1: Image.Image, image2: Image.Image, mask: Image.Image -) -> Image.Image: - """Create composite using transparency mask. Alias for - :py:func:`PIL.Image.composite`. - - :rtype: :py:class:`~PIL.Image.Image` - """ - - return Image.composite(image1, image2, mask) - - -def offset(image: Image.Image, xoffset: int, yoffset: int | None = None) -> Image.Image: - """Returns a copy of the image where data has been offset by the given - distances. Data wraps around the edges. If ``yoffset`` is omitted, it - is assumed to be equal to ``xoffset``. - - :param image: Input image. - :param xoffset: The horizontal distance. - :param yoffset: The vertical distance. If omitted, both - distances are set to the same value. - :rtype: :py:class:`~PIL.Image.Image` - """ - - if yoffset is None: - yoffset = xoffset - image.load() - return image._new(image.im.offset(xoffset, yoffset)) diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/ImageCms.py b/.venv-docs/lib/python3.12/site-packages/PIL/ImageCms.py deleted file mode 100644 index 513e28ac..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/ImageCms.py +++ /dev/null @@ -1,1076 +0,0 @@ -# The Python Imaging Library. -# $Id$ - -# Optional color management support, based on Kevin Cazabon's PyCMS -# library. - -# Originally released under LGPL. Graciously donated to PIL in -# March 2009, for distribution under the standard PIL license - -# History: - -# 2009-03-08 fl Added to PIL. - -# Copyright (C) 2002-2003 Kevin Cazabon -# Copyright (c) 2009 by Fredrik Lundh -# Copyright (c) 2013 by Eric Soroos - -# See the README file for information on usage and redistribution. See -# below for the original description. -from __future__ import annotations - -import operator -import sys -from enum import IntEnum, IntFlag -from functools import reduce -from typing import Any, Literal, SupportsFloat, SupportsInt, Union - -from . import Image -from ._deprecate import deprecate -from ._typing import SupportsRead - -try: - from . import _imagingcms as core - - _CmsProfileCompatible = Union[ - str, SupportsRead[bytes], core.CmsProfile, "ImageCmsProfile" - ] -except ImportError as ex: - # Allow error import for doc purposes, but error out when accessing - # anything in core. - from ._util import DeferredError - - core = DeferredError.new(ex) - -_DESCRIPTION = """ -pyCMS - - a Python / PIL interface to the littleCMS ICC Color Management System - Copyright (C) 2002-2003 Kevin Cazabon - kevin@cazabon.com - https://www.cazabon.com - - pyCMS home page: https://www.cazabon.com/pyCMS - littleCMS home page: https://www.littlecms.com - (littleCMS is Copyright (C) 1998-2001 Marti Maria) - - Originally released under LGPL. Graciously donated to PIL in - March 2009, for distribution under the standard PIL license - - The pyCMS.py module provides a "clean" interface between Python/PIL and - pyCMSdll, taking care of some of the more complex handling of the direct - pyCMSdll functions, as well as error-checking and making sure that all - relevant data is kept together. - - While it is possible to call pyCMSdll functions directly, it's not highly - recommended. - - Version History: - - 1.0.0 pil Oct 2013 Port to LCMS 2. - - 0.1.0 pil mod March 10, 2009 - - Renamed display profile to proof profile. The proof - profile is the profile of the device that is being - simulated, not the profile of the device which is - actually used to display/print the final simulation - (that'd be the output profile) - also see LCMSAPI.txt - input colorspace -> using 'renderingIntent' -> proof - colorspace -> using 'proofRenderingIntent' -> output - colorspace - - Added LCMS FLAGS support. - Added FLAGS["SOFTPROOFING"] as default flag for - buildProofTransform (otherwise the proof profile/intent - would be ignored). - - 0.1.0 pil March 2009 - added to PIL, as PIL.ImageCms - - 0.0.2 alpha Jan 6, 2002 - - Added try/except statements around type() checks of - potential CObjects... Python won't let you use type() - on them, and raises a TypeError (stupid, if you ask - me!) - - Added buildProofTransformFromOpenProfiles() function. - Additional fixes in DLL, see DLL code for details. - - 0.0.1 alpha first public release, Dec. 26, 2002 - - Known to-do list with current version (of Python interface, not pyCMSdll): - - none - -""" - -_VERSION = "1.0.0 pil" - - -# --------------------------------------------------------------------. - - -# -# intent/direction values - - -class Intent(IntEnum): - PERCEPTUAL = 0 - RELATIVE_COLORIMETRIC = 1 - SATURATION = 2 - ABSOLUTE_COLORIMETRIC = 3 - - -class Direction(IntEnum): - INPUT = 0 - OUTPUT = 1 - PROOF = 2 - - -# -# flags - - -class Flags(IntFlag): - """Flags and documentation are taken from ``lcms2.h``.""" - - NONE = 0 - NOCACHE = 0x0040 - """Inhibit 1-pixel cache""" - NOOPTIMIZE = 0x0100 - """Inhibit optimizations""" - NULLTRANSFORM = 0x0200 - """Don't transform anyway""" - GAMUTCHECK = 0x1000 - """Out of Gamut alarm""" - SOFTPROOFING = 0x4000 - """Do softproofing""" - BLACKPOINTCOMPENSATION = 0x2000 - NOWHITEONWHITEFIXUP = 0x0004 - """Don't fix scum dot""" - HIGHRESPRECALC = 0x0400 - """Use more memory to give better accuracy""" - LOWRESPRECALC = 0x0800 - """Use less memory to minimize resources""" - # this should be 8BITS_DEVICELINK, but that is not a valid name in Python: - USE_8BITS_DEVICELINK = 0x0008 - """Create 8 bits devicelinks""" - GUESSDEVICECLASS = 0x0020 - """Guess device class (for ``transform2devicelink``)""" - KEEP_SEQUENCE = 0x0080 - """Keep profile sequence for devicelink creation""" - FORCE_CLUT = 0x0002 - """Force CLUT optimization""" - CLUT_POST_LINEARIZATION = 0x0001 - """create postlinearization tables if possible""" - CLUT_PRE_LINEARIZATION = 0x0010 - """create prelinearization tables if possible""" - NONEGATIVES = 0x8000 - """Prevent negative numbers in floating point transforms""" - COPY_ALPHA = 0x04000000 - """Alpha channels are copied on ``cmsDoTransform()``""" - NODEFAULTRESOURCEDEF = 0x01000000 - - _GRIDPOINTS_1 = 1 << 16 - _GRIDPOINTS_2 = 2 << 16 - _GRIDPOINTS_4 = 4 << 16 - _GRIDPOINTS_8 = 8 << 16 - _GRIDPOINTS_16 = 16 << 16 - _GRIDPOINTS_32 = 32 << 16 - _GRIDPOINTS_64 = 64 << 16 - _GRIDPOINTS_128 = 128 << 16 - - @staticmethod - def GRIDPOINTS(n: int) -> Flags: - """ - Fine-tune control over number of gridpoints - - :param n: :py:class:`int` in range ``0 <= n <= 255`` - """ - return Flags.NONE | ((n & 0xFF) << 16) - - -_MAX_FLAG = reduce(operator.or_, Flags) - - -_FLAGS = { - "MATRIXINPUT": 1, - "MATRIXOUTPUT": 2, - "MATRIXONLY": (1 | 2), - "NOWHITEONWHITEFIXUP": 4, # Don't hot fix scum dot - # Don't create prelinearization tables on precalculated transforms - # (internal use): - "NOPRELINEARIZATION": 16, - "GUESSDEVICECLASS": 32, # Guess device class (for transform2devicelink) - "NOTCACHE": 64, # Inhibit 1-pixel cache - "NOTPRECALC": 256, - "NULLTRANSFORM": 512, # Don't transform anyway - "HIGHRESPRECALC": 1024, # Use more memory to give better accuracy - "LOWRESPRECALC": 2048, # Use less memory to minimize resources - "WHITEBLACKCOMPENSATION": 8192, - "BLACKPOINTCOMPENSATION": 8192, - "GAMUTCHECK": 4096, # Out of Gamut alarm - "SOFTPROOFING": 16384, # Do softproofing - "PRESERVEBLACK": 32768, # Black preservation - "NODEFAULTRESOURCEDEF": 16777216, # CRD special - "GRIDPOINTS": lambda n: (n & 0xFF) << 16, # Gridpoints -} - - -# --------------------------------------------------------------------. -# Experimental PIL-level API -# --------------------------------------------------------------------. - -## -# Profile. - - -class ImageCmsProfile: - def __init__(self, profile: str | SupportsRead[bytes] | core.CmsProfile) -> None: - """ - :param profile: Either a string representing a filename, - a file like object containing a profile or a - low-level profile object - - """ - self.filename: str | None = None - - if isinstance(profile, str): - if sys.platform == "win32": - profile_bytes_path = profile.encode() - try: - profile_bytes_path.decode("ascii") - except UnicodeDecodeError: - with open(profile, "rb") as f: - self.profile = core.profile_frombytes(f.read()) - return - self.filename = profile - self.profile = core.profile_open(profile) - elif hasattr(profile, "read"): - self.profile = core.profile_frombytes(profile.read()) - elif isinstance(profile, core.CmsProfile): - self.profile = profile - else: - msg = "Invalid type for Profile" # type: ignore[unreachable] - raise TypeError(msg) - - def __getattr__(self, name: str) -> Any: - if name in ("product_name", "product_info"): - deprecate(f"ImageCms.ImageCmsProfile.{name}", 13) - return None - msg = f"'{self.__class__.__name__}' object has no attribute '{name}'" - raise AttributeError(msg) - - def tobytes(self) -> bytes: - """ - Returns the profile in a format suitable for embedding in - saved images. - - :returns: a bytes object containing the ICC profile. - """ - - return core.profile_tobytes(self.profile) - - -class ImageCmsTransform(Image.ImagePointHandler): - """ - Transform. This can be used with the procedural API, or with the standard - :py:func:`~PIL.Image.Image.point` method. - - Will return the output profile in the ``output.info['icc_profile']``. - """ - - def __init__( - self, - input: ImageCmsProfile, - output: ImageCmsProfile, - input_mode: str, - output_mode: str, - intent: Intent = Intent.PERCEPTUAL, - proof: ImageCmsProfile | None = None, - proof_intent: Intent = Intent.ABSOLUTE_COLORIMETRIC, - flags: Flags = Flags.NONE, - ): - if proof is None: - self.transform = core.buildTransform( - input.profile, output.profile, input_mode, output_mode, intent, flags - ) - else: - self.transform = core.buildProofTransform( - input.profile, - output.profile, - proof.profile, - input_mode, - output_mode, - intent, - proof_intent, - flags, - ) - # Note: inputMode and outputMode are for pyCMS compatibility only - self.input_mode = self.inputMode = input_mode - self.output_mode = self.outputMode = output_mode - - self.output_profile = output - - def point(self, im: Image.Image) -> Image.Image: - return self.apply(im) - - def apply(self, im: Image.Image, imOut: Image.Image | None = None) -> Image.Image: - if imOut is None: - imOut = Image.new(self.output_mode, im.size, None) - self.transform.apply(im.getim(), imOut.getim()) - imOut.info["icc_profile"] = self.output_profile.tobytes() - return imOut - - def apply_in_place(self, im: Image.Image) -> Image.Image: - if im.mode != self.output_mode: - msg = "mode mismatch" - raise ValueError(msg) # wrong output mode - self.transform.apply(im.getim(), im.getim()) - im.info["icc_profile"] = self.output_profile.tobytes() - return im - - -def get_display_profile(handle: SupportsInt | None = None) -> ImageCmsProfile | None: - """ - (experimental) Fetches the profile for the current display device. - - :returns: ``None`` if the profile is not known. - """ - - if sys.platform != "win32": - return None - - from . import ImageWin # type: ignore[unused-ignore, unreachable] - - if isinstance(handle, ImageWin.HDC): - profile = core.get_display_profile_win32(int(handle), 1) - else: - profile = core.get_display_profile_win32(int(handle or 0)) - if profile is None: - return None - return ImageCmsProfile(profile) - - -# --------------------------------------------------------------------. -# pyCMS compatible layer -# --------------------------------------------------------------------. - - -class PyCMSError(Exception): - """(pyCMS) Exception class. - This is used for all errors in the pyCMS API.""" - - pass - - -def profileToProfile( - im: Image.Image, - inputProfile: _CmsProfileCompatible, - outputProfile: _CmsProfileCompatible, - renderingIntent: Intent = Intent.PERCEPTUAL, - outputMode: str | None = None, - inPlace: bool = False, - flags: Flags = Flags.NONE, -) -> Image.Image | None: - """ - (pyCMS) Applies an ICC transformation to a given image, mapping from - ``inputProfile`` to ``outputProfile``. - - If the input or output profiles specified are not valid filenames, a - :exc:`PyCMSError` will be raised. If ``inPlace`` is ``True`` and - ``outputMode != im.mode``, a :exc:`PyCMSError` will be raised. - If an error occurs during application of the profiles, - a :exc:`PyCMSError` will be raised. - If ``outputMode`` is not a mode supported by the ``outputProfile`` (or by pyCMS), - a :exc:`PyCMSError` will be raised. - - This function applies an ICC transformation to im from ``inputProfile``'s - color space to ``outputProfile``'s color space using the specified rendering - intent to decide how to handle out-of-gamut colors. - - ``outputMode`` can be used to specify that a color mode conversion is to - be done using these profiles, but the specified profiles must be able - to handle that mode. I.e., if converting im from RGB to CMYK using - profiles, the input profile must handle RGB data, and the output - profile must handle CMYK data. - - :param im: An open :py:class:`~PIL.Image.Image` object (i.e. Image.new(...) - or Image.open(...), etc.) - :param inputProfile: String, as a valid filename path to the ICC input - profile you wish to use for this image, or a profile object - :param outputProfile: String, as a valid filename path to the ICC output - profile you wish to use for this image, or a profile object - :param renderingIntent: Integer (0-3) specifying the rendering intent you - wish to use for the transform - - ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) - ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 - ImageCms.Intent.SATURATION = 2 - ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 - - see the pyCMS documentation for details on rendering intents and what - they do. - :param outputMode: A valid PIL mode for the output image (i.e. "RGB", - "CMYK", etc.). Note: if rendering the image "inPlace", outputMode - MUST be the same mode as the input, or omitted completely. If - omitted, the outputMode will be the same as the mode of the input - image (im.mode) - :param inPlace: Boolean. If ``True``, the original image is modified in-place, - and ``None`` is returned. If ``False`` (default), a new - :py:class:`~PIL.Image.Image` object is returned with the transform applied. - :param flags: Integer (0-...) specifying additional flags - :returns: Either None or a new :py:class:`~PIL.Image.Image` object, depending on - the value of ``inPlace`` - :exception PyCMSError: - """ - - if outputMode is None: - outputMode = im.mode - - if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3): - msg = "renderingIntent must be an integer between 0 and 3" - raise PyCMSError(msg) - - if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): - msg = f"flags must be an integer between 0 and {_MAX_FLAG}" - raise PyCMSError(msg) - - try: - if not isinstance(inputProfile, ImageCmsProfile): - inputProfile = ImageCmsProfile(inputProfile) - if not isinstance(outputProfile, ImageCmsProfile): - outputProfile = ImageCmsProfile(outputProfile) - transform = ImageCmsTransform( - inputProfile, - outputProfile, - im.mode, - outputMode, - renderingIntent, - flags=flags, - ) - if inPlace: - transform.apply_in_place(im) - imOut = None - else: - imOut = transform.apply(im) - except (OSError, TypeError, ValueError) as v: - raise PyCMSError(v) from v - - return imOut - - -def getOpenProfile( - profileFilename: str | SupportsRead[bytes] | core.CmsProfile, -) -> ImageCmsProfile: - """ - (pyCMS) Opens an ICC profile file. - - The PyCMSProfile object can be passed back into pyCMS for use in creating - transforms and such (as in ImageCms.buildTransformFromOpenProfiles()). - - If ``profileFilename`` is not a valid filename for an ICC profile, - a :exc:`PyCMSError` will be raised. - - :param profileFilename: String, as a valid filename path to the ICC profile - you wish to open, or a file-like object. - :returns: A CmsProfile class object. - :exception PyCMSError: - """ - - try: - return ImageCmsProfile(profileFilename) - except (OSError, TypeError, ValueError) as v: - raise PyCMSError(v) from v - - -def buildTransform( - inputProfile: _CmsProfileCompatible, - outputProfile: _CmsProfileCompatible, - inMode: str, - outMode: str, - renderingIntent: Intent = Intent.PERCEPTUAL, - flags: Flags = Flags.NONE, -) -> ImageCmsTransform: - """ - (pyCMS) Builds an ICC transform mapping from the ``inputProfile`` to the - ``outputProfile``. Use applyTransform to apply the transform to a given - image. - - If the input or output profiles specified are not valid filenames, a - :exc:`PyCMSError` will be raised. If an error occurs during creation - of the transform, a :exc:`PyCMSError` will be raised. - - If ``inMode`` or ``outMode`` are not a mode supported by the ``outputProfile`` - (or by pyCMS), a :exc:`PyCMSError` will be raised. - - This function builds and returns an ICC transform from the ``inputProfile`` - to the ``outputProfile`` using the ``renderingIntent`` to determine what to do - with out-of-gamut colors. It will ONLY work for converting images that - are in ``inMode`` to images that are in ``outMode`` color format (PIL mode, - i.e. "RGB", "RGBA", "CMYK", etc.). - - Building the transform is a fair part of the overhead in - ImageCms.profileToProfile(), so if you're planning on converting multiple - images using the same input/output settings, this can save you time. - Once you have a transform object, it can be used with - ImageCms.applyProfile() to convert images without the need to re-compute - the lookup table for the transform. - - The reason pyCMS returns a class object rather than a handle directly - to the transform is that it needs to keep track of the PIL input/output - modes that the transform is meant for. These attributes are stored in - the ``inMode`` and ``outMode`` attributes of the object (which can be - manually overridden if you really want to, but I don't know of any - time that would be of use, or would even work). - - :param inputProfile: String, as a valid filename path to the ICC input - profile you wish to use for this transform, or a profile object - :param outputProfile: String, as a valid filename path to the ICC output - profile you wish to use for this transform, or a profile object - :param inMode: String, as a valid PIL mode that the appropriate profile - also supports (i.e. "RGB", "RGBA", "CMYK", etc.) - :param outMode: String, as a valid PIL mode that the appropriate profile - also supports (i.e. "RGB", "RGBA", "CMYK", etc.) - :param renderingIntent: Integer (0-3) specifying the rendering intent you - wish to use for the transform - - ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) - ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 - ImageCms.Intent.SATURATION = 2 - ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 - - see the pyCMS documentation for details on rendering intents and what - they do. - :param flags: Integer (0-...) specifying additional flags - :returns: A CmsTransform class object. - :exception PyCMSError: - """ - - if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3): - msg = "renderingIntent must be an integer between 0 and 3" - raise PyCMSError(msg) - - if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): - msg = f"flags must be an integer between 0 and {_MAX_FLAG}" - raise PyCMSError(msg) - - try: - if not isinstance(inputProfile, ImageCmsProfile): - inputProfile = ImageCmsProfile(inputProfile) - if not isinstance(outputProfile, ImageCmsProfile): - outputProfile = ImageCmsProfile(outputProfile) - return ImageCmsTransform( - inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags - ) - except (OSError, TypeError, ValueError) as v: - raise PyCMSError(v) from v - - -def buildProofTransform( - inputProfile: _CmsProfileCompatible, - outputProfile: _CmsProfileCompatible, - proofProfile: _CmsProfileCompatible, - inMode: str, - outMode: str, - renderingIntent: Intent = Intent.PERCEPTUAL, - proofRenderingIntent: Intent = Intent.ABSOLUTE_COLORIMETRIC, - flags: Flags = Flags.SOFTPROOFING, -) -> ImageCmsTransform: - """ - (pyCMS) Builds an ICC transform mapping from the ``inputProfile`` to the - ``outputProfile``, but tries to simulate the result that would be - obtained on the ``proofProfile`` device. - - If the input, output, or proof profiles specified are not valid - filenames, a :exc:`PyCMSError` will be raised. - - If an error occurs during creation of the transform, - a :exc:`PyCMSError` will be raised. - - If ``inMode`` or ``outMode`` are not a mode supported by the ``outputProfile`` - (or by pyCMS), a :exc:`PyCMSError` will be raised. - - This function builds and returns an ICC transform from the ``inputProfile`` - to the ``outputProfile``, but tries to simulate the result that would be - obtained on the ``proofProfile`` device using ``renderingIntent`` and - ``proofRenderingIntent`` to determine what to do with out-of-gamut - colors. This is known as "soft-proofing". It will ONLY work for - converting images that are in ``inMode`` to images that are in outMode - color format (PIL mode, i.e. "RGB", "RGBA", "CMYK", etc.). - - Usage of the resulting transform object is exactly the same as with - ImageCms.buildTransform(). - - Proof profiling is generally used when using an output device to get a - good idea of what the final printed/displayed image would look like on - the ``proofProfile`` device when it's quicker and easier to use the - output device for judging color. Generally, this means that the - output device is a monitor, or a dye-sub printer (etc.), and the simulated - device is something more expensive, complicated, or time consuming - (making it difficult to make a real print for color judgement purposes). - - Soft-proofing basically functions by adjusting the colors on the - output device to match the colors of the device being simulated. However, - when the simulated device has a much wider gamut than the output - device, you may obtain marginal results. - - :param inputProfile: String, as a valid filename path to the ICC input - profile you wish to use for this transform, or a profile object - :param outputProfile: String, as a valid filename path to the ICC output - (monitor, usually) profile you wish to use for this transform, or a - profile object - :param proofProfile: String, as a valid filename path to the ICC proof - profile you wish to use for this transform, or a profile object - :param inMode: String, as a valid PIL mode that the appropriate profile - also supports (i.e. "RGB", "RGBA", "CMYK", etc.) - :param outMode: String, as a valid PIL mode that the appropriate profile - also supports (i.e. "RGB", "RGBA", "CMYK", etc.) - :param renderingIntent: Integer (0-3) specifying the rendering intent you - wish to use for the input->proof (simulated) transform - - ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) - ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 - ImageCms.Intent.SATURATION = 2 - ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 - - see the pyCMS documentation for details on rendering intents and what - they do. - :param proofRenderingIntent: Integer (0-3) specifying the rendering intent - you wish to use for proof->output transform - - ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) - ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 - ImageCms.Intent.SATURATION = 2 - ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 - - see the pyCMS documentation for details on rendering intents and what - they do. - :param flags: Integer (0-...) specifying additional flags - :returns: A CmsTransform class object. - :exception PyCMSError: - """ - - if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3): - msg = "renderingIntent must be an integer between 0 and 3" - raise PyCMSError(msg) - - if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): - msg = f"flags must be an integer between 0 and {_MAX_FLAG}" - raise PyCMSError(msg) - - try: - if not isinstance(inputProfile, ImageCmsProfile): - inputProfile = ImageCmsProfile(inputProfile) - if not isinstance(outputProfile, ImageCmsProfile): - outputProfile = ImageCmsProfile(outputProfile) - if not isinstance(proofProfile, ImageCmsProfile): - proofProfile = ImageCmsProfile(proofProfile) - return ImageCmsTransform( - inputProfile, - outputProfile, - inMode, - outMode, - renderingIntent, - proofProfile, - proofRenderingIntent, - flags, - ) - except (OSError, TypeError, ValueError) as v: - raise PyCMSError(v) from v - - -buildTransformFromOpenProfiles = buildTransform -buildProofTransformFromOpenProfiles = buildProofTransform - - -def applyTransform( - im: Image.Image, transform: ImageCmsTransform, inPlace: bool = False -) -> Image.Image | None: - """ - (pyCMS) Applies a transform to a given image. - - If ``im.mode != transform.input_mode``, a :exc:`PyCMSError` is raised. - - If ``inPlace`` is ``True`` and ``transform.input_mode != transform.output_mode``, a - :exc:`PyCMSError` is raised. - - If ``im.mode``, ``transform.input_mode`` or ``transform.output_mode`` is not - supported by pyCMSdll or the profiles you used for the transform, a - :exc:`PyCMSError` is raised. - - If an error occurs while the transform is being applied, - a :exc:`PyCMSError` is raised. - - This function applies a pre-calculated transform (from - ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles()) - to an image. The transform can be used for multiple images, saving - considerable calculation time if doing the same conversion multiple times. - - If you want to modify im in-place instead of receiving a new image as - the return value, set ``inPlace`` to ``True``. This can only be done if - ``transform.input_mode`` and ``transform.output_mode`` are the same, because we - can't change the mode in-place (the buffer sizes for some modes are - different). The default behavior is to return a new :py:class:`~PIL.Image.Image` - object of the same dimensions in mode ``transform.output_mode``. - - :param im: An :py:class:`~PIL.Image.Image` object, and ``im.mode`` must be the same - as the ``input_mode`` supported by the transform. - :param transform: A valid CmsTransform class object - :param inPlace: Bool. If ``True``, ``im`` is modified in place and ``None`` is - returned, if ``False``, a new :py:class:`~PIL.Image.Image` object with the - transform applied is returned (and ``im`` is not changed). The default is - ``False``. - :returns: Either ``None``, or a new :py:class:`~PIL.Image.Image` object, - depending on the value of ``inPlace``. The profile will be returned in - the image's ``info['icc_profile']``. - :exception PyCMSError: - """ - - try: - if inPlace: - transform.apply_in_place(im) - imOut = None - else: - imOut = transform.apply(im) - except (TypeError, ValueError) as v: - raise PyCMSError(v) from v - - return imOut - - -def createProfile( - colorSpace: Literal["LAB", "XYZ", "sRGB"], colorTemp: SupportsFloat = 0 -) -> core.CmsProfile: - """ - (pyCMS) Creates a profile. - - If colorSpace not in ``["LAB", "XYZ", "sRGB"]``, - a :exc:`PyCMSError` is raised. - - If using LAB and ``colorTemp`` is not a positive integer, - a :exc:`PyCMSError` is raised. - - If an error occurs while creating the profile, - a :exc:`PyCMSError` is raised. - - Use this function to create common profiles on-the-fly instead of - having to supply a profile on disk and knowing the path to it. It - returns a normal CmsProfile object that can be passed to - ImageCms.buildTransformFromOpenProfiles() to create a transform to apply - to images. - - :param colorSpace: String, the color space of the profile you wish to - create. - Currently only "LAB", "XYZ", and "sRGB" are supported. - :param colorTemp: Positive number for the white point for the profile, in - degrees Kelvin (i.e. 5000, 6500, 9600, etc.). The default is for D50 - illuminant if omitted (5000k). colorTemp is ONLY applied to LAB - profiles, and is ignored for XYZ and sRGB. - :returns: A CmsProfile class object - :exception PyCMSError: - """ - - if colorSpace not in ["LAB", "XYZ", "sRGB"]: - msg = ( - f"Color space not supported for on-the-fly profile creation ({colorSpace})" - ) - raise PyCMSError(msg) - - if colorSpace == "LAB": - try: - colorTemp = float(colorTemp) - except (TypeError, ValueError) as e: - msg = f'Color temperature must be numeric, "{colorTemp}" not valid' - raise PyCMSError(msg) from e - - try: - return core.createProfile(colorSpace, colorTemp) - except (TypeError, ValueError) as v: - raise PyCMSError(v) from v - - -def getProfileName(profile: _CmsProfileCompatible) -> str: - """ - - (pyCMS) Gets the internal product name for the given profile. - - If ``profile`` isn't a valid CmsProfile object or filename to a profile, - a :exc:`PyCMSError` is raised If an error occurs while trying - to obtain the name tag, a :exc:`PyCMSError` is raised. - - Use this function to obtain the INTERNAL name of the profile (stored - in an ICC tag in the profile itself), usually the one used when the - profile was originally created. Sometimes this tag also contains - additional information supplied by the creator. - - :param profile: EITHER a valid CmsProfile object, OR a string of the - filename of an ICC profile. - :returns: A string containing the internal name of the profile as stored - in an ICC tag. - :exception PyCMSError: - """ - - try: - # add an extra newline to preserve pyCMS compatibility - if not isinstance(profile, ImageCmsProfile): - profile = ImageCmsProfile(profile) - # do it in python, not c. - # // name was "%s - %s" (model, manufacturer) || Description , - # // but if the Model and Manufacturer were the same or the model - # // was long, Just the model, in 1.x - model = profile.profile.model - manufacturer = profile.profile.manufacturer - - if not (model or manufacturer): - return (profile.profile.profile_description or "") + "\n" - if not manufacturer or (model and len(model) > 30): - return f"{model}\n" - return f"{model} - {manufacturer}\n" - - except (AttributeError, OSError, TypeError, ValueError) as v: - raise PyCMSError(v) from v - - -def getProfileInfo(profile: _CmsProfileCompatible) -> str: - """ - (pyCMS) Gets the internal product information for the given profile. - - If ``profile`` isn't a valid CmsProfile object or filename to a profile, - a :exc:`PyCMSError` is raised. - - If an error occurs while trying to obtain the info tag, - a :exc:`PyCMSError` is raised. - - Use this function to obtain the information stored in the profile's - info tag. This often contains details about the profile, and how it - was created, as supplied by the creator. - - :param profile: EITHER a valid CmsProfile object, OR a string of the - filename of an ICC profile. - :returns: A string containing the internal profile information stored in - an ICC tag. - :exception PyCMSError: - """ - - try: - if not isinstance(profile, ImageCmsProfile): - profile = ImageCmsProfile(profile) - # add an extra newline to preserve pyCMS compatibility - # Python, not C. the white point bits weren't working well, - # so skipping. - # info was description \r\n\r\n copyright \r\n\r\n K007 tag \r\n\r\n whitepoint - description = profile.profile.profile_description - cpright = profile.profile.copyright - elements = [element for element in (description, cpright) if element] - return "\r\n\r\n".join(elements) + "\r\n\r\n" - - except (AttributeError, OSError, TypeError, ValueError) as v: - raise PyCMSError(v) from v - - -def getProfileCopyright(profile: _CmsProfileCompatible) -> str: - """ - (pyCMS) Gets the copyright for the given profile. - - If ``profile`` isn't a valid CmsProfile object or filename to a profile, a - :exc:`PyCMSError` is raised. - - If an error occurs while trying to obtain the copyright tag, - a :exc:`PyCMSError` is raised. - - Use this function to obtain the information stored in the profile's - copyright tag. - - :param profile: EITHER a valid CmsProfile object, OR a string of the - filename of an ICC profile. - :returns: A string containing the internal profile information stored in - an ICC tag. - :exception PyCMSError: - """ - try: - # add an extra newline to preserve pyCMS compatibility - if not isinstance(profile, ImageCmsProfile): - profile = ImageCmsProfile(profile) - return (profile.profile.copyright or "") + "\n" - except (AttributeError, OSError, TypeError, ValueError) as v: - raise PyCMSError(v) from v - - -def getProfileManufacturer(profile: _CmsProfileCompatible) -> str: - """ - (pyCMS) Gets the manufacturer for the given profile. - - If ``profile`` isn't a valid CmsProfile object or filename to a profile, a - :exc:`PyCMSError` is raised. - - If an error occurs while trying to obtain the manufacturer tag, a - :exc:`PyCMSError` is raised. - - Use this function to obtain the information stored in the profile's - manufacturer tag. - - :param profile: EITHER a valid CmsProfile object, OR a string of the - filename of an ICC profile. - :returns: A string containing the internal profile information stored in - an ICC tag. - :exception PyCMSError: - """ - try: - # add an extra newline to preserve pyCMS compatibility - if not isinstance(profile, ImageCmsProfile): - profile = ImageCmsProfile(profile) - return (profile.profile.manufacturer or "") + "\n" - except (AttributeError, OSError, TypeError, ValueError) as v: - raise PyCMSError(v) from v - - -def getProfileModel(profile: _CmsProfileCompatible) -> str: - """ - (pyCMS) Gets the model for the given profile. - - If ``profile`` isn't a valid CmsProfile object or filename to a profile, a - :exc:`PyCMSError` is raised. - - If an error occurs while trying to obtain the model tag, - a :exc:`PyCMSError` is raised. - - Use this function to obtain the information stored in the profile's - model tag. - - :param profile: EITHER a valid CmsProfile object, OR a string of the - filename of an ICC profile. - :returns: A string containing the internal profile information stored in - an ICC tag. - :exception PyCMSError: - """ - - try: - # add an extra newline to preserve pyCMS compatibility - if not isinstance(profile, ImageCmsProfile): - profile = ImageCmsProfile(profile) - return (profile.profile.model or "") + "\n" - except (AttributeError, OSError, TypeError, ValueError) as v: - raise PyCMSError(v) from v - - -def getProfileDescription(profile: _CmsProfileCompatible) -> str: - """ - (pyCMS) Gets the description for the given profile. - - If ``profile`` isn't a valid CmsProfile object or filename to a profile, a - :exc:`PyCMSError` is raised. - - If an error occurs while trying to obtain the description tag, - a :exc:`PyCMSError` is raised. - - Use this function to obtain the information stored in the profile's - description tag. - - :param profile: EITHER a valid CmsProfile object, OR a string of the - filename of an ICC profile. - :returns: A string containing the internal profile information stored in an - ICC tag. - :exception PyCMSError: - """ - - try: - # add an extra newline to preserve pyCMS compatibility - if not isinstance(profile, ImageCmsProfile): - profile = ImageCmsProfile(profile) - return (profile.profile.profile_description or "") + "\n" - except (AttributeError, OSError, TypeError, ValueError) as v: - raise PyCMSError(v) from v - - -def getDefaultIntent(profile: _CmsProfileCompatible) -> int: - """ - (pyCMS) Gets the default intent name for the given profile. - - If ``profile`` isn't a valid CmsProfile object or filename to a profile, a - :exc:`PyCMSError` is raised. - - If an error occurs while trying to obtain the default intent, a - :exc:`PyCMSError` is raised. - - Use this function to determine the default (and usually best optimized) - rendering intent for this profile. Most profiles support multiple - rendering intents, but are intended mostly for one type of conversion. - If you wish to use a different intent than returned, use - ImageCms.isIntentSupported() to verify it will work first. - - :param profile: EITHER a valid CmsProfile object, OR a string of the - filename of an ICC profile. - :returns: Integer 0-3 specifying the default rendering intent for this - profile. - - ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) - ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 - ImageCms.Intent.SATURATION = 2 - ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 - - see the pyCMS documentation for details on rendering intents and what - they do. - :exception PyCMSError: - """ - - try: - if not isinstance(profile, ImageCmsProfile): - profile = ImageCmsProfile(profile) - return profile.profile.rendering_intent - except (AttributeError, OSError, TypeError, ValueError) as v: - raise PyCMSError(v) from v - - -def isIntentSupported( - profile: _CmsProfileCompatible, intent: Intent, direction: Direction -) -> Literal[-1, 1]: - """ - (pyCMS) Checks if a given intent is supported. - - Use this function to verify that you can use your desired - ``intent`` with ``profile``, and that ``profile`` can be used for the - input/output/proof profile as you desire. - - Some profiles are created specifically for one "direction", can cannot - be used for others. Some profiles can only be used for certain - rendering intents, so it's best to either verify this before trying - to create a transform with them (using this function), or catch the - potential :exc:`PyCMSError` that will occur if they don't - support the modes you select. - - :param profile: EITHER a valid CmsProfile object, OR a string of the - filename of an ICC profile. - :param intent: Integer (0-3) specifying the rendering intent you wish to - use with this profile - - ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT) - ImageCms.Intent.RELATIVE_COLORIMETRIC = 1 - ImageCms.Intent.SATURATION = 2 - ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3 - - see the pyCMS documentation for details on rendering intents and what - they do. - :param direction: Integer specifying if the profile is to be used for - input, output, or proof - - INPUT = 0 (or use ImageCms.Direction.INPUT) - OUTPUT = 1 (or use ImageCms.Direction.OUTPUT) - PROOF = 2 (or use ImageCms.Direction.PROOF) - - :returns: 1 if the intent/direction are supported, -1 if they are not. - :exception PyCMSError: - """ - - try: - if not isinstance(profile, ImageCmsProfile): - profile = ImageCmsProfile(profile) - # FIXME: I get different results for the same data w. different - # compilers. Bug in LittleCMS or in the binding? - if profile.profile.is_intent_supported(intent, direction): - return 1 - else: - return -1 - except (AttributeError, OSError, TypeError, ValueError) as v: - raise PyCMSError(v) from v diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/ImageColor.py b/.venv-docs/lib/python3.12/site-packages/PIL/ImageColor.py deleted file mode 100644 index 9a15a8eb..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/ImageColor.py +++ /dev/null @@ -1,320 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# map CSS3-style colour description strings to RGB -# -# History: -# 2002-10-24 fl Added support for CSS-style color strings -# 2002-12-15 fl Added RGBA support -# 2004-03-27 fl Fixed remaining int() problems for Python 1.5.2 -# 2004-07-19 fl Fixed gray/grey spelling issues -# 2009-03-05 fl Fixed rounding error in grayscale calculation -# -# Copyright (c) 2002-2004 by Secret Labs AB -# Copyright (c) 2002-2004 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import re -from functools import lru_cache - -from . import Image - - -@lru_cache -def getrgb(color: str) -> tuple[int, int, int] | tuple[int, int, int, int]: - """ - Convert a color string to an RGB or RGBA tuple. If the string cannot be - parsed, this function raises a :py:exc:`ValueError` exception. - - .. versionadded:: 1.1.4 - - :param color: A color string - :return: ``(red, green, blue[, alpha])`` - """ - if len(color) > 100: - msg = "color specifier is too long" - raise ValueError(msg) - color = color.lower() - - rgb = colormap.get(color, None) - if rgb: - if isinstance(rgb, tuple): - return rgb - rgb_tuple = getrgb(rgb) - assert len(rgb_tuple) == 3 - colormap[color] = rgb_tuple - return rgb_tuple - - # check for known string formats - if re.match("#[a-f0-9]{3}$", color): - return int(color[1] * 2, 16), int(color[2] * 2, 16), int(color[3] * 2, 16) - - if re.match("#[a-f0-9]{4}$", color): - return ( - int(color[1] * 2, 16), - int(color[2] * 2, 16), - int(color[3] * 2, 16), - int(color[4] * 2, 16), - ) - - if re.match("#[a-f0-9]{6}$", color): - return int(color[1:3], 16), int(color[3:5], 16), int(color[5:7], 16) - - if re.match("#[a-f0-9]{8}$", color): - return ( - int(color[1:3], 16), - int(color[3:5], 16), - int(color[5:7], 16), - int(color[7:9], 16), - ) - - m = re.match(r"rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color) - if m: - return int(m.group(1)), int(m.group(2)), int(m.group(3)) - - m = re.match(r"rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color) - if m: - return ( - int((int(m.group(1)) * 255) / 100.0 + 0.5), - int((int(m.group(2)) * 255) / 100.0 + 0.5), - int((int(m.group(3)) * 255) / 100.0 + 0.5), - ) - - m = re.match( - r"hsl\(\s*(\d+\.?\d*)\s*,\s*(\d+\.?\d*)%\s*,\s*(\d+\.?\d*)%\s*\)$", color - ) - if m: - from colorsys import hls_to_rgb - - rgb_floats = hls_to_rgb( - float(m.group(1)) / 360.0, - float(m.group(3)) / 100.0, - float(m.group(2)) / 100.0, - ) - return ( - int(rgb_floats[0] * 255 + 0.5), - int(rgb_floats[1] * 255 + 0.5), - int(rgb_floats[2] * 255 + 0.5), - ) - - m = re.match( - r"hs[bv]\(\s*(\d+\.?\d*)\s*,\s*(\d+\.?\d*)%\s*,\s*(\d+\.?\d*)%\s*\)$", color - ) - if m: - from colorsys import hsv_to_rgb - - rgb_floats = hsv_to_rgb( - float(m.group(1)) / 360.0, - float(m.group(2)) / 100.0, - float(m.group(3)) / 100.0, - ) - return ( - int(rgb_floats[0] * 255 + 0.5), - int(rgb_floats[1] * 255 + 0.5), - int(rgb_floats[2] * 255 + 0.5), - ) - - m = re.match(r"rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color) - if m: - return int(m.group(1)), int(m.group(2)), int(m.group(3)), int(m.group(4)) - msg = f"unknown color specifier: {repr(color)}" - raise ValueError(msg) - - -@lru_cache -def getcolor(color: str, mode: str) -> int | tuple[int, ...]: - """ - Same as :py:func:`~PIL.ImageColor.getrgb` for most modes. However, if - ``mode`` is HSV, converts the RGB value to a HSV value, or if ``mode`` is - not color or a palette image, converts the RGB value to a grayscale value. - If the string cannot be parsed, this function raises a :py:exc:`ValueError` - exception. - - .. versionadded:: 1.1.4 - - :param color: A color string - :param mode: Convert result to this mode - :return: ``graylevel, (graylevel, alpha) or (red, green, blue[, alpha])`` - """ - # same as getrgb, but converts the result to the given mode - rgb, alpha = getrgb(color), 255 - if len(rgb) == 4: - alpha = rgb[3] - rgb = rgb[:3] - - if mode == "HSV": - from colorsys import rgb_to_hsv - - r, g, b = rgb - h, s, v = rgb_to_hsv(r / 255, g / 255, b / 255) - return int(h * 255), int(s * 255), int(v * 255) - elif Image.getmodebase(mode) == "L": - r, g, b = rgb - # ITU-R Recommendation 601-2 for nonlinear RGB - # scaled to 24 bits to match the convert's implementation. - graylevel = (r * 19595 + g * 38470 + b * 7471 + 0x8000) >> 16 - if mode[-1] == "A": - return graylevel, alpha - return graylevel - elif mode[-1] == "A": - return rgb + (alpha,) - return rgb - - -colormap: dict[str, str | tuple[int, int, int]] = { - # X11 colour table from https://drafts.csswg.org/css-color-4/, with - # gray/grey spelling issues fixed. This is a superset of HTML 4.0 - # colour names used in CSS 1. - "aliceblue": "#f0f8ff", - "antiquewhite": "#faebd7", - "aqua": "#00ffff", - "aquamarine": "#7fffd4", - "azure": "#f0ffff", - "beige": "#f5f5dc", - "bisque": "#ffe4c4", - "black": "#000000", - "blanchedalmond": "#ffebcd", - "blue": "#0000ff", - "blueviolet": "#8a2be2", - "brown": "#a52a2a", - "burlywood": "#deb887", - "cadetblue": "#5f9ea0", - "chartreuse": "#7fff00", - "chocolate": "#d2691e", - "coral": "#ff7f50", - "cornflowerblue": "#6495ed", - "cornsilk": "#fff8dc", - "crimson": "#dc143c", - "cyan": "#00ffff", - "darkblue": "#00008b", - "darkcyan": "#008b8b", - "darkgoldenrod": "#b8860b", - "darkgray": "#a9a9a9", - "darkgrey": "#a9a9a9", - "darkgreen": "#006400", - "darkkhaki": "#bdb76b", - "darkmagenta": "#8b008b", - "darkolivegreen": "#556b2f", - "darkorange": "#ff8c00", - "darkorchid": "#9932cc", - "darkred": "#8b0000", - "darksalmon": "#e9967a", - "darkseagreen": "#8fbc8f", - "darkslateblue": "#483d8b", - "darkslategray": "#2f4f4f", - "darkslategrey": "#2f4f4f", - "darkturquoise": "#00ced1", - "darkviolet": "#9400d3", - "deeppink": "#ff1493", - "deepskyblue": "#00bfff", - "dimgray": "#696969", - "dimgrey": "#696969", - "dodgerblue": "#1e90ff", - "firebrick": "#b22222", - "floralwhite": "#fffaf0", - "forestgreen": "#228b22", - "fuchsia": "#ff00ff", - "gainsboro": "#dcdcdc", - "ghostwhite": "#f8f8ff", - "gold": "#ffd700", - "goldenrod": "#daa520", - "gray": "#808080", - "grey": "#808080", - "green": "#008000", - "greenyellow": "#adff2f", - "honeydew": "#f0fff0", - "hotpink": "#ff69b4", - "indianred": "#cd5c5c", - "indigo": "#4b0082", - "ivory": "#fffff0", - "khaki": "#f0e68c", - "lavender": "#e6e6fa", - "lavenderblush": "#fff0f5", - "lawngreen": "#7cfc00", - "lemonchiffon": "#fffacd", - "lightblue": "#add8e6", - "lightcoral": "#f08080", - "lightcyan": "#e0ffff", - "lightgoldenrodyellow": "#fafad2", - "lightgreen": "#90ee90", - "lightgray": "#d3d3d3", - "lightgrey": "#d3d3d3", - "lightpink": "#ffb6c1", - "lightsalmon": "#ffa07a", - "lightseagreen": "#20b2aa", - "lightskyblue": "#87cefa", - "lightslategray": "#778899", - "lightslategrey": "#778899", - "lightsteelblue": "#b0c4de", - "lightyellow": "#ffffe0", - "lime": "#00ff00", - "limegreen": "#32cd32", - "linen": "#faf0e6", - "magenta": "#ff00ff", - "maroon": "#800000", - "mediumaquamarine": "#66cdaa", - "mediumblue": "#0000cd", - "mediumorchid": "#ba55d3", - "mediumpurple": "#9370db", - "mediumseagreen": "#3cb371", - "mediumslateblue": "#7b68ee", - "mediumspringgreen": "#00fa9a", - "mediumturquoise": "#48d1cc", - "mediumvioletred": "#c71585", - "midnightblue": "#191970", - "mintcream": "#f5fffa", - "mistyrose": "#ffe4e1", - "moccasin": "#ffe4b5", - "navajowhite": "#ffdead", - "navy": "#000080", - "oldlace": "#fdf5e6", - "olive": "#808000", - "olivedrab": "#6b8e23", - "orange": "#ffa500", - "orangered": "#ff4500", - "orchid": "#da70d6", - "palegoldenrod": "#eee8aa", - "palegreen": "#98fb98", - "paleturquoise": "#afeeee", - "palevioletred": "#db7093", - "papayawhip": "#ffefd5", - "peachpuff": "#ffdab9", - "peru": "#cd853f", - "pink": "#ffc0cb", - "plum": "#dda0dd", - "powderblue": "#b0e0e6", - "purple": "#800080", - "rebeccapurple": "#663399", - "red": "#ff0000", - "rosybrown": "#bc8f8f", - "royalblue": "#4169e1", - "saddlebrown": "#8b4513", - "salmon": "#fa8072", - "sandybrown": "#f4a460", - "seagreen": "#2e8b57", - "seashell": "#fff5ee", - "sienna": "#a0522d", - "silver": "#c0c0c0", - "skyblue": "#87ceeb", - "slateblue": "#6a5acd", - "slategray": "#708090", - "slategrey": "#708090", - "snow": "#fffafa", - "springgreen": "#00ff7f", - "steelblue": "#4682b4", - "tan": "#d2b48c", - "teal": "#008080", - "thistle": "#d8bfd8", - "tomato": "#ff6347", - "turquoise": "#40e0d0", - "violet": "#ee82ee", - "wheat": "#f5deb3", - "white": "#ffffff", - "whitesmoke": "#f5f5f5", - "yellow": "#ffff00", - "yellowgreen": "#9acd32", -} diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/ImageDraw.py b/.venv-docs/lib/python3.12/site-packages/PIL/ImageDraw.py deleted file mode 100644 index 8bcf2d8e..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/ImageDraw.py +++ /dev/null @@ -1,1036 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# drawing interface operations -# -# History: -# 1996-04-13 fl Created (experimental) -# 1996-08-07 fl Filled polygons, ellipses. -# 1996-08-13 fl Added text support -# 1998-06-28 fl Handle I and F images -# 1998-12-29 fl Added arc; use arc primitive to draw ellipses -# 1999-01-10 fl Added shape stuff (experimental) -# 1999-02-06 fl Added bitmap support -# 1999-02-11 fl Changed all primitives to take options -# 1999-02-20 fl Fixed backwards compatibility -# 2000-10-12 fl Copy on write, when necessary -# 2001-02-18 fl Use default ink for bitmap/text also in fill mode -# 2002-10-24 fl Added support for CSS-style color strings -# 2002-12-10 fl Added experimental support for RGBA-on-RGB drawing -# 2002-12-11 fl Refactored low-level drawing API (work in progress) -# 2004-08-26 fl Made Draw() a factory function, added getdraw() support -# 2004-09-04 fl Added width support to line primitive -# 2004-09-10 fl Added font mode handling -# 2006-06-19 fl Added font bearing support (getmask2) -# -# Copyright (c) 1997-2006 by Secret Labs AB -# Copyright (c) 1996-2006 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import math -import struct -from collections.abc import Sequence -from typing import cast - -from . import Image, ImageColor, ImageText - -TYPE_CHECKING = False -if TYPE_CHECKING: - from collections.abc import Callable - from types import ModuleType - from typing import Any, AnyStr - - from . import ImageDraw2, ImageFont - from ._typing import Coords, _Ink - -# experimental access to the outline API -Outline: Callable[[], Image.core._Outline] = Image.core.outline - -""" -A simple 2D drawing interface for PIL images. -

-Application code should use the Draw factory, instead of -directly. -""" - - -class ImageDraw: - font: ( - ImageFont.ImageFont | ImageFont.FreeTypeFont | ImageFont.TransposedFont | None - ) = None - - def __init__(self, im: Image.Image, mode: str | None = None) -> None: - """ - Create a drawing instance. - - :param im: The image to draw in. - :param mode: Optional mode to use for color values. For RGB - images, this argument can be RGB or RGBA (to blend the - drawing into the image). For all other modes, this argument - must be the same as the image mode. If omitted, the mode - defaults to the mode of the image. - """ - im._ensure_mutable() - blend = 0 - if mode is None: - mode = im.mode - if mode != im.mode: - if mode == "RGBA" and im.mode == "RGB": - blend = 1 - else: - msg = "mode mismatch" - raise ValueError(msg) - if mode == "P": - self.palette = im.palette - else: - self.palette = None - self._image = im - self.im = im.im - self.draw = Image.core.draw(self.im, blend) - self.mode = mode - if mode in ("I", "F"): - self.ink = self.draw.draw_ink(1) - else: - self.ink = self.draw.draw_ink(-1) - if mode in ("1", "P", "I", "F"): - # FIXME: fix Fill2 to properly support matte for I+F images - self.fontmode = "1" - else: - self.fontmode = "L" # aliasing is okay for other modes - self.fill = False - - def getfont( - self, - ) -> ImageFont.ImageFont | ImageFont.FreeTypeFont | ImageFont.TransposedFont: - """ - Get the current default font. - - To set the default font for this ImageDraw instance:: - - from PIL import ImageDraw, ImageFont - draw.font = ImageFont.truetype("Tests/fonts/FreeMono.ttf") - - To set the default font for all future ImageDraw instances:: - - from PIL import ImageDraw, ImageFont - ImageDraw.ImageDraw.font = ImageFont.truetype("Tests/fonts/FreeMono.ttf") - - If the current default font is ``None``, - it is initialized with ``ImageFont.load_default()``. - - :returns: An image font.""" - if not self.font: - # FIXME: should add a font repository - from . import ImageFont - - self.font = ImageFont.load_default() - return self.font - - def _getfont( - self, font_size: float | None - ) -> ImageFont.ImageFont | ImageFont.FreeTypeFont | ImageFont.TransposedFont: - if font_size is not None: - from . import ImageFont - - return ImageFont.load_default(font_size) - else: - return self.getfont() - - def _getink( - self, ink: _Ink | None, fill: _Ink | None = None - ) -> tuple[int | None, int | None]: - result_ink = None - result_fill = None - if ink is None and fill is None: - if self.fill: - result_fill = self.ink - else: - result_ink = self.ink - else: - if ink is not None: - if isinstance(ink, str): - ink = ImageColor.getcolor(ink, self.mode) - if self.palette and isinstance(ink, tuple): - ink = self.palette.getcolor(ink, self._image) - result_ink = self.draw.draw_ink(ink) - if fill is not None: - if isinstance(fill, str): - fill = ImageColor.getcolor(fill, self.mode) - if self.palette and isinstance(fill, tuple): - fill = self.palette.getcolor(fill, self._image) - result_fill = self.draw.draw_ink(fill) - return result_ink, result_fill - - def arc( - self, - xy: Coords, - start: float, - end: float, - fill: _Ink | None = None, - width: int = 1, - ) -> None: - """Draw an arc.""" - ink, fill = self._getink(fill) - if ink is not None: - self.draw.draw_arc(xy, start, end, ink, width) - - def bitmap( - self, xy: Sequence[int], bitmap: Image.Image, fill: _Ink | None = None - ) -> None: - """Draw a bitmap.""" - bitmap.load() - ink, fill = self._getink(fill) - if ink is None: - ink = fill - if ink is not None: - self.draw.draw_bitmap(xy, bitmap.im, ink) - - def chord( - self, - xy: Coords, - start: float, - end: float, - fill: _Ink | None = None, - outline: _Ink | None = None, - width: int = 1, - ) -> None: - """Draw a chord.""" - ink, fill_ink = self._getink(outline, fill) - if fill_ink is not None: - self.draw.draw_chord(xy, start, end, fill_ink, 1) - if ink is not None and ink != fill_ink and width != 0: - self.draw.draw_chord(xy, start, end, ink, 0, width) - - def ellipse( - self, - xy: Coords, - fill: _Ink | None = None, - outline: _Ink | None = None, - width: int = 1, - ) -> None: - """Draw an ellipse.""" - ink, fill_ink = self._getink(outline, fill) - if fill_ink is not None: - self.draw.draw_ellipse(xy, fill_ink, 1) - if ink is not None and ink != fill_ink and width != 0: - self.draw.draw_ellipse(xy, ink, 0, width) - - def circle( - self, - xy: Sequence[float], - radius: float, - fill: _Ink | None = None, - outline: _Ink | None = None, - width: int = 1, - ) -> None: - """Draw a circle given center coordinates and a radius.""" - ellipse_xy = (xy[0] - radius, xy[1] - radius, xy[0] + radius, xy[1] + radius) - self.ellipse(ellipse_xy, fill, outline, width) - - def line( - self, - xy: Coords, - fill: _Ink | None = None, - width: int = 0, - joint: str | None = None, - ) -> None: - """Draw a line, or a connected sequence of line segments.""" - ink = self._getink(fill)[0] - if ink is not None: - self.draw.draw_lines(xy, ink, width) - if joint == "curve" and width > 4: - points: Sequence[Sequence[float]] - if isinstance(xy[0], (list, tuple)): - points = cast(Sequence[Sequence[float]], xy) - else: - points = [ - cast(Sequence[float], tuple(xy[i : i + 2])) - for i in range(0, len(xy), 2) - ] - for i in range(1, len(points) - 1): - point = points[i] - angles = [ - math.degrees(math.atan2(end[0] - start[0], start[1] - end[1])) - % 360 - for start, end in ( - (points[i - 1], point), - (point, points[i + 1]), - ) - ] - if angles[0] == angles[1]: - # This is a straight line, so no joint is required - continue - - def coord_at_angle( - coord: Sequence[float], angle: float - ) -> tuple[float, ...]: - x, y = coord - angle -= 90 - distance = width / 2 - 1 - return tuple( - p + (math.floor(p_d) if p_d > 0 else math.ceil(p_d)) - for p, p_d in ( - (x, distance * math.cos(math.radians(angle))), - (y, distance * math.sin(math.radians(angle))), - ) - ) - - flipped = ( - angles[1] > angles[0] and angles[1] - 180 > angles[0] - ) or (angles[1] < angles[0] and angles[1] + 180 > angles[0]) - coords = [ - (point[0] - width / 2 + 1, point[1] - width / 2 + 1), - (point[0] + width / 2 - 1, point[1] + width / 2 - 1), - ] - if flipped: - start, end = (angles[1] + 90, angles[0] + 90) - else: - start, end = (angles[0] - 90, angles[1] - 90) - self.pieslice(coords, start - 90, end - 90, fill) - - if width > 8: - # Cover potential gaps between the line and the joint - if flipped: - gap_coords = [ - coord_at_angle(point, angles[0] + 90), - point, - coord_at_angle(point, angles[1] + 90), - ] - else: - gap_coords = [ - coord_at_angle(point, angles[0] - 90), - point, - coord_at_angle(point, angles[1] - 90), - ] - self.line(gap_coords, fill, width=3) - - def shape( - self, - shape: Image.core._Outline, - fill: _Ink | None = None, - outline: _Ink | None = None, - ) -> None: - """(Experimental) Draw a shape.""" - shape.close() - ink, fill_ink = self._getink(outline, fill) - if fill_ink is not None: - self.draw.draw_outline(shape, fill_ink, 1) - if ink is not None and ink != fill_ink: - self.draw.draw_outline(shape, ink, 0) - - def pieslice( - self, - xy: Coords, - start: float, - end: float, - fill: _Ink | None = None, - outline: _Ink | None = None, - width: int = 1, - ) -> None: - """Draw a pieslice.""" - ink, fill_ink = self._getink(outline, fill) - if fill_ink is not None: - self.draw.draw_pieslice(xy, start, end, fill_ink, 1) - if ink is not None and ink != fill_ink and width != 0: - self.draw.draw_pieslice(xy, start, end, ink, 0, width) - - def point(self, xy: Coords, fill: _Ink | None = None) -> None: - """Draw one or more individual pixels.""" - ink, fill = self._getink(fill) - if ink is not None: - self.draw.draw_points(xy, ink) - - def polygon( - self, - xy: Coords, - fill: _Ink | None = None, - outline: _Ink | None = None, - width: int = 1, - ) -> None: - """Draw a polygon.""" - ink, fill_ink = self._getink(outline, fill) - if fill_ink is not None: - self.draw.draw_polygon(xy, fill_ink, 1) - if ink is not None and ink != fill_ink and width != 0: - if width == 1: - self.draw.draw_polygon(xy, ink, 0, width) - elif self.im is not None: - # To avoid expanding the polygon outwards, - # use the fill as a mask - mask = Image.new("1", self.im.size) - mask_ink = self._getink(1)[0] - draw = Draw(mask) - draw.draw.draw_polygon(xy, mask_ink, 1) - - self.draw.draw_polygon(xy, ink, 0, width * 2 - 1, mask.im) - - def regular_polygon( - self, - bounding_circle: Sequence[Sequence[float] | float], - n_sides: int, - rotation: float = 0, - fill: _Ink | None = None, - outline: _Ink | None = None, - width: int = 1, - ) -> None: - """Draw a regular polygon.""" - xy = _compute_regular_polygon_vertices(bounding_circle, n_sides, rotation) - self.polygon(xy, fill, outline, width) - - def rectangle( - self, - xy: Coords, - fill: _Ink | None = None, - outline: _Ink | None = None, - width: int = 1, - ) -> None: - """Draw a rectangle.""" - ink, fill_ink = self._getink(outline, fill) - if fill_ink is not None: - self.draw.draw_rectangle(xy, fill_ink, 1) - if ink is not None and ink != fill_ink and width != 0: - self.draw.draw_rectangle(xy, ink, 0, width) - - def rounded_rectangle( - self, - xy: Coords, - radius: float = 0, - fill: _Ink | None = None, - outline: _Ink | None = None, - width: int = 1, - *, - corners: tuple[bool, bool, bool, bool] | None = None, - ) -> None: - """Draw a rounded rectangle.""" - if isinstance(xy[0], (list, tuple)): - (x0, y0), (x1, y1) = cast(Sequence[Sequence[float]], xy) - else: - x0, y0, x1, y1 = cast(Sequence[float], xy) - if x1 < x0: - msg = "x1 must be greater than or equal to x0" - raise ValueError(msg) - if y1 < y0: - msg = "y1 must be greater than or equal to y0" - raise ValueError(msg) - if corners is None: - corners = (True, True, True, True) - - d = radius * 2 - - x0 = round(x0) - y0 = round(y0) - x1 = round(x1) - y1 = round(y1) - full_x, full_y = False, False - if all(corners): - full_x = d >= x1 - x0 - 1 - if full_x: - # The two left and two right corners are joined - d = x1 - x0 - full_y = d >= y1 - y0 - 1 - if full_y: - # The two top and two bottom corners are joined - d = y1 - y0 - if full_x and full_y: - # If all corners are joined, that is a circle - return self.ellipse(xy, fill, outline, width) - - if d == 0 or not any(corners): - # If the corners have no curve, - # or there are no corners, - # that is a rectangle - return self.rectangle(xy, fill, outline, width) - - r = int(d // 2) - ink, fill_ink = self._getink(outline, fill) - - def draw_corners(pieslice: bool) -> None: - parts: tuple[tuple[tuple[float, float, float, float], int, int], ...] - if full_x: - # Draw top and bottom halves - parts = ( - ((x0, y0, x0 + d, y0 + d), 180, 360), - ((x0, y1 - d, x0 + d, y1), 0, 180), - ) - elif full_y: - # Draw left and right halves - parts = ( - ((x0, y0, x0 + d, y0 + d), 90, 270), - ((x1 - d, y0, x1, y0 + d), 270, 90), - ) - else: - # Draw four separate corners - parts = tuple( - part - for i, part in enumerate( - ( - ((x0, y0, x0 + d, y0 + d), 180, 270), - ((x1 - d, y0, x1, y0 + d), 270, 360), - ((x1 - d, y1 - d, x1, y1), 0, 90), - ((x0, y1 - d, x0 + d, y1), 90, 180), - ) - ) - if corners[i] - ) - for part in parts: - if pieslice: - self.draw.draw_pieslice(*(part + (fill_ink, 1))) - else: - self.draw.draw_arc(*(part + (ink, width))) - - if fill_ink is not None: - draw_corners(True) - - if full_x: - self.draw.draw_rectangle((x0, y0 + r + 1, x1, y1 - r - 1), fill_ink, 1) - elif x1 - r - 1 > x0 + r + 1: - self.draw.draw_rectangle((x0 + r + 1, y0, x1 - r - 1, y1), fill_ink, 1) - if not full_x and not full_y: - left = [x0, y0, x0 + r, y1] - if corners[0]: - left[1] += r + 1 - if corners[3]: - left[3] -= r + 1 - self.draw.draw_rectangle(left, fill_ink, 1) - - right = [x1 - r, y0, x1, y1] - if corners[1]: - right[1] += r + 1 - if corners[2]: - right[3] -= r + 1 - self.draw.draw_rectangle(right, fill_ink, 1) - if ink is not None and ink != fill_ink and width != 0: - draw_corners(False) - - if not full_x: - top = [x0, y0, x1, y0 + width - 1] - if corners[0]: - top[0] += r + 1 - if corners[1]: - top[2] -= r + 1 - self.draw.draw_rectangle(top, ink, 1) - - bottom = [x0, y1 - width + 1, x1, y1] - if corners[3]: - bottom[0] += r + 1 - if corners[2]: - bottom[2] -= r + 1 - self.draw.draw_rectangle(bottom, ink, 1) - if not full_y: - left = [x0, y0, x0 + width - 1, y1] - if corners[0]: - left[1] += r + 1 - if corners[3]: - left[3] -= r + 1 - self.draw.draw_rectangle(left, ink, 1) - - right = [x1 - width + 1, y0, x1, y1] - if corners[1]: - right[1] += r + 1 - if corners[2]: - right[3] -= r + 1 - self.draw.draw_rectangle(right, ink, 1) - - def text( - self, - xy: tuple[float, float], - text: AnyStr | ImageText.Text, - fill: _Ink | None = None, - font: ( - ImageFont.ImageFont - | ImageFont.FreeTypeFont - | ImageFont.TransposedFont - | None - ) = None, - anchor: str | None = None, - spacing: float = 4, - align: str = "left", - direction: str | None = None, - features: list[str] | None = None, - language: str | None = None, - stroke_width: float = 0, - stroke_fill: _Ink | None = None, - embedded_color: bool = False, - *args: Any, - **kwargs: Any, - ) -> None: - """Draw text.""" - if isinstance(text, ImageText.Text): - image_text = text - else: - if font is None: - font = self._getfont(kwargs.get("font_size")) - image_text = ImageText.Text( - text, font, self.mode, spacing, direction, features, language - ) - if embedded_color: - image_text.embed_color() - if stroke_width: - image_text.stroke(stroke_width, stroke_fill) - - def getink(fill: _Ink | None) -> int: - ink, fill_ink = self._getink(fill) - if ink is None: - assert fill_ink is not None - return fill_ink - return ink - - ink = getink(fill) - if ink is None: - return - - stroke_ink = None - if image_text.stroke_width: - stroke_ink = ( - getink(image_text.stroke_fill) - if image_text.stroke_fill is not None - else ink - ) - - for xy, anchor, line in image_text._split(xy, anchor, align): - - def draw_text(ink: int, stroke_width: float = 0) -> None: - mode = self.fontmode - if stroke_width == 0 and embedded_color: - mode = "RGBA" - coord = [] - for i in range(2): - coord.append(int(xy[i])) - start = (math.modf(xy[0])[0], math.modf(xy[1])[0]) - try: - mask, offset = image_text.font.getmask2( # type: ignore[union-attr,misc] - line, - mode, - direction=direction, - features=features, - language=language, - stroke_width=stroke_width, - stroke_filled=True, - anchor=anchor, - ink=ink, - start=start, - *args, - **kwargs, - ) - coord = [coord[0] + offset[0], coord[1] + offset[1]] - except AttributeError: - try: - mask = image_text.font.getmask( # type: ignore[misc] - line, - mode, - direction, - features, - language, - stroke_width, - anchor, - ink, - start=start, - *args, - **kwargs, - ) - except TypeError: - mask = image_text.font.getmask(line) - if mode == "RGBA": - # image_text.font.getmask2(mode="RGBA") - # returns color in RGB bands and mask in A - # extract mask and set text alpha - color, mask = mask, mask.getband(3) - ink_alpha = struct.pack("i", ink)[3] - color.fillband(3, ink_alpha) - x, y = coord - if self.im is not None: - self.im.paste( - color, (x, y, x + mask.size[0], y + mask.size[1]), mask - ) - else: - self.draw.draw_bitmap(coord, mask, ink) - - if stroke_ink is not None: - # Draw stroked text - draw_text(stroke_ink, image_text.stroke_width) - - # Draw normal text - if ink != stroke_ink: - draw_text(ink) - else: - # Only draw normal text - draw_text(ink) - - def multiline_text( - self, - xy: tuple[float, float], - text: AnyStr, - fill: _Ink | None = None, - font: ( - ImageFont.ImageFont - | ImageFont.FreeTypeFont - | ImageFont.TransposedFont - | None - ) = None, - anchor: str | None = None, - spacing: float = 4, - align: str = "left", - direction: str | None = None, - features: list[str] | None = None, - language: str | None = None, - stroke_width: float = 0, - stroke_fill: _Ink | None = None, - embedded_color: bool = False, - *, - font_size: float | None = None, - ) -> None: - return self.text( - xy, - text, - fill, - font, - anchor, - spacing, - align, - direction, - features, - language, - stroke_width, - stroke_fill, - embedded_color, - font_size=font_size, - ) - - def textlength( - self, - text: AnyStr, - font: ( - ImageFont.ImageFont - | ImageFont.FreeTypeFont - | ImageFont.TransposedFont - | None - ) = None, - direction: str | None = None, - features: list[str] | None = None, - language: str | None = None, - embedded_color: bool = False, - *, - font_size: float | None = None, - ) -> float: - """Get the length of a given string, in pixels with 1/64 precision.""" - if font is None: - font = self._getfont(font_size) - image_text = ImageText.Text( - text, - font, - self.mode, - direction=direction, - features=features, - language=language, - ) - if embedded_color: - image_text.embed_color() - return image_text.get_length() - - def textbbox( - self, - xy: tuple[float, float], - text: AnyStr, - font: ( - ImageFont.ImageFont - | ImageFont.FreeTypeFont - | ImageFont.TransposedFont - | None - ) = None, - anchor: str | None = None, - spacing: float = 4, - align: str = "left", - direction: str | None = None, - features: list[str] | None = None, - language: str | None = None, - stroke_width: float = 0, - embedded_color: bool = False, - *, - font_size: float | None = None, - ) -> tuple[float, float, float, float]: - """Get the bounding box of a given string, in pixels.""" - if font is None: - font = self._getfont(font_size) - image_text = ImageText.Text( - text, font, self.mode, spacing, direction, features, language - ) - if embedded_color: - image_text.embed_color() - if stroke_width: - image_text.stroke(stroke_width) - return image_text.get_bbox(xy, anchor, align) - - def multiline_textbbox( - self, - xy: tuple[float, float], - text: AnyStr, - font: ( - ImageFont.ImageFont - | ImageFont.FreeTypeFont - | ImageFont.TransposedFont - | None - ) = None, - anchor: str | None = None, - spacing: float = 4, - align: str = "left", - direction: str | None = None, - features: list[str] | None = None, - language: str | None = None, - stroke_width: float = 0, - embedded_color: bool = False, - *, - font_size: float | None = None, - ) -> tuple[float, float, float, float]: - return self.textbbox( - xy, - text, - font, - anchor, - spacing, - align, - direction, - features, - language, - stroke_width, - embedded_color, - font_size=font_size, - ) - - -def Draw(im: Image.Image, mode: str | None = None) -> ImageDraw: - """ - A simple 2D drawing interface for PIL images. - - :param im: The image to draw in. - :param mode: Optional mode to use for color values. For RGB - images, this argument can be RGB or RGBA (to blend the - drawing into the image). For all other modes, this argument - must be the same as the image mode. If omitted, the mode - defaults to the mode of the image. - """ - try: - return getattr(im, "getdraw")(mode) - except AttributeError: - return ImageDraw(im, mode) - - -def getdraw(im: Image.Image | None = None) -> tuple[ImageDraw2.Draw | None, ModuleType]: - """ - :param im: The image to draw in. - :returns: A (drawing context, drawing resource factory) tuple. - """ - from . import ImageDraw2 - - draw = ImageDraw2.Draw(im) if im is not None else None - return draw, ImageDraw2 - - -def floodfill( - image: Image.Image, - xy: tuple[int, int], - value: float | tuple[int, ...], - border: float | tuple[int, ...] | None = None, - thresh: float = 0, -) -> None: - """ - .. warning:: This method is experimental. - - Fills a bounded region with a given color. - - :param image: Target image. - :param xy: Seed position (a 2-item coordinate tuple). See - :ref:`coordinate-system`. - :param value: Fill color. - :param border: Optional border value. If given, the region consists of - pixels with a color different from the border color. If not given, - the region consists of pixels having the same color as the seed - pixel. - :param thresh: Optional threshold value which specifies a maximum - tolerable difference of a pixel value from the 'background' in - order for it to be replaced. Useful for filling regions of - non-homogeneous, but similar, colors. - """ - # based on an implementation by Eric S. Raymond - # amended by yo1995 @20180806 - pixel = image.load() - assert pixel is not None - x, y = xy - try: - background = pixel[x, y] - if _color_diff(value, background) <= thresh: - return # seed point already has fill color - pixel[x, y] = value - except (ValueError, IndexError): - return # seed point outside image - edge = {(x, y)} - # use a set to keep record of current and previous edge pixels - # to reduce memory consumption - full_edge = set() - while edge: - new_edge = set() - for x, y in edge: # 4 adjacent method - for s, t in ((x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)): - # If already processed, or if a coordinate is negative, skip - if (s, t) in full_edge or s < 0 or t < 0: - continue - try: - p = pixel[s, t] - except (ValueError, IndexError): - pass - else: - full_edge.add((s, t)) - if border is None: - fill = _color_diff(p, background) <= thresh - else: - fill = p not in (value, border) - if fill: - pixel[s, t] = value - new_edge.add((s, t)) - full_edge = edge # discard pixels processed - edge = new_edge - - -def _compute_regular_polygon_vertices( - bounding_circle: Sequence[Sequence[float] | float], n_sides: int, rotation: float -) -> list[tuple[float, float]]: - """ - Generate a list of vertices for a 2D regular polygon. - - :param bounding_circle: The bounding circle is a sequence defined - by a point and radius. The polygon is inscribed in this circle. - (e.g. ``bounding_circle=(x, y, r)`` or ``((x, y), r)``) - :param n_sides: Number of sides - (e.g. ``n_sides=3`` for a triangle, ``6`` for a hexagon) - :param rotation: Apply an arbitrary rotation to the polygon - (e.g. ``rotation=90``, applies a 90 degree rotation) - :return: List of regular polygon vertices - (e.g. ``[(25, 50), (50, 50), (50, 25), (25, 25)]``) - - How are the vertices computed? - 1. Compute the following variables - - theta: Angle between the apothem & the nearest polygon vertex - - side_length: Length of each polygon edge - - centroid: Center of bounding circle (1st, 2nd elements of bounding_circle) - - polygon_radius: Polygon radius (last element of bounding_circle) - - angles: Location of each polygon vertex in polar grid - (e.g. A square with 0 degree rotation => [225.0, 315.0, 45.0, 135.0]) - - 2. For each angle in angles, get the polygon vertex at that angle - The vertex is computed using the equation below. - X= xcos(φ) + ysin(φ) - Y= −xsin(φ) + ycos(φ) - - Note: - φ = angle in degrees - x = 0 - y = polygon_radius - - The formula above assumes rotation around the origin. - In our case, we are rotating around the centroid. - To account for this, we use the formula below - X = xcos(φ) + ysin(φ) + centroid_x - Y = −xsin(φ) + ycos(φ) + centroid_y - """ - # 1. Error Handling - # 1.1 Check `n_sides` has an appropriate value - if not isinstance(n_sides, int): - msg = "n_sides should be an int" # type: ignore[unreachable] - raise TypeError(msg) - if n_sides < 3: - msg = "n_sides should be an int > 2" - raise ValueError(msg) - - # 1.2 Check `bounding_circle` has an appropriate value - if not isinstance(bounding_circle, (list, tuple)): - msg = "bounding_circle should be a sequence" - raise TypeError(msg) - - if len(bounding_circle) == 3: - if not all(isinstance(i, (int, float)) for i in bounding_circle): - msg = "bounding_circle should only contain numeric data" - raise ValueError(msg) - - *centroid, polygon_radius = cast(list[float], list(bounding_circle)) - elif len(bounding_circle) == 2 and isinstance(bounding_circle[0], (list, tuple)): - if not all( - isinstance(i, (int, float)) for i in bounding_circle[0] - ) or not isinstance(bounding_circle[1], (int, float)): - msg = "bounding_circle should only contain numeric data" - raise ValueError(msg) - - if len(bounding_circle[0]) != 2: - msg = "bounding_circle centre should contain 2D coordinates (e.g. (x, y))" - raise ValueError(msg) - - centroid = cast(list[float], list(bounding_circle[0])) - polygon_radius = cast(float, bounding_circle[1]) - else: - msg = ( - "bounding_circle should contain 2D coordinates " - "and a radius (e.g. (x, y, r) or ((x, y), r) )" - ) - raise ValueError(msg) - - if polygon_radius <= 0: - msg = "bounding_circle radius should be > 0" - raise ValueError(msg) - - # 1.3 Check `rotation` has an appropriate value - if not isinstance(rotation, (int, float)): - msg = "rotation should be an int or float" # type: ignore[unreachable] - raise ValueError(msg) - - # 2. Define Helper Functions - def _apply_rotation(point: list[float], degrees: float) -> tuple[float, float]: - return ( - round( - point[0] * math.cos(math.radians(360 - degrees)) - - point[1] * math.sin(math.radians(360 - degrees)) - + centroid[0], - 2, - ), - round( - point[1] * math.cos(math.radians(360 - degrees)) - + point[0] * math.sin(math.radians(360 - degrees)) - + centroid[1], - 2, - ), - ) - - def _compute_polygon_vertex(angle: float) -> tuple[float, float]: - start_point = [polygon_radius, 0] - return _apply_rotation(start_point, angle) - - def _get_angles(n_sides: int, rotation: float) -> list[float]: - angles = [] - degrees = 360 / n_sides - # Start with the bottom left polygon vertex - current_angle = (270 - 0.5 * degrees) + rotation - for _ in range(n_sides): - angles.append(current_angle) - current_angle += degrees - if current_angle > 360: - current_angle -= 360 - return angles - - # 3. Variable Declarations - angles = _get_angles(n_sides, rotation) - - # 4. Compute Vertices - return [_compute_polygon_vertex(angle) for angle in angles] - - -def _color_diff( - color1: float | tuple[int, ...], color2: float | tuple[int, ...] -) -> float: - """ - Uses 1-norm distance to calculate difference between two values. - """ - first = color1 if isinstance(color1, tuple) else (color1,) - second = color2 if isinstance(color2, tuple) else (color2,) - - return sum(abs(first[i] - second[i]) for i in range(len(second))) diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/ImageDraw2.py b/.venv-docs/lib/python3.12/site-packages/PIL/ImageDraw2.py deleted file mode 100644 index 3d68658e..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/ImageDraw2.py +++ /dev/null @@ -1,243 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# WCK-style drawing interface operations -# -# History: -# 2003-12-07 fl created -# 2005-05-15 fl updated; added to PIL as ImageDraw2 -# 2005-05-15 fl added text support -# 2005-05-20 fl added arc/chord/pieslice support -# -# Copyright (c) 2003-2005 by Secret Labs AB -# Copyright (c) 2003-2005 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# - - -""" -(Experimental) WCK-style drawing interface operations - -.. seealso:: :py:mod:`PIL.ImageDraw` -""" -from __future__ import annotations - -from typing import Any, AnyStr, BinaryIO - -from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath -from ._typing import Coords, StrOrBytesPath - - -class Pen: - """Stores an outline color and width.""" - - def __init__(self, color: str, width: int = 1, opacity: int = 255) -> None: - self.color = ImageColor.getrgb(color) - self.width = width - - -class Brush: - """Stores a fill color""" - - def __init__(self, color: str, opacity: int = 255) -> None: - self.color = ImageColor.getrgb(color) - - -class Font: - """Stores a TrueType font and color""" - - def __init__( - self, color: str, file: StrOrBytesPath | BinaryIO, size: float = 12 - ) -> None: - # FIXME: add support for bitmap fonts - self.color = ImageColor.getrgb(color) - self.font = ImageFont.truetype(file, size) - - -class Draw: - """ - (Experimental) WCK-style drawing interface - """ - - def __init__( - self, - image: Image.Image | str, - size: tuple[int, int] | list[int] | None = None, - color: float | tuple[float, ...] | str | None = None, - ) -> None: - if isinstance(image, str): - if size is None: - msg = "If image argument is mode string, size must be a list or tuple" - raise ValueError(msg) - image = Image.new(image, size, color) - self.draw = ImageDraw.Draw(image) - self.image = image - self.transform: tuple[float, float, float, float, float, float] | None = None - - def flush(self) -> Image.Image: - return self.image - - def render( - self, - op: str, - xy: Coords, - pen: Pen | Brush | None, - brush: Brush | Pen | None = None, - **kwargs: Any, - ) -> None: - # handle color arguments - outline = fill = None - width = 1 - if isinstance(pen, Pen): - outline = pen.color - width = pen.width - elif isinstance(brush, Pen): - outline = brush.color - width = brush.width - if isinstance(brush, Brush): - fill = brush.color - elif isinstance(pen, Brush): - fill = pen.color - # handle transformation - if self.transform: - path = ImagePath.Path(xy) - path.transform(self.transform) - xy = path - # render the item - if op in ("arc", "line"): - kwargs.setdefault("fill", outline) - else: - kwargs.setdefault("fill", fill) - kwargs.setdefault("outline", outline) - if op == "line": - kwargs.setdefault("width", width) - getattr(self.draw, op)(xy, **kwargs) - - def settransform(self, offset: tuple[float, float]) -> None: - """Sets a transformation offset.""" - (xoffset, yoffset) = offset - self.transform = (1, 0, xoffset, 0, 1, yoffset) - - def arc( - self, - xy: Coords, - pen: Pen | Brush | None, - start: float, - end: float, - *options: Any, - ) -> None: - """ - Draws an arc (a portion of a circle outline) between the start and end - angles, inside the given bounding box. - - .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.arc` - """ - self.render("arc", xy, pen, *options, start=start, end=end) - - def chord( - self, - xy: Coords, - pen: Pen | Brush | None, - start: float, - end: float, - *options: Any, - ) -> None: - """ - Same as :py:meth:`~PIL.ImageDraw2.Draw.arc`, but connects the end points - with a straight line. - - .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.chord` - """ - self.render("chord", xy, pen, *options, start=start, end=end) - - def ellipse(self, xy: Coords, pen: Pen | Brush | None, *options: Any) -> None: - """ - Draws an ellipse inside the given bounding box. - - .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.ellipse` - """ - self.render("ellipse", xy, pen, *options) - - def line(self, xy: Coords, pen: Pen | Brush | None, *options: Any) -> None: - """ - Draws a line between the coordinates in the ``xy`` list. - - .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.line` - """ - self.render("line", xy, pen, *options) - - def pieslice( - self, - xy: Coords, - pen: Pen | Brush | None, - start: float, - end: float, - *options: Any, - ) -> None: - """ - Same as arc, but also draws straight lines between the end points and the - center of the bounding box. - - .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.pieslice` - """ - self.render("pieslice", xy, pen, *options, start=start, end=end) - - def polygon(self, xy: Coords, pen: Pen | Brush | None, *options: Any) -> None: - """ - Draws a polygon. - - The polygon outline consists of straight lines between the given - coordinates, plus a straight line between the last and the first - coordinate. - - - .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.polygon` - """ - self.render("polygon", xy, pen, *options) - - def rectangle(self, xy: Coords, pen: Pen | Brush | None, *options: Any) -> None: - """ - Draws a rectangle. - - .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.rectangle` - """ - self.render("rectangle", xy, pen, *options) - - def text(self, xy: tuple[float, float], text: AnyStr, font: Font) -> None: - """ - Draws the string at the given position. - - .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.text` - """ - if self.transform: - path = ImagePath.Path(xy) - path.transform(self.transform) - xy = path - self.draw.text(xy, text, font=font.font, fill=font.color) - - def textbbox( - self, xy: tuple[float, float], text: AnyStr, font: Font - ) -> tuple[float, float, float, float]: - """ - Returns bounding box (in pixels) of given text. - - :return: ``(left, top, right, bottom)`` bounding box - - .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.textbbox` - """ - if self.transform: - path = ImagePath.Path(xy) - path.transform(self.transform) - xy = path - return self.draw.textbbox(xy, text, font=font.font) - - def textlength(self, text: AnyStr, font: Font) -> float: - """ - Returns length (in pixels) of given text. - This is the amount by which following text should be offset. - - .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.textlength` - """ - return self.draw.textlength(text, font=font.font) diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/ImageEnhance.py b/.venv-docs/lib/python3.12/site-packages/PIL/ImageEnhance.py deleted file mode 100644 index 0e7e6dd8..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/ImageEnhance.py +++ /dev/null @@ -1,113 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# image enhancement classes -# -# For a background, see "Image Processing By Interpolation and -# Extrapolation", Paul Haeberli and Douglas Voorhies. Available -# at http://www.graficaobscura.com/interp/index.html -# -# History: -# 1996-03-23 fl Created -# 2009-06-16 fl Fixed mean calculation -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1996. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from . import Image, ImageFilter, ImageStat - - -class _Enhance: - image: Image.Image - degenerate: Image.Image - - def enhance(self, factor: float) -> Image.Image: - """ - Returns an enhanced image. - - :param factor: A floating point value controlling the enhancement. - Factor 1.0 always returns a copy of the original image, - lower factors mean less color (brightness, contrast, - etc), and higher values more. There are no restrictions - on this value. - :rtype: :py:class:`~PIL.Image.Image` - """ - return Image.blend(self.degenerate, self.image, factor) - - -class Color(_Enhance): - """Adjust image color balance. - - This class can be used to adjust the colour balance of an image, in - a manner similar to the controls on a colour TV set. An enhancement - factor of 0.0 gives a black and white image. A factor of 1.0 gives - the original image. - """ - - def __init__(self, image: Image.Image) -> None: - self.image = image - self.intermediate_mode = "L" - if "A" in image.getbands(): - self.intermediate_mode = "LA" - - if self.intermediate_mode != image.mode: - image = image.convert(self.intermediate_mode).convert(image.mode) - self.degenerate = image - - -class Contrast(_Enhance): - """Adjust image contrast. - - This class can be used to control the contrast of an image, similar - to the contrast control on a TV set. An enhancement factor of 0.0 - gives a solid gray image. A factor of 1.0 gives the original image. - """ - - def __init__(self, image: Image.Image) -> None: - self.image = image - if image.mode != "L": - image = image.convert("L") - mean = int(ImageStat.Stat(image).mean[0] + 0.5) - self.degenerate = Image.new("L", image.size, mean) - if self.degenerate.mode != self.image.mode: - self.degenerate = self.degenerate.convert(self.image.mode) - - if "A" in self.image.getbands(): - self.degenerate.putalpha(self.image.getchannel("A")) - - -class Brightness(_Enhance): - """Adjust image brightness. - - This class can be used to control the brightness of an image. An - enhancement factor of 0.0 gives a black image. A factor of 1.0 gives the - original image. - """ - - def __init__(self, image: Image.Image) -> None: - self.image = image - self.degenerate = Image.new(image.mode, image.size, 0) - - if "A" in image.getbands(): - self.degenerate.putalpha(image.getchannel("A")) - - -class Sharpness(_Enhance): - """Adjust image sharpness. - - This class can be used to adjust the sharpness of an image. An - enhancement factor of 0.0 gives a blurred image, a factor of 1.0 gives the - original image, and a factor of 2.0 gives a sharpened image. - """ - - def __init__(self, image: Image.Image) -> None: - self.image = image - self.degenerate = image.filter(ImageFilter.SMOOTH) - - if "A" in image.getbands(): - self.degenerate.putalpha(image.getchannel("A")) diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/ImageFile.py b/.venv-docs/lib/python3.12/site-packages/PIL/ImageFile.py deleted file mode 100644 index a1d98bd5..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/ImageFile.py +++ /dev/null @@ -1,926 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# base class for image file handlers -# -# history: -# 1995-09-09 fl Created -# 1996-03-11 fl Fixed load mechanism. -# 1996-04-15 fl Added pcx/xbm decoders. -# 1996-04-30 fl Added encoders. -# 1996-12-14 fl Added load helpers -# 1997-01-11 fl Use encode_to_file where possible -# 1997-08-27 fl Flush output in _save -# 1998-03-05 fl Use memory mapping for some modes -# 1999-02-04 fl Use memory mapping also for "I;16" and "I;16B" -# 1999-05-31 fl Added image parser -# 2000-10-12 fl Set readonly flag on memory-mapped images -# 2002-03-20 fl Use better messages for common decoder errors -# 2003-04-21 fl Fall back on mmap/map_buffer if map is not available -# 2003-10-30 fl Added StubImageFile class -# 2004-02-25 fl Made incremental parser more robust -# -# Copyright (c) 1997-2004 by Secret Labs AB -# Copyright (c) 1995-2004 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import abc -import io -import itertools -import logging -import os -import struct -from typing import IO, Any, NamedTuple, cast - -from . import ExifTags, Image -from ._util import DeferredError, is_path - -TYPE_CHECKING = False -if TYPE_CHECKING: - from ._typing import StrOrBytesPath - -logger = logging.getLogger(__name__) - -MAXBLOCK = 65536 -""" -By default, Pillow processes image data in blocks. This helps to prevent excessive use -of resources. Codecs may disable this behaviour with ``_pulls_fd`` or ``_pushes_fd``. - -When reading an image, this is the number of bytes to read at once. - -When writing an image, this is the number of bytes to write at once. -If the image width times 4 is greater, then that will be used instead. -Plugins may also set a greater number. - -User code may set this to another number. -""" - -SAFEBLOCK = 1024 * 1024 - -LOAD_TRUNCATED_IMAGES = False -"""Whether or not to load truncated image files. User code may change this.""" - -ERRORS = { - -1: "image buffer overrun error", - -2: "decoding error", - -3: "unknown error", - -8: "bad configuration", - -9: "out of memory error", -} -""" -Dict of known error codes returned from :meth:`.PyDecoder.decode`, -:meth:`.PyEncoder.encode` :meth:`.PyEncoder.encode_to_pyfd` and -:meth:`.PyEncoder.encode_to_file`. -""" - - -# -# -------------------------------------------------------------------- -# Helpers - - -def _get_oserror(error: int, *, encoder: bool) -> OSError: - try: - msg = Image.core.getcodecstatus(error) - except AttributeError: - msg = ERRORS.get(error) - if not msg: - msg = f"{'encoder' if encoder else 'decoder'} error {error}" - msg += f" when {'writing' if encoder else 'reading'} image file" - return OSError(msg) - - -def _tilesort(t: _Tile) -> int: - # sort on offset - return t[2] - - -class _Tile(NamedTuple): - codec_name: str - extents: tuple[int, int, int, int] | None - offset: int = 0 - args: tuple[Any, ...] | str | None = None - - -# -# -------------------------------------------------------------------- -# ImageFile base class - - -class ImageFile(Image.Image): - """Base class for image file format handlers.""" - - def __init__( - self, fp: StrOrBytesPath | IO[bytes], filename: str | bytes | None = None - ) -> None: - super().__init__() - - self._min_frame = 0 - - self.custom_mimetype: str | None = None - - self.tile: list[_Tile] = [] - """ A list of tile descriptors """ - - self.readonly = 1 # until we know better - - self.decoderconfig: tuple[Any, ...] = () - self.decodermaxblock = MAXBLOCK - - if is_path(fp): - # filename - self.fp = open(fp, "rb") - self.filename = os.fspath(fp) - self._exclusive_fp = True - else: - # stream - self.fp = cast(IO[bytes], fp) - self.filename = filename if filename is not None else "" - # can be overridden - self._exclusive_fp = False - - try: - try: - self._open() - except ( - IndexError, # end of data - TypeError, # end of data (ord) - KeyError, # unsupported mode - EOFError, # got header but not the first frame - struct.error, - ) as v: - raise SyntaxError(v) from v - - if not self.mode or self.size[0] <= 0 or self.size[1] <= 0: - msg = "not identified by this driver" - raise SyntaxError(msg) - except BaseException: - # close the file only if we have opened it this constructor - if self._exclusive_fp: - self.fp.close() - raise - - def _open(self) -> None: - pass - - def _close_fp(self): - if getattr(self, "_fp", False) and not isinstance(self._fp, DeferredError): - if self._fp != self.fp: - self._fp.close() - self._fp = DeferredError(ValueError("Operation on closed image")) - if self.fp: - self.fp.close() - - def close(self) -> None: - """ - Closes the file pointer, if possible. - - This operation will destroy the image core and release its memory. - The image data will be unusable afterward. - - This function is required to close images that have multiple frames or - have not had their file read and closed by the - :py:meth:`~PIL.Image.Image.load` method. See :ref:`file-handling` for - more information. - """ - try: - self._close_fp() - self.fp = None - except Exception as msg: - logger.debug("Error closing: %s", msg) - - super().close() - - def get_child_images(self) -> list[ImageFile]: - child_images = [] - exif = self.getexif() - ifds = [] - if ExifTags.Base.SubIFDs in exif: - subifd_offsets = exif[ExifTags.Base.SubIFDs] - if subifd_offsets: - if not isinstance(subifd_offsets, tuple): - subifd_offsets = (subifd_offsets,) - for subifd_offset in subifd_offsets: - ifds.append((exif._get_ifd_dict(subifd_offset), subifd_offset)) - ifd1 = exif.get_ifd(ExifTags.IFD.IFD1) - if ifd1 and ifd1.get(ExifTags.Base.JpegIFOffset): - assert exif._info is not None - ifds.append((ifd1, exif._info.next)) - - offset = None - for ifd, ifd_offset in ifds: - assert self.fp is not None - current_offset = self.fp.tell() - if offset is None: - offset = current_offset - - fp = self.fp - if ifd is not None: - thumbnail_offset = ifd.get(ExifTags.Base.JpegIFOffset) - if thumbnail_offset is not None: - thumbnail_offset += getattr(self, "_exif_offset", 0) - self.fp.seek(thumbnail_offset) - - length = ifd.get(ExifTags.Base.JpegIFByteCount) - assert isinstance(length, int) - data = self.fp.read(length) - fp = io.BytesIO(data) - - with Image.open(fp) as im: - from . import TiffImagePlugin - - if thumbnail_offset is None and isinstance( - im, TiffImagePlugin.TiffImageFile - ): - im._frame_pos = [ifd_offset] - im._seek(0) - im.load() - child_images.append(im) - - if offset is not None: - assert self.fp is not None - self.fp.seek(offset) - return child_images - - def get_format_mimetype(self) -> str | None: - if self.custom_mimetype: - return self.custom_mimetype - if self.format is not None: - return Image.MIME.get(self.format.upper()) - return None - - def __getstate__(self) -> list[Any]: - return super().__getstate__() + [self.filename] - - def __setstate__(self, state: list[Any]) -> None: - self.tile = [] - if len(state) > 5: - self.filename = state[5] - super().__setstate__(state) - - def verify(self) -> None: - """Check file integrity""" - - # raise exception if something's wrong. must be called - # directly after open, and closes file when finished. - if self._exclusive_fp: - self.fp.close() - self.fp = None - - def load(self) -> Image.core.PixelAccess | None: - """Load image data based on tile list""" - - if not self.tile and self._im is None: - msg = "cannot load this image" - raise OSError(msg) - - pixel = Image.Image.load(self) - if not self.tile: - return pixel - - self.map: mmap.mmap | None = None - use_mmap = self.filename and len(self.tile) == 1 - - readonly = 0 - - # look for read/seek overrides - if hasattr(self, "load_read"): - read = self.load_read - # don't use mmap if there are custom read/seek functions - use_mmap = False - else: - read = self.fp.read - - if hasattr(self, "load_seek"): - seek = self.load_seek - use_mmap = False - else: - seek = self.fp.seek - - if use_mmap: - # try memory mapping - decoder_name, extents, offset, args = self.tile[0] - if isinstance(args, str): - args = (args, 0, 1) - if ( - decoder_name == "raw" - and isinstance(args, tuple) - and len(args) >= 3 - and args[0] == self.mode - and args[0] in Image._MAPMODES - ): - if offset < 0: - msg = "Tile offset cannot be negative" - raise ValueError(msg) - try: - # use mmap, if possible - import mmap - - with open(self.filename) as fp: - self.map = mmap.mmap(fp.fileno(), 0, access=mmap.ACCESS_READ) - if offset + self.size[1] * args[1] > self.map.size(): - msg = "buffer is not large enough" - raise OSError(msg) - self.im = Image.core.map_buffer( - self.map, self.size, decoder_name, offset, args - ) - readonly = 1 - # After trashing self.im, - # we might need to reload the palette data. - if self.palette: - self.palette.dirty = 1 - except (AttributeError, OSError, ImportError): - self.map = None - - self.load_prepare() - err_code = -3 # initialize to unknown error - if not self.map: - # sort tiles in file order - self.tile.sort(key=_tilesort) - - # FIXME: This is a hack to handle TIFF's JpegTables tag. - prefix = getattr(self, "tile_prefix", b"") - - # Remove consecutive duplicates that only differ by their offset - self.tile = [ - list(tiles)[-1] - for _, tiles in itertools.groupby( - self.tile, lambda tile: (tile[0], tile[1], tile[3]) - ) - ] - for i, (decoder_name, extents, offset, args) in enumerate(self.tile): - seek(offset) - decoder = Image._getdecoder( - self.mode, decoder_name, args, self.decoderconfig - ) - try: - decoder.setimage(self.im, extents) - if decoder.pulls_fd: - decoder.setfd(self.fp) - err_code = decoder.decode(b"")[1] - else: - b = prefix - while True: - read_bytes = self.decodermaxblock - if i + 1 < len(self.tile): - next_offset = self.tile[i + 1].offset - if next_offset > offset: - read_bytes = next_offset - offset - try: - s = read(read_bytes) - except (IndexError, struct.error) as e: - # truncated png/gif - if LOAD_TRUNCATED_IMAGES: - break - else: - msg = "image file is truncated" - raise OSError(msg) from e - - if not s: # truncated jpeg - if LOAD_TRUNCATED_IMAGES: - break - else: - msg = ( - "image file is truncated " - f"({len(b)} bytes not processed)" - ) - raise OSError(msg) - - b = b + s - n, err_code = decoder.decode(b) - if n < 0: - break - b = b[n:] - finally: - # Need to cleanup here to prevent leaks - decoder.cleanup() - - self.tile = [] - self.readonly = readonly - - self.load_end() - - if self._exclusive_fp and self._close_exclusive_fp_after_loading: - self.fp.close() - self.fp = None - - if not self.map and not LOAD_TRUNCATED_IMAGES and err_code < 0: - # still raised if decoder fails to return anything - raise _get_oserror(err_code, encoder=False) - - return Image.Image.load(self) - - def load_prepare(self) -> None: - # create image memory if necessary - if self._im is None: - self.im = Image.core.new(self.mode, self.size) - # create palette (optional) - if self.mode == "P": - Image.Image.load(self) - - def load_end(self) -> None: - # may be overridden - pass - - # may be defined for contained formats - # def load_seek(self, pos: int) -> None: - # pass - - # may be defined for blocked formats (e.g. PNG) - # def load_read(self, read_bytes: int) -> bytes: - # pass - - def _seek_check(self, frame: int) -> bool: - if ( - frame < self._min_frame - # Only check upper limit on frames if additional seek operations - # are not required to do so - or ( - not (hasattr(self, "_n_frames") and self._n_frames is None) - and frame >= getattr(self, "n_frames") + self._min_frame - ) - ): - msg = "attempt to seek outside sequence" - raise EOFError(msg) - - return self.tell() != frame - - -class StubHandler(abc.ABC): - def open(self, im: StubImageFile) -> None: - pass - - @abc.abstractmethod - def load(self, im: StubImageFile) -> Image.Image: - pass - - -class StubImageFile(ImageFile, metaclass=abc.ABCMeta): - """ - Base class for stub image loaders. - - A stub loader is an image loader that can identify files of a - certain format, but relies on external code to load the file. - """ - - @abc.abstractmethod - def _open(self) -> None: - pass - - def load(self) -> Image.core.PixelAccess | None: - loader = self._load() - if loader is None: - msg = f"cannot find loader for this {self.format} file" - raise OSError(msg) - image = loader.load(self) - assert image is not None - # become the other object (!) - self.__class__ = image.__class__ # type: ignore[assignment] - self.__dict__ = image.__dict__ - return image.load() - - @abc.abstractmethod - def _load(self) -> StubHandler | None: - """(Hook) Find actual image loader.""" - pass - - -class Parser: - """ - Incremental image parser. This class implements the standard - feed/close consumer interface. - """ - - incremental = None - image: Image.Image | None = None - data: bytes | None = None - decoder: Image.core.ImagingDecoder | PyDecoder | None = None - offset = 0 - finished = 0 - - def reset(self) -> None: - """ - (Consumer) Reset the parser. Note that you can only call this - method immediately after you've created a parser; parser - instances cannot be reused. - """ - assert self.data is None, "cannot reuse parsers" - - def feed(self, data: bytes) -> None: - """ - (Consumer) Feed data to the parser. - - :param data: A string buffer. - :exception OSError: If the parser failed to parse the image file. - """ - # collect data - - if self.finished: - return - - if self.data is None: - self.data = data - else: - self.data = self.data + data - - # parse what we have - if self.decoder: - if self.offset > 0: - # skip header - skip = min(len(self.data), self.offset) - self.data = self.data[skip:] - self.offset = self.offset - skip - if self.offset > 0 or not self.data: - return - - n, e = self.decoder.decode(self.data) - - if n < 0: - # end of stream - self.data = None - self.finished = 1 - if e < 0: - # decoding error - self.image = None - raise _get_oserror(e, encoder=False) - else: - # end of image - return - self.data = self.data[n:] - - elif self.image: - # if we end up here with no decoder, this file cannot - # be incrementally parsed. wait until we've gotten all - # available data - pass - - else: - # attempt to open this file - try: - with io.BytesIO(self.data) as fp: - im = Image.open(fp) - except OSError: - pass # not enough data - else: - flag = hasattr(im, "load_seek") or hasattr(im, "load_read") - if flag or len(im.tile) != 1: - # custom load code, or multiple tiles - self.decode = None - else: - # initialize decoder - im.load_prepare() - d, e, o, a = im.tile[0] - im.tile = [] - self.decoder = Image._getdecoder(im.mode, d, a, im.decoderconfig) - self.decoder.setimage(im.im, e) - - # calculate decoder offset - self.offset = o - if self.offset <= len(self.data): - self.data = self.data[self.offset :] - self.offset = 0 - - self.image = im - - def __enter__(self) -> Parser: - return self - - def __exit__(self, *args: object) -> None: - self.close() - - def close(self) -> Image.Image: - """ - (Consumer) Close the stream. - - :returns: An image object. - :exception OSError: If the parser failed to parse the image file either - because it cannot be identified or cannot be - decoded. - """ - # finish decoding - if self.decoder: - # get rid of what's left in the buffers - self.feed(b"") - self.data = self.decoder = None - if not self.finished: - msg = "image was incomplete" - raise OSError(msg) - if not self.image: - msg = "cannot parse this image" - raise OSError(msg) - if self.data: - # incremental parsing not possible; reopen the file - # not that we have all data - with io.BytesIO(self.data) as fp: - try: - self.image = Image.open(fp) - finally: - self.image.load() - return self.image - - -# -------------------------------------------------------------------- - - -def _save(im: Image.Image, fp: IO[bytes], tile: list[_Tile], bufsize: int = 0) -> None: - """Helper to save image based on tile list - - :param im: Image object. - :param fp: File object. - :param tile: Tile list. - :param bufsize: Optional buffer size - """ - - im.load() - if not hasattr(im, "encoderconfig"): - im.encoderconfig = () - tile.sort(key=_tilesort) - # FIXME: make MAXBLOCK a configuration parameter - # It would be great if we could have the encoder specify what it needs - # But, it would need at least the image size in most cases. RawEncode is - # a tricky case. - bufsize = max(MAXBLOCK, bufsize, im.size[0] * 4) # see RawEncode.c - try: - fh = fp.fileno() - fp.flush() - _encode_tile(im, fp, tile, bufsize, fh) - except (AttributeError, io.UnsupportedOperation) as exc: - _encode_tile(im, fp, tile, bufsize, None, exc) - if hasattr(fp, "flush"): - fp.flush() - - -def _encode_tile( - im: Image.Image, - fp: IO[bytes], - tile: list[_Tile], - bufsize: int, - fh: int | None, - exc: BaseException | None = None, -) -> None: - for encoder_name, extents, offset, args in tile: - if offset > 0: - fp.seek(offset) - encoder = Image._getencoder(im.mode, encoder_name, args, im.encoderconfig) - try: - encoder.setimage(im.im, extents) - if encoder.pushes_fd: - encoder.setfd(fp) - errcode = encoder.encode_to_pyfd()[1] - else: - if exc: - # compress to Python file-compatible object - while True: - errcode, data = encoder.encode(bufsize)[1:] - fp.write(data) - if errcode: - break - else: - # slight speedup: compress to real file object - assert fh is not None - errcode = encoder.encode_to_file(fh, bufsize) - if errcode < 0: - raise _get_oserror(errcode, encoder=True) from exc - finally: - encoder.cleanup() - - -def _safe_read(fp: IO[bytes], size: int) -> bytes: - """ - Reads large blocks in a safe way. Unlike fp.read(n), this function - doesn't trust the user. If the requested size is larger than - SAFEBLOCK, the file is read block by block. - - :param fp: File handle. Must implement a read method. - :param size: Number of bytes to read. - :returns: A string containing size bytes of data. - - Raises an OSError if the file is truncated and the read cannot be completed - - """ - if size <= 0: - return b"" - if size <= SAFEBLOCK: - data = fp.read(size) - if len(data) < size: - msg = "Truncated File Read" - raise OSError(msg) - return data - blocks: list[bytes] = [] - remaining_size = size - while remaining_size > 0: - block = fp.read(min(remaining_size, SAFEBLOCK)) - if not block: - break - blocks.append(block) - remaining_size -= len(block) - if sum(len(block) for block in blocks) < size: - msg = "Truncated File Read" - raise OSError(msg) - return b"".join(blocks) - - -class PyCodecState: - def __init__(self) -> None: - self.xsize = 0 - self.ysize = 0 - self.xoff = 0 - self.yoff = 0 - - def extents(self) -> tuple[int, int, int, int]: - return self.xoff, self.yoff, self.xoff + self.xsize, self.yoff + self.ysize - - -class PyCodec: - fd: IO[bytes] | None - - def __init__(self, mode: str, *args: Any) -> None: - self.im: Image.core.ImagingCore | None = None - self.state = PyCodecState() - self.fd = None - self.mode = mode - self.init(args) - - def init(self, args: tuple[Any, ...]) -> None: - """ - Override to perform codec specific initialization - - :param args: Tuple of arg items from the tile entry - :returns: None - """ - self.args = args - - def cleanup(self) -> None: - """ - Override to perform codec specific cleanup - - :returns: None - """ - pass - - def setfd(self, fd: IO[bytes]) -> None: - """ - Called from ImageFile to set the Python file-like object - - :param fd: A Python file-like object - :returns: None - """ - self.fd = fd - - def setimage( - self, - im: Image.core.ImagingCore, - extents: tuple[int, int, int, int] | None = None, - ) -> None: - """ - Called from ImageFile to set the core output image for the codec - - :param im: A core image object - :param extents: a 4 tuple of (x0, y0, x1, y1) defining the rectangle - for this tile - :returns: None - """ - - # following c code - self.im = im - - if extents: - (x0, y0, x1, y1) = extents - else: - (x0, y0, x1, y1) = (0, 0, 0, 0) - - if x0 == 0 and x1 == 0: - self.state.xsize, self.state.ysize = self.im.size - else: - self.state.xoff = x0 - self.state.yoff = y0 - self.state.xsize = x1 - x0 - self.state.ysize = y1 - y0 - - if self.state.xsize <= 0 or self.state.ysize <= 0: - msg = "Size cannot be negative" - raise ValueError(msg) - - if ( - self.state.xsize + self.state.xoff > self.im.size[0] - or self.state.ysize + self.state.yoff > self.im.size[1] - ): - msg = "Tile cannot extend outside image" - raise ValueError(msg) - - -class PyDecoder(PyCodec): - """ - Python implementation of a format decoder. Override this class and - add the decoding logic in the :meth:`decode` method. - - See :ref:`Writing Your Own File Codec in Python` - """ - - _pulls_fd = False - - @property - def pulls_fd(self) -> bool: - return self._pulls_fd - - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: - """ - Override to perform the decoding process. - - :param buffer: A bytes object with the data to be decoded. - :returns: A tuple of ``(bytes consumed, errcode)``. - If finished with decoding return -1 for the bytes consumed. - Err codes are from :data:`.ImageFile.ERRORS`. - """ - msg = "unavailable in base decoder" - raise NotImplementedError(msg) - - def set_as_raw( - self, data: bytes, rawmode: str | None = None, extra: tuple[Any, ...] = () - ) -> None: - """ - Convenience method to set the internal image from a stream of raw data - - :param data: Bytes to be set - :param rawmode: The rawmode to be used for the decoder. - If not specified, it will default to the mode of the image - :param extra: Extra arguments for the decoder. - :returns: None - """ - - if not rawmode: - rawmode = self.mode - d = Image._getdecoder(self.mode, "raw", rawmode, extra) - assert self.im is not None - d.setimage(self.im, self.state.extents()) - s = d.decode(data) - - if s[0] >= 0: - msg = "not enough image data" - raise ValueError(msg) - if s[1] != 0: - msg = "cannot decode image data" - raise ValueError(msg) - - -class PyEncoder(PyCodec): - """ - Python implementation of a format encoder. Override this class and - add the decoding logic in the :meth:`encode` method. - - See :ref:`Writing Your Own File Codec in Python` - """ - - _pushes_fd = False - - @property - def pushes_fd(self) -> bool: - return self._pushes_fd - - def encode(self, bufsize: int) -> tuple[int, int, bytes]: - """ - Override to perform the encoding process. - - :param bufsize: Buffer size. - :returns: A tuple of ``(bytes encoded, errcode, bytes)``. - If finished with encoding return 1 for the error code. - Err codes are from :data:`.ImageFile.ERRORS`. - """ - msg = "unavailable in base encoder" - raise NotImplementedError(msg) - - def encode_to_pyfd(self) -> tuple[int, int]: - """ - If ``pushes_fd`` is ``True``, then this method will be used, - and ``encode()`` will only be called once. - - :returns: A tuple of ``(bytes consumed, errcode)``. - Err codes are from :data:`.ImageFile.ERRORS`. - """ - if not self.pushes_fd: - return 0, -8 # bad configuration - bytes_consumed, errcode, data = self.encode(0) - if data: - assert self.fd is not None - self.fd.write(data) - return bytes_consumed, errcode - - def encode_to_file(self, fh: int, bufsize: int) -> int: - """ - :param fh: File handle. - :param bufsize: Buffer size. - - :returns: If finished successfully, return 0. - Otherwise, return an error code. Err codes are from - :data:`.ImageFile.ERRORS`. - """ - errcode = 0 - while errcode == 0: - status, errcode, buf = self.encode(bufsize) - if status > 0: - os.write(fh, buf[status:]) - return errcode diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/ImageFilter.py b/.venv-docs/lib/python3.12/site-packages/PIL/ImageFilter.py deleted file mode 100644 index 9326eeed..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/ImageFilter.py +++ /dev/null @@ -1,607 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# standard filters -# -# History: -# 1995-11-27 fl Created -# 2002-06-08 fl Added rank and mode filters -# 2003-09-15 fl Fixed rank calculation in rank filter; added expand call -# -# Copyright (c) 1997-2003 by Secret Labs AB. -# Copyright (c) 1995-2002 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import abc -import functools -from collections.abc import Sequence -from typing import cast - -TYPE_CHECKING = False -if TYPE_CHECKING: - from collections.abc import Callable - from types import ModuleType - from typing import Any - - from . import _imaging - from ._typing import NumpyArray - - -class Filter(abc.ABC): - @abc.abstractmethod - def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: - pass - - -class MultibandFilter(Filter): - pass - - -class BuiltinFilter(MultibandFilter): - filterargs: tuple[Any, ...] - - def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: - if image.mode == "P": - msg = "cannot filter palette images" - raise ValueError(msg) - return image.filter(*self.filterargs) - - -class Kernel(BuiltinFilter): - """ - Create a convolution kernel. This only supports 3x3 and 5x5 integer and floating - point kernels. - - Kernels can only be applied to "L" and "RGB" images. - - :param size: Kernel size, given as (width, height). This must be (3,3) or (5,5). - :param kernel: A sequence containing kernel weights. The kernel will be flipped - vertically before being applied to the image. - :param scale: Scale factor. If given, the result for each pixel is divided by this - value. The default is the sum of the kernel weights. - :param offset: Offset. If given, this value is added to the result, after it has - been divided by the scale factor. - """ - - name = "Kernel" - - def __init__( - self, - size: tuple[int, int], - kernel: Sequence[float], - scale: float | None = None, - offset: float = 0, - ) -> None: - if scale is None: - # default scale is sum of kernel - scale = functools.reduce(lambda a, b: a + b, kernel) - if size[0] * size[1] != len(kernel): - msg = "not enough coefficients in kernel" - raise ValueError(msg) - self.filterargs = size, scale, offset, kernel - - -class RankFilter(Filter): - """ - Create a rank filter. The rank filter sorts all pixels in - a window of the given size, and returns the ``rank``'th value. - - :param size: The kernel size, in pixels. - :param rank: What pixel value to pick. Use 0 for a min filter, - ``size * size / 2`` for a median filter, ``size * size - 1`` - for a max filter, etc. - """ - - name = "Rank" - - def __init__(self, size: int, rank: int) -> None: - self.size = size - self.rank = rank - - def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: - if image.mode == "P": - msg = "cannot filter palette images" - raise ValueError(msg) - image = image.expand(self.size // 2, self.size // 2) - return image.rankfilter(self.size, self.rank) - - -class MedianFilter(RankFilter): - """ - Create a median filter. Picks the median pixel value in a window with the - given size. - - :param size: The kernel size, in pixels. - """ - - name = "Median" - - def __init__(self, size: int = 3) -> None: - self.size = size - self.rank = size * size // 2 - - -class MinFilter(RankFilter): - """ - Create a min filter. Picks the lowest pixel value in a window with the - given size. - - :param size: The kernel size, in pixels. - """ - - name = "Min" - - def __init__(self, size: int = 3) -> None: - self.size = size - self.rank = 0 - - -class MaxFilter(RankFilter): - """ - Create a max filter. Picks the largest pixel value in a window with the - given size. - - :param size: The kernel size, in pixels. - """ - - name = "Max" - - def __init__(self, size: int = 3) -> None: - self.size = size - self.rank = size * size - 1 - - -class ModeFilter(Filter): - """ - Create a mode filter. Picks the most frequent pixel value in a box with the - given size. Pixel values that occur only once or twice are ignored; if no - pixel value occurs more than twice, the original pixel value is preserved. - - :param size: The kernel size, in pixels. - """ - - name = "Mode" - - def __init__(self, size: int = 3) -> None: - self.size = size - - def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: - return image.modefilter(self.size) - - -class GaussianBlur(MultibandFilter): - """Blurs the image with a sequence of extended box filters, which - approximates a Gaussian kernel. For details on accuracy see - - - :param radius: Standard deviation of the Gaussian kernel. Either a sequence of two - numbers for x and y, or a single number for both. - """ - - name = "GaussianBlur" - - def __init__(self, radius: float | Sequence[float] = 2) -> None: - self.radius = radius - - def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: - xy = self.radius - if isinstance(xy, (int, float)): - xy = (xy, xy) - if xy == (0, 0): - return image.copy() - return image.gaussian_blur(xy) - - -class BoxBlur(MultibandFilter): - """Blurs the image by setting each pixel to the average value of the pixels - in a square box extending radius pixels in each direction. - Supports float radius of arbitrary size. Uses an optimized implementation - which runs in linear time relative to the size of the image - for any radius value. - - :param radius: Size of the box in a direction. Either a sequence of two numbers for - x and y, or a single number for both. - - Radius 0 does not blur, returns an identical image. - Radius 1 takes 1 pixel in each direction, i.e. 9 pixels in total. - """ - - name = "BoxBlur" - - def __init__(self, radius: float | Sequence[float]) -> None: - xy = radius if isinstance(radius, (tuple, list)) else (radius, radius) - if xy[0] < 0 or xy[1] < 0: - msg = "radius must be >= 0" - raise ValueError(msg) - self.radius = radius - - def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: - xy = self.radius - if isinstance(xy, (int, float)): - xy = (xy, xy) - if xy == (0, 0): - return image.copy() - return image.box_blur(xy) - - -class UnsharpMask(MultibandFilter): - """Unsharp mask filter. - - See Wikipedia's entry on `digital unsharp masking`_ for an explanation of - the parameters. - - :param radius: Blur Radius - :param percent: Unsharp strength, in percent - :param threshold: Threshold controls the minimum brightness change that - will be sharpened - - .. _digital unsharp masking: https://en.wikipedia.org/wiki/Unsharp_masking#Digital_unsharp_masking - - """ - - name = "UnsharpMask" - - def __init__( - self, radius: float = 2, percent: int = 150, threshold: int = 3 - ) -> None: - self.radius = radius - self.percent = percent - self.threshold = threshold - - def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: - return image.unsharp_mask(self.radius, self.percent, self.threshold) - - -class BLUR(BuiltinFilter): - name = "Blur" - # fmt: off - filterargs = (5, 5), 16, 0, ( - 1, 1, 1, 1, 1, - 1, 0, 0, 0, 1, - 1, 0, 0, 0, 1, - 1, 0, 0, 0, 1, - 1, 1, 1, 1, 1, - ) - # fmt: on - - -class CONTOUR(BuiltinFilter): - name = "Contour" - # fmt: off - filterargs = (3, 3), 1, 255, ( - -1, -1, -1, - -1, 8, -1, - -1, -1, -1, - ) - # fmt: on - - -class DETAIL(BuiltinFilter): - name = "Detail" - # fmt: off - filterargs = (3, 3), 6, 0, ( - 0, -1, 0, - -1, 10, -1, - 0, -1, 0, - ) - # fmt: on - - -class EDGE_ENHANCE(BuiltinFilter): - name = "Edge-enhance" - # fmt: off - filterargs = (3, 3), 2, 0, ( - -1, -1, -1, - -1, 10, -1, - -1, -1, -1, - ) - # fmt: on - - -class EDGE_ENHANCE_MORE(BuiltinFilter): - name = "Edge-enhance More" - # fmt: off - filterargs = (3, 3), 1, 0, ( - -1, -1, -1, - -1, 9, -1, - -1, -1, -1, - ) - # fmt: on - - -class EMBOSS(BuiltinFilter): - name = "Emboss" - # fmt: off - filterargs = (3, 3), 1, 128, ( - -1, 0, 0, - 0, 1, 0, - 0, 0, 0, - ) - # fmt: on - - -class FIND_EDGES(BuiltinFilter): - name = "Find Edges" - # fmt: off - filterargs = (3, 3), 1, 0, ( - -1, -1, -1, - -1, 8, -1, - -1, -1, -1, - ) - # fmt: on - - -class SHARPEN(BuiltinFilter): - name = "Sharpen" - # fmt: off - filterargs = (3, 3), 16, 0, ( - -2, -2, -2, - -2, 32, -2, - -2, -2, -2, - ) - # fmt: on - - -class SMOOTH(BuiltinFilter): - name = "Smooth" - # fmt: off - filterargs = (3, 3), 13, 0, ( - 1, 1, 1, - 1, 5, 1, - 1, 1, 1, - ) - # fmt: on - - -class SMOOTH_MORE(BuiltinFilter): - name = "Smooth More" - # fmt: off - filterargs = (5, 5), 100, 0, ( - 1, 1, 1, 1, 1, - 1, 5, 5, 5, 1, - 1, 5, 44, 5, 1, - 1, 5, 5, 5, 1, - 1, 1, 1, 1, 1, - ) - # fmt: on - - -class Color3DLUT(MultibandFilter): - """Three-dimensional color lookup table. - - Transforms 3-channel pixels using the values of the channels as coordinates - in the 3D lookup table and interpolating the nearest elements. - - This method allows you to apply almost any color transformation - in constant time by using pre-calculated decimated tables. - - .. versionadded:: 5.2.0 - - :param size: Size of the table. One int or tuple of (int, int, int). - Minimal size in any dimension is 2, maximum is 65. - :param table: Flat lookup table. A list of ``channels * size**3`` - float elements or a list of ``size**3`` channels-sized - tuples with floats. Channels are changed first, - then first dimension, then second, then third. - Value 0.0 corresponds lowest value of output, 1.0 highest. - :param channels: Number of channels in the table. Could be 3 or 4. - Default is 3. - :param target_mode: A mode for the result image. Should have not less - than ``channels`` channels. Default is ``None``, - which means that mode wouldn't be changed. - """ - - name = "Color 3D LUT" - - def __init__( - self, - size: int | tuple[int, int, int], - table: Sequence[float] | Sequence[Sequence[int]] | NumpyArray, - channels: int = 3, - target_mode: str | None = None, - **kwargs: bool, - ) -> None: - if channels not in (3, 4): - msg = "Only 3 or 4 output channels are supported" - raise ValueError(msg) - self.size = size = self._check_size(size) - self.channels = channels - self.mode = target_mode - - # Hidden flag `_copy_table=False` could be used to avoid extra copying - # of the table if the table is specially made for the constructor. - copy_table = kwargs.get("_copy_table", True) - items = size[0] * size[1] * size[2] - wrong_size = False - - numpy: ModuleType | None = None - if hasattr(table, "shape"): - try: - import numpy - except ImportError: - pass - - if numpy and isinstance(table, numpy.ndarray): - numpy_table: NumpyArray = table - if copy_table: - numpy_table = numpy_table.copy() - - if numpy_table.shape in [ - (items * channels,), - (items, channels), - (size[2], size[1], size[0], channels), - ]: - table = numpy_table.reshape(items * channels) - else: - wrong_size = True - - else: - if copy_table: - table = list(table) - - # Convert to a flat list - if table and isinstance(table[0], (list, tuple)): - raw_table = cast(Sequence[Sequence[int]], table) - flat_table: list[int] = [] - for pixel in raw_table: - if len(pixel) != channels: - msg = ( - "The elements of the table should " - f"have a length of {channels}." - ) - raise ValueError(msg) - flat_table.extend(pixel) - table = flat_table - - if wrong_size or len(table) != items * channels: - msg = ( - "The table should have either channels * size**3 float items " - "or size**3 items of channels-sized tuples with floats. " - f"Table should be: {channels}x{size[0]}x{size[1]}x{size[2]}. " - f"Actual length: {len(table)}" - ) - raise ValueError(msg) - self.table = table - - @staticmethod - def _check_size(size: Any) -> tuple[int, int, int]: - try: - _, _, _ = size - except ValueError as e: - msg = "Size should be either an integer or a tuple of three integers." - raise ValueError(msg) from e - except TypeError: - size = (size, size, size) - size = tuple(int(x) for x in size) - for size_1d in size: - if not 2 <= size_1d <= 65: - msg = "Size should be in [2, 65] range." - raise ValueError(msg) - return size - - @classmethod - def generate( - cls, - size: int | tuple[int, int, int], - callback: Callable[[float, float, float], tuple[float, ...]], - channels: int = 3, - target_mode: str | None = None, - ) -> Color3DLUT: - """Generates new LUT using provided callback. - - :param size: Size of the table. Passed to the constructor. - :param callback: Function with three parameters which correspond - three color channels. Will be called ``size**3`` - times with values from 0.0 to 1.0 and should return - a tuple with ``channels`` elements. - :param channels: The number of channels which should return callback. - :param target_mode: Passed to the constructor of the resulting - lookup table. - """ - size_1d, size_2d, size_3d = cls._check_size(size) - if channels not in (3, 4): - msg = "Only 3 or 4 output channels are supported" - raise ValueError(msg) - - table: list[float] = [0] * (size_1d * size_2d * size_3d * channels) - idx_out = 0 - for b in range(size_3d): - for g in range(size_2d): - for r in range(size_1d): - table[idx_out : idx_out + channels] = callback( - r / (size_1d - 1), g / (size_2d - 1), b / (size_3d - 1) - ) - idx_out += channels - - return cls( - (size_1d, size_2d, size_3d), - table, - channels=channels, - target_mode=target_mode, - _copy_table=False, - ) - - def transform( - self, - callback: Callable[..., tuple[float, ...]], - with_normals: bool = False, - channels: int | None = None, - target_mode: str | None = None, - ) -> Color3DLUT: - """Transforms the table values using provided callback and returns - a new LUT with altered values. - - :param callback: A function which takes old lookup table values - and returns a new set of values. The number - of arguments which function should take is - ``self.channels`` or ``3 + self.channels`` - if ``with_normals`` flag is set. - Should return a tuple of ``self.channels`` or - ``channels`` elements if it is set. - :param with_normals: If true, ``callback`` will be called with - coordinates in the color cube as the first - three arguments. Otherwise, ``callback`` - will be called only with actual color values. - :param channels: The number of channels in the resulting lookup table. - :param target_mode: Passed to the constructor of the resulting - lookup table. - """ - if channels not in (None, 3, 4): - msg = "Only 3 or 4 output channels are supported" - raise ValueError(msg) - ch_in = self.channels - ch_out = channels or ch_in - size_1d, size_2d, size_3d = self.size - - table: list[float] = [0] * (size_1d * size_2d * size_3d * ch_out) - idx_in = 0 - idx_out = 0 - for b in range(size_3d): - for g in range(size_2d): - for r in range(size_1d): - values = self.table[idx_in : idx_in + ch_in] - if with_normals: - values = callback( - r / (size_1d - 1), - g / (size_2d - 1), - b / (size_3d - 1), - *values, - ) - else: - values = callback(*values) - table[idx_out : idx_out + ch_out] = values - idx_in += ch_in - idx_out += ch_out - - return type(self)( - self.size, - table, - channels=ch_out, - target_mode=target_mode or self.mode, - _copy_table=False, - ) - - def __repr__(self) -> str: - r = [ - f"{self.__class__.__name__} from {self.table.__class__.__name__}", - "size={:d}x{:d}x{:d}".format(*self.size), - f"channels={self.channels:d}", - ] - if self.mode: - r.append(f"target_mode={self.mode}") - return "<{}>".format(" ".join(r)) - - def filter(self, image: _imaging.ImagingCore) -> _imaging.ImagingCore: - from . import Image - - return image.color_lut_3d( - self.mode or image.mode, - Image.Resampling.BILINEAR, - self.channels, - self.size, - self.table, - ) diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/ImageFont.py b/.venv-docs/lib/python3.12/site-packages/PIL/ImageFont.py deleted file mode 100644 index 92eb763a..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/ImageFont.py +++ /dev/null @@ -1,1312 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# PIL raster font management -# -# History: -# 1996-08-07 fl created (experimental) -# 1997-08-25 fl minor adjustments to handle fonts from pilfont 0.3 -# 1999-02-06 fl rewrote most font management stuff in C -# 1999-03-17 fl take pth files into account in load_path (from Richard Jones) -# 2001-02-17 fl added freetype support -# 2001-05-09 fl added TransposedFont wrapper class -# 2002-03-04 fl make sure we have a "L" or "1" font -# 2002-12-04 fl skip non-directory entries in the system path -# 2003-04-29 fl add embedded default font -# 2003-09-27 fl added support for truetype charmap encodings -# -# Todo: -# Adapt to PILFONT2 format (16-bit fonts, compressed, single file) -# -# Copyright (c) 1997-2003 by Secret Labs AB -# Copyright (c) 1996-2003 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# - -from __future__ import annotations - -import base64 -import os -import sys -import warnings -from enum import IntEnum -from io import BytesIO -from types import ModuleType -from typing import IO, Any, BinaryIO, TypedDict, cast - -from . import Image -from ._typing import StrOrBytesPath -from ._util import DeferredError, is_path - -TYPE_CHECKING = False -if TYPE_CHECKING: - from . import ImageFile - from ._imaging import ImagingFont - from ._imagingft import Font - - -class Axis(TypedDict): - minimum: int | None - default: int | None - maximum: int | None - name: bytes | None - - -class Layout(IntEnum): - BASIC = 0 - RAQM = 1 - - -MAX_STRING_LENGTH = 1_000_000 - - -core: ModuleType | DeferredError -try: - from . import _imagingft as core -except ImportError as ex: - core = DeferredError.new(ex) - - -def _string_length_check(text: str | bytes | bytearray) -> None: - if MAX_STRING_LENGTH is not None and len(text) > MAX_STRING_LENGTH: - msg = "too many characters in string" - raise ValueError(msg) - - -# FIXME: add support for pilfont2 format (see FontFile.py) - -# -------------------------------------------------------------------- -# Font metrics format: -# "PILfont" LF -# fontdescriptor LF -# (optional) key=value... LF -# "DATA" LF -# binary data: 256*10*2 bytes (dx, dy, dstbox, srcbox) -# -# To place a character, cut out srcbox and paste at dstbox, -# relative to the character position. Then move the character -# position according to dx, dy. -# -------------------------------------------------------------------- - - -class ImageFont: - """PIL font wrapper""" - - font: ImagingFont - - def _load_pilfont(self, filename: str) -> None: - with open(filename, "rb") as fp: - image: ImageFile.ImageFile | None = None - root = os.path.splitext(filename)[0] - - for ext in (".png", ".gif", ".pbm"): - if image: - image.close() - try: - fullname = root + ext - image = Image.open(fullname) - except Exception: - pass - else: - if image and image.mode in ("1", "L"): - break - else: - if image: - image.close() - - msg = f"cannot find glyph data file {root}.{{gif|pbm|png}}" - raise OSError(msg) - - self.file = fullname - - self._load_pilfont_data(fp, image) - image.close() - - def _load_pilfont_data(self, file: IO[bytes], image: Image.Image) -> None: - # check image - if image.mode not in ("1", "L"): - msg = "invalid font image mode" - raise TypeError(msg) - - # read PILfont header - if file.read(8) != b"PILfont\n": - msg = "Not a PILfont file" - raise SyntaxError(msg) - file.readline() - self.info = [] # FIXME: should be a dictionary - while True: - s = file.readline() - if not s or s == b"DATA\n": - break - self.info.append(s) - - # read PILfont metrics - data = file.read(256 * 20) - - image.load() - - self.font = Image.core.font(image.im, data) - - def getmask( - self, text: str | bytes, mode: str = "", *args: Any, **kwargs: Any - ) -> Image.core.ImagingCore: - """ - Create a bitmap for the text. - - If the font uses antialiasing, the bitmap should have mode ``L`` and use a - maximum value of 255. Otherwise, it should have mode ``1``. - - :param text: Text to render. - :param mode: Used by some graphics drivers to indicate what mode the - driver prefers; if empty, the renderer may return either - mode. Note that the mode is always a string, to simplify - C-level implementations. - - .. versionadded:: 1.1.5 - - :return: An internal PIL storage memory instance as defined by the - :py:mod:`PIL.Image.core` interface module. - """ - _string_length_check(text) - Image._decompression_bomb_check(self.font.getsize(text)) - return self.font.getmask(text, mode) - - def getbbox( - self, text: str | bytes | bytearray, *args: Any, **kwargs: Any - ) -> tuple[int, int, int, int]: - """ - Returns bounding box (in pixels) of given text. - - .. versionadded:: 9.2.0 - - :param text: Text to render. - - :return: ``(left, top, right, bottom)`` bounding box - """ - _string_length_check(text) - width, height = self.font.getsize(text) - return 0, 0, width, height - - def getlength( - self, text: str | bytes | bytearray, *args: Any, **kwargs: Any - ) -> int: - """ - Returns length (in pixels) of given text. - This is the amount by which following text should be offset. - - .. versionadded:: 9.2.0 - """ - _string_length_check(text) - width, height = self.font.getsize(text) - return width - - -## -# Wrapper for FreeType fonts. Application code should use the -# truetype factory function to create font objects. - - -class FreeTypeFont: - """FreeType font wrapper (requires _imagingft service)""" - - font: Font - font_bytes: bytes - - def __init__( - self, - font: StrOrBytesPath | BinaryIO, - size: float = 10, - index: int = 0, - encoding: str = "", - layout_engine: Layout | None = None, - ) -> None: - # FIXME: use service provider instead - - if isinstance(core, DeferredError): - raise core.ex - - if size <= 0: - msg = f"font size must be greater than 0, not {size}" - raise ValueError(msg) - - self.path = font - self.size = size - self.index = index - self.encoding = encoding - - if layout_engine not in (Layout.BASIC, Layout.RAQM): - layout_engine = Layout.BASIC - if core.HAVE_RAQM: - layout_engine = Layout.RAQM - elif layout_engine == Layout.RAQM and not core.HAVE_RAQM: - warnings.warn( - "Raqm layout was requested, but Raqm is not available. " - "Falling back to basic layout." - ) - layout_engine = Layout.BASIC - - self.layout_engine = layout_engine - - def load_from_bytes(f: IO[bytes]) -> None: - self.font_bytes = f.read() - self.font = core.getfont( - "", size, index, encoding, self.font_bytes, layout_engine - ) - - if is_path(font): - font = os.fspath(font) - if sys.platform == "win32": - font_bytes_path = font if isinstance(font, bytes) else font.encode() - try: - font_bytes_path.decode("ascii") - except UnicodeDecodeError: - # FreeType cannot load fonts with non-ASCII characters on Windows - # So load it into memory first - with open(font, "rb") as f: - load_from_bytes(f) - return - self.font = core.getfont( - font, size, index, encoding, layout_engine=layout_engine - ) - else: - load_from_bytes(cast(IO[bytes], font)) - - def __getstate__(self) -> list[Any]: - return [self.path, self.size, self.index, self.encoding, self.layout_engine] - - def __setstate__(self, state: list[Any]) -> None: - path, size, index, encoding, layout_engine = state - FreeTypeFont.__init__(self, path, size, index, encoding, layout_engine) - - def getname(self) -> tuple[str | None, str | None]: - """ - :return: A tuple of the font family (e.g. Helvetica) and the font style - (e.g. Bold) - """ - return self.font.family, self.font.style - - def getmetrics(self) -> tuple[int, int]: - """ - :return: A tuple of the font ascent (the distance from the baseline to - the highest outline point) and descent (the distance from the - baseline to the lowest outline point, a negative value) - """ - return self.font.ascent, self.font.descent - - def getlength( - self, - text: str | bytes, - mode: str = "", - direction: str | None = None, - features: list[str] | None = None, - language: str | None = None, - ) -> float: - """ - Returns length (in pixels with 1/64 precision) of given text when rendered - in font with provided direction, features, and language. - - This is the amount by which following text should be offset. - Text bounding box may extend past the length in some fonts, - e.g. when using italics or accents. - - The result is returned as a float; it is a whole number if using basic layout. - - Note that the sum of two lengths may not equal the length of a concatenated - string due to kerning. If you need to adjust for kerning, include the following - character and subtract its length. - - For example, instead of :: - - hello = font.getlength("Hello") - world = font.getlength("World") - hello_world = hello + world # not adjusted for kerning - assert hello_world == font.getlength("HelloWorld") # may fail - - use :: - - hello = font.getlength("HelloW") - font.getlength("W") # adjusted for kerning - world = font.getlength("World") - hello_world = hello + world # adjusted for kerning - assert hello_world == font.getlength("HelloWorld") # True - - or disable kerning with (requires libraqm) :: - - hello = draw.textlength("Hello", font, features=["-kern"]) - world = draw.textlength("World", font, features=["-kern"]) - hello_world = hello + world # kerning is disabled, no need to adjust - assert hello_world == draw.textlength("HelloWorld", font, features=["-kern"]) - - .. versionadded:: 8.0.0 - - :param text: Text to measure. - :param mode: Used by some graphics drivers to indicate what mode the - driver prefers; if empty, the renderer may return either - mode. Note that the mode is always a string, to simplify - C-level implementations. - - :param direction: Direction of the text. It can be 'rtl' (right to - left), 'ltr' (left to right) or 'ttb' (top to bottom). - Requires libraqm. - - :param features: A list of OpenType font features to be used during text - layout. This is usually used to turn on optional - font features that are not enabled by default, - for example 'dlig' or 'ss01', but can be also - used to turn off default font features for - example '-liga' to disable ligatures or '-kern' - to disable kerning. To get all supported - features, see - https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist - Requires libraqm. - - :param language: Language of the text. Different languages may use - different glyph shapes or ligatures. This parameter tells - the font which language the text is in, and to apply the - correct substitutions as appropriate, if available. - It should be a `BCP 47 language code - `_ - Requires libraqm. - - :return: Either width for horizontal text, or height for vertical text. - """ - _string_length_check(text) - return self.font.getlength(text, mode, direction, features, language) / 64 - - def getbbox( - self, - text: str | bytes, - mode: str = "", - direction: str | None = None, - features: list[str] | None = None, - language: str | None = None, - stroke_width: float = 0, - anchor: str | None = None, - ) -> tuple[float, float, float, float]: - """ - Returns bounding box (in pixels) of given text relative to given anchor - when rendered in font with provided direction, features, and language. - - Use :py:meth:`getlength()` to get the offset of following text with - 1/64 pixel precision. The bounding box includes extra margins for - some fonts, e.g. italics or accents. - - .. versionadded:: 8.0.0 - - :param text: Text to render. - :param mode: Used by some graphics drivers to indicate what mode the - driver prefers; if empty, the renderer may return either - mode. Note that the mode is always a string, to simplify - C-level implementations. - - :param direction: Direction of the text. It can be 'rtl' (right to - left), 'ltr' (left to right) or 'ttb' (top to bottom). - Requires libraqm. - - :param features: A list of OpenType font features to be used during text - layout. This is usually used to turn on optional - font features that are not enabled by default, - for example 'dlig' or 'ss01', but can be also - used to turn off default font features for - example '-liga' to disable ligatures or '-kern' - to disable kerning. To get all supported - features, see - https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist - Requires libraqm. - - :param language: Language of the text. Different languages may use - different glyph shapes or ligatures. This parameter tells - the font which language the text is in, and to apply the - correct substitutions as appropriate, if available. - It should be a `BCP 47 language code - `_ - Requires libraqm. - - :param stroke_width: The width of the text stroke. - - :param anchor: The text anchor alignment. Determines the relative location of - the anchor to the text. The default alignment is top left, - specifically ``la`` for horizontal text and ``lt`` for - vertical text. See :ref:`text-anchors` for details. - - :return: ``(left, top, right, bottom)`` bounding box - """ - _string_length_check(text) - size, offset = self.font.getsize( - text, mode, direction, features, language, anchor - ) - left, top = offset[0] - stroke_width, offset[1] - stroke_width - width, height = size[0] + 2 * stroke_width, size[1] + 2 * stroke_width - return left, top, left + width, top + height - - def getmask( - self, - text: str | bytes, - mode: str = "", - direction: str | None = None, - features: list[str] | None = None, - language: str | None = None, - stroke_width: float = 0, - anchor: str | None = None, - ink: int = 0, - start: tuple[float, float] | None = None, - ) -> Image.core.ImagingCore: - """ - Create a bitmap for the text. - - If the font uses antialiasing, the bitmap should have mode ``L`` and use a - maximum value of 255. If the font has embedded color data, the bitmap - should have mode ``RGBA``. Otherwise, it should have mode ``1``. - - :param text: Text to render. - :param mode: Used by some graphics drivers to indicate what mode the - driver prefers; if empty, the renderer may return either - mode. Note that the mode is always a string, to simplify - C-level implementations. - - .. versionadded:: 1.1.5 - - :param direction: Direction of the text. It can be 'rtl' (right to - left), 'ltr' (left to right) or 'ttb' (top to bottom). - Requires libraqm. - - .. versionadded:: 4.2.0 - - :param features: A list of OpenType font features to be used during text - layout. This is usually used to turn on optional - font features that are not enabled by default, - for example 'dlig' or 'ss01', but can be also - used to turn off default font features for - example '-liga' to disable ligatures or '-kern' - to disable kerning. To get all supported - features, see - https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist - Requires libraqm. - - .. versionadded:: 4.2.0 - - :param language: Language of the text. Different languages may use - different glyph shapes or ligatures. This parameter tells - the font which language the text is in, and to apply the - correct substitutions as appropriate, if available. - It should be a `BCP 47 language code - `_ - Requires libraqm. - - .. versionadded:: 6.0.0 - - :param stroke_width: The width of the text stroke. - - .. versionadded:: 6.2.0 - - :param anchor: The text anchor alignment. Determines the relative location of - the anchor to the text. The default alignment is top left, - specifically ``la`` for horizontal text and ``lt`` for - vertical text. See :ref:`text-anchors` for details. - - .. versionadded:: 8.0.0 - - :param ink: Foreground ink for rendering in RGBA mode. - - .. versionadded:: 8.0.0 - - :param start: Tuple of horizontal and vertical offset, as text may render - differently when starting at fractional coordinates. - - .. versionadded:: 9.4.0 - - :return: An internal PIL storage memory instance as defined by the - :py:mod:`PIL.Image.core` interface module. - """ - return self.getmask2( - text, - mode, - direction=direction, - features=features, - language=language, - stroke_width=stroke_width, - anchor=anchor, - ink=ink, - start=start, - )[0] - - def getmask2( - self, - text: str | bytes, - mode: str = "", - direction: str | None = None, - features: list[str] | None = None, - language: str | None = None, - stroke_width: float = 0, - anchor: str | None = None, - ink: int = 0, - start: tuple[float, float] | None = None, - *args: Any, - **kwargs: Any, - ) -> tuple[Image.core.ImagingCore, tuple[int, int]]: - """ - Create a bitmap for the text. - - If the font uses antialiasing, the bitmap should have mode ``L`` and use a - maximum value of 255. If the font has embedded color data, the bitmap - should have mode ``RGBA``. Otherwise, it should have mode ``1``. - - :param text: Text to render. - :param mode: Used by some graphics drivers to indicate what mode the - driver prefers; if empty, the renderer may return either - mode. Note that the mode is always a string, to simplify - C-level implementations. - - .. versionadded:: 1.1.5 - - :param direction: Direction of the text. It can be 'rtl' (right to - left), 'ltr' (left to right) or 'ttb' (top to bottom). - Requires libraqm. - - .. versionadded:: 4.2.0 - - :param features: A list of OpenType font features to be used during text - layout. This is usually used to turn on optional - font features that are not enabled by default, - for example 'dlig' or 'ss01', but can be also - used to turn off default font features for - example '-liga' to disable ligatures or '-kern' - to disable kerning. To get all supported - features, see - https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist - Requires libraqm. - - .. versionadded:: 4.2.0 - - :param language: Language of the text. Different languages may use - different glyph shapes or ligatures. This parameter tells - the font which language the text is in, and to apply the - correct substitutions as appropriate, if available. - It should be a `BCP 47 language code - `_ - Requires libraqm. - - .. versionadded:: 6.0.0 - - :param stroke_width: The width of the text stroke. - - .. versionadded:: 6.2.0 - - :param anchor: The text anchor alignment. Determines the relative location of - the anchor to the text. The default alignment is top left, - specifically ``la`` for horizontal text and ``lt`` for - vertical text. See :ref:`text-anchors` for details. - - .. versionadded:: 8.0.0 - - :param ink: Foreground ink for rendering in RGBA mode. - - .. versionadded:: 8.0.0 - - :param start: Tuple of horizontal and vertical offset, as text may render - differently when starting at fractional coordinates. - - .. versionadded:: 9.4.0 - - :return: A tuple of an internal PIL storage memory instance as defined by the - :py:mod:`PIL.Image.core` interface module, and the text offset, the - gap between the starting coordinate and the first marking - """ - _string_length_check(text) - if start is None: - start = (0, 0) - - def fill(width: int, height: int) -> Image.core.ImagingCore: - size = (width, height) - Image._decompression_bomb_check(size) - return Image.core.fill("RGBA" if mode == "RGBA" else "L", size) - - return self.font.render( - text, - fill, - mode, - direction, - features, - language, - stroke_width, - kwargs.get("stroke_filled", False), - anchor, - ink, - start, - ) - - def font_variant( - self, - font: StrOrBytesPath | BinaryIO | None = None, - size: float | None = None, - index: int | None = None, - encoding: str | None = None, - layout_engine: Layout | None = None, - ) -> FreeTypeFont: - """ - Create a copy of this FreeTypeFont object, - using any specified arguments to override the settings. - - Parameters are identical to the parameters used to initialize this - object. - - :return: A FreeTypeFont object. - """ - if font is None: - try: - font = BytesIO(self.font_bytes) - except AttributeError: - font = self.path - return FreeTypeFont( - font=font, - size=self.size if size is None else size, - index=self.index if index is None else index, - encoding=self.encoding if encoding is None else encoding, - layout_engine=layout_engine or self.layout_engine, - ) - - def get_variation_names(self) -> list[bytes]: - """ - :returns: A list of the named styles in a variation font. - :exception OSError: If the font is not a variation font. - """ - names = self.font.getvarnames() - return [name.replace(b"\x00", b"") for name in names] - - def set_variation_by_name(self, name: str | bytes) -> None: - """ - :param name: The name of the style. - :exception OSError: If the font is not a variation font. - """ - names = self.get_variation_names() - if not isinstance(name, bytes): - name = name.encode() - index = names.index(name) + 1 - - if index == getattr(self, "_last_variation_index", None): - # When the same name is set twice in a row, - # there is an 'unknown freetype error' - # https://savannah.nongnu.org/bugs/?56186 - return - self._last_variation_index = index - - self.font.setvarname(index) - - def get_variation_axes(self) -> list[Axis]: - """ - :returns: A list of the axes in a variation font. - :exception OSError: If the font is not a variation font. - """ - axes = self.font.getvaraxes() - for axis in axes: - if axis["name"]: - axis["name"] = axis["name"].replace(b"\x00", b"") - return axes - - def set_variation_by_axes(self, axes: list[float]) -> None: - """ - :param axes: A list of values for each axis. - :exception OSError: If the font is not a variation font. - """ - self.font.setvaraxes(axes) - - -class TransposedFont: - """Wrapper for writing rotated or mirrored text""" - - def __init__( - self, font: ImageFont | FreeTypeFont, orientation: Image.Transpose | None = None - ): - """ - Wrapper that creates a transposed font from any existing font - object. - - :param font: A font object. - :param orientation: An optional orientation. If given, this should - be one of Image.Transpose.FLIP_LEFT_RIGHT, Image.Transpose.FLIP_TOP_BOTTOM, - Image.Transpose.ROTATE_90, Image.Transpose.ROTATE_180, or - Image.Transpose.ROTATE_270. - """ - self.font = font - self.orientation = orientation # any 'transpose' argument, or None - - def getmask( - self, text: str | bytes, mode: str = "", *args: Any, **kwargs: Any - ) -> Image.core.ImagingCore: - im = self.font.getmask(text, mode, *args, **kwargs) - if self.orientation is not None: - return im.transpose(self.orientation) - return im - - def getbbox( - self, text: str | bytes, *args: Any, **kwargs: Any - ) -> tuple[int, int, float, float]: - # TransposedFont doesn't support getmask2, move top-left point to (0, 0) - # this has no effect on ImageFont and simulates anchor="lt" for FreeTypeFont - left, top, right, bottom = self.font.getbbox(text, *args, **kwargs) - width = right - left - height = bottom - top - if self.orientation in (Image.Transpose.ROTATE_90, Image.Transpose.ROTATE_270): - return 0, 0, height, width - return 0, 0, width, height - - def getlength(self, text: str | bytes, *args: Any, **kwargs: Any) -> float: - if self.orientation in (Image.Transpose.ROTATE_90, Image.Transpose.ROTATE_270): - msg = "text length is undefined for text rotated by 90 or 270 degrees" - raise ValueError(msg) - return self.font.getlength(text, *args, **kwargs) - - -def load(filename: str) -> ImageFont: - """ - Load a font file. This function loads a font object from the given - bitmap font file, and returns the corresponding font object. For loading TrueType - or OpenType fonts instead, see :py:func:`~PIL.ImageFont.truetype`. - - :param filename: Name of font file. - :return: A font object. - :exception OSError: If the file could not be read. - """ - f = ImageFont() - f._load_pilfont(filename) - return f - - -def truetype( - font: StrOrBytesPath | BinaryIO, - size: float = 10, - index: int = 0, - encoding: str = "", - layout_engine: Layout | None = None, -) -> FreeTypeFont: - """ - Load a TrueType or OpenType font from a file or file-like object, - and create a font object. This function loads a font object from the given - file or file-like object, and creates a font object for a font of the given - size. For loading bitmap fonts instead, see :py:func:`~PIL.ImageFont.load` - and :py:func:`~PIL.ImageFont.load_path`. - - Pillow uses FreeType to open font files. On Windows, be aware that FreeType - will keep the file open as long as the FreeTypeFont object exists. Windows - limits the number of files that can be open in C at once to 512, so if many - fonts are opened simultaneously and that limit is approached, an - ``OSError`` may be thrown, reporting that FreeType "cannot open resource". - A workaround would be to copy the file(s) into memory, and open that instead. - - This function requires the _imagingft service. - - :param font: A filename or file-like object containing a TrueType font. - If the file is not found in this filename, the loader may also - search in other directories, such as: - - * The :file:`fonts/` directory on Windows, - * :file:`/Library/Fonts/`, :file:`/System/Library/Fonts/` - and :file:`~/Library/Fonts/` on macOS. - * :file:`~/.local/share/fonts`, :file:`/usr/local/share/fonts`, - and :file:`/usr/share/fonts` on Linux; or those specified by - the ``XDG_DATA_HOME`` and ``XDG_DATA_DIRS`` environment variables - for user-installed and system-wide fonts, respectively. - - :param size: The requested size, in pixels. - :param index: Which font face to load (default is first available face). - :param encoding: Which font encoding to use (default is Unicode). Possible - encodings include (see the FreeType documentation for more - information): - - * "unic" (Unicode) - * "symb" (Microsoft Symbol) - * "ADOB" (Adobe Standard) - * "ADBE" (Adobe Expert) - * "ADBC" (Adobe Custom) - * "armn" (Apple Roman) - * "sjis" (Shift JIS) - * "gb " (PRC) - * "big5" - * "wans" (Extended Wansung) - * "joha" (Johab) - * "lat1" (Latin-1) - - This specifies the character set to use. It does not alter the - encoding of any text provided in subsequent operations. - :param layout_engine: Which layout engine to use, if available: - :attr:`.ImageFont.Layout.BASIC` or :attr:`.ImageFont.Layout.RAQM`. - If it is available, Raqm layout will be used by default. - Otherwise, basic layout will be used. - - Raqm layout is recommended for all non-English text. If Raqm layout - is not required, basic layout will have better performance. - - You can check support for Raqm layout using - :py:func:`PIL.features.check_feature` with ``feature="raqm"``. - - .. versionadded:: 4.2.0 - :return: A font object. - :exception OSError: If the file could not be read. - :exception ValueError: If the font size is not greater than zero. - """ - - def freetype(font: StrOrBytesPath | BinaryIO) -> FreeTypeFont: - return FreeTypeFont(font, size, index, encoding, layout_engine) - - try: - return freetype(font) - except OSError: - if not is_path(font): - raise - ttf_filename = os.path.basename(font) - - dirs = [] - if sys.platform == "win32": - # check the windows font repository - # NOTE: must use uppercase WINDIR, to work around bugs in - # 1.5.2's os.environ.get() - windir = os.environ.get("WINDIR") - if windir: - dirs.append(os.path.join(windir, "fonts")) - elif sys.platform in ("linux", "linux2"): - data_home = os.environ.get("XDG_DATA_HOME") - if not data_home: - # The freedesktop spec defines the following default directory for - # when XDG_DATA_HOME is unset or empty. This user-level directory - # takes precedence over system-level directories. - data_home = os.path.expanduser("~/.local/share") - xdg_dirs = [data_home] - - data_dirs = os.environ.get("XDG_DATA_DIRS") - if not data_dirs: - # Similarly, defaults are defined for the system-level directories - data_dirs = "/usr/local/share:/usr/share" - xdg_dirs += data_dirs.split(":") - - dirs += [os.path.join(xdg_dir, "fonts") for xdg_dir in xdg_dirs] - elif sys.platform == "darwin": - dirs += [ - "/Library/Fonts", - "/System/Library/Fonts", - os.path.expanduser("~/Library/Fonts"), - ] - - ext = os.path.splitext(ttf_filename)[1] - first_font_with_a_different_extension = None - for directory in dirs: - for walkroot, walkdir, walkfilenames in os.walk(directory): - for walkfilename in walkfilenames: - if ext and walkfilename == ttf_filename: - return freetype(os.path.join(walkroot, walkfilename)) - elif not ext and os.path.splitext(walkfilename)[0] == ttf_filename: - fontpath = os.path.join(walkroot, walkfilename) - if os.path.splitext(fontpath)[1] == ".ttf": - return freetype(fontpath) - if not ext and first_font_with_a_different_extension is None: - first_font_with_a_different_extension = fontpath - if first_font_with_a_different_extension: - return freetype(first_font_with_a_different_extension) - raise - - -def load_path(filename: str | bytes) -> ImageFont: - """ - Load font file. Same as :py:func:`~PIL.ImageFont.load`, but searches for a - bitmap font along the Python path. - - :param filename: Name of font file. - :return: A font object. - :exception OSError: If the file could not be read. - """ - if not isinstance(filename, str): - filename = filename.decode("utf-8") - for directory in sys.path: - try: - return load(os.path.join(directory, filename)) - except OSError: - pass - msg = f'cannot find font file "{filename}" in sys.path' - if os.path.exists(filename): - msg += f', did you mean ImageFont.load("{filename}") instead?' - - raise OSError(msg) - - -def load_default_imagefont() -> ImageFont: - f = ImageFont() - f._load_pilfont_data( - # courB08 - BytesIO( - base64.b64decode( - b""" -UElMZm9udAo7Ozs7OzsxMDsKREFUQQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAA//8AAQAAAAAAAAABAAEA -BgAAAAH/+gADAAAAAQAAAAMABgAGAAAAAf/6AAT//QADAAAABgADAAYAAAAA//kABQABAAYAAAAL -AAgABgAAAAD/+AAFAAEACwAAABAACQAGAAAAAP/5AAUAAAAQAAAAFQAHAAYAAP////oABQAAABUA -AAAbAAYABgAAAAH/+QAE//wAGwAAAB4AAwAGAAAAAf/5AAQAAQAeAAAAIQAIAAYAAAAB//kABAAB -ACEAAAAkAAgABgAAAAD/+QAE//0AJAAAACgABAAGAAAAAP/6AAX//wAoAAAALQAFAAYAAAAB//8A -BAACAC0AAAAwAAMABgAAAAD//AAF//0AMAAAADUAAQAGAAAAAf//AAMAAAA1AAAANwABAAYAAAAB -//kABQABADcAAAA7AAgABgAAAAD/+QAFAAAAOwAAAEAABwAGAAAAAP/5AAYAAABAAAAARgAHAAYA -AAAA//kABQAAAEYAAABLAAcABgAAAAD/+QAFAAAASwAAAFAABwAGAAAAAP/5AAYAAABQAAAAVgAH -AAYAAAAA//kABQAAAFYAAABbAAcABgAAAAD/+QAFAAAAWwAAAGAABwAGAAAAAP/5AAUAAABgAAAA -ZQAHAAYAAAAA//kABQAAAGUAAABqAAcABgAAAAD/+QAFAAAAagAAAG8ABwAGAAAAAf/8AAMAAABv -AAAAcQAEAAYAAAAA//wAAwACAHEAAAB0AAYABgAAAAD/+gAE//8AdAAAAHgABQAGAAAAAP/7AAT/ -/gB4AAAAfAADAAYAAAAB//oABf//AHwAAACAAAUABgAAAAD/+gAFAAAAgAAAAIUABgAGAAAAAP/5 -AAYAAQCFAAAAiwAIAAYAAP////oABgAAAIsAAACSAAYABgAA////+gAFAAAAkgAAAJgABgAGAAAA -AP/6AAUAAACYAAAAnQAGAAYAAP////oABQAAAJ0AAACjAAYABgAA////+gAFAAAAowAAAKkABgAG -AAD////6AAUAAACpAAAArwAGAAYAAAAA//oABQAAAK8AAAC0AAYABgAA////+gAGAAAAtAAAALsA -BgAGAAAAAP/6AAQAAAC7AAAAvwAGAAYAAP////oABQAAAL8AAADFAAYABgAA////+gAGAAAAxQAA -AMwABgAGAAD////6AAUAAADMAAAA0gAGAAYAAP////oABQAAANIAAADYAAYABgAA////+gAGAAAA -2AAAAN8ABgAGAAAAAP/6AAUAAADfAAAA5AAGAAYAAP////oABQAAAOQAAADqAAYABgAAAAD/+gAF -AAEA6gAAAO8ABwAGAAD////6AAYAAADvAAAA9gAGAAYAAAAA//oABQAAAPYAAAD7AAYABgAA//// -+gAFAAAA+wAAAQEABgAGAAD////6AAYAAAEBAAABCAAGAAYAAP////oABgAAAQgAAAEPAAYABgAA -////+gAGAAABDwAAARYABgAGAAAAAP/6AAYAAAEWAAABHAAGAAYAAP////oABgAAARwAAAEjAAYA -BgAAAAD/+gAFAAABIwAAASgABgAGAAAAAf/5AAQAAQEoAAABKwAIAAYAAAAA//kABAABASsAAAEv -AAgABgAAAAH/+QAEAAEBLwAAATIACAAGAAAAAP/5AAX//AEyAAABNwADAAYAAAAAAAEABgACATcA -AAE9AAEABgAAAAH/+QAE//wBPQAAAUAAAwAGAAAAAP/7AAYAAAFAAAABRgAFAAYAAP////kABQAA -AUYAAAFMAAcABgAAAAD/+wAFAAABTAAAAVEABQAGAAAAAP/5AAYAAAFRAAABVwAHAAYAAAAA//sA -BQAAAVcAAAFcAAUABgAAAAD/+QAFAAABXAAAAWEABwAGAAAAAP/7AAYAAgFhAAABZwAHAAYAAP// -//kABQAAAWcAAAFtAAcABgAAAAD/+QAGAAABbQAAAXMABwAGAAAAAP/5AAQAAgFzAAABdwAJAAYA -AP////kABgAAAXcAAAF+AAcABgAAAAD/+QAGAAABfgAAAYQABwAGAAD////7AAUAAAGEAAABigAF -AAYAAP////sABQAAAYoAAAGQAAUABgAAAAD/+wAFAAABkAAAAZUABQAGAAD////7AAUAAgGVAAAB -mwAHAAYAAAAA//sABgACAZsAAAGhAAcABgAAAAD/+wAGAAABoQAAAacABQAGAAAAAP/7AAYAAAGn -AAABrQAFAAYAAAAA//kABgAAAa0AAAGzAAcABgAA////+wAGAAABswAAAboABQAGAAD////7AAUA -AAG6AAABwAAFAAYAAP////sABgAAAcAAAAHHAAUABgAAAAD/+wAGAAABxwAAAc0ABQAGAAD////7 -AAYAAgHNAAAB1AAHAAYAAAAA//sABQAAAdQAAAHZAAUABgAAAAH/+QAFAAEB2QAAAd0ACAAGAAAA -Av/6AAMAAQHdAAAB3gAHAAYAAAAA//kABAABAd4AAAHiAAgABgAAAAD/+wAF//0B4gAAAecAAgAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAB -//sAAwACAecAAAHpAAcABgAAAAD/+QAFAAEB6QAAAe4ACAAGAAAAAP/5AAYAAAHuAAAB9AAHAAYA -AAAA//oABf//AfQAAAH5AAUABgAAAAD/+QAGAAAB+QAAAf8ABwAGAAAAAv/5AAMAAgH/AAACAAAJ -AAYAAAAA//kABQABAgAAAAIFAAgABgAAAAH/+gAE//sCBQAAAggAAQAGAAAAAP/5AAYAAAIIAAAC -DgAHAAYAAAAB//kABf/+Ag4AAAISAAUABgAA////+wAGAAACEgAAAhkABQAGAAAAAP/7AAX//gIZ -AAACHgADAAYAAAAA//wABf/9Ah4AAAIjAAEABgAAAAD/+QAHAAACIwAAAioABwAGAAAAAP/6AAT/ -+wIqAAACLgABAAYAAAAA//kABP/8Ai4AAAIyAAMABgAAAAD/+gAFAAACMgAAAjcABgAGAAAAAf/5 -AAT//QI3AAACOgAEAAYAAAAB//kABP/9AjoAAAI9AAQABgAAAAL/+QAE//sCPQAAAj8AAgAGAAD/ -///7AAYAAgI/AAACRgAHAAYAAAAA//kABgABAkYAAAJMAAgABgAAAAH//AAD//0CTAAAAk4AAQAG -AAAAAf//AAQAAgJOAAACUQADAAYAAAAB//kABP/9AlEAAAJUAAQABgAAAAH/+QAF//4CVAAAAlgA -BQAGAAD////7AAYAAAJYAAACXwAFAAYAAP////kABgAAAl8AAAJmAAcABgAA////+QAGAAACZgAA -Am0ABwAGAAD////5AAYAAAJtAAACdAAHAAYAAAAA//sABQACAnQAAAJ5AAcABgAA////9wAGAAAC -eQAAAoAACQAGAAD////3AAYAAAKAAAAChwAJAAYAAP////cABgAAAocAAAKOAAkABgAA////9wAG -AAACjgAAApUACQAGAAD////4AAYAAAKVAAACnAAIAAYAAP////cABgAAApwAAAKjAAkABgAA//// -+gAGAAACowAAAqoABgAGAAAAAP/6AAUAAgKqAAACrwAIAAYAAP////cABQAAAq8AAAK1AAkABgAA -////9wAFAAACtQAAArsACQAGAAD////3AAUAAAK7AAACwQAJAAYAAP////gABQAAAsEAAALHAAgA -BgAAAAD/9wAEAAACxwAAAssACQAGAAAAAP/3AAQAAALLAAACzwAJAAYAAAAA//cABAAAAs8AAALT -AAkABgAAAAD/+AAEAAAC0wAAAtcACAAGAAD////6AAUAAALXAAAC3QAGAAYAAP////cABgAAAt0A -AALkAAkABgAAAAD/9wAFAAAC5AAAAukACQAGAAAAAP/3AAUAAALpAAAC7gAJAAYAAAAA//cABQAA -Au4AAALzAAkABgAAAAD/9wAFAAAC8wAAAvgACQAGAAAAAP/4AAUAAAL4AAAC/QAIAAYAAAAA//oA -Bf//Av0AAAMCAAUABgAA////+gAGAAADAgAAAwkABgAGAAD////3AAYAAAMJAAADEAAJAAYAAP// -//cABgAAAxAAAAMXAAkABgAA////9wAGAAADFwAAAx4ACQAGAAD////4AAYAAAAAAAoABwASAAYA -AP////cABgAAAAcACgAOABMABgAA////+gAFAAAADgAKABQAEAAGAAD////6AAYAAAAUAAoAGwAQ -AAYAAAAA//gABgAAABsACgAhABIABgAAAAD/+AAGAAAAIQAKACcAEgAGAAAAAP/4AAYAAAAnAAoA -LQASAAYAAAAA//gABgAAAC0ACgAzABIABgAAAAD/+QAGAAAAMwAKADkAEQAGAAAAAP/3AAYAAAA5 -AAoAPwATAAYAAP////sABQAAAD8ACgBFAA8ABgAAAAD/+wAFAAIARQAKAEoAEQAGAAAAAP/4AAUA -AABKAAoATwASAAYAAAAA//gABQAAAE8ACgBUABIABgAAAAD/+AAFAAAAVAAKAFkAEgAGAAAAAP/5 -AAUAAABZAAoAXgARAAYAAAAA//gABgAAAF4ACgBkABIABgAAAAD/+AAGAAAAZAAKAGoAEgAGAAAA -AP/4AAYAAABqAAoAcAASAAYAAAAA//kABgAAAHAACgB2ABEABgAAAAD/+AAFAAAAdgAKAHsAEgAG -AAD////4AAYAAAB7AAoAggASAAYAAAAA//gABQAAAIIACgCHABIABgAAAAD/+AAFAAAAhwAKAIwA -EgAGAAAAAP/4AAUAAACMAAoAkQASAAYAAAAA//gABQAAAJEACgCWABIABgAAAAD/+QAFAAAAlgAK -AJsAEQAGAAAAAP/6AAX//wCbAAoAoAAPAAYAAAAA//oABQABAKAACgClABEABgAA////+AAGAAAA -pQAKAKwAEgAGAAD////4AAYAAACsAAoAswASAAYAAP////gABgAAALMACgC6ABIABgAA////+QAG -AAAAugAKAMEAEQAGAAD////4AAYAAgDBAAoAyAAUAAYAAP////kABQACAMgACgDOABMABgAA//// -+QAGAAIAzgAKANUAEw== -""" - ) - ), - Image.open( - BytesIO( - base64.b64decode( - b""" -iVBORw0KGgoAAAANSUhEUgAAAx4AAAAUAQAAAAArMtZoAAAEwElEQVR4nABlAJr/AHVE4czCI/4u -Mc4b7vuds/xzjz5/3/7u/n9vMe7vnfH/9++vPn/xyf5zhxzjt8GHw8+2d83u8x27199/nxuQ6Od9 -M43/5z2I+9n9ZtmDBwMQECDRQw/eQIQohJXxpBCNVE6QCCAAAAD//wBlAJr/AgALyj1t/wINwq0g -LeNZUworuN1cjTPIzrTX6ofHWeo3v336qPzfEwRmBnHTtf95/fglZK5N0PDgfRTslpGBvz7LFc4F -IUXBWQGjQ5MGCx34EDFPwXiY4YbYxavpnhHFrk14CDAAAAD//wBlAJr/AgKqRooH2gAgPeggvUAA -Bu2WfgPoAwzRAABAAAAAAACQgLz/3Uv4Gv+gX7BJgDeeGP6AAAD1NMDzKHD7ANWr3loYbxsAD791 -NAADfcoIDyP44K/jv4Y63/Z+t98Ovt+ub4T48LAAAAD//wBlAJr/AuplMlADJAAAAGuAphWpqhMx -in0A/fRvAYBABPgBwBUgABBQ/sYAyv9g0bCHgOLoGAAAAAAAREAAwI7nr0ArYpow7aX8//9LaP/9 -SjdavWA8ePHeBIKB//81/83ndznOaXx379wAAAD//wBlAJr/AqDxW+D3AABAAbUh/QMnbQag/gAY -AYDAAACgtgD/gOqAAAB5IA/8AAAk+n9w0AAA8AAAmFRJuPo27ciC0cD5oeW4E7KA/wD3ECMAn2tt -y8PgwH8AfAxFzC0JzeAMtratAsC/ffwAAAD//wBlAJr/BGKAyCAA4AAAAvgeYTAwHd1kmQF5chkG -ABoMIHcL5xVpTfQbUqzlAAAErwAQBgAAEOClA5D9il08AEh/tUzdCBsXkbgACED+woQg8Si9VeqY -lODCn7lmF6NhnAEYgAAA/NMIAAAAAAD//2JgjLZgVGBg5Pv/Tvpc8hwGBjYGJADjHDrAwPzAjv/H -/Wf3PzCwtzcwHmBgYGcwbZz8wHaCAQMDOwMDQ8MCBgYOC3W7mp+f0w+wHOYxO3OG+e376hsMZjk3 -AAAAAP//YmCMY2A4wMAIN5e5gQETPD6AZisDAwMDgzSDAAPjByiHcQMDAwMDg1nOze1lByRu5/47 -c4859311AYNZzg0AAAAA//9iYGDBYihOIIMuwIjGL39/fwffA8b//xv/P2BPtzzHwCBjUQAAAAD/ -/yLFBrIBAAAA//9i1HhcwdhizX7u8NZNzyLbvT97bfrMf/QHI8evOwcSqGUJAAAA//9iYBB81iSw -pEE170Qrg5MIYydHqwdDQRMrAwcVrQAAAAD//2J4x7j9AAMDn8Q/BgYLBoaiAwwMjPdvMDBYM1Tv -oJodAAAAAP//Yqo/83+dxePWlxl3npsel9lvLfPcqlE9725C+acfVLMEAAAA//9i+s9gwCoaaGMR -evta/58PTEWzr21hufPjA8N+qlnBwAAAAAD//2JiWLci5v1+HmFXDqcnULE/MxgYGBj+f6CaJQAA -AAD//2Ji2FrkY3iYpYC5qDeGgeEMAwPDvwQBBoYvcTwOVLMEAAAA//9isDBgkP///0EOg9z35v// -Gc/eeW7BwPj5+QGZhANUswMAAAD//2JgqGBgYGBgqEMXlvhMPUsAAAAA//8iYDd1AAAAAP//AwDR -w7IkEbzhVQAAAABJRU5ErkJggg== -""" - ) - ) - ), - ) - return f - - -def load_default(size: float | None = None) -> FreeTypeFont | ImageFont: - """If FreeType support is available, load a version of Aileron Regular, - https://dotcolon.net/fonts/aileron, with a more limited character set. - - Otherwise, load a "better than nothing" font. - - .. versionadded:: 1.1.4 - - :param size: The font size of Aileron Regular. - - .. versionadded:: 10.1.0 - - :return: A font object. - """ - if isinstance(core, ModuleType) or size is not None: - return truetype( - BytesIO( - base64.b64decode( - b""" -AAEAAAAPAIAAAwBwRkZUTYwDlUAAADFoAAAAHEdERUYAqADnAAAo8AAAACRHUE9ThhmITwAAKfgAA -AduR1NVQnHxefoAACkUAAAA4k9TLzJovoHLAAABeAAAAGBjbWFw5lFQMQAAA6gAAAGqZ2FzcP//AA -MAACjoAAAACGdseWYmRXoPAAAGQAAAHfhoZWFkE18ayQAAAPwAAAA2aGhlYQboArEAAAE0AAAAJGh -tdHjjERZ8AAAB2AAAAdBsb2NhuOexrgAABVQAAADqbWF4cAC7AEYAAAFYAAAAIG5hbWUr+h5lAAAk -OAAAA6Jwb3N0D3oPTQAAJ9wAAAEKAAEAAAABGhxJDqIhXw889QALA+gAAAAA0Bqf2QAAAADhCh2h/ -2r/LgOxAyAAAAAIAAIAAAAAAAAAAQAAA8r/GgAAA7j/av9qA7EAAQAAAAAAAAAAAAAAAAAAAHQAAQ -AAAHQAQwAFAAAAAAACAAAAAQABAAAAQAAAAAAAAAADAfoBkAAFAAgCigJYAAAASwKKAlgAAAFeADI -BPgAAAAAFAAAAAAAAAAAAAAcAAAAAAAAAAAAAAABVS1dOAEAAIPsCAwL/GgDIA8oA5iAAAJMAAAAA -AhICsgAAACAAAwH0AAAAAAAAAU0AAADYAAAA8gA5AVMAVgJEAEYCRAA1AuQAKQKOAEAAsAArATsAZ -AE7AB4CMABVAkQAUADc/+EBEgAgANwAJQEv//sCRAApAkQAggJEADwCRAAtAkQAIQJEADkCRAArAk -QAMgJEACwCRAAxANwAJQDc/+ECRABnAkQAUAJEAEQB8wAjA1QANgJ/AB0CcwBkArsALwLFAGQCSwB -kAjcAZALGAC8C2gBkAQgAZAIgADcCYQBkAj8AZANiAGQCzgBkAuEALwJWAGQC3QAvAmsAZAJJADQC -ZAAiAqoAXgJuACADuAAaAnEAGQJFABMCTwAuATMAYgEv//sBJwAiAkQAUAH0ADIBLAApAhMAJAJjA -EoCEQAeAmcAHgIlAB4BIgAVAmcAHgJRAEoA7gA+AOn/8wIKAEoA9wBGA1cASgJRAEoCSgAeAmMASg -JnAB4BSgBKAcsAGAE5ABQCUABCAgIAAQMRAAEB4v/6AgEAAQHOABQBLwBAAPoAYAEvACECRABNA0Y -AJAItAHgBKgAcAkQAUAEsAHQAygAgAi0AOQD3ADYA9wAWAaEANgGhABYCbAAlAYMAeAGDADkA6/9q -AhsAFAIKABUB/QAVAAAAAwAAAAMAAAAcAAEAAAAAAKQAAwABAAAAHAAEAIgAAAAeABAAAwAOAH4Aq -QCrALEAtAC3ALsgGSAdICYgOiBEISL7Av//AAAAIACpAKsAsAC0ALcAuyAYIBwgJiA5IEQhIvsB// -//4/+5/7j/tP+y/7D/reBR4E/gR+A14CzfTwVxAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAEGAAABAAAAAAAAAAECAAAAAgAAAAAAAAAAAAAAAAAAAAEAAAMEBQYHCAkKCwwNDg8QERIT -FBUWFxgZGhscHR4fICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj9AQUJDREVGR0hJSktMT -U5PUFFSU1RVVldYWVpbXF1eX2BhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAA -AAAAAAYnFmAAAAAABlAAAAAAAAAAAAAAAAAAAAAAAAAAAAY2htAAAAAAAAAABrbGlqAAAAAHAAbm9 -ycwBnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmACYAJgAmAD4AUgCCAMoBCgFO -AVwBcgGIAaYBvAHKAdYB6AH2AgwCIAJKAogCpgLWAw4DIgNkA5wDugPUA+gD/AQQBEYEogS8BPoFJ -gVSBWoFgAWwBcoF1gX6BhQGJAZMBmgGiga0BuIHGgdUB2YHkAeiB8AH3AfyCAoIHAgqCDoITghcCG -oIogjSCPoJKglYCXwJwgnqCgIKKApACl4Klgq8CtwLDAs8C1YLjAuyC9oL7gwMDCYMSAxgDKAMrAz -qDQoNTA1mDYQNoA2uDcAN2g3oDfYODA4iDkoOXA5sDnoOnA7EDvwAAAAFAAAAAAH0ArwAAwAGAAkA -DAAPAAAxESERAxMhExcRASELARETAfT6qv6syKr+jgFUqsiqArz9RAGLAP/+1P8B/v3VAP8BLP4CA -P8AAgA5//IAuQKyAAMACwAANyMDMwIyFhQGIiY0oE4MZk84JCQ4JLQB/v3AJDgkJDgAAgBWAeUBPA -LfAAMABwAAEyMnMxcjJzOmRgpagkYKWgHl+vr6AAAAAAIARgAAAf4CsgAbAB8AAAEHMxUjByM3Iwc -jNyM1MzcjNTM3MwczNzMHMxUrAQczAZgdZXEvOi9bLzovWmYdZXEvOi9bLzovWp9bHlsBn4w429vb -2ziMONvb29s4jAAAAAMANf+mAg4DDAAfACYALAAAJRQGBxUjNS4BJzMeARcRLgE0Njc1MxUeARcjJ -icVHgEBFBYXNQ4BExU+ATU0Ag5xWDpgcgRcBz41Xl9oVTpVYwpcC1ttXP6cLTQuM5szOrVRZwlOTQ -ZqVzZECAEAGlukZAlOTQdrUG8O7iNlAQgxNhDlCDj+8/YGOjReAAAAAAUAKf/yArsCvAAHAAsAFQA -dACcAABIyFhQGIiY0EyMBMwQiBhUUFjI2NTQSMhYUBiImNDYiBhUUFjI2NTR5iFBQiFCVVwHAV/5c -OiMjOiPmiFBQiFCxOiMjOiMCvFaSVlaS/ZoCsjIzMC80NC8w/uNWklZWkhozMC80NC8wAAAAAgBA/ -/ICbgLAACIALgAAARUjEQYjIiY1NDY3LgE1NDYzMhcVJiMiBhUUFhcWOwE1MxUFFBYzMjc1IyIHDg -ECbmBcYYOOVkg7R4hsQjY4Q0RNRD4SLDxW/pJUXzksPCkUUk0BgUb+zBVUZ0BkDw5RO1huCkULQzp -COAMBcHDHRz0J/AIHRQAAAAEAKwHlAIUC3wADAAATIycze0YKWgHl+gAAAAABAGT/sAEXAwwACQAA -EzMGEBcjLgE0Nt06dXU6OUBAAwzG/jDGVePs4wAAAAEAHv+wANEDDAAJAAATMx4BFAYHIzYQHjo5Q -EA5OnUDDFXj7ONVxgHQAAAAAQBVAFIB2wHbAA4AAAE3FwcXBycHJzcnNxcnMwEtmxOfcTJjYzJxnx -ObCj4BKD07KYolmZkliik7PbMAAQBQAFUB9AIlAAsAAAEjFSM1IzUzNTMVMwH0tTq1tTq1AR/Kyjj -OzgAAAAAB/+H/iACMAGQABAAANwcjNzOMWlFOXVrS3AAAAQAgAP8A8gE3AAMAABMjNTPy0tIA/zgA -AQAl//IApQByAAcAADYyFhQGIiY0STgkJDgkciQ4JCQ4AAAAAf/7/+IBNALQAAMAABcjEzM5Pvs+H -gLuAAAAAAIAKf/yAhsCwAADAAcAABIgECA2IBAgKQHy/g5gATL+zgLA/TJEAkYAAAAAAQCCAAABlg -KyAAgAAAERIxEHNTc2MwGWVr6SIygCsv1OAldxW1sWAAEAPAAAAg4CwAAZAAA3IRUhNRM+ATU0JiM -iDwEjNz4BMzIWFRQGB7kBUv4x+kI2QTt+EAFWAQp8aGVtSl5GRjEA/0RVLzlLmAoKa3FsUkNxXQAA -AAEALf/yAhYCwAAqAAABHgEVFAYjIi8BMxceATMyNjU0KwE1MzI2NTQmIyIGDwEjNz4BMzIWFRQGA -YxBSZJo2RUBVgEHV0JBUaQREUBUQzc5TQcBVgEKfGhfcEMBbxJbQl1x0AoKRkZHPn9GSD80QUVCCg -pfbGBPOlgAAAACACEAAAIkArIACgAPAAAlIxUjNSE1ATMRMyMRBg8BAiRXVv6qAVZWV60dHLCurq4 -rAdn+QgFLMibzAAABADn/8gIZArIAHQAAATIWFRQGIyIvATMXFjMyNjU0JiMiByMTIRUhBzc2ATNv -d5Fl1RQBVgIad0VSTkVhL1IwAYj+vh8rMAHHgGdtgcUKCoFXTU5bYgGRRvAuHQAAAAACACv/8gITA -sAAFwAjAAABMhYVFAYjIhE0NjMyFh8BIycmIyIDNzYTMjY1NCYjIgYVFBYBLmp7imr0l3RZdAgBXA -IYZ5wKJzU6QVNJSz5SUAHSgWltiQFGxcNlVQoKdv7sPiz+ZF1LTmJbU0lhAAAAAQAyAAACGgKyAAY -AAAEVASMBITUCGv6oXAFL/oECsij9dgJsRgAAAAMALP/xAhgCwAAWACAALAAAAR4BFRQGIyImNTQ2 -Ny4BNTQ2MhYVFAYmIgYVFBYyNjU0AzI2NTQmIyIGFRQWAZQ5S5BmbIpPOjA7ecp5P2F8Q0J8RIVJS -0pLTEtOAW0TXTxpZ2ZqPF0SE1A3VWVlVTdQ/UU0N0RENzT9/ko+Ok1NOj1LAAIAMf/yAhkCwAAXAC -MAAAEyERQGIyImLwEzFxYzMhMHBiMiJjU0NhMyNjU0JiMiBhUUFgEl9Jd0WXQIAVwCGGecCic1SWp -7imo+UlBAQVNJAsD+usXDZVUKCnYBFD4sgWltif5kW1NJYV1LTmIAAAACACX/8gClAiAABwAPAAAS -MhYUBiImNBIyFhQGIiY0STgkJDgkJDgkJDgkAiAkOCQkOP52JDgkJDgAAAAC/+H/iAClAiAABwAMA -AASMhYUBiImNBMHIzczSTgkJDgkaFpSTl4CICQ4JCQ4/mba5gAAAQBnAB4B+AH0AAYAAAENARUlNS -UB+P6qAVb+bwGRAbCmpkbJRMkAAAIAUAC7AfQBuwADAAcAAAEhNSERITUhAfT+XAGk/lwBpAGDOP8 -AOAABAEQAHgHVAfQABgAAARUFNS0BNQHV/m8BVv6qAStEyUSmpkYAAAAAAgAj//IB1ALAABgAIAAA -ATIWFRQHDgEHIz4BNz4BNTQmIyIGByM+ARIyFhQGIiY0AQRibmktIAJWBSEqNig+NTlHBFoDezQ4J -CQ4JALAZ1BjaS03JS1DMD5LLDQ/SUVgcv2yJDgkJDgAAAAAAgA2/5gDFgKYADYAQgAAAQMGFRQzMj -Y1NCYjIg4CFRQWMzI2NxcGIyImNTQ+AjMyFhUUBiMiJwcGIyImNTQ2MzIfATcHNzYmIyIGFRQzMjY -Cej8EJjJJlnBAfGQ+oHtAhjUYg5OPx0h2k06Os3xRWQsVLjY5VHtdPBwJETcJDyUoOkZEJz8B0f74 -EQ8kZl6EkTFZjVOLlyknMVm1pmCiaTq4lX6CSCknTVRmmR8wPdYnQzxuSWVGAAIAHQAAAncCsgAHA -AoAACUjByMTMxMjATMDAcj+UVz4dO5d/sjPZPT0ArL9TgE6ATQAAAADAGQAAAJMArIAEAAbACcAAA -EeARUUBgcGKwERMzIXFhUUJRUzMjc2NTQnJiMTPgE1NCcmKwEVMzIBvkdHZkwiNt7LOSGq/oeFHBt -hahIlSTM+cB8Yj5UWAW8QT0VYYgwFArIEF5Fv1eMED2NfDAL93AU+N24PBP0AAAAAAQAv//ICjwLA -ABsAAAEyFh8BIycmIyIGFRQWMzI/ATMHDgEjIiY1NDYBdX+PCwFWAiKiaHx5ZaIiAlYBCpWBk6a0A -sCAagoKpqN/gaOmCgplhcicn8sAAAIAZAAAAp8CsgAMABkAAAEeARUUBgcGKwERMzITPgE1NCYnJi -sBETMyAY59lJp8IzXN0jUVWmdjWRs5d3I4Aq4QqJWUug8EArL9mQ+PeHGHDgX92gAAAAABAGQAAAI -vArIACwAAJRUhESEVIRUhFSEVAi/+NQHB/pUBTf6zRkYCskbwRvAAAAABAGQAAAIlArIACQAAExUh -FSERIxEhFboBQ/69VgHBAmzwRv7KArJGAAAAAAEAL//yAo8CwAAfAAABMxEjNQcGIyImNTQ2MzIWH -wEjJyYjIgYVFBYzMjY1IwGP90wfPnWTprSSf48LAVYCIqJofHllVG+hAU3+s3hARsicn8uAagoKpq -N/gaN1XAAAAAEAZAAAAowCsgALAAABESMRIREjETMRIRECjFb+hFZWAXwCsv1OAS7+0gKy/sQBPAA -AAAABAGQAAAC6ArIAAwAAMyMRM7pWVgKyAAABADf/8gHoArIAEwAAAREUBw4BIyImLwEzFxYzMjc2 -NREB6AIFcGpgbQIBVgIHfXQKAQKy/lYxIltob2EpKYyEFD0BpwAAAAABAGQAAAJ0ArIACwAACQEjA -wcVIxEzEQEzATsBJ3ntQlZWAVVlAWH+nwEnR+ACsv6RAW8AAQBkAAACLwKyAAUAACUVIREzEQIv/j -VWRkYCsv2UAAABAGQAAAMUArIAFAAAAREjETQ3BgcDIwMmJxYVESMRMxsBAxRWAiMxemx8NxsCVo7 -MywKy/U4BY7ZLco7+nAFmoFxLtP6dArL9lwJpAAAAAAEAZAAAAoACsgANAAAhIwEWFREjETMBJjUR -MwKAhP67A1aEAUUDVAJeeov+pwKy/aJ5jAFZAAAAAgAv//ICuwLAAAkAEwAAEiAWFRQGICY1NBIyN -jU0JiIGFRTbATSsrP7MrNrYenrYegLAxaKhxsahov47nIeIm5uIhwACAGQAAAJHArIADgAYAAABHg -EVFAYHBisBESMRMzITNjQnJisBETMyAZRUX2VOHzuAVtY7GlxcGDWIiDUCrgtnVlVpCgT+5gKy/rU -V1BUF/vgAAAACAC//zAK9AsAAEgAcAAAlFhcHJiMiBwYjIiY1NDYgFhUUJRQWMjY1NCYiBgI9PUMx -UDcfKh8omqysATSs/dR62Hp62HpICTg7NgkHxqGixcWitbWHnJyHiJubAAIAZAAAAlgCsgAXACMAA -CUWFyMmJyYnJisBESMRMzIXHgEVFAYHFiUzMjc+ATU0JyYrAQIqDCJfGQwNWhAhglbiOx9QXEY1Tv -6bhDATMj1lGSyMtYgtOXR0BwH+1wKyBApbU0BSESRAAgVAOGoQBAABADT/8gIoAsAAJQAAATIWFyM -uASMiBhUUFhceARUUBiMiJiczHgEzMjY1NCYnLgE1NDYBOmd2ClwGS0E6SUNRdW+HZnKKC1wPWkQ9 -Uk1cZGuEAsBwXUJHNjQ3OhIbZVZZbm5kREo+NT5DFRdYUFdrAAAAAAEAIgAAAmQCsgAHAAABIxEjE -SM1IQJk9lb2AkICbP2UAmxGAAEAXv/yAmQCsgAXAAABERQHDgEiJicmNREzERQXHgEyNjc2NRECZA -IIgfCBCAJWAgZYmlgGAgKy/k0qFFxzc1wUKgGz/lUrEkRQUEQSKwGrAAAAAAEAIAAAAnoCsgAGAAA -hIwMzGwEzAYJ07l3N1FwCsv2PAnEAAAEAGgAAA7ECsgAMAAABAyMLASMDMxsBMxsBA7HAcZyicrZi -kaB0nJkCsv1OAlP9rQKy/ZsCW/2kAmYAAAEAGQAAAm8CsgALAAAhCwEjEwMzGwEzAxMCCsrEY/bkY -re+Y/D6AST+3AFcAVb+5gEa/q3+oQAAAQATAAACUQKyAAgAAAERIxEDMxsBMwFdVvRjwLphARD+8A -EQAaL+sQFPAAABAC4AAAI5ArIACQAAJRUhNQEhNSEVAQI5/fUBof57Aen+YUZGQgIqRkX92QAAAAA -BAGL/sAEFAwwABwAAARUjETMVIxEBBWlpowMMOP0UOANcAAAB//v/4gE0AtAAAwAABSMDMwE0Pvs+ -HgLuAAAAAQAi/7AAxQMMAAcAABcjNTMRIzUzxaNpaaNQOALsOAABAFAA1wH0AmgABgAAJQsBIxMzE -wGwjY1GsESw1wFZ/qcBkf5vAAAAAQAy/6oBwv/iAAMAAAUhNSEBwv5wAZBWOAAAAAEAKQJEALYCsg -ADAAATIycztjhVUAJEbgAAAAACACT/8gHQAiAAHQAlAAAhJwcGIyImNTQ2OwE1NCcmIyIHIz4BMzI -XFh0BFBcnMjY9ASYVFAF6CR0wVUtgkJoiAgdgaQlaBm1Zrg4DCuQ9R+5MOSFQR1tbDiwUUXBUXowf -J8c9SjRORzYSgVwAAAAAAgBK//ICRQLfABEAHgAAATIWFRQGIyImLwEVIxEzETc2EzI2NTQmIyIGH -QEUFgFUcYCVbiNJEyNWVigySElcU01JXmECIJd4i5QTEDRJAt/+3jkq/hRuZV55ZWsdX14AAQAe// -IB9wIgABgAAAEyFhcjJiMiBhUUFjMyNjczDgEjIiY1NDYBF152DFocbEJXU0A1Rw1aE3pbaoKQAiB -oWH5qZm1tPDlaXYuLgZcAAAACAB7/8gIZAt8AEQAeAAABESM1BwYjIiY1NDYzMhYfAREDMjY9ATQm -IyIGFRQWAhlWKDJacYCVbiNJEyOnSV5hQUlcUwLf/SFVOSqXeIuUExA0ARb9VWVrHV9ebmVeeQACA -B7/8gH9AiAAFQAbAAABFAchHgEzMjY3Mw4BIyImNTQ2MzIWJyIGByEmAf0C/oAGUkA1SwlaD4FXbI -WObmt45UBVBwEqDQEYFhNjWD84W16Oh3+akU9aU60AAAEAFQAAARoC8gAWAAATBh0BMxUjESMRIzU -zNTQ3PgEzMhcVJqcDbW1WOTkDB0k8Hx5oAngVITRC/jQBzEIsJRs5PwVHEwAAAAIAHv8uAhkCIAAi -AC8AAAERFAcOASMiLwEzFx4BMzI2NzY9AQcGIyImNTQ2MzIWHwE1AzI2PQE0JiMiBhUUFgIZAQSEd -NwRAVcBBU5DTlUDASgyWnGAlW4jSRMjp0leYUFJXFMCEv5wSh1zeq8KCTI8VU0ZIQk5Kpd4i5QTED -RJ/iJlax1fXm5lXnkAAQBKAAACCgLkABcAAAEWFREjETQnLgEHDgEdASMRMxE3NjMyFgIIAlYCBDs -6RVRWViE5UVViAYUbQP7WASQxGzI7AQJyf+kC5P7TPSxUAAACAD4AAACsAsAABwALAAASMhYUBiIm -NBMjETNeLiAgLiBiVlYCwCAuICAu/WACEgAC//P/LgCnAsAABwAVAAASMhYUBiImNBcRFAcGIyInN -RY3NjURWS4gIC4gYgMLcRwNSgYCAsAgLiAgLo79wCUbZAJGBzMOHgJEAAAAAQBKAAACCALfAAsAAC -EnBxUjETMREzMHEwGTwTJWVvdu9/rgN6kC3/4oAQv6/ugAAQBG//wA3gLfAA8AABMRFBceATcVBiM -iJicmNRGcAQIcIxkkKi4CAQLf/bkhERoSBD4EJC8SNAJKAAAAAQBKAAADEAIgACQAAAEWFREjETQn -JiMiFREjETQnJiMiFREjETMVNzYzMhYXNzYzMhYDCwVWBAxedFYEDF50VlYiJko7ThAvJkpEVAGfI -jn+vAEcQyRZ1v76ARxDJFnW/voCEk08HzYtRB9HAAAAAAEASgAAAgoCIAAWAAABFhURIxE0JyYjIg -YdASMRMxU3NjMyFgIIAlYCCXBEVVZWITlRVWIBhRtA/tYBJDEbbHR/6QISWz0sVAAAAAACAB7/8gI -sAiAABwARAAASIBYUBiAmNBIyNjU0JiIGFRSlAQCHh/8Ah7ieWlqeWgIgn/Cfn/D+s3ZfYHV1YF8A -AgBK/zwCRQIgABEAHgAAATIWFRQGIyImLwERIxEzFTc2EzI2NTQmIyIGHQEUFgFUcYCVbiNJEyNWV -igySElcU01JXmECIJd4i5QTEDT+8wLWVTkq/hRuZV55ZWsdX14AAgAe/zwCGQIgABEAHgAAAREjEQ -cGIyImNTQ2MzIWHwE1AzI2PQE0JiMiBhUUFgIZVigyWnGAlW4jSRMjp0leYUFJXFMCEv0qARk5Kpd -4i5QTEDRJ/iJlax1fXm5lXnkAAQBKAAABPgIeAA0AAAEyFxUmBhURIxEzFTc2ARoWDkdXVlYwIwIe -B0EFVlf+0gISU0cYAAEAGP/yAa0CIAAjAAATMhYXIyYjIgYVFBYXHgEVFAYjIiYnMxYzMjY1NCYnL -gE1NDbkV2MJWhNdKy04PF1XbVhWbgxaE2ktOjlEUllkAiBaS2MrJCUoEBlPQkhOVFZoKCUmLhIWSE -BIUwAAAAEAFP/4ARQCiQAXAAATERQXHgE3FQYjIiYnJjURIzUzNTMVMxWxAQMmMx8qMjMEAUdHVmM -BzP7PGw4mFgY/BSwxDjQBNUJ7e0IAAAABAEL/8gICAhIAFwAAAREjNQcGIyImJyY1ETMRFBceATMy -Nj0BAgJWITlRT2EKBVYEBkA1RFECEv3uWj4qTToiOQE+/tIlJC43c4DpAAAAAAEAAQAAAfwCEgAGA -AABAyMDMxsBAfzJaclfop8CEv3uAhL+LQHTAAABAAEAAAMLAhIADAAAAQMjCwEjAzMbATMbAQMLqW -Z2dmapY3t0a3Z7AhL97gG+/kICEv5AAcD+QwG9AAAB//oAAAHWAhIACwAAARMjJwcjEwMzFzczARq -8ZIuKY763ZoWFYwEO/vLV1QEMAQbNzQAAAQAB/y4B+wISABEAAAEDDgEjIic1FjMyNj8BAzMbAQH7 -2iFZQB8NDRIpNhQH02GenQIS/cFVUAJGASozEwIt/i4B0gABABQAAAGxAg4ACQAAJRUhNQEhNSEVA -QGx/mMBNP7iAYL+zkREQgGIREX+ewAAAAABAED/sAEOAwwALAAAASMiBhUUFxYVFAYHHgEVFAcGFR -QWOwEVIyImNTQ3NjU0JzU2NTQnJjU0NjsBAQ4MKiMLDS4pKS4NCyMqDAtERAwLUlILDERECwLUGBk -WTlsgKzUFBTcrIFtOFhkYOC87GFVMIkUIOAhFIkxVGDsvAAAAAAEAYP84AJoDIAADAAAXIxEzmjo6 -yAPoAAEAIf+wAO8DDAAsAAATFQYVFBcWFRQGKwE1MzI2NTQnJjU0NjcuATU0NzY1NCYrATUzMhYVF -AcGFRTvUgsMREQLDCojCw0uKSkuDQsjKgwLREQMCwF6OAhFIkxVGDsvOBgZFk5bICs1BQU3KyBbTh -YZGDgvOxhVTCJFAAABAE0A3wH2AWQAEwAAATMUIyImJyYjIhUjNDMyFhcWMzIBvjhuGywtQR0xOG4 -bLC1BHTEBZIURGCNMhREYIwAAAwAk/94DIgLoAAcAEQApAAAAIBYQBiAmECQgBhUUFiA2NTQlMhYX -IyYjIgYUFjMyNjczDgEjIiY1NDYBAQFE3d3+vN0CB/7wubkBELn+xVBnD1wSWDo+QTcqOQZcEmZWX -HN2Aujg/rbg4AFKpr+Mjb6+jYxbWEldV5ZZNShLVn5na34AAgB4AFIB9AGeAAUACwAAAQcXIyc3Mw -cXIyc3AUqJiUmJifOJiUmJiQGepqampqampqYAAAIAHAHSAQ4CwAAHAA8AABIyFhQGIiY0NiIGFBY -yNjRgakREakSTNCEhNCECwEJqQkJqCiM4IyM4AAAAAAIAUAAAAfQCCwALAA8AAAEzFSMVIzUjNTM1 -MxMhNSEBP7W1OrW1OrX+XAGkAVs4tLQ4sP31OAAAAQB0AkQBAQKyAAMAABMjNzOsOD1QAkRuAAAAA -AEAIADsAKoBdgAHAAASMhYUBiImNEg6KCg6KAF2KDooKDoAAAIAOQBSAbUBngAFAAsAACUHIzcnMw -UHIzcnMwELiUmJiUkBM4lJiYlJ+KampqampqYAAAABADYB5QDhAt8ABAAAEzczByM2Xk1OXQHv8Po -AAQAWAeUAwQLfAAQAABMHIzczwV5NTl0C1fD6AAIANgHlAYsC3wAEAAkAABM3MwcjPwEzByM2Xk1O -XapeTU5dAe/w+grw+gAAAgAWAeUBawLfAAQACQAAEwcjNzMXByM3M8FeTU5dql5NTl0C1fD6CvD6A -AADACX/8gI1AHIABwAPABcAADYyFhQGIiY0NjIWFAYiJjQ2MhYUBiImNEk4JCQ4JOw4JCQ4JOw4JC -Q4JHIkOCQkOCQkOCQkOCQkOCQkOAAAAAEAeABSAUoBngAFAAABBxcjJzcBSomJSYmJAZ6mpqamAAA -AAAEAOQBSAQsBngAFAAAlByM3JzMBC4lJiYlJ+KampgAAAf9qAAABgQKyAAMAACsBATM/VwHAVwKy -AAAAAAIAFAHIAdwClAAHABQAABMVIxUjNSM1BRUjNwcjJxcjNTMXN9pKMkoByDICKzQqATJLKysCl -CmjoykBy46KiY3Lm5sAAQAVAAABvALyABgAAAERIxEjESMRIzUzNTQ3NjMyFxUmBgcGHQEBvFbCVj -k5AxHHHx5iVgcDAg798gHM/jQBzEIOJRuWBUcIJDAVIRYAAAABABX//AHkAvIAJQAAJR4BNxUGIyI -mJyY1ESYjIgcGHQEzFSMRIxEjNTM1NDc2MzIXERQBowIcIxkkKi4CAR4nXgwDbW1WLy8DEbNdOmYa -EQQ/BCQvEjQCFQZWFSEWQv40AcxCDiUblhP9uSEAAAAAAAAWAQ4AAQAAAAAAAAATACgAAQAAAAAAA -QAHAEwAAQAAAAAAAgAHAGQAAQAAAAAAAwAaAKIAAQAAAAAABAAHAM0AAQAAAAAABQA8AU8AAQAAAA -AABgAPAawAAQAAAAAACAALAdQAAQAAAAAACQALAfgAAQAAAAAACwAXAjQAAQAAAAAADAAXAnwAAwA -BBAkAAAAmAAAAAwABBAkAAQAOADwAAwABBAkAAgAOAFQAAwABBAkAAwA0AGwAAwABBAkABAAOAL0A -AwABBAkABQB4ANUAAwABBAkABgAeAYwAAwABBAkACAAWAbwAAwABBAkACQAWAeAAAwABBAkACwAuA -gQAAwABBAkADAAuAkwATgBvACAAUgBpAGcAaAB0AHMAIABSAGUAcwBlAHIAdgBlAGQALgAATm8gUm -lnaHRzIFJlc2VydmVkLgAAQQBpAGwAZQByAG8AbgAAQWlsZXJvbgAAUgBlAGcAdQBsAGEAcgAAUmV -ndWxhcgAAMQAuADEAMAAyADsAVQBLAFcATgA7AEEAaQBsAGUAcgBvAG4ALQBSAGUAZwB1AGwAYQBy -AAAxLjEwMjtVS1dOO0FpbGVyb24tUmVndWxhcgAAQQBpAGwAZQByAG8AbgAAQWlsZXJvbgAAVgBlA -HIAcwBpAG8AbgAgADEALgAxADAAMgA7AFAAUwAgADAAMAAxAC4AMQAwADIAOwBoAG8AdABjAG8Abg -B2ACAAMQAuADAALgA3ADAAOwBtAGEAawBlAG8AdABmAC4AbABpAGIAMgAuADUALgA1ADgAMwAyADk -AAFZlcnNpb24gMS4xMDI7UFMgMDAxLjEwMjtob3Rjb252IDEuMC43MDttYWtlb3RmLmxpYjIuNS41 -ODMyOQAAQQBpAGwAZQByAG8AbgAtAFIAZQBnAHUAbABhAHIAAEFpbGVyb24tUmVndWxhcgAAUwBvA -HIAYQAgAFMAYQBnAGEAbgBvAABTb3JhIFNhZ2FubwAAUwBvAHIAYQAgAFMAYQBnAGEAbgBvAABTb3 -JhIFNhZ2FubwAAaAB0AHQAcAA6AC8ALwB3AHcAdwAuAGQAbwB0AGMAbwBsAG8AbgAuAG4AZQB0AAB -odHRwOi8vd3d3LmRvdGNvbG9uLm5ldAAAaAB0AHQAcAA6AC8ALwB3AHcAdwAuAGQAbwB0AGMAbwBs -AG8AbgAuAG4AZQB0AABodHRwOi8vd3d3LmRvdGNvbG9uLm5ldAAAAAACAAAAAAAA/4MAMgAAAAAAA -AAAAAAAAAAAAAAAAAAAAHQAAAABAAIAAwAEAAUABgAHAAgACQAKAAsADAANAA4ADwAQABEAEgATAB -QAFQAWABcAGAAZABoAGwAcAB0AHgAfACAAIQAiACMAJAAlACYAJwAoACkAKgArACwALQAuAC8AMAA -xADIAMwA0ADUANgA3ADgAOQA6ADsAPAA9AD4APwBAAEEAQgBDAEQARQBGAEcASABJAEoASwBMAE0A -TgBPAFAAUQBSAFMAVABVAFYAVwBYAFkAWgBbAFwAXQBeAF8AYABhAIsAqQCDAJMAjQDDAKoAtgC3A -LQAtQCrAL4AvwC8AIwAwADBAAAAAAAB//8AAgABAAAADAAAABwAAAACAAIAAwBxAAEAcgBzAAIABA -AAAAIAAAABAAAACgBMAGYAAkRGTFQADmxhdG4AGgAEAAAAAP//AAEAAAAWAANDQVQgAB5NT0wgABZ -ST00gABYAAP//AAEAAAAA//8AAgAAAAEAAmxpZ2EADmxvY2wAFAAAAAEAAQAAAAEAAAACAAYAEAAG -AAAAAgASADQABAAAAAEATAADAAAAAgAQABYAAQAcAAAAAQABAE8AAQABAGcAAQABAE8AAwAAAAIAE -AAWAAEAHAAAAAEAAQAvAAEAAQBnAAEAAQAvAAEAGgABAAgAAgAGAAwAcwACAE8AcgACAEwAAQABAE -kAAAABAAAACgBGAGAAAkRGTFQADmxhdG4AHAAEAAAAAP//AAIAAAABABYAA0NBVCAAFk1PTCAAFlJ -PTSAAFgAA//8AAgAAAAEAAmNwc3AADmtlcm4AFAAAAAEAAAAAAAEAAQACAAYADgABAAAAAQASAAIA -AAACAB4ANgABAAoABQAFAAoAAgABACQAPQAAAAEAEgAEAAAAAQAMAAEAOP/nAAEAAQAkAAIGigAEA -AAFJAXKABoAGQAA//gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAD/sv+4/+z/7v/MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAD/xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/9T/6AAAAAD/8QAA -ABD/vQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/7gAAAAAAAAAAAAAAAAAA//MAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABIAAAAAAAAAAP/5AAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/gAAD/4AAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//L/9AAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAA/+gAAAAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/zAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/mAAAAAAAAAAAAAAAAAAD -/4gAA//AAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/+AAAAAAAAP/OAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/zv/qAAAAAP/0AAAACAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/ZAAD/egAA/1kAAAAA/5D/rgAAAAAAAAAAAA -AAAAAAAAAAAAAAAAD/9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAD/8AAA/7b/8P+wAAD/8P/E/98AAAAA/8P/+P/0//oAAAAAAAAAAAAA//gA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+AAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/w//C/9MAAP/SAAD/9wAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAD/yAAA/+kAAAAA//QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9wAAAAD//QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAP/cAAAAAAAAAAAAAAAA/7YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAP/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/6AAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAkAFAAEAAAAAQACwAAABcA -BgAAAAAAAAAIAA4AAAAAAAsAEgAAAAAAAAATABkAAwANAAAAAQAJAAAAAAAAAAAAAAAAAAAAGAAAA -AAABwAAAAAAAAAAAAAAFQAFAAAAAAAYABgAAAAUAAAACgAAAAwAAgAPABEAFgAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAEAEQBdAAYAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAcAAAAAAAAABwAAAAAACAAAAAAAAAAAAAcAAAAHAAAAEwAJ -ABUADgAPAAAACwAQAAAAAAAAAAAAAAAAAAUAGAACAAIAAgAAAAIAGAAXAAAAGAAAABYAFgACABYAA -gAWAAAAEQADAAoAFAAMAA0ABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASAAAAEgAGAAEAHgAkAC -YAJwApACoALQAuAC8AMgAzADcAOAA5ADoAPAA9AEUASABOAE8AUgBTAFUAVwBZAFoAWwBcAF0AcwA -AAAAAAQAAAADa3tfFAAAAANAan9kAAAAA4QodoQ== -""" - ) - ), - 10 if size is None else size, - layout_engine=Layout.BASIC, - ) - return load_default_imagefont() diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/ImageGrab.py b/.venv-docs/lib/python3.12/site-packages/PIL/ImageGrab.py deleted file mode 100644 index 1eb45073..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/ImageGrab.py +++ /dev/null @@ -1,196 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# screen grabber -# -# History: -# 2001-04-26 fl created -# 2001-09-17 fl use builtin driver, if present -# 2002-11-19 fl added grabclipboard support -# -# Copyright (c) 2001-2002 by Secret Labs AB -# Copyright (c) 2001-2002 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io -import os -import shutil -import subprocess -import sys -import tempfile - -from . import Image - -TYPE_CHECKING = False -if TYPE_CHECKING: - from . import ImageWin - - -def grab( - bbox: tuple[int, int, int, int] | None = None, - include_layered_windows: bool = False, - all_screens: bool = False, - xdisplay: str | None = None, - window: int | ImageWin.HWND | None = None, -) -> Image.Image: - im: Image.Image - if xdisplay is None: - if sys.platform == "darwin": - fh, filepath = tempfile.mkstemp(".png") - os.close(fh) - args = ["screencapture"] - if bbox: - left, top, right, bottom = bbox - args += ["-R", f"{left},{top},{right-left},{bottom-top}"] - subprocess.call(args + ["-x", filepath]) - im = Image.open(filepath) - im.load() - os.unlink(filepath) - if bbox: - im_resized = im.resize((right - left, bottom - top)) - im.close() - return im_resized - return im - elif sys.platform == "win32": - if window is not None: - all_screens = -1 - offset, size, data = Image.core.grabscreen_win32( - include_layered_windows, - all_screens, - int(window) if window is not None else 0, - ) - im = Image.frombytes( - "RGB", - size, - data, - # RGB, 32-bit line padding, origin lower left corner - "raw", - "BGR", - (size[0] * 3 + 3) & -4, - -1, - ) - if bbox: - x0, y0 = offset - left, top, right, bottom = bbox - im = im.crop((left - x0, top - y0, right - x0, bottom - y0)) - return im - # Cast to Optional[str] needed for Windows and macOS. - display_name: str | None = xdisplay - try: - if not Image.core.HAVE_XCB: - msg = "Pillow was built without XCB support" - raise OSError(msg) - size, data = Image.core.grabscreen_x11(display_name) - except OSError: - if display_name is None and sys.platform not in ("darwin", "win32"): - if shutil.which("gnome-screenshot"): - args = ["gnome-screenshot", "-f"] - elif shutil.which("grim"): - args = ["grim"] - elif shutil.which("spectacle"): - args = ["spectacle", "-n", "-b", "-f", "-o"] - else: - raise - fh, filepath = tempfile.mkstemp(".png") - os.close(fh) - subprocess.call(args + [filepath]) - im = Image.open(filepath) - im.load() - os.unlink(filepath) - if bbox: - im_cropped = im.crop(bbox) - im.close() - return im_cropped - return im - else: - raise - else: - im = Image.frombytes("RGB", size, data, "raw", "BGRX", size[0] * 4, 1) - if bbox: - im = im.crop(bbox) - return im - - -def grabclipboard() -> Image.Image | list[str] | None: - if sys.platform == "darwin": - p = subprocess.run( - ["osascript", "-e", "get the clipboard as «class PNGf»"], - capture_output=True, - ) - if p.returncode != 0: - return None - - import binascii - - data = io.BytesIO(binascii.unhexlify(p.stdout[11:-3])) - return Image.open(data) - elif sys.platform == "win32": - fmt, data = Image.core.grabclipboard_win32() - if fmt == "file": # CF_HDROP - import struct - - o = struct.unpack_from("I", data)[0] - if data[16] == 0: - files = data[o:].decode("mbcs").split("\0") - else: - files = data[o:].decode("utf-16le").split("\0") - return files[: files.index("")] - if isinstance(data, bytes): - data = io.BytesIO(data) - if fmt == "png": - from . import PngImagePlugin - - return PngImagePlugin.PngImageFile(data) - elif fmt == "DIB": - from . import BmpImagePlugin - - return BmpImagePlugin.DibImageFile(data) - return None - else: - if os.getenv("WAYLAND_DISPLAY"): - session_type = "wayland" - elif os.getenv("DISPLAY"): - session_type = "x11" - else: # Session type check failed - session_type = None - - if shutil.which("wl-paste") and session_type in ("wayland", None): - args = ["wl-paste", "-t", "image"] - elif shutil.which("xclip") and session_type in ("x11", None): - args = ["xclip", "-selection", "clipboard", "-t", "image/png", "-o"] - else: - msg = "wl-paste or xclip is required for ImageGrab.grabclipboard() on Linux" - raise NotImplementedError(msg) - - p = subprocess.run(args, capture_output=True) - if p.returncode != 0: - err = p.stderr - for silent_error in [ - # wl-paste, when the clipboard is empty - b"Nothing is copied", - # Ubuntu/Debian wl-paste, when the clipboard is empty - b"No selection", - # Ubuntu/Debian wl-paste, when an image isn't available - b"No suitable type of content copied", - # wl-paste or Ubuntu/Debian xclip, when an image isn't available - b" not available", - # xclip, when an image isn't available - b"cannot convert ", - # xclip, when the clipboard isn't initialized - b"xclip: Error: There is no owner for the ", - ]: - if silent_error in err: - return None - msg = f"{args[0]} error" - if err: - msg += f": {err.strip().decode()}" - raise ChildProcessError(msg) - - data = io.BytesIO(p.stdout) - im = Image.open(data) - im.load() - return im diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/ImageMath.py b/.venv-docs/lib/python3.12/site-packages/PIL/ImageMath.py deleted file mode 100644 index dfdc50c0..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/ImageMath.py +++ /dev/null @@ -1,314 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# a simple math add-on for the Python Imaging Library -# -# History: -# 1999-02-15 fl Original PIL Plus release -# 2005-05-05 fl Simplified and cleaned up for PIL 1.1.6 -# 2005-09-12 fl Fixed int() and float() for Python 2.4.1 -# -# Copyright (c) 1999-2005 by Secret Labs AB -# Copyright (c) 2005 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import builtins - -from . import Image, _imagingmath - -TYPE_CHECKING = False -if TYPE_CHECKING: - from collections.abc import Callable - from types import CodeType - from typing import Any - - -class _Operand: - """Wraps an image operand, providing standard operators""" - - def __init__(self, im: Image.Image): - self.im = im - - def __fixup(self, im1: _Operand | float) -> Image.Image: - # convert image to suitable mode - if isinstance(im1, _Operand): - # argument was an image. - if im1.im.mode in ("1", "L"): - return im1.im.convert("I") - elif im1.im.mode in ("I", "F"): - return im1.im - else: - msg = f"unsupported mode: {im1.im.mode}" - raise ValueError(msg) - else: - # argument was a constant - if isinstance(im1, (int, float)) and self.im.mode in ("1", "L", "I"): - return Image.new("I", self.im.size, im1) - else: - return Image.new("F", self.im.size, im1) - - def apply( - self, - op: str, - im1: _Operand | float, - im2: _Operand | float | None = None, - mode: str | None = None, - ) -> _Operand: - im_1 = self.__fixup(im1) - if im2 is None: - # unary operation - out = Image.new(mode or im_1.mode, im_1.size, None) - try: - op = getattr(_imagingmath, f"{op}_{im_1.mode}") - except AttributeError as e: - msg = f"bad operand type for '{op}'" - raise TypeError(msg) from e - _imagingmath.unop(op, out.getim(), im_1.getim()) - else: - # binary operation - im_2 = self.__fixup(im2) - if im_1.mode != im_2.mode: - # convert both arguments to floating point - if im_1.mode != "F": - im_1 = im_1.convert("F") - if im_2.mode != "F": - im_2 = im_2.convert("F") - if im_1.size != im_2.size: - # crop both arguments to a common size - size = ( - min(im_1.size[0], im_2.size[0]), - min(im_1.size[1], im_2.size[1]), - ) - if im_1.size != size: - im_1 = im_1.crop((0, 0) + size) - if im_2.size != size: - im_2 = im_2.crop((0, 0) + size) - out = Image.new(mode or im_1.mode, im_1.size, None) - try: - op = getattr(_imagingmath, f"{op}_{im_1.mode}") - except AttributeError as e: - msg = f"bad operand type for '{op}'" - raise TypeError(msg) from e - _imagingmath.binop(op, out.getim(), im_1.getim(), im_2.getim()) - return _Operand(out) - - # unary operators - def __bool__(self) -> bool: - # an image is "true" if it contains at least one non-zero pixel - return self.im.getbbox() is not None - - def __abs__(self) -> _Operand: - return self.apply("abs", self) - - def __pos__(self) -> _Operand: - return self - - def __neg__(self) -> _Operand: - return self.apply("neg", self) - - # binary operators - def __add__(self, other: _Operand | float) -> _Operand: - return self.apply("add", self, other) - - def __radd__(self, other: _Operand | float) -> _Operand: - return self.apply("add", other, self) - - def __sub__(self, other: _Operand | float) -> _Operand: - return self.apply("sub", self, other) - - def __rsub__(self, other: _Operand | float) -> _Operand: - return self.apply("sub", other, self) - - def __mul__(self, other: _Operand | float) -> _Operand: - return self.apply("mul", self, other) - - def __rmul__(self, other: _Operand | float) -> _Operand: - return self.apply("mul", other, self) - - def __truediv__(self, other: _Operand | float) -> _Operand: - return self.apply("div", self, other) - - def __rtruediv__(self, other: _Operand | float) -> _Operand: - return self.apply("div", other, self) - - def __mod__(self, other: _Operand | float) -> _Operand: - return self.apply("mod", self, other) - - def __rmod__(self, other: _Operand | float) -> _Operand: - return self.apply("mod", other, self) - - def __pow__(self, other: _Operand | float) -> _Operand: - return self.apply("pow", self, other) - - def __rpow__(self, other: _Operand | float) -> _Operand: - return self.apply("pow", other, self) - - # bitwise - def __invert__(self) -> _Operand: - return self.apply("invert", self) - - def __and__(self, other: _Operand | float) -> _Operand: - return self.apply("and", self, other) - - def __rand__(self, other: _Operand | float) -> _Operand: - return self.apply("and", other, self) - - def __or__(self, other: _Operand | float) -> _Operand: - return self.apply("or", self, other) - - def __ror__(self, other: _Operand | float) -> _Operand: - return self.apply("or", other, self) - - def __xor__(self, other: _Operand | float) -> _Operand: - return self.apply("xor", self, other) - - def __rxor__(self, other: _Operand | float) -> _Operand: - return self.apply("xor", other, self) - - def __lshift__(self, other: _Operand | float) -> _Operand: - return self.apply("lshift", self, other) - - def __rshift__(self, other: _Operand | float) -> _Operand: - return self.apply("rshift", self, other) - - # logical - def __eq__(self, other: _Operand | float) -> _Operand: # type: ignore[override] - return self.apply("eq", self, other) - - def __ne__(self, other: _Operand | float) -> _Operand: # type: ignore[override] - return self.apply("ne", self, other) - - def __lt__(self, other: _Operand | float) -> _Operand: - return self.apply("lt", self, other) - - def __le__(self, other: _Operand | float) -> _Operand: - return self.apply("le", self, other) - - def __gt__(self, other: _Operand | float) -> _Operand: - return self.apply("gt", self, other) - - def __ge__(self, other: _Operand | float) -> _Operand: - return self.apply("ge", self, other) - - -# conversions -def imagemath_int(self: _Operand) -> _Operand: - return _Operand(self.im.convert("I")) - - -def imagemath_float(self: _Operand) -> _Operand: - return _Operand(self.im.convert("F")) - - -# logical -def imagemath_equal(self: _Operand, other: _Operand | float | None) -> _Operand: - return self.apply("eq", self, other, mode="I") - - -def imagemath_notequal(self: _Operand, other: _Operand | float | None) -> _Operand: - return self.apply("ne", self, other, mode="I") - - -def imagemath_min(self: _Operand, other: _Operand | float | None) -> _Operand: - return self.apply("min", self, other) - - -def imagemath_max(self: _Operand, other: _Operand | float | None) -> _Operand: - return self.apply("max", self, other) - - -def imagemath_convert(self: _Operand, mode: str) -> _Operand: - return _Operand(self.im.convert(mode)) - - -ops = { - "int": imagemath_int, - "float": imagemath_float, - "equal": imagemath_equal, - "notequal": imagemath_notequal, - "min": imagemath_min, - "max": imagemath_max, - "convert": imagemath_convert, -} - - -def lambda_eval(expression: Callable[[dict[str, Any]], Any], **kw: Any) -> Any: - """ - Returns the result of an image function. - - :py:mod:`~PIL.ImageMath` only supports single-layer images. To process multi-band - images, use the :py:meth:`~PIL.Image.Image.split` method or - :py:func:`~PIL.Image.merge` function. - - :param expression: A function that receives a dictionary. - :param **kw: Values to add to the function's dictionary. - :return: The expression result. This is usually an image object, but can - also be an integer, a floating point value, or a pixel tuple, - depending on the expression. - """ - - args: dict[str, Any] = ops.copy() - args.update(kw) - for k, v in args.items(): - if isinstance(v, Image.Image): - args[k] = _Operand(v) - - out = expression(args) - try: - return out.im - except AttributeError: - return out - - -def unsafe_eval(expression: str, **kw: Any) -> Any: - """ - Evaluates an image expression. This uses Python's ``eval()`` function to process - the expression string, and carries the security risks of doing so. It is not - recommended to process expressions without considering this. - :py:meth:`~lambda_eval` is a more secure alternative. - - :py:mod:`~PIL.ImageMath` only supports single-layer images. To process multi-band - images, use the :py:meth:`~PIL.Image.Image.split` method or - :py:func:`~PIL.Image.merge` function. - - :param expression: A string containing a Python-style expression. - :param **kw: Values to add to the evaluation context. - :return: The evaluated expression. This is usually an image object, but can - also be an integer, a floating point value, or a pixel tuple, - depending on the expression. - """ - - # build execution namespace - args: dict[str, Any] = ops.copy() - for k in kw: - if "__" in k or hasattr(builtins, k): - msg = f"'{k}' not allowed" - raise ValueError(msg) - - args.update(kw) - for k, v in args.items(): - if isinstance(v, Image.Image): - args[k] = _Operand(v) - - compiled_code = compile(expression, "", "eval") - - def scan(code: CodeType) -> None: - for const in code.co_consts: - if type(const) is type(compiled_code): - scan(const) - - for name in code.co_names: - if name not in args and name != "abs": - msg = f"'{name}' not allowed" - raise ValueError(msg) - - scan(compiled_code) - out = builtins.eval(expression, {"__builtins": {"abs": abs}}, args) - try: - return out.im - except AttributeError: - return out diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/ImageMode.py b/.venv-docs/lib/python3.12/site-packages/PIL/ImageMode.py deleted file mode 100644 index b7c6c863..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/ImageMode.py +++ /dev/null @@ -1,85 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# standard mode descriptors -# -# History: -# 2006-03-20 fl Added -# -# Copyright (c) 2006 by Secret Labs AB. -# Copyright (c) 2006 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import sys -from functools import lru_cache -from typing import NamedTuple - - -class ModeDescriptor(NamedTuple): - """Wrapper for mode strings.""" - - mode: str - bands: tuple[str, ...] - basemode: str - basetype: str - typestr: str - - def __str__(self) -> str: - return self.mode - - -@lru_cache -def getmode(mode: str) -> ModeDescriptor: - """Gets a mode descriptor for the given mode.""" - endian = "<" if sys.byteorder == "little" else ">" - - modes = { - # core modes - # Bits need to be extended to bytes - "1": ("L", "L", ("1",), "|b1"), - "L": ("L", "L", ("L",), "|u1"), - "I": ("L", "I", ("I",), f"{endian}i4"), - "F": ("L", "F", ("F",), f"{endian}f4"), - "P": ("P", "L", ("P",), "|u1"), - "RGB": ("RGB", "L", ("R", "G", "B"), "|u1"), - "RGBX": ("RGB", "L", ("R", "G", "B", "X"), "|u1"), - "RGBA": ("RGB", "L", ("R", "G", "B", "A"), "|u1"), - "CMYK": ("RGB", "L", ("C", "M", "Y", "K"), "|u1"), - "YCbCr": ("RGB", "L", ("Y", "Cb", "Cr"), "|u1"), - # UNDONE - unsigned |u1i1i1 - "LAB": ("RGB", "L", ("L", "A", "B"), "|u1"), - "HSV": ("RGB", "L", ("H", "S", "V"), "|u1"), - # extra experimental modes - "RGBa": ("RGB", "L", ("R", "G", "B", "a"), "|u1"), - "LA": ("L", "L", ("L", "A"), "|u1"), - "La": ("L", "L", ("L", "a"), "|u1"), - "PA": ("RGB", "L", ("P", "A"), "|u1"), - } - if mode in modes: - base_mode, base_type, bands, type_str = modes[mode] - return ModeDescriptor(mode, bands, base_mode, base_type, type_str) - - mapping_modes = { - # I;16 == I;16L, and I;32 == I;32L - "I;16": "u2", - "I;16BS": ">i2", - "I;16N": f"{endian}u2", - "I;16NS": f"{endian}i2", - "I;32": "u4", - "I;32L": "i4", - "I;32LS": " -from __future__ import annotations - -import re - -from . import Image, _imagingmorph - -LUT_SIZE = 1 << 9 - -# fmt: off -ROTATION_MATRIX = [ - 6, 3, 0, - 7, 4, 1, - 8, 5, 2, -] -MIRROR_MATRIX = [ - 2, 1, 0, - 5, 4, 3, - 8, 7, 6, -] -# fmt: on - - -class LutBuilder: - """A class for building a MorphLut from a descriptive language - - The input patterns is a list of a strings sequences like these:: - - 4:(... - .1. - 111)->1 - - (whitespaces including linebreaks are ignored). The option 4 - describes a series of symmetry operations (in this case a - 4-rotation), the pattern is described by: - - - . or X - Ignore - - 1 - Pixel is on - - 0 - Pixel is off - - The result of the operation is described after "->" string. - - The default is to return the current pixel value, which is - returned if no other match is found. - - Operations: - - - 4 - 4 way rotation - - N - Negate - - 1 - Dummy op for no other operation (an op must always be given) - - M - Mirroring - - Example:: - - lb = LutBuilder(patterns = ["4:(... .1. 111)->1"]) - lut = lb.build_lut() - - """ - - def __init__( - self, patterns: list[str] | None = None, op_name: str | None = None - ) -> None: - if patterns is not None: - self.patterns = patterns - else: - self.patterns = [] - self.lut: bytearray | None = None - if op_name is not None: - known_patterns = { - "corner": ["1:(... ... ...)->0", "4:(00. 01. ...)->1"], - "dilation4": ["4:(... .0. .1.)->1"], - "dilation8": ["4:(... .0. .1.)->1", "4:(... .0. ..1)->1"], - "erosion4": ["4:(... .1. .0.)->0"], - "erosion8": ["4:(... .1. .0.)->0", "4:(... .1. ..0)->0"], - "edge": [ - "1:(... ... ...)->0", - "4:(.0. .1. ...)->1", - "4:(01. .1. ...)->1", - ], - } - if op_name not in known_patterns: - msg = f"Unknown pattern {op_name}!" - raise Exception(msg) - - self.patterns = known_patterns[op_name] - - def add_patterns(self, patterns: list[str]) -> None: - self.patterns += patterns - - def build_default_lut(self) -> None: - symbols = [0, 1] - m = 1 << 4 # pos of current pixel - self.lut = bytearray(symbols[(i & m) > 0] for i in range(LUT_SIZE)) - - def get_lut(self) -> bytearray | None: - return self.lut - - def _string_permute(self, pattern: str, permutation: list[int]) -> str: - """string_permute takes a pattern and a permutation and returns the - string permuted according to the permutation list. - """ - assert len(permutation) == 9 - return "".join(pattern[p] for p in permutation) - - def _pattern_permute( - self, basic_pattern: str, options: str, basic_result: int - ) -> list[tuple[str, int]]: - """pattern_permute takes a basic pattern and its result and clones - the pattern according to the modifications described in the $options - parameter. It returns a list of all cloned patterns.""" - patterns = [(basic_pattern, basic_result)] - - # rotations - if "4" in options: - res = patterns[-1][1] - for i in range(4): - patterns.append( - (self._string_permute(patterns[-1][0], ROTATION_MATRIX), res) - ) - # mirror - if "M" in options: - n = len(patterns) - for pattern, res in patterns[:n]: - patterns.append((self._string_permute(pattern, MIRROR_MATRIX), res)) - - # negate - if "N" in options: - n = len(patterns) - for pattern, res in patterns[:n]: - # Swap 0 and 1 - pattern = pattern.replace("0", "Z").replace("1", "0").replace("Z", "1") - res = 1 - int(res) - patterns.append((pattern, res)) - - return patterns - - def build_lut(self) -> bytearray: - """Compile all patterns into a morphology lut. - - TBD :Build based on (file) morphlut:modify_lut - """ - self.build_default_lut() - assert self.lut is not None - patterns = [] - - # Parse and create symmetries of the patterns strings - for p in self.patterns: - m = re.search(r"(\w):?\s*\((.+?)\)\s*->\s*(\d)", p.replace("\n", "")) - if not m: - msg = 'Syntax error in pattern "' + p + '"' - raise Exception(msg) - options = m.group(1) - pattern = m.group(2) - result = int(m.group(3)) - - # Get rid of spaces - pattern = pattern.replace(" ", "").replace("\n", "") - - patterns += self._pattern_permute(pattern, options, result) - - # compile the patterns into regular expressions for speed - compiled_patterns = [] - for pattern in patterns: - p = pattern[0].replace(".", "X").replace("X", "[01]") - compiled_patterns.append((re.compile(p), pattern[1])) - - # Step through table and find patterns that match. - # Note that all the patterns are searched. The last one - # caught overrides - for i in range(LUT_SIZE): - # Build the bit pattern - bitpattern = bin(i)[2:] - bitpattern = ("0" * (9 - len(bitpattern)) + bitpattern)[::-1] - - for pattern, r in compiled_patterns: - if pattern.match(bitpattern): - self.lut[i] = [0, 1][r] - - return self.lut - - -class MorphOp: - """A class for binary morphological operators""" - - def __init__( - self, - lut: bytearray | None = None, - op_name: str | None = None, - patterns: list[str] | None = None, - ) -> None: - """Create a binary morphological operator""" - self.lut = lut - if op_name is not None: - self.lut = LutBuilder(op_name=op_name).build_lut() - elif patterns is not None: - self.lut = LutBuilder(patterns=patterns).build_lut() - - def apply(self, image: Image.Image) -> tuple[int, Image.Image]: - """Run a single morphological operation on an image - - Returns a tuple of the number of changed pixels and the - morphed image""" - if self.lut is None: - msg = "No operator loaded" - raise Exception(msg) - - if image.mode != "L": - msg = "Image mode must be L" - raise ValueError(msg) - outimage = Image.new(image.mode, image.size, None) - count = _imagingmorph.apply(bytes(self.lut), image.getim(), outimage.getim()) - return count, outimage - - def match(self, image: Image.Image) -> list[tuple[int, int]]: - """Get a list of coordinates matching the morphological operation on - an image. - - Returns a list of tuples of (x,y) coordinates - of all matching pixels. See :ref:`coordinate-system`.""" - if self.lut is None: - msg = "No operator loaded" - raise Exception(msg) - - if image.mode != "L": - msg = "Image mode must be L" - raise ValueError(msg) - return _imagingmorph.match(bytes(self.lut), image.getim()) - - def get_on_pixels(self, image: Image.Image) -> list[tuple[int, int]]: - """Get a list of all turned on pixels in a binary image - - Returns a list of tuples of (x,y) coordinates - of all matching pixels. See :ref:`coordinate-system`.""" - - if image.mode != "L": - msg = "Image mode must be L" - raise ValueError(msg) - return _imagingmorph.get_on_pixels(image.getim()) - - def load_lut(self, filename: str) -> None: - """Load an operator from an mrl file""" - with open(filename, "rb") as f: - self.lut = bytearray(f.read()) - - if len(self.lut) != LUT_SIZE: - self.lut = None - msg = "Wrong size operator file!" - raise Exception(msg) - - def save_lut(self, filename: str) -> None: - """Save an operator to an mrl file""" - if self.lut is None: - msg = "No operator loaded" - raise Exception(msg) - with open(filename, "wb") as f: - f.write(self.lut) - - def set_lut(self, lut: bytearray | None) -> None: - """Set the lut from an external source""" - self.lut = lut diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/ImageOps.py b/.venv-docs/lib/python3.12/site-packages/PIL/ImageOps.py deleted file mode 100644 index 42b10bd7..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/ImageOps.py +++ /dev/null @@ -1,746 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# standard image operations -# -# History: -# 2001-10-20 fl Created -# 2001-10-23 fl Added autocontrast operator -# 2001-12-18 fl Added Kevin's fit operator -# 2004-03-14 fl Fixed potential division by zero in equalize -# 2005-05-05 fl Fixed equalize for low number of values -# -# Copyright (c) 2001-2004 by Secret Labs AB -# Copyright (c) 2001-2004 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import functools -import operator -import re -from collections.abc import Sequence -from typing import Literal, Protocol, cast, overload - -from . import ExifTags, Image, ImagePalette - -# -# helpers - - -def _border(border: int | tuple[int, ...]) -> tuple[int, int, int, int]: - if isinstance(border, tuple): - if len(border) == 2: - left, top = right, bottom = border - elif len(border) == 4: - left, top, right, bottom = border - else: - left = top = right = bottom = border - return left, top, right, bottom - - -def _color(color: str | int | tuple[int, ...], mode: str) -> int | tuple[int, ...]: - if isinstance(color, str): - from . import ImageColor - - color = ImageColor.getcolor(color, mode) - return color - - -def _lut(image: Image.Image, lut: list[int]) -> Image.Image: - if image.mode == "P": - # FIXME: apply to lookup table, not image data - msg = "mode P support coming soon" - raise NotImplementedError(msg) - elif image.mode in ("L", "RGB"): - if image.mode == "RGB" and len(lut) == 256: - lut = lut + lut + lut - return image.point(lut) - else: - msg = f"not supported for mode {image.mode}" - raise OSError(msg) - - -# -# actions - - -def autocontrast( - image: Image.Image, - cutoff: float | tuple[float, float] = 0, - ignore: int | Sequence[int] | None = None, - mask: Image.Image | None = None, - preserve_tone: bool = False, -) -> Image.Image: - """ - Maximize (normalize) image contrast. This function calculates a - histogram of the input image (or mask region), removes ``cutoff`` percent of the - lightest and darkest pixels from the histogram, and remaps the image - so that the darkest pixel becomes black (0), and the lightest - becomes white (255). - - :param image: The image to process. - :param cutoff: The percent to cut off from the histogram on the low and - high ends. Either a tuple of (low, high), or a single - number for both. - :param ignore: The background pixel value (use None for no background). - :param mask: Histogram used in contrast operation is computed using pixels - within the mask. If no mask is given the entire image is used - for histogram computation. - :param preserve_tone: Preserve image tone in Photoshop-like style autocontrast. - - .. versionadded:: 8.2.0 - - :return: An image. - """ - if preserve_tone: - histogram = image.convert("L").histogram(mask) - else: - histogram = image.histogram(mask) - - lut = [] - for layer in range(0, len(histogram), 256): - h = histogram[layer : layer + 256] - if ignore is not None: - # get rid of outliers - if isinstance(ignore, int): - h[ignore] = 0 - else: - for ix in ignore: - h[ix] = 0 - if cutoff: - # cut off pixels from both ends of the histogram - if not isinstance(cutoff, tuple): - cutoff = (cutoff, cutoff) - # get number of pixels - n = 0 - for ix in range(256): - n = n + h[ix] - # remove cutoff% pixels from the low end - cut = int(n * cutoff[0] // 100) - for lo in range(256): - if cut > h[lo]: - cut = cut - h[lo] - h[lo] = 0 - else: - h[lo] -= cut - cut = 0 - if cut <= 0: - break - # remove cutoff% samples from the high end - cut = int(n * cutoff[1] // 100) - for hi in range(255, -1, -1): - if cut > h[hi]: - cut = cut - h[hi] - h[hi] = 0 - else: - h[hi] -= cut - cut = 0 - if cut <= 0: - break - # find lowest/highest samples after preprocessing - for lo in range(256): - if h[lo]: - break - for hi in range(255, -1, -1): - if h[hi]: - break - if hi <= lo: - # don't bother - lut.extend(list(range(256))) - else: - scale = 255.0 / (hi - lo) - offset = -lo * scale - for ix in range(256): - ix = int(ix * scale + offset) - if ix < 0: - ix = 0 - elif ix > 255: - ix = 255 - lut.append(ix) - return _lut(image, lut) - - -def colorize( - image: Image.Image, - black: str | tuple[int, ...], - white: str | tuple[int, ...], - mid: str | int | tuple[int, ...] | None = None, - blackpoint: int = 0, - whitepoint: int = 255, - midpoint: int = 127, -) -> Image.Image: - """ - Colorize grayscale image. - This function calculates a color wedge which maps all black pixels in - the source image to the first color and all white pixels to the - second color. If ``mid`` is specified, it uses three-color mapping. - The ``black`` and ``white`` arguments should be RGB tuples or color names; - optionally you can use three-color mapping by also specifying ``mid``. - Mapping positions for any of the colors can be specified - (e.g. ``blackpoint``), where these parameters are the integer - value corresponding to where the corresponding color should be mapped. - These parameters must have logical order, such that - ``blackpoint <= midpoint <= whitepoint`` (if ``mid`` is specified). - - :param image: The image to colorize. - :param black: The color to use for black input pixels. - :param white: The color to use for white input pixels. - :param mid: The color to use for midtone input pixels. - :param blackpoint: an int value [0, 255] for the black mapping. - :param whitepoint: an int value [0, 255] for the white mapping. - :param midpoint: an int value [0, 255] for the midtone mapping. - :return: An image. - """ - - # Initial asserts - assert image.mode == "L" - if mid is None: - assert 0 <= blackpoint <= whitepoint <= 255 - else: - assert 0 <= blackpoint <= midpoint <= whitepoint <= 255 - - # Define colors from arguments - rgb_black = cast(Sequence[int], _color(black, "RGB")) - rgb_white = cast(Sequence[int], _color(white, "RGB")) - rgb_mid = cast(Sequence[int], _color(mid, "RGB")) if mid is not None else None - - # Empty lists for the mapping - red = [] - green = [] - blue = [] - - # Create the low-end values - for i in range(blackpoint): - red.append(rgb_black[0]) - green.append(rgb_black[1]) - blue.append(rgb_black[2]) - - # Create the mapping (2-color) - if rgb_mid is None: - range_map = range(whitepoint - blackpoint) - - for i in range_map: - red.append( - rgb_black[0] + i * (rgb_white[0] - rgb_black[0]) // len(range_map) - ) - green.append( - rgb_black[1] + i * (rgb_white[1] - rgb_black[1]) // len(range_map) - ) - blue.append( - rgb_black[2] + i * (rgb_white[2] - rgb_black[2]) // len(range_map) - ) - - # Create the mapping (3-color) - else: - range_map1 = range(midpoint - blackpoint) - range_map2 = range(whitepoint - midpoint) - - for i in range_map1: - red.append( - rgb_black[0] + i * (rgb_mid[0] - rgb_black[0]) // len(range_map1) - ) - green.append( - rgb_black[1] + i * (rgb_mid[1] - rgb_black[1]) // len(range_map1) - ) - blue.append( - rgb_black[2] + i * (rgb_mid[2] - rgb_black[2]) // len(range_map1) - ) - for i in range_map2: - red.append(rgb_mid[0] + i * (rgb_white[0] - rgb_mid[0]) // len(range_map2)) - green.append( - rgb_mid[1] + i * (rgb_white[1] - rgb_mid[1]) // len(range_map2) - ) - blue.append(rgb_mid[2] + i * (rgb_white[2] - rgb_mid[2]) // len(range_map2)) - - # Create the high-end values - for i in range(256 - whitepoint): - red.append(rgb_white[0]) - green.append(rgb_white[1]) - blue.append(rgb_white[2]) - - # Return converted image - image = image.convert("RGB") - return _lut(image, red + green + blue) - - -def contain( - image: Image.Image, size: tuple[int, int], method: int = Image.Resampling.BICUBIC -) -> Image.Image: - """ - Returns a resized version of the image, set to the maximum width and height - within the requested size, while maintaining the original aspect ratio. - - :param image: The image to resize. - :param size: The requested output size in pixels, given as a - (width, height) tuple. - :param method: Resampling method to use. Default is - :py:attr:`~PIL.Image.Resampling.BICUBIC`. - See :ref:`concept-filters`. - :return: An image. - """ - - im_ratio = image.width / image.height - dest_ratio = size[0] / size[1] - - if im_ratio != dest_ratio: - if im_ratio > dest_ratio: - new_height = round(image.height / image.width * size[0]) - if new_height != size[1]: - size = (size[0], new_height) - else: - new_width = round(image.width / image.height * size[1]) - if new_width != size[0]: - size = (new_width, size[1]) - return image.resize(size, resample=method) - - -def cover( - image: Image.Image, size: tuple[int, int], method: int = Image.Resampling.BICUBIC -) -> Image.Image: - """ - Returns a resized version of the image, so that the requested size is - covered, while maintaining the original aspect ratio. - - :param image: The image to resize. - :param size: The requested output size in pixels, given as a - (width, height) tuple. - :param method: Resampling method to use. Default is - :py:attr:`~PIL.Image.Resampling.BICUBIC`. - See :ref:`concept-filters`. - :return: An image. - """ - - im_ratio = image.width / image.height - dest_ratio = size[0] / size[1] - - if im_ratio != dest_ratio: - if im_ratio < dest_ratio: - new_height = round(image.height / image.width * size[0]) - if new_height != size[1]: - size = (size[0], new_height) - else: - new_width = round(image.width / image.height * size[1]) - if new_width != size[0]: - size = (new_width, size[1]) - return image.resize(size, resample=method) - - -def pad( - image: Image.Image, - size: tuple[int, int], - method: int = Image.Resampling.BICUBIC, - color: str | int | tuple[int, ...] | None = None, - centering: tuple[float, float] = (0.5, 0.5), -) -> Image.Image: - """ - Returns a resized and padded version of the image, expanded to fill the - requested aspect ratio and size. - - :param image: The image to resize and crop. - :param size: The requested output size in pixels, given as a - (width, height) tuple. - :param method: Resampling method to use. Default is - :py:attr:`~PIL.Image.Resampling.BICUBIC`. - See :ref:`concept-filters`. - :param color: The background color of the padded image. - :param centering: Control the position of the original image within the - padded version. - - (0.5, 0.5) will keep the image centered - (0, 0) will keep the image aligned to the top left - (1, 1) will keep the image aligned to the bottom - right - :return: An image. - """ - - resized = contain(image, size, method) - if resized.size == size: - out = resized - else: - out = Image.new(image.mode, size, color) - if resized.palette: - palette = resized.getpalette() - if palette is not None: - out.putpalette(palette) - if resized.width != size[0]: - x = round((size[0] - resized.width) * max(0, min(centering[0], 1))) - out.paste(resized, (x, 0)) - else: - y = round((size[1] - resized.height) * max(0, min(centering[1], 1))) - out.paste(resized, (0, y)) - return out - - -def crop(image: Image.Image, border: int = 0) -> Image.Image: - """ - Remove border from image. The same amount of pixels are removed - from all four sides. This function works on all image modes. - - .. seealso:: :py:meth:`~PIL.Image.Image.crop` - - :param image: The image to crop. - :param border: The number of pixels to remove. - :return: An image. - """ - left, top, right, bottom = _border(border) - return image.crop((left, top, image.size[0] - right, image.size[1] - bottom)) - - -def scale( - image: Image.Image, factor: float, resample: int = Image.Resampling.BICUBIC -) -> Image.Image: - """ - Returns a rescaled image by a specific factor given in parameter. - A factor greater than 1 expands the image, between 0 and 1 contracts the - image. - - :param image: The image to rescale. - :param factor: The expansion factor, as a float. - :param resample: Resampling method to use. Default is - :py:attr:`~PIL.Image.Resampling.BICUBIC`. - See :ref:`concept-filters`. - :returns: An :py:class:`~PIL.Image.Image` object. - """ - if factor == 1: - return image.copy() - elif factor <= 0: - msg = "the factor must be greater than 0" - raise ValueError(msg) - else: - size = (round(factor * image.width), round(factor * image.height)) - return image.resize(size, resample) - - -class SupportsGetMesh(Protocol): - """ - An object that supports the ``getmesh`` method, taking an image as an - argument, and returning a list of tuples. Each tuple contains two tuples, - the source box as a tuple of 4 integers, and a tuple of 8 integers for the - final quadrilateral, in order of top left, bottom left, bottom right, top - right. - """ - - def getmesh( - self, image: Image.Image - ) -> list[ - tuple[tuple[int, int, int, int], tuple[int, int, int, int, int, int, int, int]] - ]: ... - - -def deform( - image: Image.Image, - deformer: SupportsGetMesh, - resample: int = Image.Resampling.BILINEAR, -) -> Image.Image: - """ - Deform the image. - - :param image: The image to deform. - :param deformer: A deformer object. Any object that implements a - ``getmesh`` method can be used. - :param resample: An optional resampling filter. Same values possible as - in the PIL.Image.transform function. - :return: An image. - """ - return image.transform( - image.size, Image.Transform.MESH, deformer.getmesh(image), resample - ) - - -def equalize(image: Image.Image, mask: Image.Image | None = None) -> Image.Image: - """ - Equalize the image histogram. This function applies a non-linear - mapping to the input image, in order to create a uniform - distribution of grayscale values in the output image. - - :param image: The image to equalize. - :param mask: An optional mask. If given, only the pixels selected by - the mask are included in the analysis. - :return: An image. - """ - if image.mode == "P": - image = image.convert("RGB") - h = image.histogram(mask) - lut = [] - for b in range(0, len(h), 256): - histo = [_f for _f in h[b : b + 256] if _f] - if len(histo) <= 1: - lut.extend(list(range(256))) - else: - step = (functools.reduce(operator.add, histo) - histo[-1]) // 255 - if not step: - lut.extend(list(range(256))) - else: - n = step // 2 - for i in range(256): - lut.append(n // step) - n = n + h[i + b] - return _lut(image, lut) - - -def expand( - image: Image.Image, - border: int | tuple[int, ...] = 0, - fill: str | int | tuple[int, ...] = 0, -) -> Image.Image: - """ - Add border to the image - - :param image: The image to expand. - :param border: Border width, in pixels. - :param fill: Pixel fill value (a color value). Default is 0 (black). - :return: An image. - """ - left, top, right, bottom = _border(border) - width = left + image.size[0] + right - height = top + image.size[1] + bottom - color = _color(fill, image.mode) - if image.palette: - mode = image.palette.mode - palette = ImagePalette.ImagePalette(mode, image.getpalette(mode)) - if isinstance(color, tuple) and (len(color) == 3 or len(color) == 4): - color = palette.getcolor(color) - else: - palette = None - out = Image.new(image.mode, (width, height), color) - if palette: - out.putpalette(palette.palette, mode) - out.paste(image, (left, top)) - return out - - -def fit( - image: Image.Image, - size: tuple[int, int], - method: int = Image.Resampling.BICUBIC, - bleed: float = 0.0, - centering: tuple[float, float] = (0.5, 0.5), -) -> Image.Image: - """ - Returns a resized and cropped version of the image, cropped to the - requested aspect ratio and size. - - This function was contributed by Kevin Cazabon. - - :param image: The image to resize and crop. - :param size: The requested output size in pixels, given as a - (width, height) tuple. - :param method: Resampling method to use. Default is - :py:attr:`~PIL.Image.Resampling.BICUBIC`. - See :ref:`concept-filters`. - :param bleed: Remove a border around the outside of the image from all - four edges. The value is a decimal percentage (use 0.01 for - one percent). The default value is 0 (no border). - Cannot be greater than or equal to 0.5. - :param centering: Control the cropping position. Use (0.5, 0.5) for - center cropping (e.g. if cropping the width, take 50% off - of the left side, and therefore 50% off the right side). - (0.0, 0.0) will crop from the top left corner (i.e. if - cropping the width, take all of the crop off of the right - side, and if cropping the height, take all of it off the - bottom). (1.0, 0.0) will crop from the bottom left - corner, etc. (i.e. if cropping the width, take all of the - crop off the left side, and if cropping the height take - none from the top, and therefore all off the bottom). - :return: An image. - """ - - # by Kevin Cazabon, Feb 17/2000 - # kevin@cazabon.com - # https://www.cazabon.com - - centering_x, centering_y = centering - - if not 0.0 <= centering_x <= 1.0: - centering_x = 0.5 - if not 0.0 <= centering_y <= 1.0: - centering_y = 0.5 - - if not 0.0 <= bleed < 0.5: - bleed = 0.0 - - # calculate the area to use for resizing and cropping, subtracting - # the 'bleed' around the edges - - # number of pixels to trim off on Top and Bottom, Left and Right - bleed_pixels = (bleed * image.size[0], bleed * image.size[1]) - - live_size = ( - image.size[0] - bleed_pixels[0] * 2, - image.size[1] - bleed_pixels[1] * 2, - ) - - # calculate the aspect ratio of the live_size - live_size_ratio = live_size[0] / live_size[1] - - # calculate the aspect ratio of the output image - output_ratio = size[0] / size[1] - - # figure out if the sides or top/bottom will be cropped off - if live_size_ratio == output_ratio: - # live_size is already the needed ratio - crop_width = live_size[0] - crop_height = live_size[1] - elif live_size_ratio >= output_ratio: - # live_size is wider than what's needed, crop the sides - crop_width = output_ratio * live_size[1] - crop_height = live_size[1] - else: - # live_size is taller than what's needed, crop the top and bottom - crop_width = live_size[0] - crop_height = live_size[0] / output_ratio - - # make the crop - crop_left = bleed_pixels[0] + (live_size[0] - crop_width) * centering_x - crop_top = bleed_pixels[1] + (live_size[1] - crop_height) * centering_y - - crop = (crop_left, crop_top, crop_left + crop_width, crop_top + crop_height) - - # resize the image and return it - return image.resize(size, method, box=crop) - - -def flip(image: Image.Image) -> Image.Image: - """ - Flip the image vertically (top to bottom). - - :param image: The image to flip. - :return: An image. - """ - return image.transpose(Image.Transpose.FLIP_TOP_BOTTOM) - - -def grayscale(image: Image.Image) -> Image.Image: - """ - Convert the image to grayscale. - - :param image: The image to convert. - :return: An image. - """ - return image.convert("L") - - -def invert(image: Image.Image) -> Image.Image: - """ - Invert (negate) the image. - - :param image: The image to invert. - :return: An image. - """ - lut = list(range(255, -1, -1)) - return image.point(lut) if image.mode == "1" else _lut(image, lut) - - -def mirror(image: Image.Image) -> Image.Image: - """ - Flip image horizontally (left to right). - - :param image: The image to mirror. - :return: An image. - """ - return image.transpose(Image.Transpose.FLIP_LEFT_RIGHT) - - -def posterize(image: Image.Image, bits: int) -> Image.Image: - """ - Reduce the number of bits for each color channel. - - :param image: The image to posterize. - :param bits: The number of bits to keep for each channel (1-8). - :return: An image. - """ - mask = ~(2 ** (8 - bits) - 1) - lut = [i & mask for i in range(256)] - return _lut(image, lut) - - -def solarize(image: Image.Image, threshold: int = 128) -> Image.Image: - """ - Invert all pixel values above a threshold. - - :param image: The image to solarize. - :param threshold: All pixels above this grayscale level are inverted. - :return: An image. - """ - lut = [] - for i in range(256): - if i < threshold: - lut.append(i) - else: - lut.append(255 - i) - return _lut(image, lut) - - -@overload -def exif_transpose(image: Image.Image, *, in_place: Literal[True]) -> None: ... - - -@overload -def exif_transpose( - image: Image.Image, *, in_place: Literal[False] = False -) -> Image.Image: ... - - -def exif_transpose(image: Image.Image, *, in_place: bool = False) -> Image.Image | None: - """ - If an image has an EXIF Orientation tag, other than 1, transpose the image - accordingly, and remove the orientation data. - - :param image: The image to transpose. - :param in_place: Boolean. Keyword-only argument. - If ``True``, the original image is modified in-place, and ``None`` is returned. - If ``False`` (default), a new :py:class:`~PIL.Image.Image` object is returned - with the transposition applied. If there is no transposition, a copy of the - image will be returned. - """ - image.load() - image_exif = image.getexif() - orientation = image_exif.get(ExifTags.Base.Orientation, 1) - method = { - 2: Image.Transpose.FLIP_LEFT_RIGHT, - 3: Image.Transpose.ROTATE_180, - 4: Image.Transpose.FLIP_TOP_BOTTOM, - 5: Image.Transpose.TRANSPOSE, - 6: Image.Transpose.ROTATE_270, - 7: Image.Transpose.TRANSVERSE, - 8: Image.Transpose.ROTATE_90, - }.get(orientation) - if method is not None: - if in_place: - image.im = image.im.transpose(method) - image._size = image.im.size - else: - transposed_image = image.transpose(method) - exif_image = image if in_place else transposed_image - - exif = exif_image.getexif() - if ExifTags.Base.Orientation in exif: - del exif[ExifTags.Base.Orientation] - if "exif" in exif_image.info: - exif_image.info["exif"] = exif.tobytes() - elif "Raw profile type exif" in exif_image.info: - exif_image.info["Raw profile type exif"] = exif.tobytes().hex() - for key in ("XML:com.adobe.xmp", "xmp"): - if key in exif_image.info: - for pattern in ( - r'tiff:Orientation="([0-9])"', - r"([0-9])", - ): - value = exif_image.info[key] - if isinstance(value, str): - value = re.sub(pattern, "", value) - elif isinstance(value, tuple): - value = tuple( - re.sub(pattern.encode(), b"", v) for v in value - ) - else: - value = re.sub(pattern.encode(), b"", value) - exif_image.info[key] = value - if not in_place: - return transposed_image - elif not in_place: - return image.copy() - return None diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/ImagePalette.py b/.venv-docs/lib/python3.12/site-packages/PIL/ImagePalette.py deleted file mode 100644 index 10369711..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/ImagePalette.py +++ /dev/null @@ -1,286 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# image palette object -# -# History: -# 1996-03-11 fl Rewritten. -# 1997-01-03 fl Up and running. -# 1997-08-23 fl Added load hack -# 2001-04-16 fl Fixed randint shadow bug in random() -# -# Copyright (c) 1997-2001 by Secret Labs AB -# Copyright (c) 1996-1997 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import array -from collections.abc import Sequence -from typing import IO - -from . import GimpGradientFile, GimpPaletteFile, ImageColor, PaletteFile - -TYPE_CHECKING = False -if TYPE_CHECKING: - from . import Image - - -class ImagePalette: - """ - Color palette for palette mapped images - - :param mode: The mode to use for the palette. See: - :ref:`concept-modes`. Defaults to "RGB" - :param palette: An optional palette. If given, it must be a bytearray, - an array or a list of ints between 0-255. The list must consist of - all channels for one color followed by the next color (e.g. RGBRGBRGB). - Defaults to an empty palette. - """ - - def __init__( - self, - mode: str = "RGB", - palette: Sequence[int] | bytes | bytearray | None = None, - ) -> None: - self.mode = mode - self.rawmode: str | None = None # if set, palette contains raw data - self.palette = palette or bytearray() - self.dirty: int | None = None - - @property - def palette(self) -> Sequence[int] | bytes | bytearray: - return self._palette - - @palette.setter - def palette(self, palette: Sequence[int] | bytes | bytearray) -> None: - self._colors: dict[tuple[int, ...], int] | None = None - self._palette = palette - - @property - def colors(self) -> dict[tuple[int, ...], int]: - if self._colors is None: - mode_len = len(self.mode) - self._colors = {} - for i in range(0, len(self.palette), mode_len): - color = tuple(self.palette[i : i + mode_len]) - if color in self._colors: - continue - self._colors[color] = i // mode_len - return self._colors - - @colors.setter - def colors(self, colors: dict[tuple[int, ...], int]) -> None: - self._colors = colors - - def copy(self) -> ImagePalette: - new = ImagePalette() - - new.mode = self.mode - new.rawmode = self.rawmode - if self.palette is not None: - new.palette = self.palette[:] - new.dirty = self.dirty - - return new - - def getdata(self) -> tuple[str, Sequence[int] | bytes | bytearray]: - """ - Get palette contents in format suitable for the low-level - ``im.putpalette`` primitive. - - .. warning:: This method is experimental. - """ - if self.rawmode: - return self.rawmode, self.palette - return self.mode, self.tobytes() - - def tobytes(self) -> bytes: - """Convert palette to bytes. - - .. warning:: This method is experimental. - """ - if self.rawmode: - msg = "palette contains raw palette data" - raise ValueError(msg) - if isinstance(self.palette, bytes): - return self.palette - arr = array.array("B", self.palette) - return arr.tobytes() - - # Declare tostring as an alias for tobytes - tostring = tobytes - - def _new_color_index( - self, image: Image.Image | None = None, e: Exception | None = None - ) -> int: - if not isinstance(self.palette, bytearray): - self._palette = bytearray(self.palette) - index = len(self.palette) // 3 - special_colors: tuple[int | tuple[int, ...] | None, ...] = () - if image: - special_colors = ( - image.info.get("background"), - image.info.get("transparency"), - ) - while index in special_colors: - index += 1 - if index >= 256: - if image: - # Search for an unused index - for i, count in reversed(list(enumerate(image.histogram()))): - if count == 0 and i not in special_colors: - index = i - break - if index >= 256: - msg = "cannot allocate more than 256 colors" - raise ValueError(msg) from e - return index - - def getcolor( - self, - color: tuple[int, ...], - image: Image.Image | None = None, - ) -> int: - """Given an rgb tuple, allocate palette entry. - - .. warning:: This method is experimental. - """ - if self.rawmode: - msg = "palette contains raw palette data" - raise ValueError(msg) - if isinstance(color, tuple): - if self.mode == "RGB": - if len(color) == 4: - if color[3] != 255: - msg = "cannot add non-opaque RGBA color to RGB palette" - raise ValueError(msg) - color = color[:3] - elif self.mode == "RGBA": - if len(color) == 3: - color += (255,) - try: - return self.colors[color] - except KeyError as e: - # allocate new color slot - index = self._new_color_index(image, e) - assert isinstance(self._palette, bytearray) - self.colors[color] = index - if index * 3 < len(self.palette): - self._palette = ( - self._palette[: index * 3] - + bytes(color) - + self._palette[index * 3 + 3 :] - ) - else: - self._palette += bytes(color) - self.dirty = 1 - return index - else: - msg = f"unknown color specifier: {repr(color)}" # type: ignore[unreachable] - raise ValueError(msg) - - def save(self, fp: str | IO[str]) -> None: - """Save palette to text file. - - .. warning:: This method is experimental. - """ - if self.rawmode: - msg = "palette contains raw palette data" - raise ValueError(msg) - if isinstance(fp, str): - fp = open(fp, "w") - fp.write("# Palette\n") - fp.write(f"# Mode: {self.mode}\n") - for i in range(256): - fp.write(f"{i}") - for j in range(i * len(self.mode), (i + 1) * len(self.mode)): - try: - fp.write(f" {self.palette[j]}") - except IndexError: - fp.write(" 0") - fp.write("\n") - fp.close() - - -# -------------------------------------------------------------------- -# Internal - - -def raw(rawmode: str, data: Sequence[int] | bytes | bytearray) -> ImagePalette: - palette = ImagePalette() - palette.rawmode = rawmode - palette.palette = data - palette.dirty = 1 - return palette - - -# -------------------------------------------------------------------- -# Factories - - -def make_linear_lut(black: int, white: float) -> list[int]: - if black == 0: - return [int(white * i // 255) for i in range(256)] - - msg = "unavailable when black is non-zero" - raise NotImplementedError(msg) # FIXME - - -def make_gamma_lut(exp: float) -> list[int]: - return [int(((i / 255.0) ** exp) * 255.0 + 0.5) for i in range(256)] - - -def negative(mode: str = "RGB") -> ImagePalette: - palette = list(range(256 * len(mode))) - palette.reverse() - return ImagePalette(mode, [i // len(mode) for i in palette]) - - -def random(mode: str = "RGB") -> ImagePalette: - from random import randint - - palette = [randint(0, 255) for _ in range(256 * len(mode))] - return ImagePalette(mode, palette) - - -def sepia(white: str = "#fff0c0") -> ImagePalette: - bands = [make_linear_lut(0, band) for band in ImageColor.getrgb(white)] - return ImagePalette("RGB", [bands[i % 3][i // 3] for i in range(256 * 3)]) - - -def wedge(mode: str = "RGB") -> ImagePalette: - palette = list(range(256 * len(mode))) - return ImagePalette(mode, [i // len(mode) for i in palette]) - - -def load(filename: str) -> tuple[bytes, str]: - # FIXME: supports GIMP gradients only - - with open(filename, "rb") as fp: - paletteHandlers: list[ - type[ - GimpPaletteFile.GimpPaletteFile - | GimpGradientFile.GimpGradientFile - | PaletteFile.PaletteFile - ] - ] = [ - GimpPaletteFile.GimpPaletteFile, - GimpGradientFile.GimpGradientFile, - PaletteFile.PaletteFile, - ] - for paletteHandler in paletteHandlers: - try: - fp.seek(0) - lut = paletteHandler(fp).getpalette() - if lut: - break - except (SyntaxError, ValueError): - pass - else: - msg = "cannot load palette" - raise OSError(msg) - - return lut # data, rawmode diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/ImagePath.py b/.venv-docs/lib/python3.12/site-packages/PIL/ImagePath.py deleted file mode 100644 index 77e8a609..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/ImagePath.py +++ /dev/null @@ -1,20 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# path interface -# -# History: -# 1996-11-04 fl Created -# 2002-04-14 fl Added documentation stub class -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1996. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from . import Image - -Path = Image.core.path diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/ImageQt.py b/.venv-docs/lib/python3.12/site-packages/PIL/ImageQt.py deleted file mode 100644 index af4d0742..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/ImageQt.py +++ /dev/null @@ -1,219 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# a simple Qt image interface. -# -# history: -# 2006-06-03 fl: created -# 2006-06-04 fl: inherit from QImage instead of wrapping it -# 2006-06-05 fl: removed toimage helper; move string support to ImageQt -# 2013-11-13 fl: add support for Qt5 (aurelien.ballier@cyclonit.com) -# -# Copyright (c) 2006 by Secret Labs AB -# Copyright (c) 2006 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import sys -from io import BytesIO - -from . import Image -from ._util import is_path - -TYPE_CHECKING = False -if TYPE_CHECKING: - from collections.abc import Callable - from typing import Any - - from . import ImageFile - - QBuffer: type - -qt_version: str | None -qt_versions = [ - ["6", "PyQt6"], - ["side6", "PySide6"], -] - -# If a version has already been imported, attempt it first -qt_versions.sort(key=lambda version: version[1] in sys.modules, reverse=True) -for version, qt_module in qt_versions: - try: - qRgba: Callable[[int, int, int, int], int] - if qt_module == "PyQt6": - from PyQt6.QtCore import QBuffer, QByteArray, QIODevice - from PyQt6.QtGui import QImage, QPixmap, qRgba - elif qt_module == "PySide6": - from PySide6.QtCore import ( # type: ignore[assignment] - QBuffer, - QByteArray, - QIODevice, - ) - from PySide6.QtGui import QImage, QPixmap, qRgba # type: ignore[assignment] - except (ImportError, RuntimeError): - continue - qt_is_installed = True - qt_version = version - break -else: - qt_is_installed = False - qt_version = None - - -def rgb(r: int, g: int, b: int, a: int = 255) -> int: - """(Internal) Turns an RGB color into a Qt compatible color integer.""" - # use qRgb to pack the colors, and then turn the resulting long - # into a negative integer with the same bitpattern. - return qRgba(r, g, b, a) & 0xFFFFFFFF - - -def fromqimage(im: QImage | QPixmap) -> ImageFile.ImageFile: - """ - :param im: QImage or PIL ImageQt object - """ - buffer = QBuffer() - qt_openmode: object - if qt_version == "6": - try: - qt_openmode = getattr(QIODevice, "OpenModeFlag") - except AttributeError: - qt_openmode = getattr(QIODevice, "OpenMode") - else: - qt_openmode = QIODevice - buffer.open(getattr(qt_openmode, "ReadWrite")) - # preserve alpha channel with png - # otherwise ppm is more friendly with Image.open - if im.hasAlphaChannel(): - im.save(buffer, "png") - else: - im.save(buffer, "ppm") - - b = BytesIO() - b.write(buffer.data()) - buffer.close() - b.seek(0) - - return Image.open(b) - - -def fromqpixmap(im: QPixmap) -> ImageFile.ImageFile: - return fromqimage(im) - - -def align8to32(bytes: bytes, width: int, mode: str) -> bytes: - """ - converts each scanline of data from 8 bit to 32 bit aligned - """ - - bits_per_pixel = {"1": 1, "L": 8, "P": 8, "I;16": 16}[mode] - - # calculate bytes per line and the extra padding if needed - bits_per_line = bits_per_pixel * width - full_bytes_per_line, remaining_bits_per_line = divmod(bits_per_line, 8) - bytes_per_line = full_bytes_per_line + (1 if remaining_bits_per_line else 0) - - extra_padding = -bytes_per_line % 4 - - # already 32 bit aligned by luck - if not extra_padding: - return bytes - - new_data = [ - bytes[i * bytes_per_line : (i + 1) * bytes_per_line] + b"\x00" * extra_padding - for i in range(len(bytes) // bytes_per_line) - ] - - return b"".join(new_data) - - -def _toqclass_helper(im: Image.Image | str | QByteArray) -> dict[str, Any]: - data = None - colortable = None - exclusive_fp = False - - # handle filename, if given instead of image name - if hasattr(im, "toUtf8"): - # FIXME - is this really the best way to do this? - im = str(im.toUtf8(), "utf-8") - if is_path(im): - im = Image.open(im) - exclusive_fp = True - assert isinstance(im, Image.Image) - - qt_format = getattr(QImage, "Format") if qt_version == "6" else QImage - if im.mode == "1": - format = getattr(qt_format, "Format_Mono") - elif im.mode == "L": - format = getattr(qt_format, "Format_Indexed8") - colortable = [rgb(i, i, i) for i in range(256)] - elif im.mode == "P": - format = getattr(qt_format, "Format_Indexed8") - palette = im.getpalette() - assert palette is not None - colortable = [rgb(*palette[i : i + 3]) for i in range(0, len(palette), 3)] - elif im.mode == "RGB": - # Populate the 4th channel with 255 - im = im.convert("RGBA") - - data = im.tobytes("raw", "BGRA") - format = getattr(qt_format, "Format_RGB32") - elif im.mode == "RGBA": - data = im.tobytes("raw", "BGRA") - format = getattr(qt_format, "Format_ARGB32") - elif im.mode == "I;16": - im = im.point(lambda i: i * 256) - - format = getattr(qt_format, "Format_Grayscale16") - else: - if exclusive_fp: - im.close() - msg = f"unsupported image mode {repr(im.mode)}" - raise ValueError(msg) - - size = im.size - __data = data or align8to32(im.tobytes(), size[0], im.mode) - if exclusive_fp: - im.close() - return {"data": __data, "size": size, "format": format, "colortable": colortable} - - -if qt_is_installed: - - class ImageQt(QImage): - def __init__(self, im: Image.Image | str | QByteArray) -> None: - """ - An PIL image wrapper for Qt. This is a subclass of PyQt's QImage - class. - - :param im: A PIL Image object, or a file name (given either as - Python string or a PyQt string object). - """ - im_data = _toqclass_helper(im) - # must keep a reference, or Qt will crash! - # All QImage constructors that take data operate on an existing - # buffer, so this buffer has to hang on for the life of the image. - # Fixes https://github.com/python-pillow/Pillow/issues/1370 - self.__data = im_data["data"] - super().__init__( - self.__data, - im_data["size"][0], - im_data["size"][1], - im_data["format"], - ) - if im_data["colortable"]: - self.setColorTable(im_data["colortable"]) - - -def toqimage(im: Image.Image | str | QByteArray) -> ImageQt: - return ImageQt(im) - - -def toqpixmap(im: Image.Image | str | QByteArray) -> QPixmap: - qimage = toqimage(im) - pixmap = getattr(QPixmap, "fromImage")(qimage) - if qt_version == "6": - pixmap.detach() - return pixmap diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/ImageSequence.py b/.venv-docs/lib/python3.12/site-packages/PIL/ImageSequence.py deleted file mode 100644 index 361be489..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/ImageSequence.py +++ /dev/null @@ -1,88 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# sequence support classes -# -# history: -# 1997-02-20 fl Created -# -# Copyright (c) 1997 by Secret Labs AB. -# Copyright (c) 1997 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# - -## -from __future__ import annotations - -from . import Image - -TYPE_CHECKING = False -if TYPE_CHECKING: - from collections.abc import Callable - - -class Iterator: - """ - This class implements an iterator object that can be used to loop - over an image sequence. - - You can use the ``[]`` operator to access elements by index. This operator - will raise an :py:exc:`IndexError` if you try to access a nonexistent - frame. - - :param im: An image object. - """ - - def __init__(self, im: Image.Image) -> None: - if not hasattr(im, "seek"): - msg = "im must have seek method" - raise AttributeError(msg) - self.im = im - self.position = getattr(self.im, "_min_frame", 0) - - def __getitem__(self, ix: int) -> Image.Image: - try: - self.im.seek(ix) - return self.im - except EOFError as e: - msg = "end of sequence" - raise IndexError(msg) from e - - def __iter__(self) -> Iterator: - return self - - def __next__(self) -> Image.Image: - try: - self.im.seek(self.position) - self.position += 1 - return self.im - except EOFError as e: - msg = "end of sequence" - raise StopIteration(msg) from e - - -def all_frames( - im: Image.Image | list[Image.Image], - func: Callable[[Image.Image], Image.Image] | None = None, -) -> list[Image.Image]: - """ - Applies a given function to all frames in an image or a list of images. - The frames are returned as a list of separate images. - - :param im: An image, or a list of images. - :param func: The function to apply to all of the image frames. - :returns: A list of images. - """ - if not isinstance(im, list): - im = [im] - - ims = [] - for imSequence in im: - current = imSequence.tell() - - ims += [im_frame.copy() for im_frame in Iterator(imSequence)] - - imSequence.seek(current) - return [func(im) for im in ims] if func else ims diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/ImageShow.py b/.venv-docs/lib/python3.12/site-packages/PIL/ImageShow.py deleted file mode 100644 index 7705608e..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/ImageShow.py +++ /dev/null @@ -1,362 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# im.show() drivers -# -# History: -# 2008-04-06 fl Created -# -# Copyright (c) Secret Labs AB 2008. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import abc -import os -import shutil -import subprocess -import sys -from shlex import quote -from typing import Any - -from . import Image - -_viewers = [] - - -def register(viewer: type[Viewer] | Viewer, order: int = 1) -> None: - """ - The :py:func:`register` function is used to register additional viewers:: - - from PIL import ImageShow - ImageShow.register(MyViewer()) # MyViewer will be used as a last resort - ImageShow.register(MySecondViewer(), 0) # MySecondViewer will be prioritised - ImageShow.register(ImageShow.XVViewer(), 0) # XVViewer will be prioritised - - :param viewer: The viewer to be registered. - :param order: - Zero or a negative integer to prepend this viewer to the list, - a positive integer to append it. - """ - if isinstance(viewer, type) and issubclass(viewer, Viewer): - viewer = viewer() - if order > 0: - _viewers.append(viewer) - else: - _viewers.insert(0, viewer) - - -def show(image: Image.Image, title: str | None = None, **options: Any) -> bool: - r""" - Display a given image. - - :param image: An image object. - :param title: Optional title. Not all viewers can display the title. - :param \**options: Additional viewer options. - :returns: ``True`` if a suitable viewer was found, ``False`` otherwise. - """ - for viewer in _viewers: - if viewer.show(image, title=title, **options): - return True - return False - - -class Viewer: - """Base class for viewers.""" - - # main api - - def show(self, image: Image.Image, **options: Any) -> int: - """ - The main function for displaying an image. - Converts the given image to the target format and displays it. - """ - - if not ( - image.mode in ("1", "RGBA") - or (self.format == "PNG" and image.mode in ("I;16", "LA")) - ): - base = Image.getmodebase(image.mode) - if image.mode != base: - image = image.convert(base) - - return self.show_image(image, **options) - - # hook methods - - format: str | None = None - """The format to convert the image into.""" - options: dict[str, Any] = {} - """Additional options used to convert the image.""" - - def get_format(self, image: Image.Image) -> str | None: - """Return format name, or ``None`` to save as PGM/PPM.""" - return self.format - - def get_command(self, file: str, **options: Any) -> str: - """ - Returns the command used to display the file. - Not implemented in the base class. - """ - msg = "unavailable in base viewer" - raise NotImplementedError(msg) - - def save_image(self, image: Image.Image) -> str: - """Save to temporary file and return filename.""" - return image._dump(format=self.get_format(image), **self.options) - - def show_image(self, image: Image.Image, **options: Any) -> int: - """Display the given image.""" - return self.show_file(self.save_image(image), **options) - - def show_file(self, path: str, **options: Any) -> int: - """ - Display given file. - """ - if not os.path.exists(path): - raise FileNotFoundError - os.system(self.get_command(path, **options)) # nosec - return 1 - - -# -------------------------------------------------------------------- - - -class WindowsViewer(Viewer): - """The default viewer on Windows is the default system application for PNG files.""" - - format = "PNG" - options = {"compress_level": 1, "save_all": True} - - def get_command(self, file: str, **options: Any) -> str: - return ( - f'start "Pillow" /WAIT "{file}" ' - "&& ping -n 4 127.0.0.1 >NUL " - f'&& del /f "{file}"' - ) - - def show_file(self, path: str, **options: Any) -> int: - """ - Display given file. - """ - if not os.path.exists(path): - raise FileNotFoundError - subprocess.Popen( - self.get_command(path, **options), - shell=True, - creationflags=getattr(subprocess, "CREATE_NO_WINDOW"), - ) # nosec - return 1 - - -if sys.platform == "win32": - register(WindowsViewer) - - -class MacViewer(Viewer): - """The default viewer on macOS using ``Preview.app``.""" - - format = "PNG" - options = {"compress_level": 1, "save_all": True} - - def get_command(self, file: str, **options: Any) -> str: - # on darwin open returns immediately resulting in the temp - # file removal while app is opening - command = "open -a Preview.app" - command = f"({command} {quote(file)}; sleep 20; rm -f {quote(file)})&" - return command - - def show_file(self, path: str, **options: Any) -> int: - """ - Display given file. - """ - if not os.path.exists(path): - raise FileNotFoundError - subprocess.call(["open", "-a", "Preview.app", path]) - - pyinstaller = getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS") - executable = (not pyinstaller and sys.executable) or shutil.which("python3") - if executable: - subprocess.Popen( - [ - executable, - "-c", - "import os, sys, time; time.sleep(20); os.remove(sys.argv[1])", - path, - ] - ) - return 1 - - -if sys.platform == "darwin": - register(MacViewer) - - -class UnixViewer(abc.ABC, Viewer): - format = "PNG" - options = {"compress_level": 1, "save_all": True} - - @abc.abstractmethod - def get_command_ex(self, file: str, **options: Any) -> tuple[str, str]: - pass - - def get_command(self, file: str, **options: Any) -> str: - command = self.get_command_ex(file, **options)[0] - return f"{command} {quote(file)}" - - -class XDGViewer(UnixViewer): - """ - The freedesktop.org ``xdg-open`` command. - """ - - def get_command_ex(self, file: str, **options: Any) -> tuple[str, str]: - command = executable = "xdg-open" - return command, executable - - def show_file(self, path: str, **options: Any) -> int: - """ - Display given file. - """ - if not os.path.exists(path): - raise FileNotFoundError - subprocess.Popen(["xdg-open", path]) - return 1 - - -class DisplayViewer(UnixViewer): - """ - The ImageMagick ``display`` command. - This viewer supports the ``title`` parameter. - """ - - def get_command_ex( - self, file: str, title: str | None = None, **options: Any - ) -> tuple[str, str]: - command = executable = "display" - if title: - command += f" -title {quote(title)}" - return command, executable - - def show_file(self, path: str, **options: Any) -> int: - """ - Display given file. - """ - if not os.path.exists(path): - raise FileNotFoundError - args = ["display"] - title = options.get("title") - if title: - args += ["-title", title] - args.append(path) - - subprocess.Popen(args) - return 1 - - -class GmDisplayViewer(UnixViewer): - """The GraphicsMagick ``gm display`` command.""" - - def get_command_ex(self, file: str, **options: Any) -> tuple[str, str]: - executable = "gm" - command = "gm display" - return command, executable - - def show_file(self, path: str, **options: Any) -> int: - """ - Display given file. - """ - if not os.path.exists(path): - raise FileNotFoundError - subprocess.Popen(["gm", "display", path]) - return 1 - - -class EogViewer(UnixViewer): - """The GNOME Image Viewer ``eog`` command.""" - - def get_command_ex(self, file: str, **options: Any) -> tuple[str, str]: - executable = "eog" - command = "eog -n" - return command, executable - - def show_file(self, path: str, **options: Any) -> int: - """ - Display given file. - """ - if not os.path.exists(path): - raise FileNotFoundError - subprocess.Popen(["eog", "-n", path]) - return 1 - - -class XVViewer(UnixViewer): - """ - The X Viewer ``xv`` command. - This viewer supports the ``title`` parameter. - """ - - def get_command_ex( - self, file: str, title: str | None = None, **options: Any - ) -> tuple[str, str]: - # note: xv is pretty outdated. most modern systems have - # imagemagick's display command instead. - command = executable = "xv" - if title: - command += f" -name {quote(title)}" - return command, executable - - def show_file(self, path: str, **options: Any) -> int: - """ - Display given file. - """ - if not os.path.exists(path): - raise FileNotFoundError - args = ["xv"] - title = options.get("title") - if title: - args += ["-name", title] - args.append(path) - - subprocess.Popen(args) - return 1 - - -if sys.platform not in ("win32", "darwin"): # unixoids - if shutil.which("xdg-open"): - register(XDGViewer) - if shutil.which("display"): - register(DisplayViewer) - if shutil.which("gm"): - register(GmDisplayViewer) - if shutil.which("eog"): - register(EogViewer) - if shutil.which("xv"): - register(XVViewer) - - -class IPythonViewer(Viewer): - """The viewer for IPython frontends.""" - - def show_image(self, image: Image.Image, **options: Any) -> int: - ipython_display(image) - return 1 - - -try: - from IPython.display import display as ipython_display -except ImportError: - pass -else: - register(IPythonViewer) - - -if __name__ == "__main__": - if len(sys.argv) < 2: - print("Syntax: python3 ImageShow.py imagefile [title]") - sys.exit() - - with Image.open(sys.argv[1]) as im: - print(show(im, *sys.argv[2:])) diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/ImageStat.py b/.venv-docs/lib/python3.12/site-packages/PIL/ImageStat.py deleted file mode 100644 index 3a1044ba..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/ImageStat.py +++ /dev/null @@ -1,167 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# global image statistics -# -# History: -# 1996-04-05 fl Created -# 1997-05-21 fl Added mask; added rms, var, stddev attributes -# 1997-08-05 fl Added median -# 1998-07-05 hk Fixed integer overflow error -# -# Notes: -# This class shows how to implement delayed evaluation of attributes. -# To get a certain value, simply access the corresponding attribute. -# The __getattr__ dispatcher takes care of the rest. -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1996-97. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import math -from functools import cached_property - -from . import Image - - -class Stat: - def __init__( - self, image_or_list: Image.Image | list[int], mask: Image.Image | None = None - ) -> None: - """ - Calculate statistics for the given image. If a mask is included, - only the regions covered by that mask are included in the - statistics. You can also pass in a previously calculated histogram. - - :param image: A PIL image, or a precalculated histogram. - - .. note:: - - For a PIL image, calculations rely on the - :py:meth:`~PIL.Image.Image.histogram` method. The pixel counts are - grouped into 256 bins, even if the image has more than 8 bits per - channel. So ``I`` and ``F`` mode images have a maximum ``mean``, - ``median`` and ``rms`` of 255, and cannot have an ``extrema`` maximum - of more than 255. - - :param mask: An optional mask. - """ - if isinstance(image_or_list, Image.Image): - self.h = image_or_list.histogram(mask) - elif isinstance(image_or_list, list): - self.h = image_or_list - else: - msg = "first argument must be image or list" # type: ignore[unreachable] - raise TypeError(msg) - self.bands = list(range(len(self.h) // 256)) - - @cached_property - def extrema(self) -> list[tuple[int, int]]: - """ - Min/max values for each band in the image. - - .. note:: - This relies on the :py:meth:`~PIL.Image.Image.histogram` method, and - simply returns the low and high bins used. This is correct for - images with 8 bits per channel, but fails for other modes such as - ``I`` or ``F``. Instead, use :py:meth:`~PIL.Image.Image.getextrema` to - return per-band extrema for the image. This is more correct and - efficient because, for non-8-bit modes, the histogram method uses - :py:meth:`~PIL.Image.Image.getextrema` to determine the bins used. - """ - - def minmax(histogram: list[int]) -> tuple[int, int]: - res_min, res_max = 255, 0 - for i in range(256): - if histogram[i]: - res_min = i - break - for i in range(255, -1, -1): - if histogram[i]: - res_max = i - break - return res_min, res_max - - return [minmax(self.h[i:]) for i in range(0, len(self.h), 256)] - - @cached_property - def count(self) -> list[int]: - """Total number of pixels for each band in the image.""" - return [sum(self.h[i : i + 256]) for i in range(0, len(self.h), 256)] - - @cached_property - def sum(self) -> list[float]: - """Sum of all pixels for each band in the image.""" - - v = [] - for i in range(0, len(self.h), 256): - layer_sum = 0.0 - for j in range(256): - layer_sum += j * self.h[i + j] - v.append(layer_sum) - return v - - @cached_property - def sum2(self) -> list[float]: - """Squared sum of all pixels for each band in the image.""" - - v = [] - for i in range(0, len(self.h), 256): - sum2 = 0.0 - for j in range(256): - sum2 += (j**2) * float(self.h[i + j]) - v.append(sum2) - return v - - @cached_property - def mean(self) -> list[float]: - """Average (arithmetic mean) pixel level for each band in the image.""" - return [self.sum[i] / self.count[i] if self.count[i] else 0 for i in self.bands] - - @cached_property - def median(self) -> list[int]: - """Median pixel level for each band in the image.""" - - v = [] - for i in self.bands: - s = 0 - half = self.count[i] // 2 - b = i * 256 - for j in range(256): - s = s + self.h[b + j] - if s > half: - break - v.append(j) - return v - - @cached_property - def rms(self) -> list[float]: - """RMS (root-mean-square) for each band in the image.""" - return [ - math.sqrt(self.sum2[i] / self.count[i]) if self.count[i] else 0 - for i in self.bands - ] - - @cached_property - def var(self) -> list[float]: - """Variance for each band in the image.""" - return [ - ( - (self.sum2[i] - (self.sum[i] ** 2.0) / self.count[i]) / self.count[i] - if self.count[i] - else 0 - ) - for i in self.bands - ] - - @cached_property - def stddev(self) -> list[float]: - """Standard deviation for each band in the image.""" - return [math.sqrt(self.var[i]) for i in self.bands] - - -Global = Stat # compatibility diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/ImageText.py b/.venv-docs/lib/python3.12/site-packages/PIL/ImageText.py deleted file mode 100644 index c74570e6..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/ImageText.py +++ /dev/null @@ -1,318 +0,0 @@ -from __future__ import annotations - -from . import ImageFont -from ._typing import _Ink - - -class Text: - def __init__( - self, - text: str | bytes, - font: ( - ImageFont.ImageFont - | ImageFont.FreeTypeFont - | ImageFont.TransposedFont - | None - ) = None, - mode: str = "RGB", - spacing: float = 4, - direction: str | None = None, - features: list[str] | None = None, - language: str | None = None, - ) -> None: - """ - :param text: String to be drawn. - :param font: Either an :py:class:`~PIL.ImageFont.ImageFont` instance, - :py:class:`~PIL.ImageFont.FreeTypeFont` instance, - :py:class:`~PIL.ImageFont.TransposedFont` instance or ``None``. If - ``None``, the default font from :py:meth:`.ImageFont.load_default` - will be used. - :param mode: The image mode this will be used with. - :param spacing: The number of pixels between lines. - :param direction: Direction of the text. It can be ``"rtl"`` (right to left), - ``"ltr"`` (left to right) or ``"ttb"`` (top to bottom). - Requires libraqm. - :param features: A list of OpenType font features to be used during text - layout. This is usually used to turn on optional font features - that are not enabled by default, for example ``"dlig"`` or - ``"ss01"``, but can be also used to turn off default font - features, for example ``"-liga"`` to disable ligatures or - ``"-kern"`` to disable kerning. To get all supported - features, see `OpenType docs`_. - Requires libraqm. - :param language: Language of the text. Different languages may use - different glyph shapes or ligatures. This parameter tells - the font which language the text is in, and to apply the - correct substitutions as appropriate, if available. - It should be a `BCP 47 language code`_. - Requires libraqm. - """ - self.text = text - self.font = font or ImageFont.load_default() - - self.mode = mode - self.spacing = spacing - self.direction = direction - self.features = features - self.language = language - - self.embedded_color = False - - self.stroke_width: float = 0 - self.stroke_fill: _Ink | None = None - - def embed_color(self) -> None: - """ - Use embedded color glyphs (COLR, CBDT, SBIX). - """ - if self.mode not in ("RGB", "RGBA"): - msg = "Embedded color supported only in RGB and RGBA modes" - raise ValueError(msg) - self.embedded_color = True - - def stroke(self, width: float = 0, fill: _Ink | None = None) -> None: - """ - :param width: The width of the text stroke. - :param fill: Color to use for the text stroke when drawing. If not given, will - default to the ``fill`` parameter from - :py:meth:`.ImageDraw.ImageDraw.text`. - """ - self.stroke_width = width - self.stroke_fill = fill - - def _get_fontmode(self) -> str: - if self.mode in ("1", "P", "I", "F"): - return "1" - elif self.embedded_color: - return "RGBA" - else: - return "L" - - def get_length(self): - """ - Returns length (in pixels with 1/64 precision) of text. - - This is the amount by which following text should be offset. - Text bounding box may extend past the length in some fonts, - e.g. when using italics or accents. - - The result is returned as a float; it is a whole number if using basic layout. - - Note that the sum of two lengths may not equal the length of a concatenated - string due to kerning. If you need to adjust for kerning, include the following - character and subtract its length. - - For example, instead of:: - - hello = ImageText.Text("Hello", font).get_length() - world = ImageText.Text("World", font).get_length() - helloworld = ImageText.Text("HelloWorld", font).get_length() - assert hello + world == helloworld - - use:: - - hello = ( - ImageText.Text("HelloW", font).get_length() - - ImageText.Text("W", font).get_length() - ) # adjusted for kerning - world = ImageText.Text("World", font).get_length() - helloworld = ImageText.Text("HelloWorld", font).get_length() - assert hello + world == helloworld - - or disable kerning with (requires libraqm):: - - hello = ImageText.Text("Hello", font, features=["-kern"]).get_length() - world = ImageText.Text("World", font, features=["-kern"]).get_length() - helloworld = ImageText.Text( - "HelloWorld", font, features=["-kern"] - ).get_length() - assert hello + world == helloworld - - :return: Either width for horizontal text, or height for vertical text. - """ - split_character = "\n" if isinstance(self.text, str) else b"\n" - if split_character in self.text: - msg = "can't measure length of multiline text" - raise ValueError(msg) - return self.font.getlength( - self.text, - self._get_fontmode(), - self.direction, - self.features, - self.language, - ) - - def _split( - self, xy: tuple[float, float], anchor: str | None, align: str - ) -> list[tuple[tuple[float, float], str, str | bytes]]: - if anchor is None: - anchor = "lt" if self.direction == "ttb" else "la" - elif len(anchor) != 2: - msg = "anchor must be a 2 character string" - raise ValueError(msg) - - lines = ( - self.text.split("\n") - if isinstance(self.text, str) - else self.text.split(b"\n") - ) - if len(lines) == 1: - return [(xy, anchor, self.text)] - - if anchor[1] in "tb" and self.direction != "ttb": - msg = "anchor not supported for multiline text" - raise ValueError(msg) - - fontmode = self._get_fontmode() - line_spacing = ( - self.font.getbbox( - "A", - fontmode, - None, - self.features, - self.language, - self.stroke_width, - )[3] - + self.stroke_width - + self.spacing - ) - - top = xy[1] - parts = [] - if self.direction == "ttb": - left = xy[0] - for line in lines: - parts.append(((left, top), anchor, line)) - left += line_spacing - else: - widths = [] - max_width: float = 0 - for line in lines: - line_width = self.font.getlength( - line, fontmode, self.direction, self.features, self.language - ) - widths.append(line_width) - max_width = max(max_width, line_width) - - if anchor[1] == "m": - top -= (len(lines) - 1) * line_spacing / 2.0 - elif anchor[1] == "d": - top -= (len(lines) - 1) * line_spacing - - idx = -1 - for line in lines: - left = xy[0] - idx += 1 - width_difference = max_width - widths[idx] - - # align by align parameter - if align in ("left", "justify"): - pass - elif align == "center": - left += width_difference / 2.0 - elif align == "right": - left += width_difference - else: - msg = 'align must be "left", "center", "right" or "justify"' - raise ValueError(msg) - - if ( - align == "justify" - and width_difference != 0 - and idx != len(lines) - 1 - ): - words = ( - line.split(" ") if isinstance(line, str) else line.split(b" ") - ) - if len(words) > 1: - # align left by anchor - if anchor[0] == "m": - left -= max_width / 2.0 - elif anchor[0] == "r": - left -= max_width - - word_widths = [ - self.font.getlength( - word, - fontmode, - self.direction, - self.features, - self.language, - ) - for word in words - ] - word_anchor = "l" + anchor[1] - width_difference = max_width - sum(word_widths) - i = 0 - for word in words: - parts.append(((left, top), word_anchor, word)) - left += word_widths[i] + width_difference / (len(words) - 1) - i += 1 - top += line_spacing - continue - - # align left by anchor - if anchor[0] == "m": - left -= width_difference / 2.0 - elif anchor[0] == "r": - left -= width_difference - parts.append(((left, top), anchor, line)) - top += line_spacing - - return parts - - def get_bbox( - self, - xy: tuple[float, float] = (0, 0), - anchor: str | None = None, - align: str = "left", - ) -> tuple[float, float, float, float]: - """ - Returns bounding box (in pixels) of text. - - Use :py:meth:`get_length` to get the offset of following text with 1/64 pixel - precision. The bounding box includes extra margins for some fonts, e.g. italics - or accents. - - :param xy: The anchor coordinates of the text. - :param anchor: The text anchor alignment. Determines the relative location of - the anchor to the text. The default alignment is top left, - specifically ``la`` for horizontal text and ``lt`` for - vertical text. See :ref:`text-anchors` for details. - :param align: For multiline text, ``"left"``, ``"center"``, ``"right"`` or - ``"justify"`` determines the relative alignment of lines. Use the - ``anchor`` parameter to specify the alignment to ``xy``. - - :return: ``(left, top, right, bottom)`` bounding box - """ - bbox: tuple[float, float, float, float] | None = None - fontmode = self._get_fontmode() - for xy, anchor, line in self._split(xy, anchor, align): - bbox_line = self.font.getbbox( - line, - fontmode, - self.direction, - self.features, - self.language, - self.stroke_width, - anchor, - ) - bbox_line = ( - bbox_line[0] + xy[0], - bbox_line[1] + xy[1], - bbox_line[2] + xy[0], - bbox_line[3] + xy[1], - ) - if bbox is None: - bbox = bbox_line - else: - bbox = ( - min(bbox[0], bbox_line[0]), - min(bbox[1], bbox_line[1]), - max(bbox[2], bbox_line[2]), - max(bbox[3], bbox_line[3]), - ) - - if bbox is None: - return xy[0], xy[1], xy[0], xy[1] - return bbox diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/ImageTk.py b/.venv-docs/lib/python3.12/site-packages/PIL/ImageTk.py deleted file mode 100644 index 3a4cb81e..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/ImageTk.py +++ /dev/null @@ -1,266 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# a Tk display interface -# -# History: -# 96-04-08 fl Created -# 96-09-06 fl Added getimage method -# 96-11-01 fl Rewritten, removed image attribute and crop method -# 97-05-09 fl Use PyImagingPaste method instead of image type -# 97-05-12 fl Minor tweaks to match the IFUNC95 interface -# 97-05-17 fl Support the "pilbitmap" booster patch -# 97-06-05 fl Added file= and data= argument to image constructors -# 98-03-09 fl Added width and height methods to Image classes -# 98-07-02 fl Use default mode for "P" images without palette attribute -# 98-07-02 fl Explicitly destroy Tkinter image objects -# 99-07-24 fl Support multiple Tk interpreters (from Greg Couch) -# 99-07-26 fl Automatically hook into Tkinter (if possible) -# 99-08-15 fl Hook uses _imagingtk instead of _imaging -# -# Copyright (c) 1997-1999 by Secret Labs AB -# Copyright (c) 1996-1997 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import tkinter -from io import BytesIO -from typing import Any - -from . import Image, ImageFile - -TYPE_CHECKING = False -if TYPE_CHECKING: - from ._typing import CapsuleType - -# -------------------------------------------------------------------- -# Check for Tkinter interface hooks - - -def _get_image_from_kw(kw: dict[str, Any]) -> ImageFile.ImageFile | None: - source = None - if "file" in kw: - source = kw.pop("file") - elif "data" in kw: - source = BytesIO(kw.pop("data")) - if not source: - return None - return Image.open(source) - - -def _pyimagingtkcall( - command: str, photo: PhotoImage | tkinter.PhotoImage, ptr: CapsuleType -) -> None: - tk = photo.tk - try: - tk.call(command, photo, repr(ptr)) - except tkinter.TclError: - # activate Tkinter hook - # may raise an error if it cannot attach to Tkinter - from . import _imagingtk - - _imagingtk.tkinit(tk.interpaddr()) - tk.call(command, photo, repr(ptr)) - - -# -------------------------------------------------------------------- -# PhotoImage - - -class PhotoImage: - """ - A Tkinter-compatible photo image. This can be used - everywhere Tkinter expects an image object. If the image is an RGBA - image, pixels having alpha 0 are treated as transparent. - - The constructor takes either a PIL image, or a mode and a size. - Alternatively, you can use the ``file`` or ``data`` options to initialize - the photo image object. - - :param image: Either a PIL image, or a mode string. If a mode string is - used, a size must also be given. - :param size: If the first argument is a mode string, this defines the size - of the image. - :keyword file: A filename to load the image from (using - ``Image.open(file)``). - :keyword data: An 8-bit string containing image data (as loaded from an - image file). - """ - - def __init__( - self, - image: Image.Image | str | None = None, - size: tuple[int, int] | None = None, - **kw: Any, - ) -> None: - # Tk compatibility: file or data - if image is None: - image = _get_image_from_kw(kw) - - if image is None: - msg = "Image is required" - raise ValueError(msg) - elif isinstance(image, str): - mode = image - image = None - - if size is None: - msg = "If first argument is mode, size is required" - raise ValueError(msg) - else: - # got an image instead of a mode - mode = image.mode - if mode == "P": - # palette mapped data - image.apply_transparency() - image.load() - mode = image.palette.mode if image.palette else "RGB" - size = image.size - kw["width"], kw["height"] = size - - if mode not in ["1", "L", "RGB", "RGBA"]: - mode = Image.getmodebase(mode) - - self.__mode = mode - self.__size = size - self.__photo = tkinter.PhotoImage(**kw) - self.tk = self.__photo.tk - if image: - self.paste(image) - - def __del__(self) -> None: - try: - name = self.__photo.name - except AttributeError: - return - self.__photo.name = None - try: - self.__photo.tk.call("image", "delete", name) - except Exception: - pass # ignore internal errors - - def __str__(self) -> str: - """ - Get the Tkinter photo image identifier. This method is automatically - called by Tkinter whenever a PhotoImage object is passed to a Tkinter - method. - - :return: A Tkinter photo image identifier (a string). - """ - return str(self.__photo) - - def width(self) -> int: - """ - Get the width of the image. - - :return: The width, in pixels. - """ - return self.__size[0] - - def height(self) -> int: - """ - Get the height of the image. - - :return: The height, in pixels. - """ - return self.__size[1] - - def paste(self, im: Image.Image) -> None: - """ - Paste a PIL image into the photo image. Note that this can - be very slow if the photo image is displayed. - - :param im: A PIL image. The size must match the target region. If the - mode does not match, the image is converted to the mode of - the bitmap image. - """ - # convert to blittable - ptr = im.getim() - image = im.im - if not image.isblock() or im.mode != self.__mode: - block = Image.core.new_block(self.__mode, im.size) - image.convert2(block, image) # convert directly between buffers - ptr = block.ptr - - _pyimagingtkcall("PyImagingPhoto", self.__photo, ptr) - - -# -------------------------------------------------------------------- -# BitmapImage - - -class BitmapImage: - """ - A Tkinter-compatible bitmap image. This can be used everywhere Tkinter - expects an image object. - - The given image must have mode "1". Pixels having value 0 are treated as - transparent. Options, if any, are passed on to Tkinter. The most commonly - used option is ``foreground``, which is used to specify the color for the - non-transparent parts. See the Tkinter documentation for information on - how to specify colours. - - :param image: A PIL image. - """ - - def __init__(self, image: Image.Image | None = None, **kw: Any) -> None: - # Tk compatibility: file or data - if image is None: - image = _get_image_from_kw(kw) - - if image is None: - msg = "Image is required" - raise ValueError(msg) - self.__mode = image.mode - self.__size = image.size - - self.__photo = tkinter.BitmapImage(data=image.tobitmap(), **kw) - - def __del__(self) -> None: - try: - name = self.__photo.name - except AttributeError: - return - self.__photo.name = None - try: - self.__photo.tk.call("image", "delete", name) - except Exception: - pass # ignore internal errors - - def width(self) -> int: - """ - Get the width of the image. - - :return: The width, in pixels. - """ - return self.__size[0] - - def height(self) -> int: - """ - Get the height of the image. - - :return: The height, in pixels. - """ - return self.__size[1] - - def __str__(self) -> str: - """ - Get the Tkinter bitmap image identifier. This method is automatically - called by Tkinter whenever a BitmapImage object is passed to a Tkinter - method. - - :return: A Tkinter bitmap image identifier (a string). - """ - return str(self.__photo) - - -def getimage(photo: PhotoImage) -> Image.Image: - """Copies the contents of a PhotoImage to a PIL image memory.""" - im = Image.new("RGBA", (photo.width(), photo.height())) - - _pyimagingtkcall("PyImagingPhotoGet", photo, im.getim()) - - return im diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/ImageTransform.py b/.venv-docs/lib/python3.12/site-packages/PIL/ImageTransform.py deleted file mode 100644 index fb144ff3..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/ImageTransform.py +++ /dev/null @@ -1,136 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# transform wrappers -# -# History: -# 2002-04-08 fl Created -# -# Copyright (c) 2002 by Secret Labs AB -# Copyright (c) 2002 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from collections.abc import Sequence -from typing import Any - -from . import Image - - -class Transform(Image.ImageTransformHandler): - """Base class for other transforms defined in :py:mod:`~PIL.ImageTransform`.""" - - method: Image.Transform - - def __init__(self, data: Sequence[Any]) -> None: - self.data = data - - def getdata(self) -> tuple[Image.Transform, Sequence[int]]: - return self.method, self.data - - def transform( - self, - size: tuple[int, int], - image: Image.Image, - **options: Any, - ) -> Image.Image: - """Perform the transform. Called from :py:meth:`.Image.transform`.""" - # can be overridden - method, data = self.getdata() - return image.transform(size, method, data, **options) - - -class AffineTransform(Transform): - """ - Define an affine image transform. - - This function takes a 6-tuple (a, b, c, d, e, f) which contain the first - two rows from the inverse of an affine transform matrix. For each pixel - (x, y) in the output image, the new value is taken from a position (a x + - b y + c, d x + e y + f) in the input image, rounded to nearest pixel. - - This function can be used to scale, translate, rotate, and shear the - original image. - - See :py:meth:`.Image.transform` - - :param matrix: A 6-tuple (a, b, c, d, e, f) containing the first two rows - from the inverse of an affine transform matrix. - """ - - method = Image.Transform.AFFINE - - -class PerspectiveTransform(Transform): - """ - Define a perspective image transform. - - This function takes an 8-tuple (a, b, c, d, e, f, g, h). For each pixel - (x, y) in the output image, the new value is taken from a position - ((a x + b y + c) / (g x + h y + 1), (d x + e y + f) / (g x + h y + 1)) in - the input image, rounded to nearest pixel. - - This function can be used to scale, translate, rotate, and shear the - original image. - - See :py:meth:`.Image.transform` - - :param matrix: An 8-tuple (a, b, c, d, e, f, g, h). - """ - - method = Image.Transform.PERSPECTIVE - - -class ExtentTransform(Transform): - """ - Define a transform to extract a subregion from an image. - - Maps a rectangle (defined by two corners) from the image to a rectangle of - the given size. The resulting image will contain data sampled from between - the corners, such that (x0, y0) in the input image will end up at (0,0) in - the output image, and (x1, y1) at size. - - This method can be used to crop, stretch, shrink, or mirror an arbitrary - rectangle in the current image. It is slightly slower than crop, but about - as fast as a corresponding resize operation. - - See :py:meth:`.Image.transform` - - :param bbox: A 4-tuple (x0, y0, x1, y1) which specifies two points in the - input image's coordinate system. See :ref:`coordinate-system`. - """ - - method = Image.Transform.EXTENT - - -class QuadTransform(Transform): - """ - Define a quad image transform. - - Maps a quadrilateral (a region defined by four corners) from the image to a - rectangle of the given size. - - See :py:meth:`.Image.transform` - - :param xy: An 8-tuple (x0, y0, x1, y1, x2, y2, x3, y3) which contain the - upper left, lower left, lower right, and upper right corner of the - source quadrilateral. - """ - - method = Image.Transform.QUAD - - -class MeshTransform(Transform): - """ - Define a mesh image transform. A mesh transform consists of one or more - individual quad transforms. - - See :py:meth:`.Image.transform` - - :param data: A list of (bbox, quad) tuples. - """ - - method = Image.Transform.MESH diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/ImageWin.py b/.venv-docs/lib/python3.12/site-packages/PIL/ImageWin.py deleted file mode 100644 index 98c28f29..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/ImageWin.py +++ /dev/null @@ -1,247 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# a Windows DIB display interface -# -# History: -# 1996-05-20 fl Created -# 1996-09-20 fl Fixed subregion exposure -# 1997-09-21 fl Added draw primitive (for tzPrint) -# 2003-05-21 fl Added experimental Window/ImageWindow classes -# 2003-09-05 fl Added fromstring/tostring methods -# -# Copyright (c) Secret Labs AB 1997-2003. -# Copyright (c) Fredrik Lundh 1996-2003. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from . import Image - - -class HDC: - """ - Wraps an HDC integer. The resulting object can be passed to the - :py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose` - methods. - """ - - def __init__(self, dc: int) -> None: - self.dc = dc - - def __int__(self) -> int: - return self.dc - - -class HWND: - """ - Wraps an HWND integer. The resulting object can be passed to the - :py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose` - methods, instead of a DC. - """ - - def __init__(self, wnd: int) -> None: - self.wnd = wnd - - def __int__(self) -> int: - return self.wnd - - -class Dib: - """ - A Windows bitmap with the given mode and size. The mode can be one of "1", - "L", "P", or "RGB". - - If the display requires a palette, this constructor creates a suitable - palette and associates it with the image. For an "L" image, 128 graylevels - are allocated. For an "RGB" image, a 6x6x6 colour cube is used, together - with 20 graylevels. - - To make sure that palettes work properly under Windows, you must call the - ``palette`` method upon certain events from Windows. - - :param image: Either a PIL image, or a mode string. If a mode string is - used, a size must also be given. The mode can be one of "1", - "L", "P", or "RGB". - :param size: If the first argument is a mode string, this - defines the size of the image. - """ - - def __init__( - self, image: Image.Image | str, size: tuple[int, int] | None = None - ) -> None: - if isinstance(image, str): - mode = image - image = "" - if size is None: - msg = "If first argument is mode, size is required" - raise ValueError(msg) - else: - mode = image.mode - size = image.size - if mode not in ["1", "L", "P", "RGB"]: - mode = Image.getmodebase(mode) - self.image = Image.core.display(mode, size) - self.mode = mode - self.size = size - if image: - assert not isinstance(image, str) - self.paste(image) - - def expose(self, handle: int | HDC | HWND) -> None: - """ - Copy the bitmap contents to a device context. - - :param handle: Device context (HDC), cast to a Python integer, or an - HDC or HWND instance. In PythonWin, you can use - ``CDC.GetHandleAttrib()`` to get a suitable handle. - """ - handle_int = int(handle) - if isinstance(handle, HWND): - dc = self.image.getdc(handle_int) - try: - self.image.expose(dc) - finally: - self.image.releasedc(handle_int, dc) - else: - self.image.expose(handle_int) - - def draw( - self, - handle: int | HDC | HWND, - dst: tuple[int, int, int, int], - src: tuple[int, int, int, int] | None = None, - ) -> None: - """ - Same as expose, but allows you to specify where to draw the image, and - what part of it to draw. - - The destination and source areas are given as 4-tuple rectangles. If - the source is omitted, the entire image is copied. If the source and - the destination have different sizes, the image is resized as - necessary. - """ - if src is None: - src = (0, 0) + self.size - handle_int = int(handle) - if isinstance(handle, HWND): - dc = self.image.getdc(handle_int) - try: - self.image.draw(dc, dst, src) - finally: - self.image.releasedc(handle_int, dc) - else: - self.image.draw(handle_int, dst, src) - - def query_palette(self, handle: int | HDC | HWND) -> int: - """ - Installs the palette associated with the image in the given device - context. - - This method should be called upon **QUERYNEWPALETTE** and - **PALETTECHANGED** events from Windows. If this method returns a - non-zero value, one or more display palette entries were changed, and - the image should be redrawn. - - :param handle: Device context (HDC), cast to a Python integer, or an - HDC or HWND instance. - :return: The number of entries that were changed (if one or more entries, - this indicates that the image should be redrawn). - """ - handle_int = int(handle) - if isinstance(handle, HWND): - handle = self.image.getdc(handle_int) - try: - result = self.image.query_palette(handle) - finally: - self.image.releasedc(handle, handle) - else: - result = self.image.query_palette(handle_int) - return result - - def paste( - self, im: Image.Image, box: tuple[int, int, int, int] | None = None - ) -> None: - """ - Paste a PIL image into the bitmap image. - - :param im: A PIL image. The size must match the target region. - If the mode does not match, the image is converted to the - mode of the bitmap image. - :param box: A 4-tuple defining the left, upper, right, and - lower pixel coordinate. See :ref:`coordinate-system`. If - None is given instead of a tuple, all of the image is - assumed. - """ - im.load() - if self.mode != im.mode: - im = im.convert(self.mode) - if box: - self.image.paste(im.im, box) - else: - self.image.paste(im.im) - - def frombytes(self, buffer: bytes) -> None: - """ - Load display memory contents from byte data. - - :param buffer: A buffer containing display data (usually - data returned from :py:func:`~PIL.ImageWin.Dib.tobytes`) - """ - self.image.frombytes(buffer) - - def tobytes(self) -> bytes: - """ - Copy display memory contents to bytes object. - - :return: A bytes object containing display data. - """ - return self.image.tobytes() - - -class Window: - """Create a Window with the given title size.""" - - def __init__( - self, title: str = "PIL", width: int | None = None, height: int | None = None - ) -> None: - self.hwnd = Image.core.createwindow( - title, self.__dispatcher, width or 0, height or 0 - ) - - def __dispatcher(self, action: str, *args: int) -> None: - getattr(self, f"ui_handle_{action}")(*args) - - def ui_handle_clear(self, dc: int, x0: int, y0: int, x1: int, y1: int) -> None: - pass - - def ui_handle_damage(self, x0: int, y0: int, x1: int, y1: int) -> None: - pass - - def ui_handle_destroy(self) -> None: - pass - - def ui_handle_repair(self, dc: int, x0: int, y0: int, x1: int, y1: int) -> None: - pass - - def ui_handle_resize(self, width: int, height: int) -> None: - pass - - def mainloop(self) -> None: - Image.core.eventloop() - - -class ImageWindow(Window): - """Create an image window which displays the given image.""" - - def __init__(self, image: Image.Image | Dib, title: str = "PIL") -> None: - if not isinstance(image, Dib): - image = Dib(image) - self.image = image - width, height = image.size - super().__init__(title, width=width, height=height) - - def ui_handle_repair(self, dc: int, x0: int, y0: int, x1: int, y1: int) -> None: - self.image.draw(dc, (x0, y0, x1, y1)) diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/ImtImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/ImtImagePlugin.py deleted file mode 100644 index c4eccee3..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/ImtImagePlugin.py +++ /dev/null @@ -1,103 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# IM Tools support for PIL -# -# history: -# 1996-05-27 fl Created (read 8-bit images only) -# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.2) -# -# Copyright (c) Secret Labs AB 1997-2001. -# Copyright (c) Fredrik Lundh 1996-2001. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import re - -from . import Image, ImageFile - -# -# -------------------------------------------------------------------- - -field = re.compile(rb"([a-z]*) ([^ \r\n]*)") - - -## -# Image plugin for IM Tools images. - - -class ImtImageFile(ImageFile.ImageFile): - format = "IMT" - format_description = "IM Tools" - - def _open(self) -> None: - # Quick rejection: if there's not a LF among the first - # 100 bytes, this is (probably) not a text header. - - assert self.fp is not None - - buffer = self.fp.read(100) - if b"\n" not in buffer: - msg = "not an IM file" - raise SyntaxError(msg) - - xsize = ysize = 0 - - while True: - if buffer: - s = buffer[:1] - buffer = buffer[1:] - else: - s = self.fp.read(1) - if not s: - break - - if s == b"\x0c": - # image data begins - self.tile = [ - ImageFile._Tile( - "raw", - (0, 0) + self.size, - self.fp.tell() - len(buffer), - self.mode, - ) - ] - - break - - else: - # read key/value pair - if b"\n" not in buffer: - buffer += self.fp.read(100) - lines = buffer.split(b"\n") - s += lines.pop(0) - buffer = b"\n".join(lines) - if len(s) == 1 or len(s) > 100: - break - if s[0] == ord(b"*"): - continue # comment - - m = field.match(s) - if not m: - break - k, v = m.group(1, 2) - if k == b"width": - xsize = int(v) - self._size = xsize, ysize - elif k == b"height": - ysize = int(v) - self._size = xsize, ysize - elif k == b"pixel" and v == b"n8": - self._mode = "L" - - -# -# -------------------------------------------------------------------- - -Image.register_open(ImtImageFile.format, ImtImageFile) - -# -# no extension registered (".im" is simply too common) diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/IptcImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/IptcImagePlugin.py deleted file mode 100644 index c28f4dcc..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/IptcImagePlugin.py +++ /dev/null @@ -1,229 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# IPTC/NAA file handling -# -# history: -# 1995-10-01 fl Created -# 1998-03-09 fl Cleaned up and added to PIL -# 2002-06-18 fl Added getiptcinfo helper -# -# Copyright (c) Secret Labs AB 1997-2002. -# Copyright (c) Fredrik Lundh 1995. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from io import BytesIO -from typing import cast - -from . import Image, ImageFile -from ._binary import i16be as i16 -from ._binary import i32be as i32 - -COMPRESSION = {1: "raw", 5: "jpeg"} - - -# -# Helpers - - -def _i(c: bytes) -> int: - return i32((b"\0\0\0\0" + c)[-4:]) - - -## -# Image plugin for IPTC/NAA datastreams. To read IPTC/NAA fields -# from TIFF and JPEG files, use the getiptcinfo function. - - -class IptcImageFile(ImageFile.ImageFile): - format = "IPTC" - format_description = "IPTC/NAA" - - def getint(self, key: tuple[int, int]) -> int: - return _i(self.info[key]) - - def field(self) -> tuple[tuple[int, int] | None, int]: - # - # get a IPTC field header - s = self.fp.read(5) - if not s.strip(b"\x00"): - return None, 0 - - tag = s[1], s[2] - - # syntax - if s[0] != 0x1C or tag[0] not in [1, 2, 3, 4, 5, 6, 7, 8, 9, 240]: - msg = "invalid IPTC/NAA file" - raise SyntaxError(msg) - - # field size - size = s[3] - if size > 132: - msg = "illegal field length in IPTC/NAA file" - raise OSError(msg) - elif size == 128: - size = 0 - elif size > 128: - size = _i(self.fp.read(size - 128)) - else: - size = i16(s, 3) - - return tag, size - - def _open(self) -> None: - # load descriptive fields - while True: - offset = self.fp.tell() - tag, size = self.field() - if not tag or tag == (8, 10): - break - if size: - tagdata = self.fp.read(size) - else: - tagdata = None - if tag in self.info: - if isinstance(self.info[tag], list): - self.info[tag].append(tagdata) - else: - self.info[tag] = [self.info[tag], tagdata] - else: - self.info[tag] = tagdata - - # mode - layers = self.info[(3, 60)][0] - component = self.info[(3, 60)][1] - if layers == 1 and not component: - self._mode = "L" - band = None - else: - if layers == 3 and component: - self._mode = "RGB" - elif layers == 4 and component: - self._mode = "CMYK" - if (3, 65) in self.info: - band = self.info[(3, 65)][0] - 1 - else: - band = 0 - - # size - self._size = self.getint((3, 20)), self.getint((3, 30)) - - # compression - try: - compression = COMPRESSION[self.getint((3, 120))] - except KeyError as e: - msg = "Unknown IPTC image compression" - raise OSError(msg) from e - - # tile - if tag == (8, 10): - self.tile = [ - ImageFile._Tile("iptc", (0, 0) + self.size, offset, (compression, band)) - ] - - def load(self) -> Image.core.PixelAccess | None: - if self.tile: - args = self.tile[0].args - assert isinstance(args, tuple) - compression, band = args - - self.fp.seek(self.tile[0].offset) - - # Copy image data to temporary file - o = BytesIO() - if compression == "raw": - # To simplify access to the extracted file, - # prepend a PPM header - o.write(b"P5\n%d %d\n255\n" % self.size) - while True: - type, size = self.field() - if type != (8, 10): - break - while size > 0: - s = self.fp.read(min(size, 8192)) - if not s: - break - o.write(s) - size -= len(s) - - with Image.open(o) as _im: - if band is not None: - bands = [Image.new("L", _im.size)] * Image.getmodebands(self.mode) - bands[band] = _im - _im = Image.merge(self.mode, bands) - else: - _im.load() - self.im = _im.im - self.tile = [] - return ImageFile.ImageFile.load(self) - - -Image.register_open(IptcImageFile.format, IptcImageFile) - -Image.register_extension(IptcImageFile.format, ".iim") - - -def getiptcinfo( - im: ImageFile.ImageFile, -) -> dict[tuple[int, int], bytes | list[bytes]] | None: - """ - Get IPTC information from TIFF, JPEG, or IPTC file. - - :param im: An image containing IPTC data. - :returns: A dictionary containing IPTC information, or None if - no IPTC information block was found. - """ - from . import JpegImagePlugin, TiffImagePlugin - - data = None - - info: dict[tuple[int, int], bytes | list[bytes]] = {} - if isinstance(im, IptcImageFile): - # return info dictionary right away - for k, v in im.info.items(): - if isinstance(k, tuple): - info[k] = v - return info - - elif isinstance(im, JpegImagePlugin.JpegImageFile): - # extract the IPTC/NAA resource - photoshop = im.info.get("photoshop") - if photoshop: - data = photoshop.get(0x0404) - - elif isinstance(im, TiffImagePlugin.TiffImageFile): - # get raw data from the IPTC/NAA tag (PhotoShop tags the data - # as 4-byte integers, so we cannot use the get method...) - try: - data = im.tag_v2._tagdata[TiffImagePlugin.IPTC_NAA_CHUNK] - except KeyError: - pass - - if data is None: - return None # no properties - - # create an IptcImagePlugin object without initializing it - class FakeImage: - pass - - fake_im = FakeImage() - fake_im.__class__ = IptcImageFile # type: ignore[assignment] - iptc_im = cast(IptcImageFile, fake_im) - - # parse the IPTC information chunk - iptc_im.info = {} - iptc_im.fp = BytesIO(data) - - try: - iptc_im._open() - except (IndexError, KeyError): - pass # expected failure - - for k, v in iptc_im.info.items(): - if isinstance(k, tuple): - info[k] = v - return info diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/Jpeg2KImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/Jpeg2KImagePlugin.py deleted file mode 100644 index 4c85dd4e..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/Jpeg2KImagePlugin.py +++ /dev/null @@ -1,446 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# JPEG2000 file handling -# -# History: -# 2014-03-12 ajh Created -# 2021-06-30 rogermb Extract dpi information from the 'resc' header box -# -# Copyright (c) 2014 Coriolis Systems Limited -# Copyright (c) 2014 Alastair Houghton -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io -import os -import struct -from typing import cast - -from . import Image, ImageFile, ImagePalette, _binary - -TYPE_CHECKING = False -if TYPE_CHECKING: - from collections.abc import Callable - from typing import IO - - -class BoxReader: - """ - A small helper class to read fields stored in JPEG2000 header boxes - and to easily step into and read sub-boxes. - """ - - def __init__(self, fp: IO[bytes], length: int = -1) -> None: - self.fp = fp - self.has_length = length >= 0 - self.length = length - self.remaining_in_box = -1 - - def _can_read(self, num_bytes: int) -> bool: - if self.has_length and self.fp.tell() + num_bytes > self.length: - # Outside box: ensure we don't read past the known file length - return False - if self.remaining_in_box >= 0: - # Inside box contents: ensure read does not go past box boundaries - return num_bytes <= self.remaining_in_box - else: - return True # No length known, just read - - def _read_bytes(self, num_bytes: int) -> bytes: - if not self._can_read(num_bytes): - msg = "Not enough data in header" - raise SyntaxError(msg) - - data = self.fp.read(num_bytes) - if len(data) < num_bytes: - msg = f"Expected to read {num_bytes} bytes but only got {len(data)}." - raise OSError(msg) - - if self.remaining_in_box > 0: - self.remaining_in_box -= num_bytes - return data - - def read_fields(self, field_format: str) -> tuple[int | bytes, ...]: - size = struct.calcsize(field_format) - data = self._read_bytes(size) - return struct.unpack(field_format, data) - - def read_boxes(self) -> BoxReader: - size = self.remaining_in_box - data = self._read_bytes(size) - return BoxReader(io.BytesIO(data), size) - - def has_next_box(self) -> bool: - if self.has_length: - return self.fp.tell() + self.remaining_in_box < self.length - else: - return True - - def next_box_type(self) -> bytes: - # Skip the rest of the box if it has not been read - if self.remaining_in_box > 0: - self.fp.seek(self.remaining_in_box, os.SEEK_CUR) - self.remaining_in_box = -1 - - # Read the length and type of the next box - lbox, tbox = cast(tuple[int, bytes], self.read_fields(">I4s")) - if lbox == 1: - lbox = cast(int, self.read_fields(">Q")[0]) - hlen = 16 - else: - hlen = 8 - - if lbox < hlen or not self._can_read(lbox - hlen): - msg = "Invalid header length" - raise SyntaxError(msg) - - self.remaining_in_box = lbox - hlen - return tbox - - -def _parse_codestream(fp: IO[bytes]) -> tuple[tuple[int, int], str]: - """Parse the JPEG 2000 codestream to extract the size and component - count from the SIZ marker segment, returning a PIL (size, mode) tuple.""" - - hdr = fp.read(2) - lsiz = _binary.i16be(hdr) - siz = hdr + fp.read(lsiz - 2) - lsiz, rsiz, xsiz, ysiz, xosiz, yosiz, _, _, _, _, csiz = struct.unpack_from( - ">HHIIIIIIIIH", siz - ) - - size = (xsiz - xosiz, ysiz - yosiz) - if csiz == 1: - ssiz = struct.unpack_from(">B", siz, 38) - if (ssiz[0] & 0x7F) + 1 > 8: - mode = "I;16" - else: - mode = "L" - elif csiz == 2: - mode = "LA" - elif csiz == 3: - mode = "RGB" - elif csiz == 4: - mode = "RGBA" - else: - msg = "unable to determine J2K image mode" - raise SyntaxError(msg) - - return size, mode - - -def _res_to_dpi(num: int, denom: int, exp: int) -> float | None: - """Convert JPEG2000's (numerator, denominator, exponent-base-10) resolution, - calculated as (num / denom) * 10^exp and stored in dots per meter, - to floating-point dots per inch.""" - if denom == 0: - return None - return (254 * num * (10**exp)) / (10000 * denom) - - -def _parse_jp2_header( - fp: IO[bytes], -) -> tuple[ - tuple[int, int], - str, - str | None, - tuple[float, float] | None, - ImagePalette.ImagePalette | None, -]: - """Parse the JP2 header box to extract size, component count, - color space information, and optionally DPI information, - returning a (size, mode, mimetype, dpi) tuple.""" - - # Find the JP2 header box - reader = BoxReader(fp) - header = None - mimetype = None - while reader.has_next_box(): - tbox = reader.next_box_type() - - if tbox == b"jp2h": - header = reader.read_boxes() - break - elif tbox == b"ftyp": - if reader.read_fields(">4s")[0] == b"jpx ": - mimetype = "image/jpx" - assert header is not None - - size = None - mode = None - bpc = None - nc = None - dpi = None # 2-tuple of DPI info, or None - palette = None - - while header.has_next_box(): - tbox = header.next_box_type() - - if tbox == b"ihdr": - height, width, nc, bpc = header.read_fields(">IIHB") - assert isinstance(height, int) - assert isinstance(width, int) - assert isinstance(bpc, int) - size = (width, height) - if nc == 1 and (bpc & 0x7F) > 8: - mode = "I;16" - elif nc == 1: - mode = "L" - elif nc == 2: - mode = "LA" - elif nc == 3: - mode = "RGB" - elif nc == 4: - mode = "RGBA" - elif tbox == b"colr" and nc == 4: - meth, _, _, enumcs = header.read_fields(">BBBI") - if meth == 1 and enumcs == 12: - mode = "CMYK" - elif tbox == b"pclr" and mode in ("L", "LA"): - ne, npc = header.read_fields(">HB") - assert isinstance(ne, int) - assert isinstance(npc, int) - max_bitdepth = 0 - for bitdepth in header.read_fields(">" + ("B" * npc)): - assert isinstance(bitdepth, int) - if bitdepth > max_bitdepth: - max_bitdepth = bitdepth - if max_bitdepth <= 8: - palette = ImagePalette.ImagePalette("RGBA" if npc == 4 else "RGB") - for i in range(ne): - color: list[int] = [] - for value in header.read_fields(">" + ("B" * npc)): - assert isinstance(value, int) - color.append(value) - palette.getcolor(tuple(color)) - mode = "P" if mode == "L" else "PA" - elif tbox == b"res ": - res = header.read_boxes() - while res.has_next_box(): - tres = res.next_box_type() - if tres == b"resc": - vrcn, vrcd, hrcn, hrcd, vrce, hrce = res.read_fields(">HHHHBB") - assert isinstance(vrcn, int) - assert isinstance(vrcd, int) - assert isinstance(hrcn, int) - assert isinstance(hrcd, int) - assert isinstance(vrce, int) - assert isinstance(hrce, int) - hres = _res_to_dpi(hrcn, hrcd, hrce) - vres = _res_to_dpi(vrcn, vrcd, vrce) - if hres is not None and vres is not None: - dpi = (hres, vres) - break - - if size is None or mode is None: - msg = "Malformed JP2 header" - raise SyntaxError(msg) - - return size, mode, mimetype, dpi, palette - - -## -# Image plugin for JPEG2000 images. - - -class Jpeg2KImageFile(ImageFile.ImageFile): - format = "JPEG2000" - format_description = "JPEG 2000 (ISO 15444)" - - def _open(self) -> None: - sig = self.fp.read(4) - if sig == b"\xff\x4f\xff\x51": - self.codec = "j2k" - self._size, self._mode = _parse_codestream(self.fp) - self._parse_comment() - else: - sig = sig + self.fp.read(8) - - if sig == b"\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a": - self.codec = "jp2" - header = _parse_jp2_header(self.fp) - self._size, self._mode, self.custom_mimetype, dpi, self.palette = header - if dpi is not None: - self.info["dpi"] = dpi - if self.fp.read(12).endswith(b"jp2c\xff\x4f\xff\x51"): - hdr = self.fp.read(2) - length = _binary.i16be(hdr) - self.fp.seek(length - 2, os.SEEK_CUR) - self._parse_comment() - else: - msg = "not a JPEG 2000 file" - raise SyntaxError(msg) - - self._reduce = 0 - self.layers = 0 - - fd = -1 - length = -1 - - try: - fd = self.fp.fileno() - length = os.fstat(fd).st_size - except Exception: - fd = -1 - try: - pos = self.fp.tell() - self.fp.seek(0, io.SEEK_END) - length = self.fp.tell() - self.fp.seek(pos) - except Exception: - length = -1 - - self.tile = [ - ImageFile._Tile( - "jpeg2k", - (0, 0) + self.size, - 0, - (self.codec, self._reduce, self.layers, fd, length), - ) - ] - - def _parse_comment(self) -> None: - while True: - marker = self.fp.read(2) - if not marker: - break - typ = marker[1] - if typ in (0x90, 0xD9): - # Start of tile or end of codestream - break - hdr = self.fp.read(2) - length = _binary.i16be(hdr) - if typ == 0x64: - # Comment - self.info["comment"] = self.fp.read(length - 2)[2:] - break - else: - self.fp.seek(length - 2, os.SEEK_CUR) - - @property # type: ignore[override] - def reduce( - self, - ) -> ( - Callable[[int | tuple[int, int], tuple[int, int, int, int] | None], Image.Image] - | int - ): - # https://github.com/python-pillow/Pillow/issues/4343 found that the - # new Image 'reduce' method was shadowed by this plugin's 'reduce' - # property. This attempts to allow for both scenarios - return self._reduce or super().reduce - - @reduce.setter - def reduce(self, value: int) -> None: - self._reduce = value - - def load(self) -> Image.core.PixelAccess | None: - if self.tile and self._reduce: - power = 1 << self._reduce - adjust = power >> 1 - self._size = ( - int((self.size[0] + adjust) / power), - int((self.size[1] + adjust) / power), - ) - - # Update the reduce and layers settings - t = self.tile[0] - assert isinstance(t[3], tuple) - t3 = (t[3][0], self._reduce, self.layers, t[3][3], t[3][4]) - self.tile = [ImageFile._Tile(t[0], (0, 0) + self.size, t[2], t3)] - - return ImageFile.ImageFile.load(self) - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith( - (b"\xff\x4f\xff\x51", b"\x00\x00\x00\x0cjP \x0d\x0a\x87\x0a") - ) - - -# ------------------------------------------------------------ -# Save support - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - # Get the keyword arguments - info = im.encoderinfo - - if isinstance(filename, str): - filename = filename.encode() - if filename.endswith(b".j2k") or info.get("no_jp2", False): - kind = "j2k" - else: - kind = "jp2" - - offset = info.get("offset", None) - tile_offset = info.get("tile_offset", None) - tile_size = info.get("tile_size", None) - quality_mode = info.get("quality_mode", "rates") - quality_layers = info.get("quality_layers", None) - if quality_layers is not None and not ( - isinstance(quality_layers, (list, tuple)) - and all( - isinstance(quality_layer, (int, float)) for quality_layer in quality_layers - ) - ): - msg = "quality_layers must be a sequence of numbers" - raise ValueError(msg) - - num_resolutions = info.get("num_resolutions", 0) - cblk_size = info.get("codeblock_size", None) - precinct_size = info.get("precinct_size", None) - irreversible = info.get("irreversible", False) - progression = info.get("progression", "LRCP") - cinema_mode = info.get("cinema_mode", "no") - mct = info.get("mct", 0) - signed = info.get("signed", False) - comment = info.get("comment") - if isinstance(comment, str): - comment = comment.encode() - plt = info.get("plt", False) - - fd = -1 - if hasattr(fp, "fileno"): - try: - fd = fp.fileno() - except Exception: - fd = -1 - - im.encoderconfig = ( - offset, - tile_offset, - tile_size, - quality_mode, - quality_layers, - num_resolutions, - cblk_size, - precinct_size, - irreversible, - progression, - cinema_mode, - mct, - signed, - fd, - comment, - plt, - ) - - ImageFile._save(im, fp, [ImageFile._Tile("jpeg2k", (0, 0) + im.size, 0, kind)]) - - -# ------------------------------------------------------------ -# Registry stuff - - -Image.register_open(Jpeg2KImageFile.format, Jpeg2KImageFile, _accept) -Image.register_save(Jpeg2KImageFile.format, _save) - -Image.register_extensions( - Jpeg2KImageFile.format, [".jp2", ".j2k", ".jpc", ".jpf", ".jpx", ".j2c"] -) - -Image.register_mime(Jpeg2KImageFile.format, "image/jp2") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/JpegImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/JpegImagePlugin.py deleted file mode 100644 index 755ca648..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/JpegImagePlugin.py +++ /dev/null @@ -1,888 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# JPEG (JFIF) file handling -# -# See "Digital Compression and Coding of Continuous-Tone Still Images, -# Part 1, Requirements and Guidelines" (CCITT T.81 / ISO 10918-1) -# -# History: -# 1995-09-09 fl Created -# 1995-09-13 fl Added full parser -# 1996-03-25 fl Added hack to use the IJG command line utilities -# 1996-05-05 fl Workaround Photoshop 2.5 CMYK polarity bug -# 1996-05-28 fl Added draft support, JFIF version (0.1) -# 1996-12-30 fl Added encoder options, added progression property (0.2) -# 1997-08-27 fl Save mode 1 images as BW (0.3) -# 1998-07-12 fl Added YCbCr to draft and save methods (0.4) -# 1998-10-19 fl Don't hang on files using 16-bit DQT's (0.4.1) -# 2001-04-16 fl Extract DPI settings from JFIF files (0.4.2) -# 2002-07-01 fl Skip pad bytes before markers; identify Exif files (0.4.3) -# 2003-04-25 fl Added experimental EXIF decoder (0.5) -# 2003-06-06 fl Added experimental EXIF GPSinfo decoder -# 2003-09-13 fl Extract COM markers -# 2009-09-06 fl Added icc_profile support (from Florian Hoech) -# 2009-03-06 fl Changed CMYK handling; always use Adobe polarity (0.6) -# 2009-03-08 fl Added subsampling support (from Justin Huff). -# -# Copyright (c) 1997-2003 by Secret Labs AB. -# Copyright (c) 1995-1996 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import array -import io -import math -import os -import struct -import subprocess -import sys -import tempfile -import warnings - -from . import Image, ImageFile -from ._binary import i16be as i16 -from ._binary import i32be as i32 -from ._binary import o8 -from ._binary import o16be as o16 -from .JpegPresets import presets - -TYPE_CHECKING = False -if TYPE_CHECKING: - from typing import IO, Any - - from .MpoImagePlugin import MpoImageFile - -# -# Parser - - -def Skip(self: JpegImageFile, marker: int) -> None: - n = i16(self.fp.read(2)) - 2 - ImageFile._safe_read(self.fp, n) - - -def APP(self: JpegImageFile, marker: int) -> None: - # - # Application marker. Store these in the APP dictionary. - # Also look for well-known application markers. - - n = i16(self.fp.read(2)) - 2 - s = ImageFile._safe_read(self.fp, n) - - app = f"APP{marker & 15}" - - self.app[app] = s # compatibility - self.applist.append((app, s)) - - if marker == 0xFFE0 and s.startswith(b"JFIF"): - # extract JFIF information - self.info["jfif"] = version = i16(s, 5) # version - self.info["jfif_version"] = divmod(version, 256) - # extract JFIF properties - try: - jfif_unit = s[7] - jfif_density = i16(s, 8), i16(s, 10) - except Exception: - pass - else: - if jfif_unit == 1: - self.info["dpi"] = jfif_density - elif jfif_unit == 2: # cm - # 1 dpcm = 2.54 dpi - self.info["dpi"] = tuple(d * 2.54 for d in jfif_density) - self.info["jfif_unit"] = jfif_unit - self.info["jfif_density"] = jfif_density - elif marker == 0xFFE1 and s.startswith(b"Exif\0\0"): - # extract EXIF information - if "exif" in self.info: - self.info["exif"] += s[6:] - else: - self.info["exif"] = s - self._exif_offset = self.fp.tell() - n + 6 - elif marker == 0xFFE1 and s.startswith(b"http://ns.adobe.com/xap/1.0/\x00"): - self.info["xmp"] = s.split(b"\x00", 1)[1] - elif marker == 0xFFE2 and s.startswith(b"FPXR\0"): - # extract FlashPix information (incomplete) - self.info["flashpix"] = s # FIXME: value will change - elif marker == 0xFFE2 and s.startswith(b"ICC_PROFILE\0"): - # Since an ICC profile can be larger than the maximum size of - # a JPEG marker (64K), we need provisions to split it into - # multiple markers. The format defined by the ICC specifies - # one or more APP2 markers containing the following data: - # Identifying string ASCII "ICC_PROFILE\0" (12 bytes) - # Marker sequence number 1, 2, etc (1 byte) - # Number of markers Total of APP2's used (1 byte) - # Profile data (remainder of APP2 data) - # Decoders should use the marker sequence numbers to - # reassemble the profile, rather than assuming that the APP2 - # markers appear in the correct sequence. - self.icclist.append(s) - elif marker == 0xFFED and s.startswith(b"Photoshop 3.0\x00"): - # parse the image resource block - offset = 14 - photoshop = self.info.setdefault("photoshop", {}) - while s[offset : offset + 4] == b"8BIM": - try: - offset += 4 - # resource code - code = i16(s, offset) - offset += 2 - # resource name (usually empty) - name_len = s[offset] - # name = s[offset+1:offset+1+name_len] - offset += 1 + name_len - offset += offset & 1 # align - # resource data block - size = i32(s, offset) - offset += 4 - data = s[offset : offset + size] - if code == 0x03ED: # ResolutionInfo - photoshop[code] = { - "XResolution": i32(data, 0) / 65536, - "DisplayedUnitsX": i16(data, 4), - "YResolution": i32(data, 8) / 65536, - "DisplayedUnitsY": i16(data, 12), - } - else: - photoshop[code] = data - offset += size - offset += offset & 1 # align - except struct.error: - break # insufficient data - - elif marker == 0xFFEE and s.startswith(b"Adobe"): - self.info["adobe"] = i16(s, 5) - # extract Adobe custom properties - try: - adobe_transform = s[11] - except IndexError: - pass - else: - self.info["adobe_transform"] = adobe_transform - elif marker == 0xFFE2 and s.startswith(b"MPF\0"): - # extract MPO information - self.info["mp"] = s[4:] - # offset is current location minus buffer size - # plus constant header size - self.info["mpoffset"] = self.fp.tell() - n + 4 - - -def COM(self: JpegImageFile, marker: int) -> None: - # - # Comment marker. Store these in the APP dictionary. - n = i16(self.fp.read(2)) - 2 - s = ImageFile._safe_read(self.fp, n) - - self.info["comment"] = s - self.app["COM"] = s # compatibility - self.applist.append(("COM", s)) - - -def SOF(self: JpegImageFile, marker: int) -> None: - # - # Start of frame marker. Defines the size and mode of the - # image. JPEG is colour blind, so we use some simple - # heuristics to map the number of layers to an appropriate - # mode. Note that this could be made a bit brighter, by - # looking for JFIF and Adobe APP markers. - - n = i16(self.fp.read(2)) - 2 - s = ImageFile._safe_read(self.fp, n) - self._size = i16(s, 3), i16(s, 1) - if self._im is not None and self.size != self.im.size: - self._im = None - - self.bits = s[0] - if self.bits != 8: - msg = f"cannot handle {self.bits}-bit layers" - raise SyntaxError(msg) - - self.layers = s[5] - if self.layers == 1: - self._mode = "L" - elif self.layers == 3: - self._mode = "RGB" - elif self.layers == 4: - self._mode = "CMYK" - else: - msg = f"cannot handle {self.layers}-layer images" - raise SyntaxError(msg) - - if marker in [0xFFC2, 0xFFC6, 0xFFCA, 0xFFCE]: - self.info["progressive"] = self.info["progression"] = 1 - - if self.icclist: - # fixup icc profile - self.icclist.sort() # sort by sequence number - if self.icclist[0][13] == len(self.icclist): - profile = [p[14:] for p in self.icclist] - icc_profile = b"".join(profile) - else: - icc_profile = None # wrong number of fragments - self.info["icc_profile"] = icc_profile - self.icclist = [] - - for i in range(6, len(s), 3): - t = s[i : i + 3] - # 4-tuples: id, vsamp, hsamp, qtable - self.layer.append((t[0], t[1] // 16, t[1] & 15, t[2])) - - -def DQT(self: JpegImageFile, marker: int) -> None: - # - # Define quantization table. Note that there might be more - # than one table in each marker. - - # FIXME: The quantization tables can be used to estimate the - # compression quality. - - n = i16(self.fp.read(2)) - 2 - s = ImageFile._safe_read(self.fp, n) - while len(s): - v = s[0] - precision = 1 if (v // 16 == 0) else 2 # in bytes - qt_length = 1 + precision * 64 - if len(s) < qt_length: - msg = "bad quantization table marker" - raise SyntaxError(msg) - data = array.array("B" if precision == 1 else "H", s[1:qt_length]) - if sys.byteorder == "little" and precision > 1: - data.byteswap() # the values are always big-endian - self.quantization[v & 15] = [data[i] for i in zigzag_index] - s = s[qt_length:] - - -# -# JPEG marker table - -MARKER = { - 0xFFC0: ("SOF0", "Baseline DCT", SOF), - 0xFFC1: ("SOF1", "Extended Sequential DCT", SOF), - 0xFFC2: ("SOF2", "Progressive DCT", SOF), - 0xFFC3: ("SOF3", "Spatial lossless", SOF), - 0xFFC4: ("DHT", "Define Huffman table", Skip), - 0xFFC5: ("SOF5", "Differential sequential DCT", SOF), - 0xFFC6: ("SOF6", "Differential progressive DCT", SOF), - 0xFFC7: ("SOF7", "Differential spatial", SOF), - 0xFFC8: ("JPG", "Extension", None), - 0xFFC9: ("SOF9", "Extended sequential DCT (AC)", SOF), - 0xFFCA: ("SOF10", "Progressive DCT (AC)", SOF), - 0xFFCB: ("SOF11", "Spatial lossless DCT (AC)", SOF), - 0xFFCC: ("DAC", "Define arithmetic coding conditioning", Skip), - 0xFFCD: ("SOF13", "Differential sequential DCT (AC)", SOF), - 0xFFCE: ("SOF14", "Differential progressive DCT (AC)", SOF), - 0xFFCF: ("SOF15", "Differential spatial (AC)", SOF), - 0xFFD0: ("RST0", "Restart 0", None), - 0xFFD1: ("RST1", "Restart 1", None), - 0xFFD2: ("RST2", "Restart 2", None), - 0xFFD3: ("RST3", "Restart 3", None), - 0xFFD4: ("RST4", "Restart 4", None), - 0xFFD5: ("RST5", "Restart 5", None), - 0xFFD6: ("RST6", "Restart 6", None), - 0xFFD7: ("RST7", "Restart 7", None), - 0xFFD8: ("SOI", "Start of image", None), - 0xFFD9: ("EOI", "End of image", None), - 0xFFDA: ("SOS", "Start of scan", Skip), - 0xFFDB: ("DQT", "Define quantization table", DQT), - 0xFFDC: ("DNL", "Define number of lines", Skip), - 0xFFDD: ("DRI", "Define restart interval", Skip), - 0xFFDE: ("DHP", "Define hierarchical progression", SOF), - 0xFFDF: ("EXP", "Expand reference component", Skip), - 0xFFE0: ("APP0", "Application segment 0", APP), - 0xFFE1: ("APP1", "Application segment 1", APP), - 0xFFE2: ("APP2", "Application segment 2", APP), - 0xFFE3: ("APP3", "Application segment 3", APP), - 0xFFE4: ("APP4", "Application segment 4", APP), - 0xFFE5: ("APP5", "Application segment 5", APP), - 0xFFE6: ("APP6", "Application segment 6", APP), - 0xFFE7: ("APP7", "Application segment 7", APP), - 0xFFE8: ("APP8", "Application segment 8", APP), - 0xFFE9: ("APP9", "Application segment 9", APP), - 0xFFEA: ("APP10", "Application segment 10", APP), - 0xFFEB: ("APP11", "Application segment 11", APP), - 0xFFEC: ("APP12", "Application segment 12", APP), - 0xFFED: ("APP13", "Application segment 13", APP), - 0xFFEE: ("APP14", "Application segment 14", APP), - 0xFFEF: ("APP15", "Application segment 15", APP), - 0xFFF0: ("JPG0", "Extension 0", None), - 0xFFF1: ("JPG1", "Extension 1", None), - 0xFFF2: ("JPG2", "Extension 2", None), - 0xFFF3: ("JPG3", "Extension 3", None), - 0xFFF4: ("JPG4", "Extension 4", None), - 0xFFF5: ("JPG5", "Extension 5", None), - 0xFFF6: ("JPG6", "Extension 6", None), - 0xFFF7: ("JPG7", "Extension 7", None), - 0xFFF8: ("JPG8", "Extension 8", None), - 0xFFF9: ("JPG9", "Extension 9", None), - 0xFFFA: ("JPG10", "Extension 10", None), - 0xFFFB: ("JPG11", "Extension 11", None), - 0xFFFC: ("JPG12", "Extension 12", None), - 0xFFFD: ("JPG13", "Extension 13", None), - 0xFFFE: ("COM", "Comment", COM), -} - - -def _accept(prefix: bytes) -> bool: - # Magic number was taken from https://en.wikipedia.org/wiki/JPEG - return prefix.startswith(b"\xff\xd8\xff") - - -## -# Image plugin for JPEG and JFIF images. - - -class JpegImageFile(ImageFile.ImageFile): - format = "JPEG" - format_description = "JPEG (ISO 10918)" - - def _open(self) -> None: - s = self.fp.read(3) - - if not _accept(s): - msg = "not a JPEG file" - raise SyntaxError(msg) - s = b"\xff" - - # Create attributes - self.bits = self.layers = 0 - self._exif_offset = 0 - - # JPEG specifics (internal) - self.layer: list[tuple[int, int, int, int]] = [] - self._huffman_dc: dict[Any, Any] = {} - self._huffman_ac: dict[Any, Any] = {} - self.quantization: dict[int, list[int]] = {} - self.app: dict[str, bytes] = {} # compatibility - self.applist: list[tuple[str, bytes]] = [] - self.icclist: list[bytes] = [] - - while True: - i = s[0] - if i == 0xFF: - s = s + self.fp.read(1) - i = i16(s) - else: - # Skip non-0xFF junk - s = self.fp.read(1) - continue - - if i in MARKER: - name, description, handler = MARKER[i] - if handler is not None: - handler(self, i) - if i == 0xFFDA: # start of scan - rawmode = self.mode - if self.mode == "CMYK": - rawmode = "CMYK;I" # assume adobe conventions - self.tile = [ - ImageFile._Tile("jpeg", (0, 0) + self.size, 0, (rawmode, "")) - ] - # self.__offset = self.fp.tell() - break - s = self.fp.read(1) - elif i in {0, 0xFFFF}: - # padded marker or junk; move on - s = b"\xff" - elif i == 0xFF00: # Skip extraneous data (escaped 0xFF) - s = self.fp.read(1) - else: - msg = "no marker found" - raise SyntaxError(msg) - - self._read_dpi_from_exif() - - def __getstate__(self) -> list[Any]: - return super().__getstate__() + [self.layers, self.layer] - - def __setstate__(self, state: list[Any]) -> None: - self.layers, self.layer = state[6:] - super().__setstate__(state) - - def load_read(self, read_bytes: int) -> bytes: - """ - internal: read more image data - For premature EOF and LOAD_TRUNCATED_IMAGES adds EOI marker - so libjpeg can finish decoding - """ - s = self.fp.read(read_bytes) - - if not s and ImageFile.LOAD_TRUNCATED_IMAGES and not hasattr(self, "_ended"): - # Premature EOF. - # Pretend file is finished adding EOI marker - self._ended = True - return b"\xff\xd9" - - return s - - def draft( - self, mode: str | None, size: tuple[int, int] | None - ) -> tuple[str, tuple[int, int, float, float]] | None: - if len(self.tile) != 1: - return None - - # Protect from second call - if self.decoderconfig: - return None - - d, e, o, a = self.tile[0] - scale = 1 - original_size = self.size - - assert isinstance(a, tuple) - if a[0] == "RGB" and mode in ["L", "YCbCr"]: - self._mode = mode - a = mode, "" - - if size: - scale = min(self.size[0] // size[0], self.size[1] // size[1]) - for s in [8, 4, 2, 1]: - if scale >= s: - break - assert e is not None - e = ( - e[0], - e[1], - (e[2] - e[0] + s - 1) // s + e[0], - (e[3] - e[1] + s - 1) // s + e[1], - ) - self._size = ((self.size[0] + s - 1) // s, (self.size[1] + s - 1) // s) - scale = s - - self.tile = [ImageFile._Tile(d, e, o, a)] - self.decoderconfig = (scale, 0) - - box = (0, 0, original_size[0] / scale, original_size[1] / scale) - return self.mode, box - - def load_djpeg(self) -> None: - # ALTERNATIVE: handle JPEGs via the IJG command line utilities - - f, path = tempfile.mkstemp() - os.close(f) - if os.path.exists(self.filename): - subprocess.check_call(["djpeg", "-outfile", path, self.filename]) - else: - try: - os.unlink(path) - except OSError: - pass - - msg = "Invalid Filename" - raise ValueError(msg) - - try: - with Image.open(path) as _im: - _im.load() - self.im = _im.im - finally: - try: - os.unlink(path) - except OSError: - pass - - self._mode = self.im.mode - self._size = self.im.size - - self.tile = [] - - def _getexif(self) -> dict[int, Any] | None: - return _getexif(self) - - def _read_dpi_from_exif(self) -> None: - # If DPI isn't in JPEG header, fetch from EXIF - if "dpi" in self.info or "exif" not in self.info: - return - try: - exif = self.getexif() - resolution_unit = exif[0x0128] - x_resolution = exif[0x011A] - try: - dpi = float(x_resolution[0]) / x_resolution[1] - except TypeError: - dpi = x_resolution - if math.isnan(dpi): - msg = "DPI is not a number" - raise ValueError(msg) - if resolution_unit == 3: # cm - # 1 dpcm = 2.54 dpi - dpi *= 2.54 - self.info["dpi"] = dpi, dpi - except ( - struct.error, # truncated EXIF - KeyError, # dpi not included - SyntaxError, # invalid/unreadable EXIF - TypeError, # dpi is an invalid float - ValueError, # dpi is an invalid float - ZeroDivisionError, # invalid dpi rational value - ): - self.info["dpi"] = 72, 72 - - def _getmp(self) -> dict[int, Any] | None: - return _getmp(self) - - -def _getexif(self: JpegImageFile) -> dict[int, Any] | None: - if "exif" not in self.info: - return None - return self.getexif()._get_merged_dict() - - -def _getmp(self: JpegImageFile) -> dict[int, Any] | None: - # Extract MP information. This method was inspired by the "highly - # experimental" _getexif version that's been in use for years now, - # itself based on the ImageFileDirectory class in the TIFF plugin. - - # The MP record essentially consists of a TIFF file embedded in a JPEG - # application marker. - try: - data = self.info["mp"] - except KeyError: - return None - file_contents = io.BytesIO(data) - head = file_contents.read(8) - endianness = ">" if head.startswith(b"\x4d\x4d\x00\x2a") else "<" - # process dictionary - from . import TiffImagePlugin - - try: - info = TiffImagePlugin.ImageFileDirectory_v2(head) - file_contents.seek(info.next) - info.load(file_contents) - mp = dict(info) - except Exception as e: - msg = "malformed MP Index (unreadable directory)" - raise SyntaxError(msg) from e - # it's an error not to have a number of images - try: - quant = mp[0xB001] - except KeyError as e: - msg = "malformed MP Index (no number of images)" - raise SyntaxError(msg) from e - # get MP entries - mpentries = [] - try: - rawmpentries = mp[0xB002] - for entrynum in range(quant): - unpackedentry = struct.unpack_from( - f"{endianness}LLLHH", rawmpentries, entrynum * 16 - ) - labels = ("Attribute", "Size", "DataOffset", "EntryNo1", "EntryNo2") - mpentry = dict(zip(labels, unpackedentry)) - mpentryattr = { - "DependentParentImageFlag": bool(mpentry["Attribute"] & (1 << 31)), - "DependentChildImageFlag": bool(mpentry["Attribute"] & (1 << 30)), - "RepresentativeImageFlag": bool(mpentry["Attribute"] & (1 << 29)), - "Reserved": (mpentry["Attribute"] & (3 << 27)) >> 27, - "ImageDataFormat": (mpentry["Attribute"] & (7 << 24)) >> 24, - "MPType": mpentry["Attribute"] & 0x00FFFFFF, - } - if mpentryattr["ImageDataFormat"] == 0: - mpentryattr["ImageDataFormat"] = "JPEG" - else: - msg = "unsupported picture format in MPO" - raise SyntaxError(msg) - mptypemap = { - 0x000000: "Undefined", - 0x010001: "Large Thumbnail (VGA Equivalent)", - 0x010002: "Large Thumbnail (Full HD Equivalent)", - 0x020001: "Multi-Frame Image (Panorama)", - 0x020002: "Multi-Frame Image: (Disparity)", - 0x020003: "Multi-Frame Image: (Multi-Angle)", - 0x030000: "Baseline MP Primary Image", - } - mpentryattr["MPType"] = mptypemap.get(mpentryattr["MPType"], "Unknown") - mpentry["Attribute"] = mpentryattr - mpentries.append(mpentry) - mp[0xB002] = mpentries - except KeyError as e: - msg = "malformed MP Index (bad MP Entry)" - raise SyntaxError(msg) from e - # Next we should try and parse the individual image unique ID list; - # we don't because I've never seen this actually used in a real MPO - # file and so can't test it. - return mp - - -# -------------------------------------------------------------------- -# stuff to save JPEG files - -RAWMODE = { - "1": "L", - "L": "L", - "RGB": "RGB", - "RGBX": "RGB", - "CMYK": "CMYK;I", # assume adobe conventions - "YCbCr": "YCbCr", -} - -# fmt: off -zigzag_index = ( - 0, 1, 5, 6, 14, 15, 27, 28, - 2, 4, 7, 13, 16, 26, 29, 42, - 3, 8, 12, 17, 25, 30, 41, 43, - 9, 11, 18, 24, 31, 40, 44, 53, - 10, 19, 23, 32, 39, 45, 52, 54, - 20, 22, 33, 38, 46, 51, 55, 60, - 21, 34, 37, 47, 50, 56, 59, 61, - 35, 36, 48, 49, 57, 58, 62, 63, -) - -samplings = { - (1, 1, 1, 1, 1, 1): 0, - (2, 1, 1, 1, 1, 1): 1, - (2, 2, 1, 1, 1, 1): 2, -} -# fmt: on - - -def get_sampling(im: Image.Image) -> int: - # There's no subsampling when images have only 1 layer - # (grayscale images) or when they are CMYK (4 layers), - # so set subsampling to the default value. - # - # NOTE: currently Pillow can't encode JPEG to YCCK format. - # If YCCK support is added in the future, subsampling code will have - # to be updated (here and in JpegEncode.c) to deal with 4 layers. - if not isinstance(im, JpegImageFile) or im.layers in (1, 4): - return -1 - sampling = im.layer[0][1:3] + im.layer[1][1:3] + im.layer[2][1:3] - return samplings.get(sampling, -1) - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - if im.width == 0 or im.height == 0: - msg = "cannot write empty image as JPEG" - raise ValueError(msg) - - try: - rawmode = RAWMODE[im.mode] - except KeyError as e: - msg = f"cannot write mode {im.mode} as JPEG" - raise OSError(msg) from e - - info = im.encoderinfo - - dpi = [round(x) for x in info.get("dpi", (0, 0))] - - quality = info.get("quality", -1) - subsampling = info.get("subsampling", -1) - qtables = info.get("qtables") - - if quality == "keep": - quality = -1 - subsampling = "keep" - qtables = "keep" - elif quality in presets: - preset = presets[quality] - quality = -1 - subsampling = preset.get("subsampling", -1) - qtables = preset.get("quantization") - elif not isinstance(quality, int): - msg = "Invalid quality setting" - raise ValueError(msg) - else: - if subsampling in presets: - subsampling = presets[subsampling].get("subsampling", -1) - if isinstance(qtables, str) and qtables in presets: - qtables = presets[qtables].get("quantization") - - if subsampling == "4:4:4": - subsampling = 0 - elif subsampling == "4:2:2": - subsampling = 1 - elif subsampling == "4:2:0": - subsampling = 2 - elif subsampling == "4:1:1": - # For compatibility. Before Pillow 4.3, 4:1:1 actually meant 4:2:0. - # Set 4:2:0 if someone is still using that value. - subsampling = 2 - elif subsampling == "keep": - if im.format != "JPEG": - msg = "Cannot use 'keep' when original image is not a JPEG" - raise ValueError(msg) - subsampling = get_sampling(im) - - def validate_qtables( - qtables: ( - str | tuple[list[int], ...] | list[list[int]] | dict[int, list[int]] | None - ), - ) -> list[list[int]] | None: - if qtables is None: - return qtables - if isinstance(qtables, str): - try: - lines = [ - int(num) - for line in qtables.splitlines() - for num in line.split("#", 1)[0].split() - ] - except ValueError as e: - msg = "Invalid quantization table" - raise ValueError(msg) from e - else: - qtables = [lines[s : s + 64] for s in range(0, len(lines), 64)] - if isinstance(qtables, (tuple, list, dict)): - if isinstance(qtables, dict): - qtables = [ - qtables[key] for key in range(len(qtables)) if key in qtables - ] - elif isinstance(qtables, tuple): - qtables = list(qtables) - if not (0 < len(qtables) < 5): - msg = "None or too many quantization tables" - raise ValueError(msg) - for idx, table in enumerate(qtables): - try: - if len(table) != 64: - msg = "Invalid quantization table" - raise TypeError(msg) - table_array = array.array("H", table) - except TypeError as e: - msg = "Invalid quantization table" - raise ValueError(msg) from e - else: - qtables[idx] = list(table_array) - return qtables - - if qtables == "keep": - if im.format != "JPEG": - msg = "Cannot use 'keep' when original image is not a JPEG" - raise ValueError(msg) - qtables = getattr(im, "quantization", None) - qtables = validate_qtables(qtables) - - extra = info.get("extra", b"") - - MAX_BYTES_IN_MARKER = 65533 - if xmp := info.get("xmp"): - overhead_len = 29 # b"http://ns.adobe.com/xap/1.0/\x00" - max_data_bytes_in_marker = MAX_BYTES_IN_MARKER - overhead_len - if len(xmp) > max_data_bytes_in_marker: - msg = "XMP data is too long" - raise ValueError(msg) - size = o16(2 + overhead_len + len(xmp)) - extra += b"\xff\xe1" + size + b"http://ns.adobe.com/xap/1.0/\x00" + xmp - - if icc_profile := info.get("icc_profile"): - overhead_len = 14 # b"ICC_PROFILE\0" + o8(i) + o8(len(markers)) - max_data_bytes_in_marker = MAX_BYTES_IN_MARKER - overhead_len - markers = [] - while icc_profile: - markers.append(icc_profile[:max_data_bytes_in_marker]) - icc_profile = icc_profile[max_data_bytes_in_marker:] - i = 1 - for marker in markers: - size = o16(2 + overhead_len + len(marker)) - extra += ( - b"\xff\xe2" - + size - + b"ICC_PROFILE\0" - + o8(i) - + o8(len(markers)) - + marker - ) - i += 1 - - comment = info.get("comment", im.info.get("comment")) - - # "progressive" is the official name, but older documentation - # says "progression" - # FIXME: issue a warning if the wrong form is used (post-1.1.7) - progressive = info.get("progressive", False) or info.get("progression", False) - - optimize = info.get("optimize", False) - - exif = info.get("exif", b"") - if isinstance(exif, Image.Exif): - exif = exif.tobytes() - if len(exif) > MAX_BYTES_IN_MARKER: - msg = "EXIF data is too long" - raise ValueError(msg) - - # get keyword arguments - im.encoderconfig = ( - quality, - progressive, - info.get("smooth", 0), - optimize, - info.get("keep_rgb", False), - info.get("streamtype", 0), - dpi, - subsampling, - info.get("restart_marker_blocks", 0), - info.get("restart_marker_rows", 0), - qtables, - comment, - extra, - exif, - ) - - # if we optimize, libjpeg needs a buffer big enough to hold the whole image - # in a shot. Guessing on the size, at im.size bytes. (raw pixel size is - # channels*size, this is a value that's been used in a django patch. - # https://github.com/matthewwithanm/django-imagekit/issues/50 - if optimize or progressive: - # CMYK can be bigger - if im.mode == "CMYK": - bufsize = 4 * im.size[0] * im.size[1] - # keep sets quality to -1, but the actual value may be high. - elif quality >= 95 or quality == -1: - bufsize = 2 * im.size[0] * im.size[1] - else: - bufsize = im.size[0] * im.size[1] - if exif: - bufsize += len(exif) + 5 - if extra: - bufsize += len(extra) + 1 - else: - # The EXIF info needs to be written as one block, + APP1, + one spare byte. - # Ensure that our buffer is big enough. Same with the icc_profile block. - bufsize = max(len(exif) + 5, len(extra) + 1) - - ImageFile._save( - im, fp, [ImageFile._Tile("jpeg", (0, 0) + im.size, 0, rawmode)], bufsize - ) - - -## -# Factory for making JPEG and MPO instances -def jpeg_factory( - fp: IO[bytes], filename: str | bytes | None = None -) -> JpegImageFile | MpoImageFile: - im = JpegImageFile(fp, filename) - try: - mpheader = im._getmp() - if mpheader is not None and mpheader[45057] > 1: - for segment, content in im.applist: - if segment == "APP1" and b' hdrgm:Version="' in content: - # Ultra HDR images are not yet supported - return im - # It's actually an MPO - from .MpoImagePlugin import MpoImageFile - - # Don't reload everything, just convert it. - im = MpoImageFile.adopt(im, mpheader) - except (TypeError, IndexError): - # It is really a JPEG - pass - except SyntaxError: - warnings.warn( - "Image appears to be a malformed MPO file, it will be " - "interpreted as a base JPEG file" - ) - return im - - -# --------------------------------------------------------------------- -# Registry stuff - -Image.register_open(JpegImageFile.format, jpeg_factory, _accept) -Image.register_save(JpegImageFile.format, _save) - -Image.register_extensions(JpegImageFile.format, [".jfif", ".jpe", ".jpg", ".jpeg"]) - -Image.register_mime(JpegImageFile.format, "image/jpeg") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/JpegPresets.py b/.venv-docs/lib/python3.12/site-packages/PIL/JpegPresets.py deleted file mode 100644 index d0e64a35..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/JpegPresets.py +++ /dev/null @@ -1,242 +0,0 @@ -""" -JPEG quality settings equivalent to the Photoshop settings. -Can be used when saving JPEG files. - -The following presets are available by default: -``web_low``, ``web_medium``, ``web_high``, ``web_very_high``, ``web_maximum``, -``low``, ``medium``, ``high``, ``maximum``. -More presets can be added to the :py:data:`presets` dict if needed. - -To apply the preset, specify:: - - quality="preset_name" - -To apply only the quantization table:: - - qtables="preset_name" - -To apply only the subsampling setting:: - - subsampling="preset_name" - -Example:: - - im.save("image_name.jpg", quality="web_high") - -Subsampling ------------ - -Subsampling is the practice of encoding images by implementing less resolution -for chroma information than for luma information. -(ref.: https://en.wikipedia.org/wiki/Chroma_subsampling) - -Possible subsampling values are 0, 1 and 2 that correspond to 4:4:4, 4:2:2 and -4:2:0. - -You can get the subsampling of a JPEG with the -:func:`.JpegImagePlugin.get_sampling` function. - -In JPEG compressed data a JPEG marker is used instead of an EXIF tag. -(ref.: https://exiv2.org/tags.html) - - -Quantization tables -------------------- - -They are values use by the DCT (Discrete cosine transform) to remove -*unnecessary* information from the image (the lossy part of the compression). -(ref.: https://en.wikipedia.org/wiki/Quantization_matrix#Quantization_matrices, -https://en.wikipedia.org/wiki/JPEG#Quantization) - -You can get the quantization tables of a JPEG with:: - - im.quantization - -This will return a dict with a number of lists. You can pass this dict -directly as the qtables argument when saving a JPEG. - -The quantization table format in presets is a list with sublists. These formats -are interchangeable. - -Libjpeg ref.: -https://web.archive.org/web/20120328125543/http://www.jpegcameras.com/libjpeg/libjpeg-3.html - -""" - -from __future__ import annotations - -# fmt: off -presets = { - 'web_low': {'subsampling': 2, # "4:2:0" - 'quantization': [ - [20, 16, 25, 39, 50, 46, 62, 68, - 16, 18, 23, 38, 38, 53, 65, 68, - 25, 23, 31, 38, 53, 65, 68, 68, - 39, 38, 38, 53, 65, 68, 68, 68, - 50, 38, 53, 65, 68, 68, 68, 68, - 46, 53, 65, 68, 68, 68, 68, 68, - 62, 65, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68], - [21, 25, 32, 38, 54, 68, 68, 68, - 25, 28, 24, 38, 54, 68, 68, 68, - 32, 24, 32, 43, 66, 68, 68, 68, - 38, 38, 43, 53, 68, 68, 68, 68, - 54, 54, 66, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68] - ]}, - 'web_medium': {'subsampling': 2, # "4:2:0" - 'quantization': [ - [16, 11, 11, 16, 23, 27, 31, 30, - 11, 12, 12, 15, 20, 23, 23, 30, - 11, 12, 13, 16, 23, 26, 35, 47, - 16, 15, 16, 23, 26, 37, 47, 64, - 23, 20, 23, 26, 39, 51, 64, 64, - 27, 23, 26, 37, 51, 64, 64, 64, - 31, 23, 35, 47, 64, 64, 64, 64, - 30, 30, 47, 64, 64, 64, 64, 64], - [17, 15, 17, 21, 20, 26, 38, 48, - 15, 19, 18, 17, 20, 26, 35, 43, - 17, 18, 20, 22, 26, 30, 46, 53, - 21, 17, 22, 28, 30, 39, 53, 64, - 20, 20, 26, 30, 39, 48, 64, 64, - 26, 26, 30, 39, 48, 63, 64, 64, - 38, 35, 46, 53, 64, 64, 64, 64, - 48, 43, 53, 64, 64, 64, 64, 64] - ]}, - 'web_high': {'subsampling': 0, # "4:4:4" - 'quantization': [ - [6, 4, 4, 6, 9, 11, 12, 16, - 4, 5, 5, 6, 8, 10, 12, 12, - 4, 5, 5, 6, 10, 12, 14, 19, - 6, 6, 6, 11, 12, 15, 19, 28, - 9, 8, 10, 12, 16, 20, 27, 31, - 11, 10, 12, 15, 20, 27, 31, 31, - 12, 12, 14, 19, 27, 31, 31, 31, - 16, 12, 19, 28, 31, 31, 31, 31], - [7, 7, 13, 24, 26, 31, 31, 31, - 7, 12, 16, 21, 31, 31, 31, 31, - 13, 16, 17, 31, 31, 31, 31, 31, - 24, 21, 31, 31, 31, 31, 31, 31, - 26, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31] - ]}, - 'web_very_high': {'subsampling': 0, # "4:4:4" - 'quantization': [ - [2, 2, 2, 2, 3, 4, 5, 6, - 2, 2, 2, 2, 3, 4, 5, 6, - 2, 2, 2, 2, 4, 5, 7, 9, - 2, 2, 2, 4, 5, 7, 9, 12, - 3, 3, 4, 5, 8, 10, 12, 12, - 4, 4, 5, 7, 10, 12, 12, 12, - 5, 5, 7, 9, 12, 12, 12, 12, - 6, 6, 9, 12, 12, 12, 12, 12], - [3, 3, 5, 9, 13, 15, 15, 15, - 3, 4, 6, 11, 14, 12, 12, 12, - 5, 6, 9, 14, 12, 12, 12, 12, - 9, 11, 14, 12, 12, 12, 12, 12, - 13, 14, 12, 12, 12, 12, 12, 12, - 15, 12, 12, 12, 12, 12, 12, 12, - 15, 12, 12, 12, 12, 12, 12, 12, - 15, 12, 12, 12, 12, 12, 12, 12] - ]}, - 'web_maximum': {'subsampling': 0, # "4:4:4" - 'quantization': [ - [1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 2, - 1, 1, 1, 1, 1, 1, 2, 2, - 1, 1, 1, 1, 1, 2, 2, 3, - 1, 1, 1, 1, 2, 2, 3, 3, - 1, 1, 1, 2, 2, 3, 3, 3, - 1, 1, 2, 2, 3, 3, 3, 3], - [1, 1, 1, 2, 2, 3, 3, 3, - 1, 1, 1, 2, 3, 3, 3, 3, - 1, 1, 1, 3, 3, 3, 3, 3, - 2, 2, 3, 3, 3, 3, 3, 3, - 2, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3] - ]}, - 'low': {'subsampling': 2, # "4:2:0" - 'quantization': [ - [18, 14, 14, 21, 30, 35, 34, 17, - 14, 16, 16, 19, 26, 23, 12, 12, - 14, 16, 17, 21, 23, 12, 12, 12, - 21, 19, 21, 23, 12, 12, 12, 12, - 30, 26, 23, 12, 12, 12, 12, 12, - 35, 23, 12, 12, 12, 12, 12, 12, - 34, 12, 12, 12, 12, 12, 12, 12, - 17, 12, 12, 12, 12, 12, 12, 12], - [20, 19, 22, 27, 20, 20, 17, 17, - 19, 25, 23, 14, 14, 12, 12, 12, - 22, 23, 14, 14, 12, 12, 12, 12, - 27, 14, 14, 12, 12, 12, 12, 12, - 20, 14, 12, 12, 12, 12, 12, 12, - 20, 12, 12, 12, 12, 12, 12, 12, - 17, 12, 12, 12, 12, 12, 12, 12, - 17, 12, 12, 12, 12, 12, 12, 12] - ]}, - 'medium': {'subsampling': 2, # "4:2:0" - 'quantization': [ - [12, 8, 8, 12, 17, 21, 24, 17, - 8, 9, 9, 11, 15, 19, 12, 12, - 8, 9, 10, 12, 19, 12, 12, 12, - 12, 11, 12, 21, 12, 12, 12, 12, - 17, 15, 19, 12, 12, 12, 12, 12, - 21, 19, 12, 12, 12, 12, 12, 12, - 24, 12, 12, 12, 12, 12, 12, 12, - 17, 12, 12, 12, 12, 12, 12, 12], - [13, 11, 13, 16, 20, 20, 17, 17, - 11, 14, 14, 14, 14, 12, 12, 12, - 13, 14, 14, 14, 12, 12, 12, 12, - 16, 14, 14, 12, 12, 12, 12, 12, - 20, 14, 12, 12, 12, 12, 12, 12, - 20, 12, 12, 12, 12, 12, 12, 12, - 17, 12, 12, 12, 12, 12, 12, 12, - 17, 12, 12, 12, 12, 12, 12, 12] - ]}, - 'high': {'subsampling': 0, # "4:4:4" - 'quantization': [ - [6, 4, 4, 6, 9, 11, 12, 16, - 4, 5, 5, 6, 8, 10, 12, 12, - 4, 5, 5, 6, 10, 12, 12, 12, - 6, 6, 6, 11, 12, 12, 12, 12, - 9, 8, 10, 12, 12, 12, 12, 12, - 11, 10, 12, 12, 12, 12, 12, 12, - 12, 12, 12, 12, 12, 12, 12, 12, - 16, 12, 12, 12, 12, 12, 12, 12], - [7, 7, 13, 24, 20, 20, 17, 17, - 7, 12, 16, 14, 14, 12, 12, 12, - 13, 16, 14, 14, 12, 12, 12, 12, - 24, 14, 14, 12, 12, 12, 12, 12, - 20, 14, 12, 12, 12, 12, 12, 12, - 20, 12, 12, 12, 12, 12, 12, 12, - 17, 12, 12, 12, 12, 12, 12, 12, - 17, 12, 12, 12, 12, 12, 12, 12] - ]}, - 'maximum': {'subsampling': 0, # "4:4:4" - 'quantization': [ - [2, 2, 2, 2, 3, 4, 5, 6, - 2, 2, 2, 2, 3, 4, 5, 6, - 2, 2, 2, 2, 4, 5, 7, 9, - 2, 2, 2, 4, 5, 7, 9, 12, - 3, 3, 4, 5, 8, 10, 12, 12, - 4, 4, 5, 7, 10, 12, 12, 12, - 5, 5, 7, 9, 12, 12, 12, 12, - 6, 6, 9, 12, 12, 12, 12, 12], - [3, 3, 5, 9, 13, 15, 15, 15, - 3, 4, 6, 10, 14, 12, 12, 12, - 5, 6, 9, 14, 12, 12, 12, 12, - 9, 10, 14, 12, 12, 12, 12, 12, - 13, 14, 12, 12, 12, 12, 12, 12, - 15, 12, 12, 12, 12, 12, 12, 12, - 15, 12, 12, 12, 12, 12, 12, 12, - 15, 12, 12, 12, 12, 12, 12, 12] - ]}, -} -# fmt: on diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/McIdasImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/McIdasImagePlugin.py deleted file mode 100644 index 9a47933b..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/McIdasImagePlugin.py +++ /dev/null @@ -1,78 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# Basic McIdas support for PIL -# -# History: -# 1997-05-05 fl Created (8-bit images only) -# 2009-03-08 fl Added 16/32-bit support. -# -# Thanks to Richard Jones and Craig Swank for specs and samples. -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1997. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import struct - -from . import Image, ImageFile - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(b"\x00\x00\x00\x00\x00\x00\x00\x04") - - -## -# Image plugin for McIdas area images. - - -class McIdasImageFile(ImageFile.ImageFile): - format = "MCIDAS" - format_description = "McIdas area file" - - def _open(self) -> None: - # parse area file directory - assert self.fp is not None - - s = self.fp.read(256) - if not _accept(s) or len(s) != 256: - msg = "not an McIdas area file" - raise SyntaxError(msg) - - self.area_descriptor_raw = s - self.area_descriptor = w = [0, *struct.unpack("!64i", s)] - - # get mode - if w[11] == 1: - mode = rawmode = "L" - elif w[11] == 2: - mode = rawmode = "I;16B" - elif w[11] == 4: - # FIXME: add memory map support - mode = "I" - rawmode = "I;32B" - else: - msg = "unsupported McIdas format" - raise SyntaxError(msg) - - self._mode = mode - self._size = w[10], w[9] - - offset = w[34] + w[15] - stride = w[15] + w[10] * w[11] * w[14] - - self.tile = [ - ImageFile._Tile("raw", (0, 0) + self.size, offset, (rawmode, stride, 1)) - ] - - -# -------------------------------------------------------------------- -# registry - -Image.register_open(McIdasImageFile.format, McIdasImageFile, _accept) - -# no default extension diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/MicImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/MicImagePlugin.py deleted file mode 100644 index 9ce38c42..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/MicImagePlugin.py +++ /dev/null @@ -1,102 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# Microsoft Image Composer support for PIL -# -# Notes: -# uses TiffImagePlugin.py to read the actual image streams -# -# History: -# 97-01-20 fl Created -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1997. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import olefile - -from . import Image, TiffImagePlugin - -# -# -------------------------------------------------------------------- - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(olefile.MAGIC) - - -## -# Image plugin for Microsoft's Image Composer file format. - - -class MicImageFile(TiffImagePlugin.TiffImageFile): - format = "MIC" - format_description = "Microsoft Image Composer" - _close_exclusive_fp_after_loading = False - - def _open(self) -> None: - # read the OLE directory and see if this is a likely - # to be a Microsoft Image Composer file - - try: - self.ole = olefile.OleFileIO(self.fp) - except OSError as e: - msg = "not an MIC file; invalid OLE file" - raise SyntaxError(msg) from e - - # find ACI subfiles with Image members (maybe not the - # best way to identify MIC files, but what the... ;-) - - self.images = [ - path - for path in self.ole.listdir() - if path[1:] and path[0].endswith(".ACI") and path[1] == "Image" - ] - - # if we didn't find any images, this is probably not - # an MIC file. - if not self.images: - msg = "not an MIC file; no image entries" - raise SyntaxError(msg) - - self.frame = -1 - self._n_frames = len(self.images) - self.is_animated = self._n_frames > 1 - - self.__fp = self.fp - self.seek(0) - - def seek(self, frame: int) -> None: - if not self._seek_check(frame): - return - filename = self.images[frame] - self.fp = self.ole.openstream(filename) - - TiffImagePlugin.TiffImageFile._open(self) - - self.frame = frame - - def tell(self) -> int: - return self.frame - - def close(self) -> None: - self.__fp.close() - self.ole.close() - super().close() - - def __exit__(self, *args: object) -> None: - self.__fp.close() - self.ole.close() - super().__exit__() - - -# -# -------------------------------------------------------------------- - -Image.register_open(MicImageFile.format, MicImageFile, _accept) - -Image.register_extension(MicImageFile.format, ".mic") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/MpegImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/MpegImagePlugin.py deleted file mode 100644 index 47ebe9d6..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/MpegImagePlugin.py +++ /dev/null @@ -1,84 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# MPEG file handling -# -# History: -# 95-09-09 fl Created -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1995. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from . import Image, ImageFile -from ._binary import i8 -from ._typing import SupportsRead - -# -# Bitstream parser - - -class BitStream: - def __init__(self, fp: SupportsRead[bytes]) -> None: - self.fp = fp - self.bits = 0 - self.bitbuffer = 0 - - def next(self) -> int: - return i8(self.fp.read(1)) - - def peek(self, bits: int) -> int: - while self.bits < bits: - self.bitbuffer = (self.bitbuffer << 8) + self.next() - self.bits += 8 - return self.bitbuffer >> (self.bits - bits) & (1 << bits) - 1 - - def skip(self, bits: int) -> None: - while self.bits < bits: - self.bitbuffer = (self.bitbuffer << 8) + i8(self.fp.read(1)) - self.bits += 8 - self.bits = self.bits - bits - - def read(self, bits: int) -> int: - v = self.peek(bits) - self.bits = self.bits - bits - return v - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(b"\x00\x00\x01\xb3") - - -## -# Image plugin for MPEG streams. This plugin can identify a stream, -# but it cannot read it. - - -class MpegImageFile(ImageFile.ImageFile): - format = "MPEG" - format_description = "MPEG" - - def _open(self) -> None: - assert self.fp is not None - - s = BitStream(self.fp) - if s.read(32) != 0x1B3: - msg = "not an MPEG file" - raise SyntaxError(msg) - - self._mode = "RGB" - self._size = s.read(12), s.read(12) - - -# -------------------------------------------------------------------- -# Registry stuff - -Image.register_open(MpegImageFile.format, MpegImageFile, _accept) - -Image.register_extensions(MpegImageFile.format, [".mpg", ".mpeg"]) - -Image.register_mime(MpegImageFile.format, "video/mpeg") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/MpoImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/MpoImagePlugin.py deleted file mode 100644 index b1ae0787..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/MpoImagePlugin.py +++ /dev/null @@ -1,202 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# MPO file handling -# -# See "Multi-Picture Format" (CIPA DC-007-Translation 2009, Standard of the -# Camera & Imaging Products Association) -# -# The multi-picture object combines multiple JPEG images (with a modified EXIF -# data format) into a single file. While it can theoretically be used much like -# a GIF animation, it is commonly used to represent 3D photographs and is (as -# of this writing) the most commonly used format by 3D cameras. -# -# History: -# 2014-03-13 Feneric Created -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import os -import struct -from typing import IO, Any, cast - -from . import ( - Image, - ImageFile, - ImageSequence, - JpegImagePlugin, - TiffImagePlugin, -) -from ._binary import o32le -from ._util import DeferredError - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - JpegImagePlugin._save(im, fp, filename) - - -def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - append_images = im.encoderinfo.get("append_images", []) - if not append_images and not getattr(im, "is_animated", False): - _save(im, fp, filename) - return - - mpf_offset = 28 - offsets: list[int] = [] - im_sequences = [im, *append_images] - total = sum(getattr(seq, "n_frames", 1) for seq in im_sequences) - for im_sequence in im_sequences: - for im_frame in ImageSequence.Iterator(im_sequence): - if not offsets: - # APP2 marker - ifd_length = 66 + 16 * total - im_frame.encoderinfo["extra"] = ( - b"\xff\xe2" - + struct.pack(">H", 6 + ifd_length) - + b"MPF\0" - + b" " * ifd_length - ) - exif = im_frame.encoderinfo.get("exif") - if isinstance(exif, Image.Exif): - exif = exif.tobytes() - im_frame.encoderinfo["exif"] = exif - if exif: - mpf_offset += 4 + len(exif) - - JpegImagePlugin._save(im_frame, fp, filename) - offsets.append(fp.tell()) - else: - encoderinfo = im_frame._attach_default_encoderinfo(im) - im_frame.save(fp, "JPEG") - im_frame.encoderinfo = encoderinfo - offsets.append(fp.tell() - offsets[-1]) - - ifd = TiffImagePlugin.ImageFileDirectory_v2() - ifd[0xB000] = b"0100" - ifd[0xB001] = len(offsets) - - mpentries = b"" - data_offset = 0 - for i, size in enumerate(offsets): - if i == 0: - mptype = 0x030000 # Baseline MP Primary Image - else: - mptype = 0x000000 # Undefined - mpentries += struct.pack(" None: - self.fp.seek(0) # prep the fp in order to pass the JPEG test - JpegImagePlugin.JpegImageFile._open(self) - self._after_jpeg_open() - - def _after_jpeg_open(self, mpheader: dict[int, Any] | None = None) -> None: - self.mpinfo = mpheader if mpheader is not None else self._getmp() - if self.mpinfo is None: - msg = "Image appears to be a malformed MPO file" - raise ValueError(msg) - self.n_frames = self.mpinfo[0xB001] - self.__mpoffsets = [ - mpent["DataOffset"] + self.info["mpoffset"] for mpent in self.mpinfo[0xB002] - ] - self.__mpoffsets[0] = 0 - # Note that the following assertion will only be invalid if something - # gets broken within JpegImagePlugin. - assert self.n_frames == len(self.__mpoffsets) - del self.info["mpoffset"] # no longer needed - self.is_animated = self.n_frames > 1 - self._fp = self.fp # FIXME: hack - self._fp.seek(self.__mpoffsets[0]) # get ready to read first frame - self.__frame = 0 - self.offset = 0 - # for now we can only handle reading and individual frame extraction - self.readonly = 1 - - def load_seek(self, pos: int) -> None: - if isinstance(self._fp, DeferredError): - raise self._fp.ex - self._fp.seek(pos) - - def seek(self, frame: int) -> None: - if not self._seek_check(frame): - return - if isinstance(self._fp, DeferredError): - raise self._fp.ex - self.fp = self._fp - self.offset = self.__mpoffsets[frame] - - original_exif = self.info.get("exif") - if "exif" in self.info: - del self.info["exif"] - - self.fp.seek(self.offset + 2) # skip SOI marker - if not self.fp.read(2): - msg = "No data found for frame" - raise ValueError(msg) - self.fp.seek(self.offset) - JpegImagePlugin.JpegImageFile._open(self) - if self.info.get("exif") != original_exif: - self._reload_exif() - - self.tile = [ - ImageFile._Tile("jpeg", (0, 0) + self.size, self.offset, self.tile[0][-1]) - ] - self.__frame = frame - - def tell(self) -> int: - return self.__frame - - @staticmethod - def adopt( - jpeg_instance: JpegImagePlugin.JpegImageFile, - mpheader: dict[int, Any] | None = None, - ) -> MpoImageFile: - """ - Transform the instance of JpegImageFile into - an instance of MpoImageFile. - After the call, the JpegImageFile is extended - to be an MpoImageFile. - - This is essentially useful when opening a JPEG - file that reveals itself as an MPO, to avoid - double call to _open. - """ - jpeg_instance.__class__ = MpoImageFile - mpo_instance = cast(MpoImageFile, jpeg_instance) - mpo_instance._after_jpeg_open(mpheader) - return mpo_instance - - -# --------------------------------------------------------------------- -# Registry stuff - -# Note that since MPO shares a factory with JPEG, we do not need to do a -# separate registration for it here. -# Image.register_open(MpoImageFile.format, -# JpegImagePlugin.jpeg_factory, _accept) -Image.register_save(MpoImageFile.format, _save) -Image.register_save_all(MpoImageFile.format, _save_all) - -Image.register_extension(MpoImageFile.format, ".mpo") - -Image.register_mime(MpoImageFile.format, "image/mpo") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/MspImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/MspImagePlugin.py deleted file mode 100644 index 277087a8..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/MspImagePlugin.py +++ /dev/null @@ -1,200 +0,0 @@ -# -# The Python Imaging Library. -# -# MSP file handling -# -# This is the format used by the Paint program in Windows 1 and 2. -# -# History: -# 95-09-05 fl Created -# 97-01-03 fl Read/write MSP images -# 17-02-21 es Fixed RLE interpretation -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1995-97. -# Copyright (c) Eric Soroos 2017. -# -# See the README file for information on usage and redistribution. -# -# More info on this format: https://archive.org/details/gg243631 -# Page 313: -# Figure 205. Windows Paint Version 1: "DanM" Format -# Figure 206. Windows Paint Version 2: "LinS" Format. Used in Windows V2.03 -# -# See also: https://www.fileformat.info/format/mspaint/egff.htm -from __future__ import annotations - -import io -import struct -from typing import IO - -from . import Image, ImageFile -from ._binary import i16le as i16 -from ._binary import o16le as o16 - -# -# read MSP files - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith((b"DanM", b"LinS")) - - -## -# Image plugin for Windows MSP images. This plugin supports both -# uncompressed (Windows 1.0). - - -class MspImageFile(ImageFile.ImageFile): - format = "MSP" - format_description = "Windows Paint" - - def _open(self) -> None: - # Header - assert self.fp is not None - - s = self.fp.read(32) - if not _accept(s): - msg = "not an MSP file" - raise SyntaxError(msg) - - # Header checksum - checksum = 0 - for i in range(0, 32, 2): - checksum = checksum ^ i16(s, i) - if checksum != 0: - msg = "bad MSP checksum" - raise SyntaxError(msg) - - self._mode = "1" - self._size = i16(s, 4), i16(s, 6) - - if s.startswith(b"DanM"): - self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 32, "1")] - else: - self.tile = [ImageFile._Tile("MSP", (0, 0) + self.size, 32)] - - -class MspDecoder(ImageFile.PyDecoder): - # The algo for the MSP decoder is from - # https://www.fileformat.info/format/mspaint/egff.htm - # cc-by-attribution -- That page references is taken from the - # Encyclopedia of Graphics File Formats and is licensed by - # O'Reilly under the Creative Common/Attribution license - # - # For RLE encoded files, the 32byte header is followed by a scan - # line map, encoded as one 16bit word of encoded byte length per - # line. - # - # NOTE: the encoded length of the line can be 0. This was not - # handled in the previous version of this encoder, and there's no - # mention of how to handle it in the documentation. From the few - # examples I've seen, I've assumed that it is a fill of the - # background color, in this case, white. - # - # - # Pseudocode of the decoder: - # Read a BYTE value as the RunType - # If the RunType value is zero - # Read next byte as the RunCount - # Read the next byte as the RunValue - # Write the RunValue byte RunCount times - # If the RunType value is non-zero - # Use this value as the RunCount - # Read and write the next RunCount bytes literally - # - # e.g.: - # 0x00 03 ff 05 00 01 02 03 04 - # would yield the bytes: - # 0xff ff ff 00 01 02 03 04 - # - # which are then interpreted as a bit packed mode '1' image - - _pulls_fd = True - - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: - assert self.fd is not None - - img = io.BytesIO() - blank_line = bytearray((0xFF,) * ((self.state.xsize + 7) // 8)) - try: - self.fd.seek(32) - rowmap = struct.unpack_from( - f"<{self.state.ysize}H", self.fd.read(self.state.ysize * 2) - ) - except struct.error as e: - msg = "Truncated MSP file in row map" - raise OSError(msg) from e - - for x, rowlen in enumerate(rowmap): - try: - if rowlen == 0: - img.write(blank_line) - continue - row = self.fd.read(rowlen) - if len(row) != rowlen: - msg = f"Truncated MSP file, expected {rowlen} bytes on row {x}" - raise OSError(msg) - idx = 0 - while idx < rowlen: - runtype = row[idx] - idx += 1 - if runtype == 0: - (runcount, runval) = struct.unpack_from("Bc", row, idx) - img.write(runval * runcount) - idx += 2 - else: - runcount = runtype - img.write(row[idx : idx + runcount]) - idx += runcount - - except struct.error as e: - msg = f"Corrupted MSP file in row {x}" - raise OSError(msg) from e - - self.set_as_raw(img.getvalue(), "1") - - return -1, 0 - - -Image.register_decoder("MSP", MspDecoder) - - -# -# write MSP files (uncompressed only) - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - if im.mode != "1": - msg = f"cannot write mode {im.mode} as MSP" - raise OSError(msg) - - # create MSP header - header = [0] * 16 - - header[0], header[1] = i16(b"Da"), i16(b"nM") # version 1 - header[2], header[3] = im.size - header[4], header[5] = 1, 1 - header[6], header[7] = 1, 1 - header[8], header[9] = im.size - - checksum = 0 - for h in header: - checksum = checksum ^ h - header[12] = checksum # FIXME: is this the right field? - - # header - for h in header: - fp.write(o16(h)) - - # image body - ImageFile._save(im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 32, "1")]) - - -# -# registry - -Image.register_open(MspImageFile.format, MspImageFile, _accept) -Image.register_save(MspImageFile.format, _save) - -Image.register_extension(MspImageFile.format, ".msp") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/PSDraw.py b/.venv-docs/lib/python3.12/site-packages/PIL/PSDraw.py deleted file mode 100644 index 7fd4c5c9..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/PSDraw.py +++ /dev/null @@ -1,237 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# Simple PostScript graphics interface -# -# History: -# 1996-04-20 fl Created -# 1999-01-10 fl Added gsave/grestore to image method -# 2005-05-04 fl Fixed floating point issue in image (from Eric Etheridge) -# -# Copyright (c) 1997-2005 by Secret Labs AB. All rights reserved. -# Copyright (c) 1996 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import sys -from typing import IO - -from . import EpsImagePlugin - -TYPE_CHECKING = False - - -## -# Simple PostScript graphics interface. - - -class PSDraw: - """ - Sets up printing to the given file. If ``fp`` is omitted, - ``sys.stdout.buffer`` is assumed. - """ - - def __init__(self, fp: IO[bytes] | None = None) -> None: - if not fp: - fp = sys.stdout.buffer - self.fp = fp - - def begin_document(self, id: str | None = None) -> None: - """Set up printing of a document. (Write PostScript DSC header.)""" - # FIXME: incomplete - self.fp.write( - b"%!PS-Adobe-3.0\n" - b"save\n" - b"/showpage { } def\n" - b"%%EndComments\n" - b"%%BeginDocument\n" - ) - # self.fp.write(ERROR_PS) # debugging! - self.fp.write(EDROFF_PS) - self.fp.write(VDI_PS) - self.fp.write(b"%%EndProlog\n") - self.isofont: dict[bytes, int] = {} - - def end_document(self) -> None: - """Ends printing. (Write PostScript DSC footer.)""" - self.fp.write(b"%%EndDocument\nrestore showpage\n%%End\n") - if hasattr(self.fp, "flush"): - self.fp.flush() - - def setfont(self, font: str, size: int) -> None: - """ - Selects which font to use. - - :param font: A PostScript font name - :param size: Size in points. - """ - font_bytes = bytes(font, "UTF-8") - if font_bytes not in self.isofont: - # reencode font - self.fp.write( - b"/PSDraw-%s ISOLatin1Encoding /%s E\n" % (font_bytes, font_bytes) - ) - self.isofont[font_bytes] = 1 - # rough - self.fp.write(b"/F0 %d /PSDraw-%s F\n" % (size, font_bytes)) - - def line(self, xy0: tuple[int, int], xy1: tuple[int, int]) -> None: - """ - Draws a line between the two points. Coordinates are given in - PostScript point coordinates (72 points per inch, (0, 0) is the lower - left corner of the page). - """ - self.fp.write(b"%d %d %d %d Vl\n" % (*xy0, *xy1)) - - def rectangle(self, box: tuple[int, int, int, int]) -> None: - """ - Draws a rectangle. - - :param box: A tuple of four integers, specifying left, bottom, width and - height. - """ - self.fp.write(b"%d %d M 0 %d %d Vr\n" % box) - - def text(self, xy: tuple[int, int], text: str) -> None: - """ - Draws text at the given position. You must use - :py:meth:`~PIL.PSDraw.PSDraw.setfont` before calling this method. - """ - text_bytes = bytes(text, "UTF-8") - text_bytes = b"\\(".join(text_bytes.split(b"(")) - text_bytes = b"\\)".join(text_bytes.split(b")")) - self.fp.write(b"%d %d M (%s) S\n" % (xy + (text_bytes,))) - - if TYPE_CHECKING: - from . import Image - - def image( - self, box: tuple[int, int, int, int], im: Image.Image, dpi: int | None = None - ) -> None: - """Draw a PIL image, centered in the given box.""" - # default resolution depends on mode - if not dpi: - if im.mode == "1": - dpi = 200 # fax - else: - dpi = 100 # grayscale - # image size (on paper) - x = im.size[0] * 72 / dpi - y = im.size[1] * 72 / dpi - # max allowed size - xmax = float(box[2] - box[0]) - ymax = float(box[3] - box[1]) - if x > xmax: - y = y * xmax / x - x = xmax - if y > ymax: - x = x * ymax / y - y = ymax - dx = (xmax - x) / 2 + box[0] - dy = (ymax - y) / 2 + box[1] - self.fp.write(b"gsave\n%f %f translate\n" % (dx, dy)) - if (x, y) != im.size: - # EpsImagePlugin._save prints the image at (0,0,xsize,ysize) - sx = x / im.size[0] - sy = y / im.size[1] - self.fp.write(b"%f %f scale\n" % (sx, sy)) - EpsImagePlugin._save(im, self.fp, "", 0) - self.fp.write(b"\ngrestore\n") - - -# -------------------------------------------------------------------- -# PostScript driver - -# -# EDROFF.PS -- PostScript driver for Edroff 2 -# -# History: -# 94-01-25 fl: created (edroff 2.04) -# -# Copyright (c) Fredrik Lundh 1994. -# - - -EDROFF_PS = b"""\ -/S { show } bind def -/P { moveto show } bind def -/M { moveto } bind def -/X { 0 rmoveto } bind def -/Y { 0 exch rmoveto } bind def -/E { findfont - dup maxlength dict begin - { - 1 index /FID ne { def } { pop pop } ifelse - } forall - /Encoding exch def - dup /FontName exch def - currentdict end definefont pop -} bind def -/F { findfont exch scalefont dup setfont - [ exch /setfont cvx ] cvx bind def -} bind def -""" - -# -# VDI.PS -- PostScript driver for VDI meta commands -# -# History: -# 94-01-25 fl: created (edroff 2.04) -# -# Copyright (c) Fredrik Lundh 1994. -# - -VDI_PS = b"""\ -/Vm { moveto } bind def -/Va { newpath arcn stroke } bind def -/Vl { moveto lineto stroke } bind def -/Vc { newpath 0 360 arc closepath } bind def -/Vr { exch dup 0 rlineto - exch dup 0 exch rlineto - exch neg 0 rlineto - 0 exch neg rlineto - setgray fill } bind def -/Tm matrix def -/Ve { Tm currentmatrix pop - translate scale newpath 0 0 .5 0 360 arc closepath - Tm setmatrix -} bind def -/Vf { currentgray exch setgray fill setgray } bind def -""" - -# -# ERROR.PS -- Error handler -# -# History: -# 89-11-21 fl: created (pslist 1.10) -# - -ERROR_PS = b"""\ -/landscape false def -/errorBUF 200 string def -/errorNL { currentpoint 10 sub exch pop 72 exch moveto } def -errordict begin /handleerror { - initmatrix /Courier findfont 10 scalefont setfont - newpath 72 720 moveto $error begin /newerror false def - (PostScript Error) show errorNL errorNL - (Error: ) show - /errorname load errorBUF cvs show errorNL errorNL - (Command: ) show - /command load dup type /stringtype ne { errorBUF cvs } if show - errorNL errorNL - (VMstatus: ) show - vmstatus errorBUF cvs show ( bytes available, ) show - errorBUF cvs show ( bytes used at level ) show - errorBUF cvs show errorNL errorNL - (Operand stargck: ) show errorNL /ostargck load { - dup type /stringtype ne { errorBUF cvs } if 72 0 rmoveto show errorNL - } forall errorNL - (Execution stargck: ) show errorNL /estargck load { - dup type /stringtype ne { errorBUF cvs } if 72 0 rmoveto show errorNL - } forall - end showpage -} def end -""" diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/PaletteFile.py b/.venv-docs/lib/python3.12/site-packages/PIL/PaletteFile.py deleted file mode 100644 index 2a26e5d4..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/PaletteFile.py +++ /dev/null @@ -1,54 +0,0 @@ -# -# Python Imaging Library -# $Id$ -# -# stuff to read simple, teragon-style palette files -# -# History: -# 97-08-23 fl Created -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1997. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from typing import IO - -from ._binary import o8 - - -class PaletteFile: - """File handler for Teragon-style palette files.""" - - rawmode = "RGB" - - def __init__(self, fp: IO[bytes]) -> None: - palette = [o8(i) * 3 for i in range(256)] - - while True: - s = fp.readline() - - if not s: - break - if s.startswith(b"#"): - continue - if len(s) > 100: - msg = "bad palette file" - raise SyntaxError(msg) - - v = [int(x) for x in s.split()] - try: - [i, r, g, b] = v - except ValueError: - [i, r] = v - g = b = r - - if 0 <= i <= 255: - palette[i] = o8(r) + o8(g) + o8(b) - - self.palette = b"".join(palette) - - def getpalette(self) -> tuple[bytes, str]: - return self.palette, self.rawmode diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/PalmImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/PalmImagePlugin.py deleted file mode 100644 index 15f71290..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/PalmImagePlugin.py +++ /dev/null @@ -1,217 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# - -## -# Image plugin for Palm pixmap images (output only). -## -from __future__ import annotations - -from typing import IO - -from . import Image, ImageFile -from ._binary import o8 -from ._binary import o16be as o16b - -# fmt: off -_Palm8BitColormapValues = ( - (255, 255, 255), (255, 204, 255), (255, 153, 255), (255, 102, 255), - (255, 51, 255), (255, 0, 255), (255, 255, 204), (255, 204, 204), - (255, 153, 204), (255, 102, 204), (255, 51, 204), (255, 0, 204), - (255, 255, 153), (255, 204, 153), (255, 153, 153), (255, 102, 153), - (255, 51, 153), (255, 0, 153), (204, 255, 255), (204, 204, 255), - (204, 153, 255), (204, 102, 255), (204, 51, 255), (204, 0, 255), - (204, 255, 204), (204, 204, 204), (204, 153, 204), (204, 102, 204), - (204, 51, 204), (204, 0, 204), (204, 255, 153), (204, 204, 153), - (204, 153, 153), (204, 102, 153), (204, 51, 153), (204, 0, 153), - (153, 255, 255), (153, 204, 255), (153, 153, 255), (153, 102, 255), - (153, 51, 255), (153, 0, 255), (153, 255, 204), (153, 204, 204), - (153, 153, 204), (153, 102, 204), (153, 51, 204), (153, 0, 204), - (153, 255, 153), (153, 204, 153), (153, 153, 153), (153, 102, 153), - (153, 51, 153), (153, 0, 153), (102, 255, 255), (102, 204, 255), - (102, 153, 255), (102, 102, 255), (102, 51, 255), (102, 0, 255), - (102, 255, 204), (102, 204, 204), (102, 153, 204), (102, 102, 204), - (102, 51, 204), (102, 0, 204), (102, 255, 153), (102, 204, 153), - (102, 153, 153), (102, 102, 153), (102, 51, 153), (102, 0, 153), - (51, 255, 255), (51, 204, 255), (51, 153, 255), (51, 102, 255), - (51, 51, 255), (51, 0, 255), (51, 255, 204), (51, 204, 204), - (51, 153, 204), (51, 102, 204), (51, 51, 204), (51, 0, 204), - (51, 255, 153), (51, 204, 153), (51, 153, 153), (51, 102, 153), - (51, 51, 153), (51, 0, 153), (0, 255, 255), (0, 204, 255), - (0, 153, 255), (0, 102, 255), (0, 51, 255), (0, 0, 255), - (0, 255, 204), (0, 204, 204), (0, 153, 204), (0, 102, 204), - (0, 51, 204), (0, 0, 204), (0, 255, 153), (0, 204, 153), - (0, 153, 153), (0, 102, 153), (0, 51, 153), (0, 0, 153), - (255, 255, 102), (255, 204, 102), (255, 153, 102), (255, 102, 102), - (255, 51, 102), (255, 0, 102), (255, 255, 51), (255, 204, 51), - (255, 153, 51), (255, 102, 51), (255, 51, 51), (255, 0, 51), - (255, 255, 0), (255, 204, 0), (255, 153, 0), (255, 102, 0), - (255, 51, 0), (255, 0, 0), (204, 255, 102), (204, 204, 102), - (204, 153, 102), (204, 102, 102), (204, 51, 102), (204, 0, 102), - (204, 255, 51), (204, 204, 51), (204, 153, 51), (204, 102, 51), - (204, 51, 51), (204, 0, 51), (204, 255, 0), (204, 204, 0), - (204, 153, 0), (204, 102, 0), (204, 51, 0), (204, 0, 0), - (153, 255, 102), (153, 204, 102), (153, 153, 102), (153, 102, 102), - (153, 51, 102), (153, 0, 102), (153, 255, 51), (153, 204, 51), - (153, 153, 51), (153, 102, 51), (153, 51, 51), (153, 0, 51), - (153, 255, 0), (153, 204, 0), (153, 153, 0), (153, 102, 0), - (153, 51, 0), (153, 0, 0), (102, 255, 102), (102, 204, 102), - (102, 153, 102), (102, 102, 102), (102, 51, 102), (102, 0, 102), - (102, 255, 51), (102, 204, 51), (102, 153, 51), (102, 102, 51), - (102, 51, 51), (102, 0, 51), (102, 255, 0), (102, 204, 0), - (102, 153, 0), (102, 102, 0), (102, 51, 0), (102, 0, 0), - (51, 255, 102), (51, 204, 102), (51, 153, 102), (51, 102, 102), - (51, 51, 102), (51, 0, 102), (51, 255, 51), (51, 204, 51), - (51, 153, 51), (51, 102, 51), (51, 51, 51), (51, 0, 51), - (51, 255, 0), (51, 204, 0), (51, 153, 0), (51, 102, 0), - (51, 51, 0), (51, 0, 0), (0, 255, 102), (0, 204, 102), - (0, 153, 102), (0, 102, 102), (0, 51, 102), (0, 0, 102), - (0, 255, 51), (0, 204, 51), (0, 153, 51), (0, 102, 51), - (0, 51, 51), (0, 0, 51), (0, 255, 0), (0, 204, 0), - (0, 153, 0), (0, 102, 0), (0, 51, 0), (17, 17, 17), - (34, 34, 34), (68, 68, 68), (85, 85, 85), (119, 119, 119), - (136, 136, 136), (170, 170, 170), (187, 187, 187), (221, 221, 221), - (238, 238, 238), (192, 192, 192), (128, 0, 0), (128, 0, 128), - (0, 128, 0), (0, 128, 128), (0, 0, 0), (0, 0, 0), - (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), - (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), - (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), - (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), - (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), - (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0)) -# fmt: on - - -# so build a prototype image to be used for palette resampling -def build_prototype_image() -> Image.Image: - image = Image.new("L", (1, len(_Palm8BitColormapValues))) - image.putdata(list(range(len(_Palm8BitColormapValues)))) - palettedata: tuple[int, ...] = () - for colormapValue in _Palm8BitColormapValues: - palettedata += colormapValue - palettedata += (0, 0, 0) * (256 - len(_Palm8BitColormapValues)) - image.putpalette(palettedata) - return image - - -Palm8BitColormapImage = build_prototype_image() - -# OK, we now have in Palm8BitColormapImage, -# a "P"-mode image with the right palette -# -# -------------------------------------------------------------------- - -_FLAGS = {"custom-colormap": 0x4000, "is-compressed": 0x8000, "has-transparent": 0x2000} - -_COMPRESSION_TYPES = {"none": 0xFF, "rle": 0x01, "scanline": 0x00} - - -# -# -------------------------------------------------------------------- - -## -# (Internal) Image save plugin for the Palm format. - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - if im.mode == "P": - rawmode = "P" - bpp = 8 - version = 1 - - elif im.mode == "L": - if im.encoderinfo.get("bpp") in (1, 2, 4): - # this is 8-bit grayscale, so we shift it to get the high-order bits, - # and invert it because - # Palm does grayscale from white (0) to black (1) - bpp = im.encoderinfo["bpp"] - maxval = (1 << bpp) - 1 - shift = 8 - bpp - im = im.point(lambda x: maxval - (x >> shift)) - elif im.info.get("bpp") in (1, 2, 4): - # here we assume that even though the inherent mode is 8-bit grayscale, - # only the lower bpp bits are significant. - # We invert them to match the Palm. - bpp = im.info["bpp"] - maxval = (1 << bpp) - 1 - im = im.point(lambda x: maxval - (x & maxval)) - else: - msg = f"cannot write mode {im.mode} as Palm" - raise OSError(msg) - - # we ignore the palette here - im._mode = "P" - rawmode = f"P;{bpp}" - version = 1 - - elif im.mode == "1": - # monochrome -- write it inverted, as is the Palm standard - rawmode = "1;I" - bpp = 1 - version = 0 - - else: - msg = f"cannot write mode {im.mode} as Palm" - raise OSError(msg) - - # - # make sure image data is available - im.load() - - # write header - - cols = im.size[0] - rows = im.size[1] - - rowbytes = int((cols + (16 // bpp - 1)) / (16 // bpp)) * 2 - transparent_index = 0 - compression_type = _COMPRESSION_TYPES["none"] - - flags = 0 - if im.mode == "P": - flags |= _FLAGS["custom-colormap"] - colormap = im.im.getpalette() - colors = len(colormap) // 3 - colormapsize = 4 * colors + 2 - else: - colormapsize = 0 - - if "offset" in im.info: - offset = (rowbytes * rows + 16 + 3 + colormapsize) // 4 - else: - offset = 0 - - fp.write(o16b(cols) + o16b(rows) + o16b(rowbytes) + o16b(flags)) - fp.write(o8(bpp)) - fp.write(o8(version)) - fp.write(o16b(offset)) - fp.write(o8(transparent_index)) - fp.write(o8(compression_type)) - fp.write(o16b(0)) # reserved by Palm - - # now write colormap if necessary - - if colormapsize: - fp.write(o16b(colors)) - for i in range(colors): - fp.write(o8(i)) - fp.write(colormap[3 * i : 3 * i + 3]) - - # now convert data to raw form - ImageFile._save( - im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 0, (rawmode, rowbytes, 1))] - ) - - if hasattr(fp, "flush"): - fp.flush() - - -# -# -------------------------------------------------------------------- - -Image.register_save("Palm", _save) - -Image.register_extension("Palm", ".palm") - -Image.register_mime("Palm", "image/palm") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/PcdImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/PcdImagePlugin.py deleted file mode 100644 index 296f3775..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/PcdImagePlugin.py +++ /dev/null @@ -1,68 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# PCD file handling -# -# History: -# 96-05-10 fl Created -# 96-05-27 fl Added draft mode (128x192, 256x384) -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1996. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from . import Image, ImageFile - -## -# Image plugin for PhotoCD images. This plugin only reads the 768x512 -# image from the file; higher resolutions are encoded in a proprietary -# encoding. - - -class PcdImageFile(ImageFile.ImageFile): - format = "PCD" - format_description = "Kodak PhotoCD" - - def _open(self) -> None: - # rough - assert self.fp is not None - - self.fp.seek(2048) - s = self.fp.read(1539) - - if not s.startswith(b"PCD_"): - msg = "not a PCD file" - raise SyntaxError(msg) - - orientation = s[1538] & 3 - self.tile_post_rotate = None - if orientation == 1: - self.tile_post_rotate = 90 - elif orientation == 3: - self.tile_post_rotate = 270 - - self._mode = "RGB" - self._size = (512, 768) if orientation in (1, 3) else (768, 512) - self.tile = [ImageFile._Tile("pcd", (0, 0, 768, 512), 96 * 2048)] - - def load_prepare(self) -> None: - if self._im is None and self.tile_post_rotate: - self.im = Image.core.new(self.mode, (768, 512)) - ImageFile.ImageFile.load_prepare(self) - - def load_end(self) -> None: - if self.tile_post_rotate: - # Handle rotated PCDs - self.im = self.rotate(self.tile_post_rotate, expand=True).im - - -# -# registry - -Image.register_open(PcdImageFile.format, PcdImageFile) - -Image.register_extension(PcdImageFile.format, ".pcd") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/PcfFontFile.py b/.venv-docs/lib/python3.12/site-packages/PIL/PcfFontFile.py deleted file mode 100644 index a00e9b91..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/PcfFontFile.py +++ /dev/null @@ -1,258 +0,0 @@ -# -# THIS IS WORK IN PROGRESS -# -# The Python Imaging Library -# $Id$ -# -# portable compiled font file parser -# -# history: -# 1997-08-19 fl created -# 2003-09-13 fl fixed loading of unicode fonts -# -# Copyright (c) 1997-2003 by Secret Labs AB. -# Copyright (c) 1997-2003 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io - -from . import FontFile, Image -from ._binary import i8 -from ._binary import i16be as b16 -from ._binary import i16le as l16 -from ._binary import i32be as b32 -from ._binary import i32le as l32 - -TYPE_CHECKING = False -if TYPE_CHECKING: - from collections.abc import Callable - from typing import BinaryIO - -# -------------------------------------------------------------------- -# declarations - -PCF_MAGIC = 0x70636601 # "\x01fcp" - -PCF_PROPERTIES = 1 << 0 -PCF_ACCELERATORS = 1 << 1 -PCF_METRICS = 1 << 2 -PCF_BITMAPS = 1 << 3 -PCF_INK_METRICS = 1 << 4 -PCF_BDF_ENCODINGS = 1 << 5 -PCF_SWIDTHS = 1 << 6 -PCF_GLYPH_NAMES = 1 << 7 -PCF_BDF_ACCELERATORS = 1 << 8 - -BYTES_PER_ROW: list[Callable[[int], int]] = [ - lambda bits: ((bits + 7) >> 3), - lambda bits: ((bits + 15) >> 3) & ~1, - lambda bits: ((bits + 31) >> 3) & ~3, - lambda bits: ((bits + 63) >> 3) & ~7, -] - - -def sz(s: bytes, o: int) -> bytes: - return s[o : s.index(b"\0", o)] - - -class PcfFontFile(FontFile.FontFile): - """Font file plugin for the X11 PCF format.""" - - name = "name" - - def __init__(self, fp: BinaryIO, charset_encoding: str = "iso8859-1"): - self.charset_encoding = charset_encoding - - magic = l32(fp.read(4)) - if magic != PCF_MAGIC: - msg = "not a PCF file" - raise SyntaxError(msg) - - super().__init__() - - count = l32(fp.read(4)) - self.toc = {} - for i in range(count): - type = l32(fp.read(4)) - self.toc[type] = l32(fp.read(4)), l32(fp.read(4)), l32(fp.read(4)) - - self.fp = fp - - self.info = self._load_properties() - - metrics = self._load_metrics() - bitmaps = self._load_bitmaps(metrics) - encoding = self._load_encoding() - - # - # create glyph structure - - for ch, ix in enumerate(encoding): - if ix is not None: - ( - xsize, - ysize, - left, - right, - width, - ascent, - descent, - attributes, - ) = metrics[ix] - self.glyph[ch] = ( - (width, 0), - (left, descent - ysize, xsize + left, descent), - (0, 0, xsize, ysize), - bitmaps[ix], - ) - - def _getformat( - self, tag: int - ) -> tuple[BinaryIO, int, Callable[[bytes], int], Callable[[bytes], int]]: - format, size, offset = self.toc[tag] - - fp = self.fp - fp.seek(offset) - - format = l32(fp.read(4)) - - if format & 4: - i16, i32 = b16, b32 - else: - i16, i32 = l16, l32 - - return fp, format, i16, i32 - - def _load_properties(self) -> dict[bytes, bytes | int]: - # - # font properties - - properties = {} - - fp, format, i16, i32 = self._getformat(PCF_PROPERTIES) - - nprops = i32(fp.read(4)) - - # read property description - p = [(i32(fp.read(4)), i8(fp.read(1)), i32(fp.read(4))) for _ in range(nprops)] - - if nprops & 3: - fp.seek(4 - (nprops & 3), io.SEEK_CUR) # pad - - data = fp.read(i32(fp.read(4))) - - for k, s, v in p: - property_value: bytes | int = sz(data, v) if s else v - properties[sz(data, k)] = property_value - - return properties - - def _load_metrics(self) -> list[tuple[int, int, int, int, int, int, int, int]]: - # - # font metrics - - metrics: list[tuple[int, int, int, int, int, int, int, int]] = [] - - fp, format, i16, i32 = self._getformat(PCF_METRICS) - - append = metrics.append - - if (format & 0xFF00) == 0x100: - # "compressed" metrics - for i in range(i16(fp.read(2))): - left = i8(fp.read(1)) - 128 - right = i8(fp.read(1)) - 128 - width = i8(fp.read(1)) - 128 - ascent = i8(fp.read(1)) - 128 - descent = i8(fp.read(1)) - 128 - xsize = right - left - ysize = ascent + descent - append((xsize, ysize, left, right, width, ascent, descent, 0)) - - else: - # "jumbo" metrics - for i in range(i32(fp.read(4))): - left = i16(fp.read(2)) - right = i16(fp.read(2)) - width = i16(fp.read(2)) - ascent = i16(fp.read(2)) - descent = i16(fp.read(2)) - attributes = i16(fp.read(2)) - xsize = right - left - ysize = ascent + descent - append((xsize, ysize, left, right, width, ascent, descent, attributes)) - - return metrics - - def _load_bitmaps( - self, metrics: list[tuple[int, int, int, int, int, int, int, int]] - ) -> list[Image.Image]: - # - # bitmap data - - fp, format, i16, i32 = self._getformat(PCF_BITMAPS) - - nbitmaps = i32(fp.read(4)) - - if nbitmaps != len(metrics): - msg = "Wrong number of bitmaps" - raise OSError(msg) - - offsets = [i32(fp.read(4)) for _ in range(nbitmaps)] - - bitmap_sizes = [i32(fp.read(4)) for _ in range(4)] - - # byteorder = format & 4 # non-zero => MSB - bitorder = format & 8 # non-zero => MSB - padindex = format & 3 - - bitmapsize = bitmap_sizes[padindex] - offsets.append(bitmapsize) - - data = fp.read(bitmapsize) - - pad = BYTES_PER_ROW[padindex] - mode = "1;R" - if bitorder: - mode = "1" - - bitmaps = [] - for i in range(nbitmaps): - xsize, ysize = metrics[i][:2] - b, e = offsets[i : i + 2] - bitmaps.append( - Image.frombytes("1", (xsize, ysize), data[b:e], "raw", mode, pad(xsize)) - ) - - return bitmaps - - def _load_encoding(self) -> list[int | None]: - fp, format, i16, i32 = self._getformat(PCF_BDF_ENCODINGS) - - first_col, last_col = i16(fp.read(2)), i16(fp.read(2)) - first_row, last_row = i16(fp.read(2)), i16(fp.read(2)) - - i16(fp.read(2)) # default - - nencoding = (last_col - first_col + 1) * (last_row - first_row + 1) - - # map character code to bitmap index - encoding: list[int | None] = [None] * min(256, nencoding) - - encoding_offsets = [i16(fp.read(2)) for _ in range(nencoding)] - - for i in range(first_col, len(encoding)): - try: - encoding_offset = encoding_offsets[ - ord(bytearray([i]).decode(self.charset_encoding)) - ] - if encoding_offset != 0xFFFF: - encoding[i] = encoding_offset - except UnicodeDecodeError: - # character is not supported in selected encoding - pass - - return encoding diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/PcxImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/PcxImagePlugin.py deleted file mode 100644 index 6b16d538..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/PcxImagePlugin.py +++ /dev/null @@ -1,228 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# PCX file handling -# -# This format was originally used by ZSoft's popular PaintBrush -# program for the IBM PC. It is also supported by many MS-DOS and -# Windows applications, including the Windows PaintBrush program in -# Windows 3. -# -# history: -# 1995-09-01 fl Created -# 1996-05-20 fl Fixed RGB support -# 1997-01-03 fl Fixed 2-bit and 4-bit support -# 1999-02-03 fl Fixed 8-bit support (broken in 1.0b1) -# 1999-02-07 fl Added write support -# 2002-06-09 fl Made 2-bit and 4-bit support a bit more robust -# 2002-07-30 fl Seek from to current position, not beginning of file -# 2003-06-03 fl Extract DPI settings (info["dpi"]) -# -# Copyright (c) 1997-2003 by Secret Labs AB. -# Copyright (c) 1995-2003 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io -import logging -from typing import IO - -from . import Image, ImageFile, ImagePalette -from ._binary import i16le as i16 -from ._binary import o8 -from ._binary import o16le as o16 - -logger = logging.getLogger(__name__) - - -def _accept(prefix: bytes) -> bool: - return len(prefix) >= 2 and prefix[0] == 10 and prefix[1] in [0, 2, 3, 5] - - -## -# Image plugin for Paintbrush images. - - -class PcxImageFile(ImageFile.ImageFile): - format = "PCX" - format_description = "Paintbrush" - - def _open(self) -> None: - # header - assert self.fp is not None - - s = self.fp.read(68) - if not _accept(s): - msg = "not a PCX file" - raise SyntaxError(msg) - - # image - bbox = i16(s, 4), i16(s, 6), i16(s, 8) + 1, i16(s, 10) + 1 - if bbox[2] <= bbox[0] or bbox[3] <= bbox[1]: - msg = "bad PCX image size" - raise SyntaxError(msg) - logger.debug("BBox: %s %s %s %s", *bbox) - - offset = self.fp.tell() + 60 - - # format - version = s[1] - bits = s[3] - planes = s[65] - provided_stride = i16(s, 66) - logger.debug( - "PCX version %s, bits %s, planes %s, stride %s", - version, - bits, - planes, - provided_stride, - ) - - self.info["dpi"] = i16(s, 12), i16(s, 14) - - if bits == 1 and planes == 1: - mode = rawmode = "1" - - elif bits == 1 and planes in (2, 4): - mode = "P" - rawmode = f"P;{planes}L" - self.palette = ImagePalette.raw("RGB", s[16:64]) - - elif version == 5 and bits == 8 and planes == 1: - mode = rawmode = "L" - # FIXME: hey, this doesn't work with the incremental loader !!! - self.fp.seek(-769, io.SEEK_END) - s = self.fp.read(769) - if len(s) == 769 and s[0] == 12: - # check if the palette is linear grayscale - for i in range(256): - if s[i * 3 + 1 : i * 3 + 4] != o8(i) * 3: - mode = rawmode = "P" - break - if mode == "P": - self.palette = ImagePalette.raw("RGB", s[1:]) - - elif version == 5 and bits == 8 and planes == 3: - mode = "RGB" - rawmode = "RGB;L" - - else: - msg = "unknown PCX mode" - raise OSError(msg) - - self._mode = mode - self._size = bbox[2] - bbox[0], bbox[3] - bbox[1] - - # Don't trust the passed in stride. - # Calculate the approximate position for ourselves. - # CVE-2020-35653 - stride = (self._size[0] * bits + 7) // 8 - - # While the specification states that this must be even, - # not all images follow this - if provided_stride != stride: - stride += stride % 2 - - bbox = (0, 0) + self.size - logger.debug("size: %sx%s", *self.size) - - self.tile = [ImageFile._Tile("pcx", bbox, offset, (rawmode, planes * stride))] - - -# -------------------------------------------------------------------- -# save PCX files - - -SAVE = { - # mode: (version, bits, planes, raw mode) - "1": (2, 1, 1, "1"), - "L": (5, 8, 1, "L"), - "P": (5, 8, 1, "P"), - "RGB": (5, 8, 3, "RGB;L"), -} - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - try: - version, bits, planes, rawmode = SAVE[im.mode] - except KeyError as e: - msg = f"Cannot save {im.mode} images as PCX" - raise ValueError(msg) from e - - # bytes per plane - stride = (im.size[0] * bits + 7) // 8 - # stride should be even - stride += stride % 2 - # Stride needs to be kept in sync with the PcxEncode.c version. - # Ideally it should be passed in in the state, but the bytes value - # gets overwritten. - - logger.debug( - "PcxImagePlugin._save: xwidth: %d, bits: %d, stride: %d", - im.size[0], - bits, - stride, - ) - - # under windows, we could determine the current screen size with - # "Image.core.display_mode()[1]", but I think that's overkill... - - screen = im.size - - dpi = 100, 100 - - # PCX header - fp.write( - o8(10) - + o8(version) - + o8(1) - + o8(bits) - + o16(0) - + o16(0) - + o16(im.size[0] - 1) - + o16(im.size[1] - 1) - + o16(dpi[0]) - + o16(dpi[1]) - + b"\0" * 24 - + b"\xff" * 24 - + b"\0" - + o8(planes) - + o16(stride) - + o16(1) - + o16(screen[0]) - + o16(screen[1]) - + b"\0" * 54 - ) - - assert fp.tell() == 128 - - ImageFile._save( - im, fp, [ImageFile._Tile("pcx", (0, 0) + im.size, 0, (rawmode, bits * planes))] - ) - - if im.mode == "P": - # colour palette - fp.write(o8(12)) - palette = im.im.getpalette("RGB", "RGB") - palette += b"\x00" * (768 - len(palette)) - fp.write(palette) # 768 bytes - elif im.mode == "L": - # grayscale palette - fp.write(o8(12)) - for i in range(256): - fp.write(o8(i) * 3) - - -# -------------------------------------------------------------------- -# registry - - -Image.register_open(PcxImageFile.format, PcxImageFile, _accept) -Image.register_save(PcxImageFile.format, _save) - -Image.register_extension(PcxImageFile.format, ".pcx") - -Image.register_mime(PcxImageFile.format, "image/x-pcx") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/PdfImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/PdfImagePlugin.py deleted file mode 100644 index 5594c7e0..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/PdfImagePlugin.py +++ /dev/null @@ -1,311 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# PDF (Acrobat) file handling -# -# History: -# 1996-07-16 fl Created -# 1997-01-18 fl Fixed header -# 2004-02-21 fl Fixes for 1/L/CMYK images, etc. -# 2004-02-24 fl Fixes for 1 and P images. -# -# Copyright (c) 1997-2004 by Secret Labs AB. All rights reserved. -# Copyright (c) 1996-1997 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# - -## -# Image plugin for PDF images (output only). -## -from __future__ import annotations - -import io -import math -import os -import time -from typing import IO, Any - -from . import Image, ImageFile, ImageSequence, PdfParser, features - -# -# -------------------------------------------------------------------- - -# object ids: -# 1. catalogue -# 2. pages -# 3. image -# 4. page -# 5. page contents - - -def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - _save(im, fp, filename, save_all=True) - - -## -# (Internal) Image save plugin for the PDF format. - - -def _write_image( - im: Image.Image, - filename: str | bytes, - existing_pdf: PdfParser.PdfParser, - image_refs: list[PdfParser.IndirectReference], -) -> tuple[PdfParser.IndirectReference, str]: - # FIXME: Should replace ASCIIHexDecode with RunLengthDecode - # (packbits) or LZWDecode (tiff/lzw compression). Note that - # PDF 1.2 also supports Flatedecode (zip compression). - - params = None - decode = None - - # - # Get image characteristics - - width, height = im.size - - dict_obj: dict[str, Any] = {"BitsPerComponent": 8} - if im.mode == "1": - if features.check("libtiff"): - decode_filter = "CCITTFaxDecode" - dict_obj["BitsPerComponent"] = 1 - params = PdfParser.PdfArray( - [ - PdfParser.PdfDict( - { - "K": -1, - "BlackIs1": True, - "Columns": width, - "Rows": height, - } - ) - ] - ) - else: - decode_filter = "DCTDecode" - dict_obj["ColorSpace"] = PdfParser.PdfName("DeviceGray") - procset = "ImageB" # grayscale - elif im.mode == "L": - decode_filter = "DCTDecode" - # params = f"<< /Predictor 15 /Columns {width-2} >>" - dict_obj["ColorSpace"] = PdfParser.PdfName("DeviceGray") - procset = "ImageB" # grayscale - elif im.mode == "LA": - decode_filter = "JPXDecode" - # params = f"<< /Predictor 15 /Columns {width-2} >>" - procset = "ImageB" # grayscale - dict_obj["SMaskInData"] = 1 - elif im.mode == "P": - decode_filter = "ASCIIHexDecode" - palette = im.getpalette() - assert palette is not None - dict_obj["ColorSpace"] = [ - PdfParser.PdfName("Indexed"), - PdfParser.PdfName("DeviceRGB"), - len(palette) // 3 - 1, - PdfParser.PdfBinary(palette), - ] - procset = "ImageI" # indexed color - - if "transparency" in im.info: - smask = im.convert("LA").getchannel("A") - smask.encoderinfo = {} - - image_ref = _write_image(smask, filename, existing_pdf, image_refs)[0] - dict_obj["SMask"] = image_ref - elif im.mode == "RGB": - decode_filter = "DCTDecode" - dict_obj["ColorSpace"] = PdfParser.PdfName("DeviceRGB") - procset = "ImageC" # color images - elif im.mode == "RGBA": - decode_filter = "JPXDecode" - procset = "ImageC" # color images - dict_obj["SMaskInData"] = 1 - elif im.mode == "CMYK": - decode_filter = "DCTDecode" - dict_obj["ColorSpace"] = PdfParser.PdfName("DeviceCMYK") - procset = "ImageC" # color images - decode = [1, 0, 1, 0, 1, 0, 1, 0] - else: - msg = f"cannot save mode {im.mode}" - raise ValueError(msg) - - # - # image - - op = io.BytesIO() - - if decode_filter == "ASCIIHexDecode": - ImageFile._save(im, op, [ImageFile._Tile("hex", (0, 0) + im.size, 0, im.mode)]) - elif decode_filter == "CCITTFaxDecode": - im.save( - op, - "TIFF", - compression="group4", - # use a single strip - strip_size=math.ceil(width / 8) * height, - ) - elif decode_filter == "DCTDecode": - Image.SAVE["JPEG"](im, op, filename) - elif decode_filter == "JPXDecode": - del dict_obj["BitsPerComponent"] - Image.SAVE["JPEG2000"](im, op, filename) - else: - msg = f"unsupported PDF filter ({decode_filter})" - raise ValueError(msg) - - stream = op.getvalue() - filter: PdfParser.PdfArray | PdfParser.PdfName - if decode_filter == "CCITTFaxDecode": - stream = stream[8:] - filter = PdfParser.PdfArray([PdfParser.PdfName(decode_filter)]) - else: - filter = PdfParser.PdfName(decode_filter) - - image_ref = image_refs.pop(0) - existing_pdf.write_obj( - image_ref, - stream=stream, - Type=PdfParser.PdfName("XObject"), - Subtype=PdfParser.PdfName("Image"), - Width=width, # * 72.0 / x_resolution, - Height=height, # * 72.0 / y_resolution, - Filter=filter, - Decode=decode, - DecodeParms=params, - **dict_obj, - ) - - return image_ref, procset - - -def _save( - im: Image.Image, fp: IO[bytes], filename: str | bytes, save_all: bool = False -) -> None: - is_appending = im.encoderinfo.get("append", False) - filename_str = filename.decode() if isinstance(filename, bytes) else filename - if is_appending: - existing_pdf = PdfParser.PdfParser(f=fp, filename=filename_str, mode="r+b") - else: - existing_pdf = PdfParser.PdfParser(f=fp, filename=filename_str, mode="w+b") - - dpi = im.encoderinfo.get("dpi") - if dpi: - x_resolution = dpi[0] - y_resolution = dpi[1] - else: - x_resolution = y_resolution = im.encoderinfo.get("resolution", 72.0) - - info = { - "title": ( - None if is_appending else os.path.splitext(os.path.basename(filename))[0] - ), - "author": None, - "subject": None, - "keywords": None, - "creator": None, - "producer": None, - "creationDate": None if is_appending else time.gmtime(), - "modDate": None if is_appending else time.gmtime(), - } - for k, default in info.items(): - v = im.encoderinfo.get(k) if k in im.encoderinfo else default - if v: - existing_pdf.info[k[0].upper() + k[1:]] = v - - # - # make sure image data is available - im.load() - - existing_pdf.start_writing() - existing_pdf.write_header() - existing_pdf.write_comment("created by Pillow PDF driver") - - # - # pages - ims = [im] - if save_all: - append_images = im.encoderinfo.get("append_images", []) - for append_im in append_images: - append_im.encoderinfo = im.encoderinfo.copy() - ims.append(append_im) - number_of_pages = 0 - image_refs = [] - page_refs = [] - contents_refs = [] - for im in ims: - im_number_of_pages = 1 - if save_all: - im_number_of_pages = getattr(im, "n_frames", 1) - number_of_pages += im_number_of_pages - for i in range(im_number_of_pages): - image_refs.append(existing_pdf.next_object_id(0)) - if im.mode == "P" and "transparency" in im.info: - image_refs.append(existing_pdf.next_object_id(0)) - - page_refs.append(existing_pdf.next_object_id(0)) - contents_refs.append(existing_pdf.next_object_id(0)) - existing_pdf.pages.append(page_refs[-1]) - - # - # catalog and list of pages - existing_pdf.write_catalog() - - page_number = 0 - for im_sequence in ims: - im_pages: ImageSequence.Iterator | list[Image.Image] = ( - ImageSequence.Iterator(im_sequence) if save_all else [im_sequence] - ) - for im in im_pages: - image_ref, procset = _write_image(im, filename, existing_pdf, image_refs) - - # - # page - - existing_pdf.write_page( - page_refs[page_number], - Resources=PdfParser.PdfDict( - ProcSet=[PdfParser.PdfName("PDF"), PdfParser.PdfName(procset)], - XObject=PdfParser.PdfDict(image=image_ref), - ), - MediaBox=[ - 0, - 0, - im.width * 72.0 / x_resolution, - im.height * 72.0 / y_resolution, - ], - Contents=contents_refs[page_number], - ) - - # - # page contents - - page_contents = b"q %f 0 0 %f 0 0 cm /image Do Q\n" % ( - im.width * 72.0 / x_resolution, - im.height * 72.0 / y_resolution, - ) - - existing_pdf.write_obj(contents_refs[page_number], stream=page_contents) - - page_number += 1 - - # - # trailer - existing_pdf.write_xref_and_trailer() - if hasattr(fp, "flush"): - fp.flush() - existing_pdf.close() - - -# -# -------------------------------------------------------------------- - - -Image.register_save("PDF", _save) -Image.register_save_all("PDF", _save_all) - -Image.register_extension("PDF", ".pdf") - -Image.register_mime("PDF", "application/pdf") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/PdfParser.py b/.venv-docs/lib/python3.12/site-packages/PIL/PdfParser.py deleted file mode 100644 index 2c903146..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/PdfParser.py +++ /dev/null @@ -1,1075 +0,0 @@ -from __future__ import annotations - -import calendar -import codecs -import collections -import mmap -import os -import re -import time -import zlib -from typing import Any, NamedTuple - -TYPE_CHECKING = False -if TYPE_CHECKING: - from typing import IO - - _DictBase = collections.UserDict[str | bytes, Any] -else: - _DictBase = collections.UserDict - - -# see 7.9.2.2 Text String Type on page 86 and D.3 PDFDocEncoding Character Set -# on page 656 -def encode_text(s: str) -> bytes: - return codecs.BOM_UTF16_BE + s.encode("utf_16_be") - - -PDFDocEncoding = { - 0x16: "\u0017", - 0x18: "\u02d8", - 0x19: "\u02c7", - 0x1A: "\u02c6", - 0x1B: "\u02d9", - 0x1C: "\u02dd", - 0x1D: "\u02db", - 0x1E: "\u02da", - 0x1F: "\u02dc", - 0x80: "\u2022", - 0x81: "\u2020", - 0x82: "\u2021", - 0x83: "\u2026", - 0x84: "\u2014", - 0x85: "\u2013", - 0x86: "\u0192", - 0x87: "\u2044", - 0x88: "\u2039", - 0x89: "\u203a", - 0x8A: "\u2212", - 0x8B: "\u2030", - 0x8C: "\u201e", - 0x8D: "\u201c", - 0x8E: "\u201d", - 0x8F: "\u2018", - 0x90: "\u2019", - 0x91: "\u201a", - 0x92: "\u2122", - 0x93: "\ufb01", - 0x94: "\ufb02", - 0x95: "\u0141", - 0x96: "\u0152", - 0x97: "\u0160", - 0x98: "\u0178", - 0x99: "\u017d", - 0x9A: "\u0131", - 0x9B: "\u0142", - 0x9C: "\u0153", - 0x9D: "\u0161", - 0x9E: "\u017e", - 0xA0: "\u20ac", -} - - -def decode_text(b: bytes) -> str: - if b[: len(codecs.BOM_UTF16_BE)] == codecs.BOM_UTF16_BE: - return b[len(codecs.BOM_UTF16_BE) :].decode("utf_16_be") - else: - return "".join(PDFDocEncoding.get(byte, chr(byte)) for byte in b) - - -class PdfFormatError(RuntimeError): - """An error that probably indicates a syntactic or semantic error in the - PDF file structure""" - - pass - - -def check_format_condition(condition: bool, error_message: str) -> None: - if not condition: - raise PdfFormatError(error_message) - - -class IndirectReferenceTuple(NamedTuple): - object_id: int - generation: int - - -class IndirectReference(IndirectReferenceTuple): - def __str__(self) -> str: - return f"{self.object_id} {self.generation} R" - - def __bytes__(self) -> bytes: - return self.__str__().encode("us-ascii") - - def __eq__(self, other: object) -> bool: - if self.__class__ is not other.__class__: - return False - assert isinstance(other, IndirectReference) - return other.object_id == self.object_id and other.generation == self.generation - - def __ne__(self, other: object) -> bool: - return not (self == other) - - def __hash__(self) -> int: - return hash((self.object_id, self.generation)) - - -class IndirectObjectDef(IndirectReference): - def __str__(self) -> str: - return f"{self.object_id} {self.generation} obj" - - -class XrefTable: - def __init__(self) -> None: - self.existing_entries: dict[int, tuple[int, int]] = ( - {} - ) # object ID => (offset, generation) - self.new_entries: dict[int, tuple[int, int]] = ( - {} - ) # object ID => (offset, generation) - self.deleted_entries = {0: 65536} # object ID => generation - self.reading_finished = False - - def __setitem__(self, key: int, value: tuple[int, int]) -> None: - if self.reading_finished: - self.new_entries[key] = value - else: - self.existing_entries[key] = value - if key in self.deleted_entries: - del self.deleted_entries[key] - - def __getitem__(self, key: int) -> tuple[int, int]: - try: - return self.new_entries[key] - except KeyError: - return self.existing_entries[key] - - def __delitem__(self, key: int) -> None: - if key in self.new_entries: - generation = self.new_entries[key][1] + 1 - del self.new_entries[key] - self.deleted_entries[key] = generation - elif key in self.existing_entries: - generation = self.existing_entries[key][1] + 1 - self.deleted_entries[key] = generation - elif key in self.deleted_entries: - generation = self.deleted_entries[key] - else: - msg = f"object ID {key} cannot be deleted because it doesn't exist" - raise IndexError(msg) - - def __contains__(self, key: int) -> bool: - return key in self.existing_entries or key in self.new_entries - - def __len__(self) -> int: - return len( - set(self.existing_entries.keys()) - | set(self.new_entries.keys()) - | set(self.deleted_entries.keys()) - ) - - def keys(self) -> set[int]: - return ( - set(self.existing_entries.keys()) - set(self.deleted_entries.keys()) - ) | set(self.new_entries.keys()) - - def write(self, f: IO[bytes]) -> int: - keys = sorted(set(self.new_entries.keys()) | set(self.deleted_entries.keys())) - deleted_keys = sorted(set(self.deleted_entries.keys())) - startxref = f.tell() - f.write(b"xref\n") - while keys: - # find a contiguous sequence of object IDs - prev: int | None = None - for index, key in enumerate(keys): - if prev is None or prev + 1 == key: - prev = key - else: - contiguous_keys = keys[:index] - keys = keys[index:] - break - else: - contiguous_keys = keys - keys = [] - f.write(b"%d %d\n" % (contiguous_keys[0], len(contiguous_keys))) - for object_id in contiguous_keys: - if object_id in self.new_entries: - f.write(b"%010d %05d n \n" % self.new_entries[object_id]) - else: - this_deleted_object_id = deleted_keys.pop(0) - check_format_condition( - object_id == this_deleted_object_id, - f"expected the next deleted object ID to be {object_id}, " - f"instead found {this_deleted_object_id}", - ) - try: - next_in_linked_list = deleted_keys[0] - except IndexError: - next_in_linked_list = 0 - f.write( - b"%010d %05d f \n" - % (next_in_linked_list, self.deleted_entries[object_id]) - ) - return startxref - - -class PdfName: - name: bytes - - def __init__(self, name: PdfName | bytes | str) -> None: - if isinstance(name, PdfName): - self.name = name.name - elif isinstance(name, bytes): - self.name = name - else: - self.name = name.encode("us-ascii") - - def name_as_str(self) -> str: - return self.name.decode("us-ascii") - - def __eq__(self, other: object) -> bool: - return ( - isinstance(other, PdfName) and other.name == self.name - ) or other == self.name - - def __hash__(self) -> int: - return hash(self.name) - - def __repr__(self) -> str: - return f"{self.__class__.__name__}({repr(self.name)})" - - @classmethod - def from_pdf_stream(cls, data: bytes) -> PdfName: - return cls(PdfParser.interpret_name(data)) - - allowed_chars = set(range(33, 127)) - {ord(c) for c in "#%/()<>[]{}"} - - def __bytes__(self) -> bytes: - result = bytearray(b"/") - for b in self.name: - if b in self.allowed_chars: - result.append(b) - else: - result.extend(b"#%02X" % b) - return bytes(result) - - -class PdfArray(list[Any]): - def __bytes__(self) -> bytes: - return b"[ " + b" ".join(pdf_repr(x) for x in self) + b" ]" - - -class PdfDict(_DictBase): - def __setattr__(self, key: str, value: Any) -> None: - if key == "data": - collections.UserDict.__setattr__(self, key, value) - else: - self[key.encode("us-ascii")] = value - - def __getattr__(self, key: str) -> str | time.struct_time: - try: - value = self[key.encode("us-ascii")] - except KeyError as e: - raise AttributeError(key) from e - if isinstance(value, bytes): - value = decode_text(value) - if key.endswith("Date"): - if value.startswith("D:"): - value = value[2:] - - relationship = "Z" - if len(value) > 17: - relationship = value[14] - offset = int(value[15:17]) * 60 - if len(value) > 20: - offset += int(value[18:20]) - - format = "%Y%m%d%H%M%S"[: len(value) - 2] - value = time.strptime(value[: len(format) + 2], format) - if relationship in ["+", "-"]: - offset *= 60 - if relationship == "+": - offset *= -1 - value = time.gmtime(calendar.timegm(value) + offset) - return value - - def __bytes__(self) -> bytes: - out = bytearray(b"<<") - for key, value in self.items(): - if value is None: - continue - value = pdf_repr(value) - out.extend(b"\n") - out.extend(bytes(PdfName(key))) - out.extend(b" ") - out.extend(value) - out.extend(b"\n>>") - return bytes(out) - - -class PdfBinary: - def __init__(self, data: list[int] | bytes) -> None: - self.data = data - - def __bytes__(self) -> bytes: - return b"<%s>" % b"".join(b"%02X" % b for b in self.data) - - -class PdfStream: - def __init__(self, dictionary: PdfDict, buf: bytes) -> None: - self.dictionary = dictionary - self.buf = buf - - def decode(self) -> bytes: - try: - filter = self.dictionary[b"Filter"] - except KeyError: - return self.buf - if filter == b"FlateDecode": - try: - expected_length = self.dictionary[b"DL"] - except KeyError: - expected_length = self.dictionary[b"Length"] - return zlib.decompress(self.buf, bufsize=int(expected_length)) - else: - msg = f"stream filter {repr(filter)} unknown/unsupported" - raise NotImplementedError(msg) - - -def pdf_repr(x: Any) -> bytes: - if x is True: - return b"true" - elif x is False: - return b"false" - elif x is None: - return b"null" - elif isinstance(x, (PdfName, PdfDict, PdfArray, PdfBinary)): - return bytes(x) - elif isinstance(x, (int, float)): - return str(x).encode("us-ascii") - elif isinstance(x, time.struct_time): - return b"(D:" + time.strftime("%Y%m%d%H%M%SZ", x).encode("us-ascii") + b")" - elif isinstance(x, dict): - return bytes(PdfDict(x)) - elif isinstance(x, list): - return bytes(PdfArray(x)) - elif isinstance(x, str): - return pdf_repr(encode_text(x)) - elif isinstance(x, bytes): - # XXX escape more chars? handle binary garbage - x = x.replace(b"\\", b"\\\\") - x = x.replace(b"(", b"\\(") - x = x.replace(b")", b"\\)") - return b"(" + x + b")" - else: - return bytes(x) - - -class PdfParser: - """Based on - https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/PDF32000_2008.pdf - Supports PDF up to 1.4 - """ - - def __init__( - self, - filename: str | None = None, - f: IO[bytes] | None = None, - buf: bytes | bytearray | None = None, - start_offset: int = 0, - mode: str = "rb", - ) -> None: - if buf and f: - msg = "specify buf or f or filename, but not both buf and f" - raise RuntimeError(msg) - self.filename = filename - self.buf: bytes | bytearray | mmap.mmap | None = buf - self.f = f - self.start_offset = start_offset - self.should_close_buf = False - self.should_close_file = False - if filename is not None and f is None: - self.f = f = open(filename, mode) - self.should_close_file = True - if f is not None: - self.buf = self.get_buf_from_file(f) - self.should_close_buf = True - if not filename and hasattr(f, "name"): - self.filename = f.name - self.cached_objects: dict[IndirectReference, Any] = {} - self.root_ref: IndirectReference | None - self.info_ref: IndirectReference | None - self.pages_ref: IndirectReference | None - self.last_xref_section_offset: int | None - if self.buf: - self.read_pdf_info() - else: - self.file_size_total = self.file_size_this = 0 - self.root = PdfDict() - self.root_ref = None - self.info = PdfDict() - self.info_ref = None - self.page_tree_root = PdfDict() - self.pages: list[IndirectReference] = [] - self.orig_pages: list[IndirectReference] = [] - self.pages_ref = None - self.last_xref_section_offset = None - self.trailer_dict: dict[bytes, Any] = {} - self.xref_table = XrefTable() - self.xref_table.reading_finished = True - if f: - self.seek_end() - - def __enter__(self) -> PdfParser: - return self - - def __exit__(self, *args: object) -> None: - self.close() - - def start_writing(self) -> None: - self.close_buf() - self.seek_end() - - def close_buf(self) -> None: - if isinstance(self.buf, mmap.mmap): - self.buf.close() - self.buf = None - - def close(self) -> None: - if self.should_close_buf: - self.close_buf() - if self.f is not None and self.should_close_file: - self.f.close() - self.f = None - - def seek_end(self) -> None: - assert self.f is not None - self.f.seek(0, os.SEEK_END) - - def write_header(self) -> None: - assert self.f is not None - self.f.write(b"%PDF-1.4\n") - - def write_comment(self, s: str) -> None: - assert self.f is not None - self.f.write(f"% {s}\n".encode()) - - def write_catalog(self) -> IndirectReference: - assert self.f is not None - self.del_root() - self.root_ref = self.next_object_id(self.f.tell()) - self.pages_ref = self.next_object_id(0) - self.rewrite_pages() - self.write_obj(self.root_ref, Type=PdfName(b"Catalog"), Pages=self.pages_ref) - self.write_obj( - self.pages_ref, - Type=PdfName(b"Pages"), - Count=len(self.pages), - Kids=self.pages, - ) - return self.root_ref - - def rewrite_pages(self) -> None: - pages_tree_nodes_to_delete = [] - for i, page_ref in enumerate(self.orig_pages): - page_info = self.cached_objects[page_ref] - del self.xref_table[page_ref.object_id] - pages_tree_nodes_to_delete.append(page_info[PdfName(b"Parent")]) - if page_ref not in self.pages: - # the page has been deleted - continue - # make dict keys into strings for passing to write_page - stringified_page_info = {} - for key, value in page_info.items(): - # key should be a PdfName - stringified_page_info[key.name_as_str()] = value - stringified_page_info["Parent"] = self.pages_ref - new_page_ref = self.write_page(None, **stringified_page_info) - for j, cur_page_ref in enumerate(self.pages): - if cur_page_ref == page_ref: - # replace the page reference with the new one - self.pages[j] = new_page_ref - # delete redundant Pages tree nodes from xref table - for pages_tree_node_ref in pages_tree_nodes_to_delete: - while pages_tree_node_ref: - pages_tree_node = self.cached_objects[pages_tree_node_ref] - if pages_tree_node_ref.object_id in self.xref_table: - del self.xref_table[pages_tree_node_ref.object_id] - pages_tree_node_ref = pages_tree_node.get(b"Parent", None) - self.orig_pages = [] - - def write_xref_and_trailer( - self, new_root_ref: IndirectReference | None = None - ) -> None: - assert self.f is not None - if new_root_ref: - self.del_root() - self.root_ref = new_root_ref - if self.info: - self.info_ref = self.write_obj(None, self.info) - start_xref = self.xref_table.write(self.f) - num_entries = len(self.xref_table) - trailer_dict: dict[str | bytes, Any] = { - b"Root": self.root_ref, - b"Size": num_entries, - } - if self.last_xref_section_offset is not None: - trailer_dict[b"Prev"] = self.last_xref_section_offset - if self.info: - trailer_dict[b"Info"] = self.info_ref - self.last_xref_section_offset = start_xref - self.f.write( - b"trailer\n" - + bytes(PdfDict(trailer_dict)) - + b"\nstartxref\n%d\n%%%%EOF" % start_xref - ) - - def write_page( - self, ref: int | IndirectReference | None, *objs: Any, **dict_obj: Any - ) -> IndirectReference: - obj_ref = self.pages[ref] if isinstance(ref, int) else ref - if "Type" not in dict_obj: - dict_obj["Type"] = PdfName(b"Page") - if "Parent" not in dict_obj: - dict_obj["Parent"] = self.pages_ref - return self.write_obj(obj_ref, *objs, **dict_obj) - - def write_obj( - self, ref: IndirectReference | None, *objs: Any, **dict_obj: Any - ) -> IndirectReference: - assert self.f is not None - f = self.f - if ref is None: - ref = self.next_object_id(f.tell()) - else: - self.xref_table[ref.object_id] = (f.tell(), ref.generation) - f.write(bytes(IndirectObjectDef(*ref))) - stream = dict_obj.pop("stream", None) - if stream is not None: - dict_obj["Length"] = len(stream) - if dict_obj: - f.write(pdf_repr(dict_obj)) - for obj in objs: - f.write(pdf_repr(obj)) - if stream is not None: - f.write(b"stream\n") - f.write(stream) - f.write(b"\nendstream\n") - f.write(b"endobj\n") - return ref - - def del_root(self) -> None: - if self.root_ref is None: - return - del self.xref_table[self.root_ref.object_id] - del self.xref_table[self.root[b"Pages"].object_id] - - @staticmethod - def get_buf_from_file(f: IO[bytes]) -> bytes | mmap.mmap: - if hasattr(f, "getbuffer"): - return f.getbuffer() - elif hasattr(f, "getvalue"): - return f.getvalue() - else: - try: - return mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) - except ValueError: # cannot mmap an empty file - return b"" - - def read_pdf_info(self) -> None: - assert self.buf is not None - self.file_size_total = len(self.buf) - self.file_size_this = self.file_size_total - self.start_offset - self.read_trailer() - check_format_condition( - self.trailer_dict.get(b"Root") is not None, "Root is missing" - ) - self.root_ref = self.trailer_dict[b"Root"] - assert self.root_ref is not None - self.info_ref = self.trailer_dict.get(b"Info", None) - self.root = PdfDict(self.read_indirect(self.root_ref)) - if self.info_ref is None: - self.info = PdfDict() - else: - self.info = PdfDict(self.read_indirect(self.info_ref)) - check_format_condition(b"Type" in self.root, "/Type missing in Root") - check_format_condition( - self.root[b"Type"] == b"Catalog", "/Type in Root is not /Catalog" - ) - check_format_condition( - self.root.get(b"Pages") is not None, "/Pages missing in Root" - ) - check_format_condition( - isinstance(self.root[b"Pages"], IndirectReference), - "/Pages in Root is not an indirect reference", - ) - self.pages_ref = self.root[b"Pages"] - assert self.pages_ref is not None - self.page_tree_root = self.read_indirect(self.pages_ref) - self.pages = self.linearize_page_tree(self.page_tree_root) - # save the original list of page references - # in case the user modifies, adds or deletes some pages - # and we need to rewrite the pages and their list - self.orig_pages = self.pages[:] - - def next_object_id(self, offset: int | None = None) -> IndirectReference: - try: - # TODO: support reuse of deleted objects - reference = IndirectReference(max(self.xref_table.keys()) + 1, 0) - except ValueError: - reference = IndirectReference(1, 0) - if offset is not None: - self.xref_table[reference.object_id] = (offset, 0) - return reference - - delimiter = rb"[][()<>{}/%]" - delimiter_or_ws = rb"[][()<>{}/%\000\011\012\014\015\040]" - whitespace = rb"[\000\011\012\014\015\040]" - whitespace_or_hex = rb"[\000\011\012\014\015\0400-9a-fA-F]" - whitespace_optional = whitespace + b"*" - whitespace_mandatory = whitespace + b"+" - # No "\012" aka "\n" or "\015" aka "\r": - whitespace_optional_no_nl = rb"[\000\011\014\040]*" - newline_only = rb"[\r\n]+" - newline = whitespace_optional_no_nl + newline_only + whitespace_optional_no_nl - re_trailer_end = re.compile( - whitespace_mandatory - + rb"trailer" - + whitespace_optional - + rb"<<(.*>>)" - + newline - + rb"startxref" - + newline - + rb"([0-9]+)" - + newline - + rb"%%EOF" - + whitespace_optional - + rb"$", - re.DOTALL, - ) - re_trailer_prev = re.compile( - whitespace_optional - + rb"trailer" - + whitespace_optional - + rb"<<(.*?>>)" - + newline - + rb"startxref" - + newline - + rb"([0-9]+)" - + newline - + rb"%%EOF" - + whitespace_optional, - re.DOTALL, - ) - - def read_trailer(self) -> None: - assert self.buf is not None - search_start_offset = len(self.buf) - 16384 - if search_start_offset < self.start_offset: - search_start_offset = self.start_offset - m = self.re_trailer_end.search(self.buf, search_start_offset) - check_format_condition(m is not None, "trailer end not found") - # make sure we found the LAST trailer - last_match = m - while m: - last_match = m - m = self.re_trailer_end.search(self.buf, m.start() + 16) - if not m: - m = last_match - assert m is not None - trailer_data = m.group(1) - self.last_xref_section_offset = int(m.group(2)) - self.trailer_dict = self.interpret_trailer(trailer_data) - self.xref_table = XrefTable() - self.read_xref_table(xref_section_offset=self.last_xref_section_offset) - if b"Prev" in self.trailer_dict: - self.read_prev_trailer(self.trailer_dict[b"Prev"]) - - def read_prev_trailer(self, xref_section_offset: int) -> None: - assert self.buf is not None - trailer_offset = self.read_xref_table(xref_section_offset=xref_section_offset) - m = self.re_trailer_prev.search( - self.buf[trailer_offset : trailer_offset + 16384] - ) - check_format_condition(m is not None, "previous trailer not found") - assert m is not None - trailer_data = m.group(1) - check_format_condition( - int(m.group(2)) == xref_section_offset, - "xref section offset in previous trailer doesn't match what was expected", - ) - trailer_dict = self.interpret_trailer(trailer_data) - if b"Prev" in trailer_dict: - self.read_prev_trailer(trailer_dict[b"Prev"]) - - re_whitespace_optional = re.compile(whitespace_optional) - re_name = re.compile( - whitespace_optional - + rb"/([!-$&'*-.0-;=?-Z\\^-z|~]+)(?=" - + delimiter_or_ws - + rb")" - ) - re_dict_start = re.compile(whitespace_optional + rb"<<") - re_dict_end = re.compile(whitespace_optional + rb">>" + whitespace_optional) - - @classmethod - def interpret_trailer(cls, trailer_data: bytes) -> dict[bytes, Any]: - trailer = {} - offset = 0 - while True: - m = cls.re_name.match(trailer_data, offset) - if not m: - m = cls.re_dict_end.match(trailer_data, offset) - check_format_condition( - m is not None and m.end() == len(trailer_data), - "name not found in trailer, remaining data: " - + repr(trailer_data[offset:]), - ) - break - key = cls.interpret_name(m.group(1)) - assert isinstance(key, bytes) - value, value_offset = cls.get_value(trailer_data, m.end()) - trailer[key] = value - if value_offset is None: - break - offset = value_offset - check_format_condition( - b"Size" in trailer and isinstance(trailer[b"Size"], int), - "/Size not in trailer or not an integer", - ) - check_format_condition( - b"Root" in trailer and isinstance(trailer[b"Root"], IndirectReference), - "/Root not in trailer or not an indirect reference", - ) - return trailer - - re_hashes_in_name = re.compile(rb"([^#]*)(#([0-9a-fA-F]{2}))?") - - @classmethod - def interpret_name(cls, raw: bytes, as_text: bool = False) -> str | bytes: - name = b"" - for m in cls.re_hashes_in_name.finditer(raw): - if m.group(3): - name += m.group(1) + bytearray.fromhex(m.group(3).decode("us-ascii")) - else: - name += m.group(1) - if as_text: - return name.decode("utf-8") - else: - return bytes(name) - - re_null = re.compile(whitespace_optional + rb"null(?=" + delimiter_or_ws + rb")") - re_true = re.compile(whitespace_optional + rb"true(?=" + delimiter_or_ws + rb")") - re_false = re.compile(whitespace_optional + rb"false(?=" + delimiter_or_ws + rb")") - re_int = re.compile( - whitespace_optional + rb"([-+]?[0-9]+)(?=" + delimiter_or_ws + rb")" - ) - re_real = re.compile( - whitespace_optional - + rb"([-+]?([0-9]+\.[0-9]*|[0-9]*\.[0-9]+))(?=" - + delimiter_or_ws - + rb")" - ) - re_array_start = re.compile(whitespace_optional + rb"\[") - re_array_end = re.compile(whitespace_optional + rb"]") - re_string_hex = re.compile( - whitespace_optional + rb"<(" + whitespace_or_hex + rb"*)>" - ) - re_string_lit = re.compile(whitespace_optional + rb"\(") - re_indirect_reference = re.compile( - whitespace_optional - + rb"([-+]?[0-9]+)" - + whitespace_mandatory - + rb"([-+]?[0-9]+)" - + whitespace_mandatory - + rb"R(?=" - + delimiter_or_ws - + rb")" - ) - re_indirect_def_start = re.compile( - whitespace_optional - + rb"([-+]?[0-9]+)" - + whitespace_mandatory - + rb"([-+]?[0-9]+)" - + whitespace_mandatory - + rb"obj(?=" - + delimiter_or_ws - + rb")" - ) - re_indirect_def_end = re.compile( - whitespace_optional + rb"endobj(?=" + delimiter_or_ws + rb")" - ) - re_comment = re.compile( - rb"(" + whitespace_optional + rb"%[^\r\n]*" + newline + rb")*" - ) - re_stream_start = re.compile(whitespace_optional + rb"stream\r?\n") - re_stream_end = re.compile( - whitespace_optional + rb"endstream(?=" + delimiter_or_ws + rb")" - ) - - @classmethod - def get_value( - cls, - data: bytes | bytearray | mmap.mmap, - offset: int, - expect_indirect: IndirectReference | None = None, - max_nesting: int = -1, - ) -> tuple[Any, int | None]: - if max_nesting == 0: - return None, None - m = cls.re_comment.match(data, offset) - if m: - offset = m.end() - m = cls.re_indirect_def_start.match(data, offset) - if m: - check_format_condition( - int(m.group(1)) > 0, - "indirect object definition: object ID must be greater than 0", - ) - check_format_condition( - int(m.group(2)) >= 0, - "indirect object definition: generation must be non-negative", - ) - check_format_condition( - expect_indirect is None - or expect_indirect - == IndirectReference(int(m.group(1)), int(m.group(2))), - "indirect object definition different than expected", - ) - object, object_offset = cls.get_value( - data, m.end(), max_nesting=max_nesting - 1 - ) - if object_offset is None: - return object, None - m = cls.re_indirect_def_end.match(data, object_offset) - check_format_condition( - m is not None, "indirect object definition end not found" - ) - assert m is not None - return object, m.end() - check_format_condition( - not expect_indirect, "indirect object definition not found" - ) - m = cls.re_indirect_reference.match(data, offset) - if m: - check_format_condition( - int(m.group(1)) > 0, - "indirect object reference: object ID must be greater than 0", - ) - check_format_condition( - int(m.group(2)) >= 0, - "indirect object reference: generation must be non-negative", - ) - return IndirectReference(int(m.group(1)), int(m.group(2))), m.end() - m = cls.re_dict_start.match(data, offset) - if m: - offset = m.end() - result: dict[Any, Any] = {} - m = cls.re_dict_end.match(data, offset) - current_offset: int | None = offset - while not m: - assert current_offset is not None - key, current_offset = cls.get_value( - data, current_offset, max_nesting=max_nesting - 1 - ) - if current_offset is None: - return result, None - value, current_offset = cls.get_value( - data, current_offset, max_nesting=max_nesting - 1 - ) - result[key] = value - if current_offset is None: - return result, None - m = cls.re_dict_end.match(data, current_offset) - current_offset = m.end() - m = cls.re_stream_start.match(data, current_offset) - if m: - stream_len = result.get(b"Length") - if stream_len is None or not isinstance(stream_len, int): - msg = f"bad or missing Length in stream dict ({stream_len})" - raise PdfFormatError(msg) - stream_data = data[m.end() : m.end() + stream_len] - m = cls.re_stream_end.match(data, m.end() + stream_len) - check_format_condition(m is not None, "stream end not found") - assert m is not None - current_offset = m.end() - return PdfStream(PdfDict(result), stream_data), current_offset - return PdfDict(result), current_offset - m = cls.re_array_start.match(data, offset) - if m: - offset = m.end() - results = [] - m = cls.re_array_end.match(data, offset) - current_offset = offset - while not m: - assert current_offset is not None - value, current_offset = cls.get_value( - data, current_offset, max_nesting=max_nesting - 1 - ) - results.append(value) - if current_offset is None: - return results, None - m = cls.re_array_end.match(data, current_offset) - return results, m.end() - m = cls.re_null.match(data, offset) - if m: - return None, m.end() - m = cls.re_true.match(data, offset) - if m: - return True, m.end() - m = cls.re_false.match(data, offset) - if m: - return False, m.end() - m = cls.re_name.match(data, offset) - if m: - return PdfName(cls.interpret_name(m.group(1))), m.end() - m = cls.re_int.match(data, offset) - if m: - return int(m.group(1)), m.end() - m = cls.re_real.match(data, offset) - if m: - # XXX Decimal instead of float??? - return float(m.group(1)), m.end() - m = cls.re_string_hex.match(data, offset) - if m: - # filter out whitespace - hex_string = bytearray( - b for b in m.group(1) if b in b"0123456789abcdefABCDEF" - ) - if len(hex_string) % 2 == 1: - # append a 0 if the length is not even - yes, at the end - hex_string.append(ord(b"0")) - return bytearray.fromhex(hex_string.decode("us-ascii")), m.end() - m = cls.re_string_lit.match(data, offset) - if m: - return cls.get_literal_string(data, m.end()) - # return None, offset # fallback (only for debugging) - msg = f"unrecognized object: {repr(data[offset : offset + 32])}" - raise PdfFormatError(msg) - - re_lit_str_token = re.compile( - rb"(\\[nrtbf()\\])|(\\[0-9]{1,3})|(\\(\r\n|\r|\n))|(\r\n|\r|\n)|(\()|(\))" - ) - escaped_chars = { - b"n": b"\n", - b"r": b"\r", - b"t": b"\t", - b"b": b"\b", - b"f": b"\f", - b"(": b"(", - b")": b")", - b"\\": b"\\", - ord(b"n"): b"\n", - ord(b"r"): b"\r", - ord(b"t"): b"\t", - ord(b"b"): b"\b", - ord(b"f"): b"\f", - ord(b"("): b"(", - ord(b")"): b")", - ord(b"\\"): b"\\", - } - - @classmethod - def get_literal_string( - cls, data: bytes | bytearray | mmap.mmap, offset: int - ) -> tuple[bytes, int]: - nesting_depth = 0 - result = bytearray() - for m in cls.re_lit_str_token.finditer(data, offset): - result.extend(data[offset : m.start()]) - if m.group(1): - result.extend(cls.escaped_chars[m.group(1)[1]]) - elif m.group(2): - result.append(int(m.group(2)[1:], 8)) - elif m.group(3): - pass - elif m.group(5): - result.extend(b"\n") - elif m.group(6): - result.extend(b"(") - nesting_depth += 1 - elif m.group(7): - if nesting_depth == 0: - return bytes(result), m.end() - result.extend(b")") - nesting_depth -= 1 - offset = m.end() - msg = "unfinished literal string" - raise PdfFormatError(msg) - - re_xref_section_start = re.compile(whitespace_optional + rb"xref" + newline) - re_xref_subsection_start = re.compile( - whitespace_optional - + rb"([0-9]+)" - + whitespace_mandatory - + rb"([0-9]+)" - + whitespace_optional - + newline_only - ) - re_xref_entry = re.compile(rb"([0-9]{10}) ([0-9]{5}) ([fn])( \r| \n|\r\n)") - - def read_xref_table(self, xref_section_offset: int) -> int: - assert self.buf is not None - subsection_found = False - m = self.re_xref_section_start.match( - self.buf, xref_section_offset + self.start_offset - ) - check_format_condition(m is not None, "xref section start not found") - assert m is not None - offset = m.end() - while True: - m = self.re_xref_subsection_start.match(self.buf, offset) - if not m: - check_format_condition( - subsection_found, "xref subsection start not found" - ) - break - subsection_found = True - offset = m.end() - first_object = int(m.group(1)) - num_objects = int(m.group(2)) - for i in range(first_object, first_object + num_objects): - m = self.re_xref_entry.match(self.buf, offset) - check_format_condition(m is not None, "xref entry not found") - assert m is not None - offset = m.end() - is_free = m.group(3) == b"f" - if not is_free: - generation = int(m.group(2)) - new_entry = (int(m.group(1)), generation) - if i not in self.xref_table: - self.xref_table[i] = new_entry - return offset - - def read_indirect(self, ref: IndirectReference, max_nesting: int = -1) -> Any: - offset, generation = self.xref_table[ref[0]] - check_format_condition( - generation == ref[1], - f"expected to find generation {ref[1]} for object ID {ref[0]} in xref " - f"table, instead found generation {generation} at offset {offset}", - ) - assert self.buf is not None - value = self.get_value( - self.buf, - offset + self.start_offset, - expect_indirect=IndirectReference(*ref), - max_nesting=max_nesting, - )[0] - self.cached_objects[ref] = value - return value - - def linearize_page_tree( - self, node: PdfDict | None = None - ) -> list[IndirectReference]: - page_node = node if node is not None else self.page_tree_root - check_format_condition( - page_node[b"Type"] == b"Pages", "/Type of page tree node is not /Pages" - ) - pages = [] - for kid in page_node[b"Kids"]: - kid_object = self.read_indirect(kid) - if kid_object[b"Type"] == b"Page": - pages.append(kid) - else: - pages.extend(self.linearize_page_tree(node=kid_object)) - return pages diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/PixarImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/PixarImagePlugin.py deleted file mode 100644 index d2b6d0a9..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/PixarImagePlugin.py +++ /dev/null @@ -1,72 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# PIXAR raster support for PIL -# -# history: -# 97-01-29 fl Created -# -# notes: -# This is incomplete; it is based on a few samples created with -# Photoshop 2.5 and 3.0, and a summary description provided by -# Greg Coats . Hopefully, "L" and -# "RGBA" support will be added in future versions. -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1997. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from . import Image, ImageFile -from ._binary import i16le as i16 - -# -# helpers - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(b"\200\350\000\000") - - -## -# Image plugin for PIXAR raster images. - - -class PixarImageFile(ImageFile.ImageFile): - format = "PIXAR" - format_description = "PIXAR raster image" - - def _open(self) -> None: - # assuming a 4-byte magic label - assert self.fp is not None - - s = self.fp.read(4) - if not _accept(s): - msg = "not a PIXAR file" - raise SyntaxError(msg) - - # read rest of header - s = s + self.fp.read(508) - - self._size = i16(s, 418), i16(s, 416) - - # get channel/depth descriptions - mode = i16(s, 424), i16(s, 426) - - if mode == (14, 2): - self._mode = "RGB" - # FIXME: to be continued... - - # create tile descriptor (assuming "dumped") - self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 1024, self.mode)] - - -# -# -------------------------------------------------------------------- - -Image.register_open(PixarImageFile.format, PixarImageFile, _accept) - -Image.register_extension(PixarImageFile.format, ".pxr") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/PngImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/PngImagePlugin.py deleted file mode 100644 index d0f22f81..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/PngImagePlugin.py +++ /dev/null @@ -1,1553 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# PNG support code -# -# See "PNG (Portable Network Graphics) Specification, version 1.0; -# W3C Recommendation", 1996-10-01, Thomas Boutell (ed.). -# -# history: -# 1996-05-06 fl Created (couldn't resist it) -# 1996-12-14 fl Upgraded, added read and verify support (0.2) -# 1996-12-15 fl Separate PNG stream parser -# 1996-12-29 fl Added write support, added getchunks -# 1996-12-30 fl Eliminated circular references in decoder (0.3) -# 1998-07-12 fl Read/write 16-bit images as mode I (0.4) -# 2001-02-08 fl Added transparency support (from Zircon) (0.5) -# 2001-04-16 fl Don't close data source in "open" method (0.6) -# 2004-02-24 fl Don't even pretend to support interlaced files (0.7) -# 2004-08-31 fl Do basic sanity check on chunk identifiers (0.8) -# 2004-09-20 fl Added PngInfo chunk container -# 2004-12-18 fl Added DPI read support (based on code by Niki Spahiev) -# 2008-08-13 fl Added tRNS support for RGB images -# 2009-03-06 fl Support for preserving ICC profiles (by Florian Hoech) -# 2009-03-08 fl Added zTXT support (from Lowell Alleman) -# 2009-03-29 fl Read interlaced PNG files (from Conrado Porto Lopes Gouvua) -# -# Copyright (c) 1997-2009 by Secret Labs AB -# Copyright (c) 1996 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import itertools -import logging -import re -import struct -import warnings -import zlib -from enum import IntEnum -from typing import IO, NamedTuple, cast - -from . import Image, ImageChops, ImageFile, ImagePalette, ImageSequence -from ._binary import i16be as i16 -from ._binary import i32be as i32 -from ._binary import o8 -from ._binary import o16be as o16 -from ._binary import o32be as o32 -from ._deprecate import deprecate -from ._util import DeferredError - -TYPE_CHECKING = False -if TYPE_CHECKING: - from collections.abc import Callable - from typing import Any, NoReturn - - from . import _imaging - -logger = logging.getLogger(__name__) - -is_cid = re.compile(rb"\w\w\w\w").match - - -_MAGIC = b"\211PNG\r\n\032\n" - - -_MODES = { - # supported bits/color combinations, and corresponding modes/rawmodes - # Grayscale - (1, 0): ("1", "1"), - (2, 0): ("L", "L;2"), - (4, 0): ("L", "L;4"), - (8, 0): ("L", "L"), - (16, 0): ("I;16", "I;16B"), - # Truecolour - (8, 2): ("RGB", "RGB"), - (16, 2): ("RGB", "RGB;16B"), - # Indexed-colour - (1, 3): ("P", "P;1"), - (2, 3): ("P", "P;2"), - (4, 3): ("P", "P;4"), - (8, 3): ("P", "P"), - # Grayscale with alpha - (8, 4): ("LA", "LA"), - (16, 4): ("RGBA", "LA;16B"), # LA;16B->LA not yet available - # Truecolour with alpha - (8, 6): ("RGBA", "RGBA"), - (16, 6): ("RGBA", "RGBA;16B"), -} - - -_simple_palette = re.compile(b"^\xff*\x00\xff*$") - -MAX_TEXT_CHUNK = ImageFile.SAFEBLOCK -""" -Maximum decompressed size for a iTXt or zTXt chunk. -Eliminates decompression bombs where compressed chunks can expand 1000x. -See :ref:`Text in PNG File Format`. -""" -MAX_TEXT_MEMORY = 64 * MAX_TEXT_CHUNK -""" -Set the maximum total text chunk size. -See :ref:`Text in PNG File Format`. -""" - - -# APNG frame disposal modes -class Disposal(IntEnum): - OP_NONE = 0 - """ - No disposal is done on this frame before rendering the next frame. - See :ref:`Saving APNG sequences`. - """ - OP_BACKGROUND = 1 - """ - This frame’s modified region is cleared to fully transparent black before rendering - the next frame. - See :ref:`Saving APNG sequences`. - """ - OP_PREVIOUS = 2 - """ - This frame’s modified region is reverted to the previous frame’s contents before - rendering the next frame. - See :ref:`Saving APNG sequences`. - """ - - -# APNG frame blend modes -class Blend(IntEnum): - OP_SOURCE = 0 - """ - All color components of this frame, including alpha, overwrite the previous output - image contents. - See :ref:`Saving APNG sequences`. - """ - OP_OVER = 1 - """ - This frame should be alpha composited with the previous output image contents. - See :ref:`Saving APNG sequences`. - """ - - -def _safe_zlib_decompress(s: bytes) -> bytes: - dobj = zlib.decompressobj() - plaintext = dobj.decompress(s, MAX_TEXT_CHUNK) - if dobj.unconsumed_tail: - msg = "Decompressed data too large for PngImagePlugin.MAX_TEXT_CHUNK" - raise ValueError(msg) - return plaintext - - -def _crc32(data: bytes, seed: int = 0) -> int: - return zlib.crc32(data, seed) & 0xFFFFFFFF - - -# -------------------------------------------------------------------- -# Support classes. Suitable for PNG and related formats like MNG etc. - - -class ChunkStream: - def __init__(self, fp: IO[bytes]) -> None: - self.fp: IO[bytes] | None = fp - self.queue: list[tuple[bytes, int, int]] | None = [] - - def read(self) -> tuple[bytes, int, int]: - """Fetch a new chunk. Returns header information.""" - cid = None - - assert self.fp is not None - if self.queue: - cid, pos, length = self.queue.pop() - self.fp.seek(pos) - else: - s = self.fp.read(8) - cid = s[4:] - pos = self.fp.tell() - length = i32(s) - - if not is_cid(cid): - if not ImageFile.LOAD_TRUNCATED_IMAGES: - msg = f"broken PNG file (chunk {repr(cid)})" - raise SyntaxError(msg) - - return cid, pos, length - - def __enter__(self) -> ChunkStream: - return self - - def __exit__(self, *args: object) -> None: - self.close() - - def close(self) -> None: - self.queue = self.fp = None - - def push(self, cid: bytes, pos: int, length: int) -> None: - assert self.queue is not None - self.queue.append((cid, pos, length)) - - def call(self, cid: bytes, pos: int, length: int) -> bytes: - """Call the appropriate chunk handler""" - - logger.debug("STREAM %r %s %s", cid, pos, length) - return getattr(self, f"chunk_{cid.decode('ascii')}")(pos, length) - - def crc(self, cid: bytes, data: bytes) -> None: - """Read and verify checksum""" - - # Skip CRC checks for ancillary chunks if allowed to load truncated - # images - # 5th byte of first char is 1 [specs, section 5.4] - if ImageFile.LOAD_TRUNCATED_IMAGES and (cid[0] >> 5 & 1): - self.crc_skip(cid, data) - return - - assert self.fp is not None - try: - crc1 = _crc32(data, _crc32(cid)) - crc2 = i32(self.fp.read(4)) - if crc1 != crc2: - msg = f"broken PNG file (bad header checksum in {repr(cid)})" - raise SyntaxError(msg) - except struct.error as e: - msg = f"broken PNG file (incomplete checksum in {repr(cid)})" - raise SyntaxError(msg) from e - - def crc_skip(self, cid: bytes, data: bytes) -> None: - """Read checksum""" - - assert self.fp is not None - self.fp.read(4) - - def verify(self, endchunk: bytes = b"IEND") -> list[bytes]: - # Simple approach; just calculate checksum for all remaining - # blocks. Must be called directly after open. - - cids = [] - - assert self.fp is not None - while True: - try: - cid, pos, length = self.read() - except struct.error as e: - msg = "truncated PNG file" - raise OSError(msg) from e - - if cid == endchunk: - break - self.crc(cid, ImageFile._safe_read(self.fp, length)) - cids.append(cid) - - return cids - - -class iTXt(str): - """ - Subclass of string to allow iTXt chunks to look like strings while - keeping their extra information - - """ - - lang: str | bytes | None - tkey: str | bytes | None - - @staticmethod - def __new__( - cls, text: str, lang: str | None = None, tkey: str | None = None - ) -> iTXt: - """ - :param cls: the class to use when creating the instance - :param text: value for this key - :param lang: language code - :param tkey: UTF-8 version of the key name - """ - - self = str.__new__(cls, text) - self.lang = lang - self.tkey = tkey - return self - - -class PngInfo: - """ - PNG chunk container (for use with save(pnginfo=)) - - """ - - def __init__(self) -> None: - self.chunks: list[tuple[bytes, bytes, bool]] = [] - - def add(self, cid: bytes, data: bytes, after_idat: bool = False) -> None: - """Appends an arbitrary chunk. Use with caution. - - :param cid: a byte string, 4 bytes long. - :param data: a byte string of the encoded data - :param after_idat: for use with private chunks. Whether the chunk - should be written after IDAT - - """ - - self.chunks.append((cid, data, after_idat)) - - def add_itxt( - self, - key: str | bytes, - value: str | bytes, - lang: str | bytes = "", - tkey: str | bytes = "", - zip: bool = False, - ) -> None: - """Appends an iTXt chunk. - - :param key: latin-1 encodable text key name - :param value: value for this key - :param lang: language code - :param tkey: UTF-8 version of the key name - :param zip: compression flag - - """ - - if not isinstance(key, bytes): - key = key.encode("latin-1", "strict") - if not isinstance(value, bytes): - value = value.encode("utf-8", "strict") - if not isinstance(lang, bytes): - lang = lang.encode("utf-8", "strict") - if not isinstance(tkey, bytes): - tkey = tkey.encode("utf-8", "strict") - - if zip: - self.add( - b"iTXt", - key + b"\0\x01\0" + lang + b"\0" + tkey + b"\0" + zlib.compress(value), - ) - else: - self.add(b"iTXt", key + b"\0\0\0" + lang + b"\0" + tkey + b"\0" + value) - - def add_text( - self, key: str | bytes, value: str | bytes | iTXt, zip: bool = False - ) -> None: - """Appends a text chunk. - - :param key: latin-1 encodable text key name - :param value: value for this key, text or an - :py:class:`PIL.PngImagePlugin.iTXt` instance - :param zip: compression flag - - """ - if isinstance(value, iTXt): - return self.add_itxt( - key, - value, - value.lang if value.lang is not None else b"", - value.tkey if value.tkey is not None else b"", - zip=zip, - ) - - # The tEXt chunk stores latin-1 text - if not isinstance(value, bytes): - try: - value = value.encode("latin-1", "strict") - except UnicodeError: - return self.add_itxt(key, value, zip=zip) - - if not isinstance(key, bytes): - key = key.encode("latin-1", "strict") - - if zip: - self.add(b"zTXt", key + b"\0\0" + zlib.compress(value)) - else: - self.add(b"tEXt", key + b"\0" + value) - - -# -------------------------------------------------------------------- -# PNG image stream (IHDR/IEND) - - -class _RewindState(NamedTuple): - info: dict[str | tuple[int, int], Any] - tile: list[ImageFile._Tile] - seq_num: int | None - - -class PngStream(ChunkStream): - def __init__(self, fp: IO[bytes]) -> None: - super().__init__(fp) - - # local copies of Image attributes - self.im_info: dict[str | tuple[int, int], Any] = {} - self.im_text: dict[str, str | iTXt] = {} - self.im_size = (0, 0) - self.im_mode = "" - self.im_tile: list[ImageFile._Tile] = [] - self.im_palette: tuple[str, bytes] | None = None - self.im_custom_mimetype: str | None = None - self.im_n_frames: int | None = None - self._seq_num: int | None = None - self.rewind_state = _RewindState({}, [], None) - - self.text_memory = 0 - - def check_text_memory(self, chunklen: int) -> None: - self.text_memory += chunklen - if self.text_memory > MAX_TEXT_MEMORY: - msg = ( - "Too much memory used in text chunks: " - f"{self.text_memory}>MAX_TEXT_MEMORY" - ) - raise ValueError(msg) - - def save_rewind(self) -> None: - self.rewind_state = _RewindState( - self.im_info.copy(), - self.im_tile, - self._seq_num, - ) - - def rewind(self) -> None: - self.im_info = self.rewind_state.info.copy() - self.im_tile = self.rewind_state.tile - self._seq_num = self.rewind_state.seq_num - - def chunk_iCCP(self, pos: int, length: int) -> bytes: - # ICC profile - assert self.fp is not None - s = ImageFile._safe_read(self.fp, length) - # according to PNG spec, the iCCP chunk contains: - # Profile name 1-79 bytes (character string) - # Null separator 1 byte (null character) - # Compression method 1 byte (0) - # Compressed profile n bytes (zlib with deflate compression) - i = s.find(b"\0") - logger.debug("iCCP profile name %r", s[:i]) - comp_method = s[i + 1] - logger.debug("Compression method %s", comp_method) - if comp_method != 0: - msg = f"Unknown compression method {comp_method} in iCCP chunk" - raise SyntaxError(msg) - try: - icc_profile = _safe_zlib_decompress(s[i + 2 :]) - except ValueError: - if ImageFile.LOAD_TRUNCATED_IMAGES: - icc_profile = None - else: - raise - except zlib.error: - icc_profile = None # FIXME - self.im_info["icc_profile"] = icc_profile - return s - - def chunk_IHDR(self, pos: int, length: int) -> bytes: - # image header - assert self.fp is not None - s = ImageFile._safe_read(self.fp, length) - if length < 13: - if ImageFile.LOAD_TRUNCATED_IMAGES: - return s - msg = "Truncated IHDR chunk" - raise ValueError(msg) - self.im_size = i32(s, 0), i32(s, 4) - try: - self.im_mode, self.im_rawmode = _MODES[(s[8], s[9])] - except Exception: - pass - if s[12]: - self.im_info["interlace"] = 1 - if s[11]: - msg = "unknown filter category" - raise SyntaxError(msg) - return s - - def chunk_IDAT(self, pos: int, length: int) -> NoReturn: - # image data - if "bbox" in self.im_info: - tile = [ImageFile._Tile("zip", self.im_info["bbox"], pos, self.im_rawmode)] - else: - if self.im_n_frames is not None: - self.im_info["default_image"] = True - tile = [ImageFile._Tile("zip", (0, 0) + self.im_size, pos, self.im_rawmode)] - self.im_tile = tile - self.im_idat = length - msg = "image data found" - raise EOFError(msg) - - def chunk_IEND(self, pos: int, length: int) -> NoReturn: - msg = "end of PNG image" - raise EOFError(msg) - - def chunk_PLTE(self, pos: int, length: int) -> bytes: - # palette - assert self.fp is not None - s = ImageFile._safe_read(self.fp, length) - if self.im_mode == "P": - self.im_palette = "RGB", s - return s - - def chunk_tRNS(self, pos: int, length: int) -> bytes: - # transparency - assert self.fp is not None - s = ImageFile._safe_read(self.fp, length) - if self.im_mode == "P": - if _simple_palette.match(s): - # tRNS contains only one full-transparent entry, - # other entries are full opaque - i = s.find(b"\0") - if i >= 0: - self.im_info["transparency"] = i - else: - # otherwise, we have a byte string with one alpha value - # for each palette entry - self.im_info["transparency"] = s - elif self.im_mode in ("1", "L", "I;16"): - self.im_info["transparency"] = i16(s) - elif self.im_mode == "RGB": - self.im_info["transparency"] = i16(s), i16(s, 2), i16(s, 4) - return s - - def chunk_gAMA(self, pos: int, length: int) -> bytes: - # gamma setting - assert self.fp is not None - s = ImageFile._safe_read(self.fp, length) - self.im_info["gamma"] = i32(s) / 100000.0 - return s - - def chunk_cHRM(self, pos: int, length: int) -> bytes: - # chromaticity, 8 unsigned ints, actual value is scaled by 100,000 - # WP x,y, Red x,y, Green x,y Blue x,y - - assert self.fp is not None - s = ImageFile._safe_read(self.fp, length) - raw_vals = struct.unpack(f">{len(s) // 4}I", s) - self.im_info["chromaticity"] = tuple(elt / 100000.0 for elt in raw_vals) - return s - - def chunk_sRGB(self, pos: int, length: int) -> bytes: - # srgb rendering intent, 1 byte - # 0 perceptual - # 1 relative colorimetric - # 2 saturation - # 3 absolute colorimetric - - assert self.fp is not None - s = ImageFile._safe_read(self.fp, length) - if length < 1: - if ImageFile.LOAD_TRUNCATED_IMAGES: - return s - msg = "Truncated sRGB chunk" - raise ValueError(msg) - self.im_info["srgb"] = s[0] - return s - - def chunk_pHYs(self, pos: int, length: int) -> bytes: - # pixels per unit - assert self.fp is not None - s = ImageFile._safe_read(self.fp, length) - if length < 9: - if ImageFile.LOAD_TRUNCATED_IMAGES: - return s - msg = "Truncated pHYs chunk" - raise ValueError(msg) - px, py = i32(s, 0), i32(s, 4) - unit = s[8] - if unit == 1: # meter - dpi = px * 0.0254, py * 0.0254 - self.im_info["dpi"] = dpi - elif unit == 0: - self.im_info["aspect"] = px, py - return s - - def chunk_tEXt(self, pos: int, length: int) -> bytes: - # text - assert self.fp is not None - s = ImageFile._safe_read(self.fp, length) - try: - k, v = s.split(b"\0", 1) - except ValueError: - # fallback for broken tEXt tags - k = s - v = b"" - if k: - k_str = k.decode("latin-1", "strict") - v_str = v.decode("latin-1", "replace") - - self.im_info[k_str] = v if k == b"exif" else v_str - self.im_text[k_str] = v_str - self.check_text_memory(len(v_str)) - - return s - - def chunk_zTXt(self, pos: int, length: int) -> bytes: - # compressed text - assert self.fp is not None - s = ImageFile._safe_read(self.fp, length) - try: - k, v = s.split(b"\0", 1) - except ValueError: - k = s - v = b"" - if v: - comp_method = v[0] - else: - comp_method = 0 - if comp_method != 0: - msg = f"Unknown compression method {comp_method} in zTXt chunk" - raise SyntaxError(msg) - try: - v = _safe_zlib_decompress(v[1:]) - except ValueError: - if ImageFile.LOAD_TRUNCATED_IMAGES: - v = b"" - else: - raise - except zlib.error: - v = b"" - - if k: - k_str = k.decode("latin-1", "strict") - v_str = v.decode("latin-1", "replace") - - self.im_info[k_str] = self.im_text[k_str] = v_str - self.check_text_memory(len(v_str)) - - return s - - def chunk_iTXt(self, pos: int, length: int) -> bytes: - # international text - assert self.fp is not None - r = s = ImageFile._safe_read(self.fp, length) - try: - k, r = r.split(b"\0", 1) - except ValueError: - return s - if len(r) < 2: - return s - cf, cm, r = r[0], r[1], r[2:] - try: - lang, tk, v = r.split(b"\0", 2) - except ValueError: - return s - if cf != 0: - if cm == 0: - try: - v = _safe_zlib_decompress(v) - except ValueError: - if ImageFile.LOAD_TRUNCATED_IMAGES: - return s - else: - raise - except zlib.error: - return s - else: - return s - if k == b"XML:com.adobe.xmp": - self.im_info["xmp"] = v - try: - k_str = k.decode("latin-1", "strict") - lang_str = lang.decode("utf-8", "strict") - tk_str = tk.decode("utf-8", "strict") - v_str = v.decode("utf-8", "strict") - except UnicodeError: - return s - - self.im_info[k_str] = self.im_text[k_str] = iTXt(v_str, lang_str, tk_str) - self.check_text_memory(len(v_str)) - - return s - - def chunk_eXIf(self, pos: int, length: int) -> bytes: - assert self.fp is not None - s = ImageFile._safe_read(self.fp, length) - self.im_info["exif"] = b"Exif\x00\x00" + s - return s - - # APNG chunks - def chunk_acTL(self, pos: int, length: int) -> bytes: - assert self.fp is not None - s = ImageFile._safe_read(self.fp, length) - if length < 8: - if ImageFile.LOAD_TRUNCATED_IMAGES: - return s - msg = "APNG contains truncated acTL chunk" - raise ValueError(msg) - if self.im_n_frames is not None: - self.im_n_frames = None - warnings.warn("Invalid APNG, will use default PNG image if possible") - return s - n_frames = i32(s) - if n_frames == 0 or n_frames > 0x80000000: - warnings.warn("Invalid APNG, will use default PNG image if possible") - return s - self.im_n_frames = n_frames - self.im_info["loop"] = i32(s, 4) - self.im_custom_mimetype = "image/apng" - return s - - def chunk_fcTL(self, pos: int, length: int) -> bytes: - assert self.fp is not None - s = ImageFile._safe_read(self.fp, length) - if length < 26: - if ImageFile.LOAD_TRUNCATED_IMAGES: - return s - msg = "APNG contains truncated fcTL chunk" - raise ValueError(msg) - seq = i32(s) - if (self._seq_num is None and seq != 0) or ( - self._seq_num is not None and self._seq_num != seq - 1 - ): - msg = "APNG contains frame sequence errors" - raise SyntaxError(msg) - self._seq_num = seq - width, height = i32(s, 4), i32(s, 8) - px, py = i32(s, 12), i32(s, 16) - im_w, im_h = self.im_size - if px + width > im_w or py + height > im_h: - msg = "APNG contains invalid frames" - raise SyntaxError(msg) - self.im_info["bbox"] = (px, py, px + width, py + height) - delay_num, delay_den = i16(s, 20), i16(s, 22) - if delay_den == 0: - delay_den = 100 - self.im_info["duration"] = float(delay_num) / float(delay_den) * 1000 - self.im_info["disposal"] = s[24] - self.im_info["blend"] = s[25] - return s - - def chunk_fdAT(self, pos: int, length: int) -> bytes: - assert self.fp is not None - if length < 4: - if ImageFile.LOAD_TRUNCATED_IMAGES: - s = ImageFile._safe_read(self.fp, length) - return s - msg = "APNG contains truncated fDAT chunk" - raise ValueError(msg) - s = ImageFile._safe_read(self.fp, 4) - seq = i32(s) - if self._seq_num != seq - 1: - msg = "APNG contains frame sequence errors" - raise SyntaxError(msg) - self._seq_num = seq - return self.chunk_IDAT(pos + 4, length - 4) - - -# -------------------------------------------------------------------- -# PNG reader - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(_MAGIC) - - -## -# Image plugin for PNG images. - - -class PngImageFile(ImageFile.ImageFile): - format = "PNG" - format_description = "Portable network graphics" - - def _open(self) -> None: - if not _accept(self.fp.read(8)): - msg = "not a PNG file" - raise SyntaxError(msg) - self._fp = self.fp - self.__frame = 0 - - # - # Parse headers up to the first IDAT or fDAT chunk - - self.private_chunks: list[tuple[bytes, bytes] | tuple[bytes, bytes, bool]] = [] - self.png: PngStream | None = PngStream(self.fp) - - while True: - # - # get next chunk - - cid, pos, length = self.png.read() - - try: - s = self.png.call(cid, pos, length) - except EOFError: - break - except AttributeError: - logger.debug("%r %s %s (unknown)", cid, pos, length) - s = ImageFile._safe_read(self.fp, length) - if cid[1:2].islower(): - self.private_chunks.append((cid, s)) - - self.png.crc(cid, s) - - # - # Copy relevant attributes from the PngStream. An alternative - # would be to let the PngStream class modify these attributes - # directly, but that introduces circular references which are - # difficult to break if things go wrong in the decoder... - # (believe me, I've tried ;-) - - self._mode = self.png.im_mode - self._size = self.png.im_size - self.info = self.png.im_info - self._text: dict[str, str | iTXt] | None = None - self.tile = self.png.im_tile - self.custom_mimetype = self.png.im_custom_mimetype - self.n_frames = self.png.im_n_frames or 1 - self.default_image = self.info.get("default_image", False) - - if self.png.im_palette: - rawmode, data = self.png.im_palette - self.palette = ImagePalette.raw(rawmode, data) - - if cid == b"fdAT": - self.__prepare_idat = length - 4 - else: - self.__prepare_idat = length # used by load_prepare() - - if self.png.im_n_frames is not None: - self._close_exclusive_fp_after_loading = False - self.png.save_rewind() - self.__rewind_idat = self.__prepare_idat - self.__rewind = self._fp.tell() - if self.default_image: - # IDAT chunk contains default image and not first animation frame - self.n_frames += 1 - self._seek(0) - self.is_animated = self.n_frames > 1 - - @property - def text(self) -> dict[str, str | iTXt]: - # experimental - if self._text is None: - # iTxt, tEXt and zTXt chunks may appear at the end of the file - # So load the file to ensure that they are read - if self.is_animated: - frame = self.__frame - # for APNG, seek to the final frame before loading - self.seek(self.n_frames - 1) - self.load() - if self.is_animated: - self.seek(frame) - assert self._text is not None - return self._text - - def verify(self) -> None: - """Verify PNG file""" - - if self.fp is None: - msg = "verify must be called directly after open" - raise RuntimeError(msg) - - # back up to beginning of IDAT block - self.fp.seek(self.tile[0][2] - 8) - - assert self.png is not None - self.png.verify() - self.png.close() - - if self._exclusive_fp: - self.fp.close() - self.fp = None - - def seek(self, frame: int) -> None: - if not self._seek_check(frame): - return - if frame < self.__frame: - self._seek(0, True) - - last_frame = self.__frame - for f in range(self.__frame + 1, frame + 1): - try: - self._seek(f) - except EOFError as e: - self.seek(last_frame) - msg = "no more images in APNG file" - raise EOFError(msg) from e - - def _seek(self, frame: int, rewind: bool = False) -> None: - assert self.png is not None - if isinstance(self._fp, DeferredError): - raise self._fp.ex - - self.dispose: _imaging.ImagingCore | None - dispose_extent = None - if frame == 0: - if rewind: - self._fp.seek(self.__rewind) - self.png.rewind() - self.__prepare_idat = self.__rewind_idat - self._im = None - self.info = self.png.im_info - self.tile = self.png.im_tile - self.fp = self._fp - self._prev_im = None - self.dispose = None - self.default_image = self.info.get("default_image", False) - self.dispose_op = self.info.get("disposal") - self.blend_op = self.info.get("blend") - dispose_extent = self.info.get("bbox") - self.__frame = 0 - else: - if frame != self.__frame + 1: - msg = f"cannot seek to frame {frame}" - raise ValueError(msg) - - # ensure previous frame was loaded - self.load() - - if self.dispose: - self.im.paste(self.dispose, self.dispose_extent) - self._prev_im = self.im.copy() - - self.fp = self._fp - - # advance to the next frame - if self.__prepare_idat: - ImageFile._safe_read(self.fp, self.__prepare_idat) - self.__prepare_idat = 0 - frame_start = False - while True: - self.fp.read(4) # CRC - - try: - cid, pos, length = self.png.read() - except (struct.error, SyntaxError): - break - - if cid == b"IEND": - msg = "No more images in APNG file" - raise EOFError(msg) - if cid == b"fcTL": - if frame_start: - # there must be at least one fdAT chunk between fcTL chunks - msg = "APNG missing frame data" - raise SyntaxError(msg) - frame_start = True - - try: - self.png.call(cid, pos, length) - except UnicodeDecodeError: - break - except EOFError: - if cid == b"fdAT": - length -= 4 - if frame_start: - self.__prepare_idat = length - break - ImageFile._safe_read(self.fp, length) - except AttributeError: - logger.debug("%r %s %s (unknown)", cid, pos, length) - ImageFile._safe_read(self.fp, length) - - self.__frame = frame - self.tile = self.png.im_tile - self.dispose_op = self.info.get("disposal") - self.blend_op = self.info.get("blend") - dispose_extent = self.info.get("bbox") - - if not self.tile: - msg = "image not found in APNG frame" - raise EOFError(msg) - if dispose_extent: - self.dispose_extent: tuple[float, float, float, float] = dispose_extent - - # setup frame disposal (actual disposal done when needed in the next _seek()) - if self._prev_im is None and self.dispose_op == Disposal.OP_PREVIOUS: - self.dispose_op = Disposal.OP_BACKGROUND - - self.dispose = None - if self.dispose_op == Disposal.OP_PREVIOUS: - if self._prev_im: - self.dispose = self._prev_im.copy() - self.dispose = self._crop(self.dispose, self.dispose_extent) - elif self.dispose_op == Disposal.OP_BACKGROUND: - self.dispose = Image.core.fill(self.mode, self.size) - self.dispose = self._crop(self.dispose, self.dispose_extent) - - def tell(self) -> int: - return self.__frame - - def load_prepare(self) -> None: - """internal: prepare to read PNG file""" - - if self.info.get("interlace"): - self.decoderconfig = self.decoderconfig + (1,) - - self.__idat = self.__prepare_idat # used by load_read() - ImageFile.ImageFile.load_prepare(self) - - def load_read(self, read_bytes: int) -> bytes: - """internal: read more image data""" - - assert self.png is not None - while self.__idat == 0: - # end of chunk, skip forward to next one - - self.fp.read(4) # CRC - - cid, pos, length = self.png.read() - - if cid not in [b"IDAT", b"DDAT", b"fdAT"]: - self.png.push(cid, pos, length) - return b"" - - if cid == b"fdAT": - try: - self.png.call(cid, pos, length) - except EOFError: - pass - self.__idat = length - 4 # sequence_num has already been read - else: - self.__idat = length # empty chunks are allowed - - # read more data from this chunk - if read_bytes <= 0: - read_bytes = self.__idat - else: - read_bytes = min(read_bytes, self.__idat) - - self.__idat = self.__idat - read_bytes - - return self.fp.read(read_bytes) - - def load_end(self) -> None: - """internal: finished reading image data""" - assert self.png is not None - if self.__idat != 0: - self.fp.read(self.__idat) - while True: - self.fp.read(4) # CRC - - try: - cid, pos, length = self.png.read() - except (struct.error, SyntaxError): - break - - if cid == b"IEND": - break - elif cid == b"fcTL" and self.is_animated: - # start of the next frame, stop reading - self.__prepare_idat = 0 - self.png.push(cid, pos, length) - break - - try: - self.png.call(cid, pos, length) - except UnicodeDecodeError: - break - except EOFError: - if cid == b"fdAT": - length -= 4 - try: - ImageFile._safe_read(self.fp, length) - except OSError as e: - if ImageFile.LOAD_TRUNCATED_IMAGES: - break - else: - raise e - except AttributeError: - logger.debug("%r %s %s (unknown)", cid, pos, length) - s = ImageFile._safe_read(self.fp, length) - if cid[1:2].islower(): - self.private_chunks.append((cid, s, True)) - self._text = self.png.im_text - if not self.is_animated: - self.png.close() - self.png = None - else: - if self._prev_im and self.blend_op == Blend.OP_OVER: - updated = self._crop(self.im, self.dispose_extent) - if self.im.mode == "RGB" and "transparency" in self.info: - mask = updated.convert_transparent( - "RGBA", self.info["transparency"] - ) - else: - if self.im.mode == "P" and "transparency" in self.info: - t = self.info["transparency"] - if isinstance(t, bytes): - updated.putpalettealphas(t) - elif isinstance(t, int): - updated.putpalettealpha(t) - mask = updated.convert("RGBA") - self._prev_im.paste(updated, self.dispose_extent, mask) - self.im = self._prev_im - - def _getexif(self) -> dict[int, Any] | None: - if "exif" not in self.info: - self.load() - if "exif" not in self.info and "Raw profile type exif" not in self.info: - return None - return self.getexif()._get_merged_dict() - - def getexif(self) -> Image.Exif: - if "exif" not in self.info: - self.load() - - return super().getexif() - - -# -------------------------------------------------------------------- -# PNG writer - -_OUTMODES = { - # supported PIL modes, and corresponding rawmode, bit depth and color type - "1": ("1", b"\x01", b"\x00"), - "L;1": ("L;1", b"\x01", b"\x00"), - "L;2": ("L;2", b"\x02", b"\x00"), - "L;4": ("L;4", b"\x04", b"\x00"), - "L": ("L", b"\x08", b"\x00"), - "LA": ("LA", b"\x08", b"\x04"), - "I": ("I;16B", b"\x10", b"\x00"), - "I;16": ("I;16B", b"\x10", b"\x00"), - "I;16B": ("I;16B", b"\x10", b"\x00"), - "P;1": ("P;1", b"\x01", b"\x03"), - "P;2": ("P;2", b"\x02", b"\x03"), - "P;4": ("P;4", b"\x04", b"\x03"), - "P": ("P", b"\x08", b"\x03"), - "RGB": ("RGB", b"\x08", b"\x02"), - "RGBA": ("RGBA", b"\x08", b"\x06"), -} - - -def putchunk(fp: IO[bytes], cid: bytes, *data: bytes) -> None: - """Write a PNG chunk (including CRC field)""" - - byte_data = b"".join(data) - - fp.write(o32(len(byte_data)) + cid) - fp.write(byte_data) - crc = _crc32(byte_data, _crc32(cid)) - fp.write(o32(crc)) - - -class _idat: - # wrap output from the encoder in IDAT chunks - - def __init__(self, fp: IO[bytes], chunk: Callable[..., None]) -> None: - self.fp = fp - self.chunk = chunk - - def write(self, data: bytes) -> None: - self.chunk(self.fp, b"IDAT", data) - - -class _fdat: - # wrap encoder output in fdAT chunks - - def __init__(self, fp: IO[bytes], chunk: Callable[..., None], seq_num: int) -> None: - self.fp = fp - self.chunk = chunk - self.seq_num = seq_num - - def write(self, data: bytes) -> None: - self.chunk(self.fp, b"fdAT", o32(self.seq_num), data) - self.seq_num += 1 - - -class _Frame(NamedTuple): - im: Image.Image - bbox: tuple[int, int, int, int] | None - encoderinfo: dict[str, Any] - - -def _write_multiple_frames( - im: Image.Image, - fp: IO[bytes], - chunk: Callable[..., None], - mode: str, - rawmode: str, - default_image: Image.Image | None, - append_images: list[Image.Image], -) -> Image.Image | None: - duration = im.encoderinfo.get("duration") - loop = im.encoderinfo.get("loop", im.info.get("loop", 0)) - disposal = im.encoderinfo.get("disposal", im.info.get("disposal", Disposal.OP_NONE)) - blend = im.encoderinfo.get("blend", im.info.get("blend", Blend.OP_SOURCE)) - - if default_image: - chain = itertools.chain(append_images) - else: - chain = itertools.chain([im], append_images) - - im_frames: list[_Frame] = [] - frame_count = 0 - for im_seq in chain: - for im_frame in ImageSequence.Iterator(im_seq): - if im_frame.mode == mode: - im_frame = im_frame.copy() - else: - im_frame = im_frame.convert(mode) - encoderinfo = im.encoderinfo.copy() - if isinstance(duration, (list, tuple)): - encoderinfo["duration"] = duration[frame_count] - elif duration is None and "duration" in im_frame.info: - encoderinfo["duration"] = im_frame.info["duration"] - if isinstance(disposal, (list, tuple)): - encoderinfo["disposal"] = disposal[frame_count] - if isinstance(blend, (list, tuple)): - encoderinfo["blend"] = blend[frame_count] - frame_count += 1 - - if im_frames: - previous = im_frames[-1] - prev_disposal = previous.encoderinfo.get("disposal") - prev_blend = previous.encoderinfo.get("blend") - if prev_disposal == Disposal.OP_PREVIOUS and len(im_frames) < 2: - prev_disposal = Disposal.OP_BACKGROUND - - if prev_disposal == Disposal.OP_BACKGROUND: - base_im = previous.im.copy() - dispose = Image.core.fill("RGBA", im.size, (0, 0, 0, 0)) - bbox = previous.bbox - if bbox: - dispose = dispose.crop(bbox) - else: - bbox = (0, 0) + im.size - base_im.paste(dispose, bbox) - elif prev_disposal == Disposal.OP_PREVIOUS: - base_im = im_frames[-2].im - else: - base_im = previous.im - delta = ImageChops.subtract_modulo( - im_frame.convert("RGBA"), base_im.convert("RGBA") - ) - bbox = delta.getbbox(alpha_only=False) - if ( - not bbox - and prev_disposal == encoderinfo.get("disposal") - and prev_blend == encoderinfo.get("blend") - and "duration" in encoderinfo - ): - previous.encoderinfo["duration"] += encoderinfo["duration"] - continue - else: - bbox = None - im_frames.append(_Frame(im_frame, bbox, encoderinfo)) - - if len(im_frames) == 1 and not default_image: - return im_frames[0].im - - # animation control - chunk( - fp, - b"acTL", - o32(len(im_frames)), # 0: num_frames - o32(loop), # 4: num_plays - ) - - # default image IDAT (if it exists) - if default_image: - if im.mode != mode: - im = im.convert(mode) - ImageFile._save( - im, - cast(IO[bytes], _idat(fp, chunk)), - [ImageFile._Tile("zip", (0, 0) + im.size, 0, rawmode)], - ) - - seq_num = 0 - for frame, frame_data in enumerate(im_frames): - im_frame = frame_data.im - if not frame_data.bbox: - bbox = (0, 0) + im_frame.size - else: - bbox = frame_data.bbox - im_frame = im_frame.crop(bbox) - size = im_frame.size - encoderinfo = frame_data.encoderinfo - frame_duration = int(round(encoderinfo.get("duration", 0))) - frame_disposal = encoderinfo.get("disposal", disposal) - frame_blend = encoderinfo.get("blend", blend) - # frame control - chunk( - fp, - b"fcTL", - o32(seq_num), # sequence_number - o32(size[0]), # width - o32(size[1]), # height - o32(bbox[0]), # x_offset - o32(bbox[1]), # y_offset - o16(frame_duration), # delay_numerator - o16(1000), # delay_denominator - o8(frame_disposal), # dispose_op - o8(frame_blend), # blend_op - ) - seq_num += 1 - # frame data - if frame == 0 and not default_image: - # first frame must be in IDAT chunks for backwards compatibility - ImageFile._save( - im_frame, - cast(IO[bytes], _idat(fp, chunk)), - [ImageFile._Tile("zip", (0, 0) + im_frame.size, 0, rawmode)], - ) - else: - fdat_chunks = _fdat(fp, chunk, seq_num) - ImageFile._save( - im_frame, - cast(IO[bytes], fdat_chunks), - [ImageFile._Tile("zip", (0, 0) + im_frame.size, 0, rawmode)], - ) - seq_num = fdat_chunks.seq_num - return None - - -def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - _save(im, fp, filename, save_all=True) - - -def _save( - im: Image.Image, - fp: IO[bytes], - filename: str | bytes, - chunk: Callable[..., None] = putchunk, - save_all: bool = False, -) -> None: - # save an image to disk (called by the save method) - - if save_all: - default_image = im.encoderinfo.get( - "default_image", im.info.get("default_image") - ) - modes = set() - sizes = set() - append_images = im.encoderinfo.get("append_images", []) - for im_seq in itertools.chain([im], append_images): - for im_frame in ImageSequence.Iterator(im_seq): - modes.add(im_frame.mode) - sizes.add(im_frame.size) - for mode in ("RGBA", "RGB", "P"): - if mode in modes: - break - else: - mode = modes.pop() - size = tuple(max(frame_size[i] for frame_size in sizes) for i in range(2)) - else: - size = im.size - mode = im.mode - - outmode = mode - if mode == "P": - # - # attempt to minimize storage requirements for palette images - if "bits" in im.encoderinfo: - # number of bits specified by user - colors = min(1 << im.encoderinfo["bits"], 256) - else: - # check palette contents - if im.palette: - colors = max(min(len(im.palette.getdata()[1]) // 3, 256), 1) - else: - colors = 256 - - if colors <= 16: - if colors <= 2: - bits = 1 - elif colors <= 4: - bits = 2 - else: - bits = 4 - outmode += f";{bits}" - - # encoder options - im.encoderconfig = ( - im.encoderinfo.get("optimize", False), - im.encoderinfo.get("compress_level", -1), - im.encoderinfo.get("compress_type", -1), - im.encoderinfo.get("dictionary", b""), - ) - - # get the corresponding PNG mode - try: - rawmode, bit_depth, color_type = _OUTMODES[outmode] - except KeyError as e: - msg = f"cannot write mode {mode} as PNG" - raise OSError(msg) from e - if outmode == "I": - deprecate("Saving I mode images as PNG", 13, stacklevel=4) - - # - # write minimal PNG file - - fp.write(_MAGIC) - - chunk( - fp, - b"IHDR", - o32(size[0]), # 0: size - o32(size[1]), - bit_depth, - color_type, - b"\0", # 10: compression - b"\0", # 11: filter category - b"\0", # 12: interlace flag - ) - - chunks = [b"cHRM", b"cICP", b"gAMA", b"sBIT", b"sRGB", b"tIME"] - - icc = im.encoderinfo.get("icc_profile", im.info.get("icc_profile")) - if icc: - # ICC profile - # according to PNG spec, the iCCP chunk contains: - # Profile name 1-79 bytes (character string) - # Null separator 1 byte (null character) - # Compression method 1 byte (0) - # Compressed profile n bytes (zlib with deflate compression) - name = b"ICC Profile" - data = name + b"\0\0" + zlib.compress(icc) - chunk(fp, b"iCCP", data) - - # You must either have sRGB or iCCP. - # Disallow sRGB chunks when an iCCP-chunk has been emitted. - chunks.remove(b"sRGB") - - info = im.encoderinfo.get("pnginfo") - if info: - chunks_multiple_allowed = [b"sPLT", b"iTXt", b"tEXt", b"zTXt"] - for info_chunk in info.chunks: - cid, data = info_chunk[:2] - if cid in chunks: - chunks.remove(cid) - chunk(fp, cid, data) - elif cid in chunks_multiple_allowed: - chunk(fp, cid, data) - elif cid[1:2].islower(): - # Private chunk - after_idat = len(info_chunk) == 3 and info_chunk[2] - if not after_idat: - chunk(fp, cid, data) - - if im.mode == "P": - palette_byte_number = colors * 3 - palette_bytes = im.im.getpalette("RGB")[:palette_byte_number] - while len(palette_bytes) < palette_byte_number: - palette_bytes += b"\0" - chunk(fp, b"PLTE", palette_bytes) - - transparency = im.encoderinfo.get("transparency", im.info.get("transparency", None)) - - if transparency or transparency == 0: - if im.mode == "P": - # limit to actual palette size - alpha_bytes = colors - if isinstance(transparency, bytes): - chunk(fp, b"tRNS", transparency[:alpha_bytes]) - else: - transparency = max(0, min(255, transparency)) - alpha = b"\xff" * transparency + b"\0" - chunk(fp, b"tRNS", alpha[:alpha_bytes]) - elif im.mode in ("1", "L", "I", "I;16"): - transparency = max(0, min(65535, transparency)) - chunk(fp, b"tRNS", o16(transparency)) - elif im.mode == "RGB": - red, green, blue = transparency - chunk(fp, b"tRNS", o16(red) + o16(green) + o16(blue)) - else: - if "transparency" in im.encoderinfo: - # don't bother with transparency if it's an RGBA - # and it's in the info dict. It's probably just stale. - msg = "cannot use transparency for this mode" - raise OSError(msg) - else: - if im.mode == "P" and im.im.getpalettemode() == "RGBA": - alpha = im.im.getpalette("RGBA", "A") - alpha_bytes = colors - chunk(fp, b"tRNS", alpha[:alpha_bytes]) - - dpi = im.encoderinfo.get("dpi") - if dpi: - chunk( - fp, - b"pHYs", - o32(int(dpi[0] / 0.0254 + 0.5)), - o32(int(dpi[1] / 0.0254 + 0.5)), - b"\x01", - ) - - if info: - chunks = [b"bKGD", b"hIST"] - for info_chunk in info.chunks: - cid, data = info_chunk[:2] - if cid in chunks: - chunks.remove(cid) - chunk(fp, cid, data) - - exif = im.encoderinfo.get("exif") - if exif: - if isinstance(exif, Image.Exif): - exif = exif.tobytes(8) - if exif.startswith(b"Exif\x00\x00"): - exif = exif[6:] - chunk(fp, b"eXIf", exif) - - single_im: Image.Image | None = im - if save_all: - single_im = _write_multiple_frames( - im, fp, chunk, mode, rawmode, default_image, append_images - ) - if single_im: - ImageFile._save( - single_im, - cast(IO[bytes], _idat(fp, chunk)), - [ImageFile._Tile("zip", (0, 0) + single_im.size, 0, rawmode)], - ) - - if info: - for info_chunk in info.chunks: - cid, data = info_chunk[:2] - if cid[1:2].islower(): - # Private chunk - after_idat = len(info_chunk) == 3 and info_chunk[2] - if after_idat: - chunk(fp, cid, data) - - chunk(fp, b"IEND", b"") - - if hasattr(fp, "flush"): - fp.flush() - - -# -------------------------------------------------------------------- -# PNG chunk converter - - -def getchunks(im: Image.Image, **params: Any) -> list[tuple[bytes, bytes, bytes]]: - """Return a list of PNG chunks representing this image.""" - from io import BytesIO - - chunks = [] - - def append(fp: IO[bytes], cid: bytes, *data: bytes) -> None: - byte_data = b"".join(data) - crc = o32(_crc32(byte_data, _crc32(cid))) - chunks.append((cid, byte_data, crc)) - - fp = BytesIO() - - try: - im.encoderinfo = params - _save(im, fp, "", append) - finally: - del im.encoderinfo - - return chunks - - -# -------------------------------------------------------------------- -# Registry - -Image.register_open(PngImageFile.format, PngImageFile, _accept) -Image.register_save(PngImageFile.format, _save) -Image.register_save_all(PngImageFile.format, _save_all) - -Image.register_extensions(PngImageFile.format, [".png", ".apng"]) - -Image.register_mime(PngImageFile.format, "image/png") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/PpmImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/PpmImagePlugin.py deleted file mode 100644 index 307bc97f..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/PpmImagePlugin.py +++ /dev/null @@ -1,375 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# PPM support for PIL -# -# History: -# 96-03-24 fl Created -# 98-03-06 fl Write RGBA images (as RGB, that is) -# -# Copyright (c) Secret Labs AB 1997-98. -# Copyright (c) Fredrik Lundh 1996. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import math -from typing import IO - -from . import Image, ImageFile -from ._binary import i16be as i16 -from ._binary import o8 -from ._binary import o32le as o32 - -# -# -------------------------------------------------------------------- - -b_whitespace = b"\x20\x09\x0a\x0b\x0c\x0d" - -MODES = { - # standard - b"P1": "1", - b"P2": "L", - b"P3": "RGB", - b"P4": "1", - b"P5": "L", - b"P6": "RGB", - # extensions - b"P0CMYK": "CMYK", - b"Pf": "F", - # PIL extensions (for test purposes only) - b"PyP": "P", - b"PyRGBA": "RGBA", - b"PyCMYK": "CMYK", -} - - -def _accept(prefix: bytes) -> bool: - return len(prefix) >= 2 and prefix.startswith(b"P") and prefix[1] in b"0123456fy" - - -## -# Image plugin for PBM, PGM, and PPM images. - - -class PpmImageFile(ImageFile.ImageFile): - format = "PPM" - format_description = "Pbmplus image" - - def _read_magic(self) -> bytes: - assert self.fp is not None - - magic = b"" - # read until whitespace or longest available magic number - for _ in range(6): - c = self.fp.read(1) - if not c or c in b_whitespace: - break - magic += c - return magic - - def _read_token(self) -> bytes: - assert self.fp is not None - - token = b"" - while len(token) <= 10: # read until next whitespace or limit of 10 characters - c = self.fp.read(1) - if not c: - break - elif c in b_whitespace: # token ended - if not token: - # skip whitespace at start - continue - break - elif c == b"#": - # ignores rest of the line; stops at CR, LF or EOF - while self.fp.read(1) not in b"\r\n": - pass - continue - token += c - if not token: - # Token was not even 1 byte - msg = "Reached EOF while reading header" - raise ValueError(msg) - elif len(token) > 10: - msg_too_long = b"Token too long in file header: %s" % token - raise ValueError(msg_too_long) - return token - - def _open(self) -> None: - assert self.fp is not None - - magic_number = self._read_magic() - try: - mode = MODES[magic_number] - except KeyError: - msg = "not a PPM file" - raise SyntaxError(msg) - self._mode = mode - - if magic_number in (b"P1", b"P4"): - self.custom_mimetype = "image/x-portable-bitmap" - elif magic_number in (b"P2", b"P5"): - self.custom_mimetype = "image/x-portable-graymap" - elif magic_number in (b"P3", b"P6"): - self.custom_mimetype = "image/x-portable-pixmap" - - self._size = int(self._read_token()), int(self._read_token()) - - decoder_name = "raw" - if magic_number in (b"P1", b"P2", b"P3"): - decoder_name = "ppm_plain" - - args: str | tuple[str | int, ...] - if mode == "1": - args = "1;I" - elif mode == "F": - scale = float(self._read_token()) - if scale == 0.0 or not math.isfinite(scale): - msg = "scale must be finite and non-zero" - raise ValueError(msg) - self.info["scale"] = abs(scale) - - rawmode = "F;32F" if scale < 0 else "F;32BF" - args = (rawmode, 0, -1) - else: - maxval = int(self._read_token()) - if not 0 < maxval < 65536: - msg = "maxval must be greater than 0 and less than 65536" - raise ValueError(msg) - if maxval > 255 and mode == "L": - self._mode = "I" - - rawmode = mode - if decoder_name != "ppm_plain": - # If maxval matches a bit depth, use the raw decoder directly - if maxval == 65535 and mode == "L": - rawmode = "I;16B" - elif maxval != 255: - decoder_name = "ppm" - - args = rawmode if decoder_name == "raw" else (rawmode, maxval) - self.tile = [ - ImageFile._Tile(decoder_name, (0, 0) + self.size, self.fp.tell(), args) - ] - - -# -# -------------------------------------------------------------------- - - -class PpmPlainDecoder(ImageFile.PyDecoder): - _pulls_fd = True - _comment_spans: bool - - def _read_block(self) -> bytes: - assert self.fd is not None - - return self.fd.read(ImageFile.SAFEBLOCK) - - def _find_comment_end(self, block: bytes, start: int = 0) -> int: - a = block.find(b"\n", start) - b = block.find(b"\r", start) - return min(a, b) if a * b > 0 else max(a, b) # lowest nonnegative index (or -1) - - def _ignore_comments(self, block: bytes) -> bytes: - if self._comment_spans: - # Finish current comment - while block: - comment_end = self._find_comment_end(block) - if comment_end != -1: - # Comment ends in this block - # Delete tail of comment - block = block[comment_end + 1 :] - break - else: - # Comment spans whole block - # So read the next block, looking for the end - block = self._read_block() - - # Search for any further comments - self._comment_spans = False - while True: - comment_start = block.find(b"#") - if comment_start == -1: - # No comment found - break - comment_end = self._find_comment_end(block, comment_start) - if comment_end != -1: - # Comment ends in this block - # Delete comment - block = block[:comment_start] + block[comment_end + 1 :] - else: - # Comment continues to next block(s) - block = block[:comment_start] - self._comment_spans = True - break - return block - - def _decode_bitonal(self) -> bytearray: - """ - This is a separate method because in the plain PBM format, all data tokens are - exactly one byte, so the inter-token whitespace is optional. - """ - data = bytearray() - total_bytes = self.state.xsize * self.state.ysize - - while len(data) != total_bytes: - block = self._read_block() # read next block - if not block: - # eof - break - - block = self._ignore_comments(block) - - tokens = b"".join(block.split()) - for token in tokens: - if token not in (48, 49): - msg = b"Invalid token for this mode: %s" % bytes([token]) - raise ValueError(msg) - data = (data + tokens)[:total_bytes] - invert = bytes.maketrans(b"01", b"\xff\x00") - return data.translate(invert) - - def _decode_blocks(self, maxval: int) -> bytearray: - data = bytearray() - max_len = 10 - out_byte_count = 4 if self.mode == "I" else 1 - out_max = 65535 if self.mode == "I" else 255 - bands = Image.getmodebands(self.mode) - total_bytes = self.state.xsize * self.state.ysize * bands * out_byte_count - - half_token = b"" - while len(data) != total_bytes: - block = self._read_block() # read next block - if not block: - if half_token: - block = bytearray(b" ") # flush half_token - else: - # eof - break - - block = self._ignore_comments(block) - - if half_token: - block = half_token + block # stitch half_token to new block - half_token = b"" - - tokens = block.split() - - if block and not block[-1:].isspace(): # block might split token - half_token = tokens.pop() # save half token for later - if len(half_token) > max_len: # prevent buildup of half_token - msg = ( - b"Token too long found in data: %s" % half_token[: max_len + 1] - ) - raise ValueError(msg) - - for token in tokens: - if len(token) > max_len: - msg = b"Token too long found in data: %s" % token[: max_len + 1] - raise ValueError(msg) - value = int(token) - if value < 0: - msg_str = f"Channel value is negative: {value}" - raise ValueError(msg_str) - if value > maxval: - msg_str = f"Channel value too large for this mode: {value}" - raise ValueError(msg_str) - value = round(value / maxval * out_max) - data += o32(value) if self.mode == "I" else o8(value) - if len(data) == total_bytes: # finished! - break - return data - - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: - self._comment_spans = False - if self.mode == "1": - data = self._decode_bitonal() - rawmode = "1;8" - else: - maxval = self.args[-1] - data = self._decode_blocks(maxval) - rawmode = "I;32" if self.mode == "I" else self.mode - self.set_as_raw(bytes(data), rawmode) - return -1, 0 - - -class PpmDecoder(ImageFile.PyDecoder): - _pulls_fd = True - - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: - assert self.fd is not None - - data = bytearray() - maxval = self.args[-1] - in_byte_count = 1 if maxval < 256 else 2 - out_byte_count = 4 if self.mode == "I" else 1 - out_max = 65535 if self.mode == "I" else 255 - bands = Image.getmodebands(self.mode) - dest_length = self.state.xsize * self.state.ysize * bands * out_byte_count - while len(data) < dest_length: - pixels = self.fd.read(in_byte_count * bands) - if len(pixels) < in_byte_count * bands: - # eof - break - for b in range(bands): - value = ( - pixels[b] if in_byte_count == 1 else i16(pixels, b * in_byte_count) - ) - value = min(out_max, round(value / maxval * out_max)) - data += o32(value) if self.mode == "I" else o8(value) - rawmode = "I;32" if self.mode == "I" else self.mode - self.set_as_raw(bytes(data), rawmode) - return -1, 0 - - -# -# -------------------------------------------------------------------- - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - if im.mode == "1": - rawmode, head = "1;I", b"P4" - elif im.mode == "L": - rawmode, head = "L", b"P5" - elif im.mode in ("I", "I;16"): - rawmode, head = "I;16B", b"P5" - elif im.mode in ("RGB", "RGBA"): - rawmode, head = "RGB", b"P6" - elif im.mode == "F": - rawmode, head = "F;32F", b"Pf" - else: - msg = f"cannot write mode {im.mode} as PPM" - raise OSError(msg) - fp.write(head + b"\n%d %d\n" % im.size) - if head == b"P6": - fp.write(b"255\n") - elif head == b"P5": - if rawmode == "L": - fp.write(b"255\n") - else: - fp.write(b"65535\n") - elif head == b"Pf": - fp.write(b"-1.0\n") - row_order = -1 if im.mode == "F" else 1 - ImageFile._save( - im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 0, (rawmode, 0, row_order))] - ) - - -# -# -------------------------------------------------------------------- - - -Image.register_open(PpmImageFile.format, PpmImageFile, _accept) -Image.register_save(PpmImageFile.format, _save) - -Image.register_decoder("ppm", PpmDecoder) -Image.register_decoder("ppm_plain", PpmPlainDecoder) - -Image.register_extensions(PpmImageFile.format, [".pbm", ".pgm", ".ppm", ".pnm", ".pfm"]) - -Image.register_mime(PpmImageFile.format, "image/x-portable-anymap") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/PsdImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/PsdImagePlugin.py deleted file mode 100644 index f49aaeeb..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/PsdImagePlugin.py +++ /dev/null @@ -1,333 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# Adobe PSD 2.5/3.0 file handling -# -# History: -# 1995-09-01 fl Created -# 1997-01-03 fl Read most PSD images -# 1997-01-18 fl Fixed P and CMYK support -# 2001-10-21 fl Added seek/tell support (for layers) -# -# Copyright (c) 1997-2001 by Secret Labs AB. -# Copyright (c) 1995-2001 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io -from functools import cached_property -from typing import IO - -from . import Image, ImageFile, ImagePalette -from ._binary import i8 -from ._binary import i16be as i16 -from ._binary import i32be as i32 -from ._binary import si16be as si16 -from ._binary import si32be as si32 -from ._util import DeferredError - -MODES = { - # (photoshop mode, bits) -> (pil mode, required channels) - (0, 1): ("1", 1), - (0, 8): ("L", 1), - (1, 8): ("L", 1), - (2, 8): ("P", 1), - (3, 8): ("RGB", 3), - (4, 8): ("CMYK", 4), - (7, 8): ("L", 1), # FIXME: multilayer - (8, 8): ("L", 1), # duotone - (9, 8): ("LAB", 3), -} - - -# --------------------------------------------------------------------. -# read PSD images - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(b"8BPS") - - -## -# Image plugin for Photoshop images. - - -class PsdImageFile(ImageFile.ImageFile): - format = "PSD" - format_description = "Adobe Photoshop" - _close_exclusive_fp_after_loading = False - - def _open(self) -> None: - read = self.fp.read - - # - # header - - s = read(26) - if not _accept(s) or i16(s, 4) != 1: - msg = "not a PSD file" - raise SyntaxError(msg) - - psd_bits = i16(s, 22) - psd_channels = i16(s, 12) - psd_mode = i16(s, 24) - - mode, channels = MODES[(psd_mode, psd_bits)] - - if channels > psd_channels: - msg = "not enough channels" - raise OSError(msg) - if mode == "RGB" and psd_channels == 4: - mode = "RGBA" - channels = 4 - - self._mode = mode - self._size = i32(s, 18), i32(s, 14) - - # - # color mode data - - size = i32(read(4)) - if size: - data = read(size) - if mode == "P" and size == 768: - self.palette = ImagePalette.raw("RGB;L", data) - - # - # image resources - - self.resources = [] - - size = i32(read(4)) - if size: - # load resources - end = self.fp.tell() + size - while self.fp.tell() < end: - read(4) # signature - id = i16(read(2)) - name = read(i8(read(1))) - if not (len(name) & 1): - read(1) # padding - data = read(i32(read(4))) - if len(data) & 1: - read(1) # padding - self.resources.append((id, name, data)) - if id == 1039: # ICC profile - self.info["icc_profile"] = data - - # - # layer and mask information - - self._layers_position = None - - size = i32(read(4)) - if size: - end = self.fp.tell() + size - size = i32(read(4)) - if size: - self._layers_position = self.fp.tell() - self._layers_size = size - self.fp.seek(end) - self._n_frames: int | None = None - - # - # image descriptor - - self.tile = _maketile(self.fp, mode, (0, 0) + self.size, channels) - - # keep the file open - self._fp = self.fp - self.frame = 1 - self._min_frame = 1 - - @cached_property - def layers( - self, - ) -> list[tuple[str, str, tuple[int, int, int, int], list[ImageFile._Tile]]]: - layers = [] - if self._layers_position is not None: - if isinstance(self._fp, DeferredError): - raise self._fp.ex - self._fp.seek(self._layers_position) - _layer_data = io.BytesIO(ImageFile._safe_read(self._fp, self._layers_size)) - layers = _layerinfo(_layer_data, self._layers_size) - self._n_frames = len(layers) - return layers - - @property - def n_frames(self) -> int: - if self._n_frames is None: - self._n_frames = len(self.layers) - return self._n_frames - - @property - def is_animated(self) -> bool: - return len(self.layers) > 1 - - def seek(self, layer: int) -> None: - if not self._seek_check(layer): - return - if isinstance(self._fp, DeferredError): - raise self._fp.ex - - # seek to given layer (1..max) - _, mode, _, tile = self.layers[layer - 1] - self._mode = mode - self.tile = tile - self.frame = layer - self.fp = self._fp - - def tell(self) -> int: - # return layer number (0=image, 1..max=layers) - return self.frame - - -def _layerinfo( - fp: IO[bytes], ct_bytes: int -) -> list[tuple[str, str, tuple[int, int, int, int], list[ImageFile._Tile]]]: - # read layerinfo block - layers = [] - - def read(size: int) -> bytes: - return ImageFile._safe_read(fp, size) - - ct = si16(read(2)) - - # sanity check - if ct_bytes < (abs(ct) * 20): - msg = "Layer block too short for number of layers requested" - raise SyntaxError(msg) - - for _ in range(abs(ct)): - # bounding box - y0 = si32(read(4)) - x0 = si32(read(4)) - y1 = si32(read(4)) - x1 = si32(read(4)) - - # image info - bands = [] - ct_types = i16(read(2)) - if ct_types > 4: - fp.seek(ct_types * 6 + 12, io.SEEK_CUR) - size = i32(read(4)) - fp.seek(size, io.SEEK_CUR) - continue - - for _ in range(ct_types): - type = i16(read(2)) - - if type == 65535: - b = "A" - else: - b = "RGBA"[type] - - bands.append(b) - read(4) # size - - # figure out the image mode - bands.sort() - if bands == ["R"]: - mode = "L" - elif bands == ["B", "G", "R"]: - mode = "RGB" - elif bands == ["A", "B", "G", "R"]: - mode = "RGBA" - else: - mode = "" # unknown - - # skip over blend flags and extra information - read(12) # filler - name = "" - size = i32(read(4)) # length of the extra data field - if size: - data_end = fp.tell() + size - - length = i32(read(4)) - if length: - fp.seek(length - 16, io.SEEK_CUR) - - length = i32(read(4)) - if length: - fp.seek(length, io.SEEK_CUR) - - length = i8(read(1)) - if length: - # Don't know the proper encoding, - # Latin-1 should be a good guess - name = read(length).decode("latin-1", "replace") - - fp.seek(data_end) - layers.append((name, mode, (x0, y0, x1, y1))) - - # get tiles - layerinfo = [] - for i, (name, mode, bbox) in enumerate(layers): - tile = [] - for m in mode: - t = _maketile(fp, m, bbox, 1) - if t: - tile.extend(t) - layerinfo.append((name, mode, bbox, tile)) - - return layerinfo - - -def _maketile( - file: IO[bytes], mode: str, bbox: tuple[int, int, int, int], channels: int -) -> list[ImageFile._Tile]: - tiles = [] - read = file.read - - compression = i16(read(2)) - - xsize = bbox[2] - bbox[0] - ysize = bbox[3] - bbox[1] - - offset = file.tell() - - if compression == 0: - # - # raw compression - for channel in range(channels): - layer = mode[channel] - if mode == "CMYK": - layer += ";I" - tiles.append(ImageFile._Tile("raw", bbox, offset, layer)) - offset = offset + xsize * ysize - - elif compression == 1: - # - # packbits compression - i = 0 - bytecount = read(channels * ysize * 2) - offset = file.tell() - for channel in range(channels): - layer = mode[channel] - if mode == "CMYK": - layer += ";I" - tiles.append(ImageFile._Tile("packbits", bbox, offset, layer)) - for y in range(ysize): - offset = offset + i16(bytecount, i) - i += 2 - - file.seek(offset) - - if offset & 1: - read(1) # padding - - return tiles - - -# -------------------------------------------------------------------- -# registry - - -Image.register_open(PsdImageFile.format, PsdImageFile, _accept) - -Image.register_extension(PsdImageFile.format, ".psd") - -Image.register_mime(PsdImageFile.format, "image/vnd.adobe.photoshop") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/QoiImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/QoiImagePlugin.py deleted file mode 100644 index dba5d809..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/QoiImagePlugin.py +++ /dev/null @@ -1,234 +0,0 @@ -# -# The Python Imaging Library. -# -# QOI support for PIL -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import os -from typing import IO - -from . import Image, ImageFile -from ._binary import i32be as i32 -from ._binary import o8 -from ._binary import o32be as o32 - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(b"qoif") - - -class QoiImageFile(ImageFile.ImageFile): - format = "QOI" - format_description = "Quite OK Image" - - def _open(self) -> None: - if not _accept(self.fp.read(4)): - msg = "not a QOI file" - raise SyntaxError(msg) - - self._size = i32(self.fp.read(4)), i32(self.fp.read(4)) - - channels = self.fp.read(1)[0] - self._mode = "RGB" if channels == 3 else "RGBA" - - self.fp.seek(1, os.SEEK_CUR) # colorspace - self.tile = [ImageFile._Tile("qoi", (0, 0) + self._size, self.fp.tell())] - - -class QoiDecoder(ImageFile.PyDecoder): - _pulls_fd = True - _previous_pixel: bytes | bytearray | None = None - _previously_seen_pixels: dict[int, bytes | bytearray] = {} - - def _add_to_previous_pixels(self, value: bytes | bytearray) -> None: - self._previous_pixel = value - - r, g, b, a = value - hash_value = (r * 3 + g * 5 + b * 7 + a * 11) % 64 - self._previously_seen_pixels[hash_value] = value - - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: - assert self.fd is not None - - self._previously_seen_pixels = {} - self._previous_pixel = bytearray((0, 0, 0, 255)) - - data = bytearray() - bands = Image.getmodebands(self.mode) - dest_length = self.state.xsize * self.state.ysize * bands - while len(data) < dest_length: - byte = self.fd.read(1)[0] - value: bytes | bytearray - if byte == 0b11111110 and self._previous_pixel: # QOI_OP_RGB - value = bytearray(self.fd.read(3)) + self._previous_pixel[3:] - elif byte == 0b11111111: # QOI_OP_RGBA - value = self.fd.read(4) - else: - op = byte >> 6 - if op == 0: # QOI_OP_INDEX - op_index = byte & 0b00111111 - value = self._previously_seen_pixels.get( - op_index, bytearray((0, 0, 0, 0)) - ) - elif op == 1 and self._previous_pixel: # QOI_OP_DIFF - value = bytearray( - ( - (self._previous_pixel[0] + ((byte & 0b00110000) >> 4) - 2) - % 256, - (self._previous_pixel[1] + ((byte & 0b00001100) >> 2) - 2) - % 256, - (self._previous_pixel[2] + (byte & 0b00000011) - 2) % 256, - self._previous_pixel[3], - ) - ) - elif op == 2 and self._previous_pixel: # QOI_OP_LUMA - second_byte = self.fd.read(1)[0] - diff_green = (byte & 0b00111111) - 32 - diff_red = ((second_byte & 0b11110000) >> 4) - 8 - diff_blue = (second_byte & 0b00001111) - 8 - - value = bytearray( - tuple( - (self._previous_pixel[i] + diff_green + diff) % 256 - for i, diff in enumerate((diff_red, 0, diff_blue)) - ) - ) - value += self._previous_pixel[3:] - elif op == 3 and self._previous_pixel: # QOI_OP_RUN - run_length = (byte & 0b00111111) + 1 - value = self._previous_pixel - if bands == 3: - value = value[:3] - data += value * run_length - continue - self._add_to_previous_pixels(value) - - if bands == 3: - value = value[:3] - data += value - self.set_as_raw(data) - return -1, 0 - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - if im.mode == "RGB": - channels = 3 - elif im.mode == "RGBA": - channels = 4 - else: - msg = "Unsupported QOI image mode" - raise ValueError(msg) - - colorspace = 0 if im.encoderinfo.get("colorspace") == "sRGB" else 1 - - fp.write(b"qoif") - fp.write(o32(im.size[0])) - fp.write(o32(im.size[1])) - fp.write(o8(channels)) - fp.write(o8(colorspace)) - - ImageFile._save(im, fp, [ImageFile._Tile("qoi", (0, 0) + im.size)]) - - -class QoiEncoder(ImageFile.PyEncoder): - _pushes_fd = True - _previous_pixel: tuple[int, int, int, int] | None = None - _previously_seen_pixels: dict[int, tuple[int, int, int, int]] = {} - _run = 0 - - def _write_run(self) -> bytes: - data = o8(0b11000000 | (self._run - 1)) # QOI_OP_RUN - self._run = 0 - return data - - def _delta(self, left: int, right: int) -> int: - result = (left - right) & 255 - if result >= 128: - result -= 256 - return result - - def encode(self, bufsize: int) -> tuple[int, int, bytes]: - assert self.im is not None - - self._previously_seen_pixels = {0: (0, 0, 0, 0)} - self._previous_pixel = (0, 0, 0, 255) - - data = bytearray() - w, h = self.im.size - bands = Image.getmodebands(self.mode) - - for y in range(h): - for x in range(w): - pixel = self.im.getpixel((x, y)) - if bands == 3: - pixel = (*pixel, 255) - - if pixel == self._previous_pixel: - self._run += 1 - if self._run == 62: - data += self._write_run() - else: - if self._run: - data += self._write_run() - - r, g, b, a = pixel - hash_value = (r * 3 + g * 5 + b * 7 + a * 11) % 64 - if self._previously_seen_pixels.get(hash_value) == pixel: - data += o8(hash_value) # QOI_OP_INDEX - elif self._previous_pixel: - self._previously_seen_pixels[hash_value] = pixel - - prev_r, prev_g, prev_b, prev_a = self._previous_pixel - if prev_a == a: - delta_r = self._delta(r, prev_r) - delta_g = self._delta(g, prev_g) - delta_b = self._delta(b, prev_b) - - if ( - -2 <= delta_r < 2 - and -2 <= delta_g < 2 - and -2 <= delta_b < 2 - ): - data += o8( - 0b01000000 - | (delta_r + 2) << 4 - | (delta_g + 2) << 2 - | (delta_b + 2) - ) # QOI_OP_DIFF - else: - delta_gr = self._delta(delta_r, delta_g) - delta_gb = self._delta(delta_b, delta_g) - if ( - -8 <= delta_gr < 8 - and -32 <= delta_g < 32 - and -8 <= delta_gb < 8 - ): - data += o8( - 0b10000000 | (delta_g + 32) - ) # QOI_OP_LUMA - data += o8((delta_gr + 8) << 4 | (delta_gb + 8)) - else: - data += o8(0b11111110) # QOI_OP_RGB - data += bytes(pixel[:3]) - else: - data += o8(0b11111111) # QOI_OP_RGBA - data += bytes(pixel) - - self._previous_pixel = pixel - - if self._run: - data += self._write_run() - data += bytes((0, 0, 0, 0, 0, 0, 0, 1)) # padding - - return len(data), 0, data - - -Image.register_open(QoiImageFile.format, QoiImageFile, _accept) -Image.register_decoder("qoi", QoiDecoder) -Image.register_extension(QoiImageFile.format, ".qoi") - -Image.register_save(QoiImageFile.format, _save) -Image.register_encoder("qoi", QoiEncoder) diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/SgiImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/SgiImagePlugin.py deleted file mode 100644 index 85302215..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/SgiImagePlugin.py +++ /dev/null @@ -1,231 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# SGI image file handling -# -# See "The SGI Image File Format (Draft version 0.97)", Paul Haeberli. -# -# -# -# History: -# 2017-22-07 mb Add RLE decompression -# 2016-16-10 mb Add save method without compression -# 1995-09-10 fl Created -# -# Copyright (c) 2016 by Mickael Bonfill. -# Copyright (c) 2008 by Karsten Hiddemann. -# Copyright (c) 1997 by Secret Labs AB. -# Copyright (c) 1995 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import os -import struct -from typing import IO - -from . import Image, ImageFile -from ._binary import i16be as i16 -from ._binary import o8 - - -def _accept(prefix: bytes) -> bool: - return len(prefix) >= 2 and i16(prefix) == 474 - - -MODES = { - (1, 1, 1): "L", - (1, 2, 1): "L", - (2, 1, 1): "L;16B", - (2, 2, 1): "L;16B", - (1, 3, 3): "RGB", - (2, 3, 3): "RGB;16B", - (1, 3, 4): "RGBA", - (2, 3, 4): "RGBA;16B", -} - - -## -# Image plugin for SGI images. -class SgiImageFile(ImageFile.ImageFile): - format = "SGI" - format_description = "SGI Image File Format" - - def _open(self) -> None: - # HEAD - assert self.fp is not None - - headlen = 512 - s = self.fp.read(headlen) - - if not _accept(s): - msg = "Not an SGI image file" - raise ValueError(msg) - - # compression : verbatim or RLE - compression = s[2] - - # bpc : 1 or 2 bytes (8bits or 16bits) - bpc = s[3] - - # dimension : 1, 2 or 3 (depending on xsize, ysize and zsize) - dimension = i16(s, 4) - - # xsize : width - xsize = i16(s, 6) - - # ysize : height - ysize = i16(s, 8) - - # zsize : channels count - zsize = i16(s, 10) - - # determine mode from bits/zsize - try: - rawmode = MODES[(bpc, dimension, zsize)] - except KeyError: - msg = "Unsupported SGI image mode" - raise ValueError(msg) - - self._size = xsize, ysize - self._mode = rawmode.split(";")[0] - if self.mode == "RGB": - self.custom_mimetype = "image/rgb" - - # orientation -1 : scanlines begins at the bottom-left corner - orientation = -1 - - # decoder info - if compression == 0: - pagesize = xsize * ysize * bpc - if bpc == 2: - self.tile = [ - ImageFile._Tile( - "SGI16", - (0, 0) + self.size, - headlen, - (self.mode, 0, orientation), - ) - ] - else: - self.tile = [] - offset = headlen - for layer in self.mode: - self.tile.append( - ImageFile._Tile( - "raw", (0, 0) + self.size, offset, (layer, 0, orientation) - ) - ) - offset += pagesize - elif compression == 1: - self.tile = [ - ImageFile._Tile( - "sgi_rle", (0, 0) + self.size, headlen, (rawmode, orientation, bpc) - ) - ] - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - if im.mode not in {"RGB", "RGBA", "L"}: - msg = "Unsupported SGI image mode" - raise ValueError(msg) - - # Get the keyword arguments - info = im.encoderinfo - - # Byte-per-pixel precision, 1 = 8bits per pixel - bpc = info.get("bpc", 1) - - if bpc not in (1, 2): - msg = "Unsupported number of bytes per pixel" - raise ValueError(msg) - - # Flip the image, since the origin of SGI file is the bottom-left corner - orientation = -1 - # Define the file as SGI File Format - magic_number = 474 - # Run-Length Encoding Compression - Unsupported at this time - rle = 0 - - # X Dimension = width / Y Dimension = height - x, y = im.size - # Z Dimension: Number of channels - z = len(im.mode) - # Number of dimensions (x,y,z) - if im.mode == "L": - dimension = 1 if y == 1 else 2 - else: - dimension = 3 - - # Minimum Byte value - pinmin = 0 - # Maximum Byte value (255 = 8bits per pixel) - pinmax = 255 - # Image name (79 characters max, truncated below in write) - img_name = os.path.splitext(os.path.basename(filename))[0] - if isinstance(img_name, str): - img_name = img_name.encode("ascii", "ignore") - # Standard representation of pixel in the file - colormap = 0 - fp.write(struct.pack(">h", magic_number)) - fp.write(o8(rle)) - fp.write(o8(bpc)) - fp.write(struct.pack(">H", dimension)) - fp.write(struct.pack(">H", x)) - fp.write(struct.pack(">H", y)) - fp.write(struct.pack(">H", z)) - fp.write(struct.pack(">l", pinmin)) - fp.write(struct.pack(">l", pinmax)) - fp.write(struct.pack("4s", b"")) # dummy - fp.write(struct.pack("79s", img_name)) # truncates to 79 chars - fp.write(struct.pack("s", b"")) # force null byte after img_name - fp.write(struct.pack(">l", colormap)) - fp.write(struct.pack("404s", b"")) # dummy - - rawmode = "L" - if bpc == 2: - rawmode = "L;16B" - - for channel in im.split(): - fp.write(channel.tobytes("raw", rawmode, 0, orientation)) - - if hasattr(fp, "flush"): - fp.flush() - - -class SGI16Decoder(ImageFile.PyDecoder): - _pulls_fd = True - - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: - assert self.fd is not None - assert self.im is not None - - rawmode, stride, orientation = self.args - pagesize = self.state.xsize * self.state.ysize - zsize = len(self.mode) - self.fd.seek(512) - - for band in range(zsize): - channel = Image.new("L", (self.state.xsize, self.state.ysize)) - channel.frombytes( - self.fd.read(2 * pagesize), "raw", "L;16B", stride, orientation - ) - self.im.putband(channel.im, band) - - return -1, 0 - - -# -# registry - - -Image.register_decoder("SGI16", SGI16Decoder) -Image.register_open(SgiImageFile.format, SgiImageFile, _accept) -Image.register_save(SgiImageFile.format, _save) -Image.register_mime(SgiImageFile.format, "image/sgi") - -Image.register_extensions(SgiImageFile.format, [".bw", ".rgb", ".rgba", ".sgi"]) - -# End of file diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/SpiderImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/SpiderImagePlugin.py deleted file mode 100644 index 868019e8..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/SpiderImagePlugin.py +++ /dev/null @@ -1,331 +0,0 @@ -# -# The Python Imaging Library. -# -# SPIDER image file handling -# -# History: -# 2004-08-02 Created BB -# 2006-03-02 added save method -# 2006-03-13 added support for stack images -# -# Copyright (c) 2004 by Health Research Inc. (HRI) RENSSELAER, NY 12144. -# Copyright (c) 2004 by William Baxter. -# Copyright (c) 2004 by Secret Labs AB. -# Copyright (c) 2004 by Fredrik Lundh. -# - -## -# Image plugin for the Spider image format. This format is used -# by the SPIDER software, in processing image data from electron -# microscopy and tomography. -## - -# -# SpiderImagePlugin.py -# -# The Spider image format is used by SPIDER software, in processing -# image data from electron microscopy and tomography. -# -# Spider home page: -# https://spider.wadsworth.org/spider_doc/spider/docs/spider.html -# -# Details about the Spider image format: -# https://spider.wadsworth.org/spider_doc/spider/docs/image_doc.html -# -from __future__ import annotations - -import os -import struct -import sys -from typing import IO, Any, cast - -from . import Image, ImageFile -from ._util import DeferredError - -TYPE_CHECKING = False - - -def isInt(f: Any) -> int: - try: - i = int(f) - if f - i == 0: - return 1 - else: - return 0 - except (ValueError, OverflowError): - return 0 - - -iforms = [1, 3, -11, -12, -21, -22] - - -# There is no magic number to identify Spider files, so just check a -# series of header locations to see if they have reasonable values. -# Returns no. of bytes in the header, if it is a valid Spider header, -# otherwise returns 0 - - -def isSpiderHeader(t: tuple[float, ...]) -> int: - h = (99,) + t # add 1 value so can use spider header index start=1 - # header values 1,2,5,12,13,22,23 should be integers - for i in [1, 2, 5, 12, 13, 22, 23]: - if not isInt(h[i]): - return 0 - # check iform - iform = int(h[5]) - if iform not in iforms: - return 0 - # check other header values - labrec = int(h[13]) # no. records in file header - labbyt = int(h[22]) # total no. of bytes in header - lenbyt = int(h[23]) # record length in bytes - if labbyt != (labrec * lenbyt): - return 0 - # looks like a valid header - return labbyt - - -def isSpiderImage(filename: str) -> int: - with open(filename, "rb") as fp: - f = fp.read(92) # read 23 * 4 bytes - t = struct.unpack(">23f", f) # try big-endian first - hdrlen = isSpiderHeader(t) - if hdrlen == 0: - t = struct.unpack("<23f", f) # little-endian - hdrlen = isSpiderHeader(t) - return hdrlen - - -class SpiderImageFile(ImageFile.ImageFile): - format = "SPIDER" - format_description = "Spider 2D image" - _close_exclusive_fp_after_loading = False - - def _open(self) -> None: - # check header - n = 27 * 4 # read 27 float values - f = self.fp.read(n) - - try: - self.bigendian = 1 - t = struct.unpack(">27f", f) # try big-endian first - hdrlen = isSpiderHeader(t) - if hdrlen == 0: - self.bigendian = 0 - t = struct.unpack("<27f", f) # little-endian - hdrlen = isSpiderHeader(t) - if hdrlen == 0: - msg = "not a valid Spider file" - raise SyntaxError(msg) - except struct.error as e: - msg = "not a valid Spider file" - raise SyntaxError(msg) from e - - h = (99,) + t # add 1 value : spider header index starts at 1 - iform = int(h[5]) - if iform != 1: - msg = "not a Spider 2D image" - raise SyntaxError(msg) - - self._size = int(h[12]), int(h[2]) # size in pixels (width, height) - self.istack = int(h[24]) - self.imgnumber = int(h[27]) - - if self.istack == 0 and self.imgnumber == 0: - # stk=0, img=0: a regular 2D image - offset = hdrlen - self._nimages = 1 - elif self.istack > 0 and self.imgnumber == 0: - # stk>0, img=0: Opening the stack for the first time - self.imgbytes = int(h[12]) * int(h[2]) * 4 - self.hdrlen = hdrlen - self._nimages = int(h[26]) - # Point to the first image in the stack - offset = hdrlen * 2 - self.imgnumber = 1 - elif self.istack == 0 and self.imgnumber > 0: - # stk=0, img>0: an image within the stack - offset = hdrlen + self.stkoffset - self.istack = 2 # So Image knows it's still a stack - else: - msg = "inconsistent stack header values" - raise SyntaxError(msg) - - if self.bigendian: - self.rawmode = "F;32BF" - else: - self.rawmode = "F;32F" - self._mode = "F" - - self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, offset, self.rawmode)] - self._fp = self.fp # FIXME: hack - - @property - def n_frames(self) -> int: - return self._nimages - - @property - def is_animated(self) -> bool: - return self._nimages > 1 - - # 1st image index is zero (although SPIDER imgnumber starts at 1) - def tell(self) -> int: - if self.imgnumber < 1: - return 0 - else: - return self.imgnumber - 1 - - def seek(self, frame: int) -> None: - if self.istack == 0: - msg = "attempt to seek in a non-stack file" - raise EOFError(msg) - if not self._seek_check(frame): - return - if isinstance(self._fp, DeferredError): - raise self._fp.ex - self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes) - self.fp = self._fp - self.fp.seek(self.stkoffset) - self._open() - - # returns a byte image after rescaling to 0..255 - def convert2byte(self, depth: int = 255) -> Image.Image: - extrema = self.getextrema() - assert isinstance(extrema[0], float) - minimum, maximum = cast(tuple[float, float], extrema) - m: float = 1 - if maximum != minimum: - m = depth / (maximum - minimum) - b = -m * minimum - return self.point(lambda i: i * m + b).convert("L") - - if TYPE_CHECKING: - from . import ImageTk - - # returns a ImageTk.PhotoImage object, after rescaling to 0..255 - def tkPhotoImage(self) -> ImageTk.PhotoImage: - from . import ImageTk - - return ImageTk.PhotoImage(self.convert2byte(), palette=256) - - -# -------------------------------------------------------------------- -# Image series - - -# given a list of filenames, return a list of images -def loadImageSeries(filelist: list[str] | None = None) -> list[Image.Image] | None: - """create a list of :py:class:`~PIL.Image.Image` objects for use in a montage""" - if filelist is None or len(filelist) < 1: - return None - - byte_imgs = [] - for img in filelist: - if not os.path.exists(img): - print(f"unable to find {img}") - continue - try: - with Image.open(img) as im: - assert isinstance(im, SpiderImageFile) - byte_im = im.convert2byte() - except Exception: - if not isSpiderImage(img): - print(f"{img} is not a Spider image file") - continue - byte_im.info["filename"] = img - byte_imgs.append(byte_im) - return byte_imgs - - -# -------------------------------------------------------------------- -# For saving images in Spider format - - -def makeSpiderHeader(im: Image.Image) -> list[bytes]: - nsam, nrow = im.size - lenbyt = nsam * 4 # There are labrec records in the header - labrec = int(1024 / lenbyt) - if 1024 % lenbyt != 0: - labrec += 1 - labbyt = labrec * lenbyt - nvalues = int(labbyt / 4) - if nvalues < 23: - return [] - - hdr = [0.0] * nvalues - - # NB these are Fortran indices - hdr[1] = 1.0 # nslice (=1 for an image) - hdr[2] = float(nrow) # number of rows per slice - hdr[3] = float(nrow) # number of records in the image - hdr[5] = 1.0 # iform for 2D image - hdr[12] = float(nsam) # number of pixels per line - hdr[13] = float(labrec) # number of records in file header - hdr[22] = float(labbyt) # total number of bytes in header - hdr[23] = float(lenbyt) # record length in bytes - - # adjust for Fortran indexing - hdr = hdr[1:] - hdr.append(0.0) - # pack binary data into a string - return [struct.pack("f", v) for v in hdr] - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - if im.mode != "F": - im = im.convert("F") - - hdr = makeSpiderHeader(im) - if len(hdr) < 256: - msg = "Error creating Spider header" - raise OSError(msg) - - # write the SPIDER header - fp.writelines(hdr) - - rawmode = "F;32NF" # 32-bit native floating point - ImageFile._save(im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 0, rawmode)]) - - -def _save_spider(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - # get the filename extension and register it with Image - filename_ext = os.path.splitext(filename)[1] - ext = filename_ext.decode() if isinstance(filename_ext, bytes) else filename_ext - Image.register_extension(SpiderImageFile.format, ext) - _save(im, fp, filename) - - -# -------------------------------------------------------------------- - - -Image.register_open(SpiderImageFile.format, SpiderImageFile) -Image.register_save(SpiderImageFile.format, _save_spider) - -if __name__ == "__main__": - if len(sys.argv) < 2: - print("Syntax: python3 SpiderImagePlugin.py [infile] [outfile]") - sys.exit() - - filename = sys.argv[1] - if not isSpiderImage(filename): - print("input image must be in Spider format") - sys.exit() - - with Image.open(filename) as im: - print(f"image: {im}") - print(f"format: {im.format}") - print(f"size: {im.size}") - print(f"mode: {im.mode}") - print("max, min: ", end=" ") - print(im.getextrema()) - - if len(sys.argv) > 2: - outfile = sys.argv[2] - - # perform some image operation - im = im.transpose(Image.Transpose.FLIP_LEFT_RIGHT) - print( - f"saving a flipped version of {os.path.basename(filename)} " - f"as {outfile} " - ) - im.save(outfile, SpiderImageFile.format) diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/SunImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/SunImagePlugin.py deleted file mode 100644 index 8912379e..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/SunImagePlugin.py +++ /dev/null @@ -1,145 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# Sun image file handling -# -# History: -# 1995-09-10 fl Created -# 1996-05-28 fl Fixed 32-bit alignment -# 1998-12-29 fl Import ImagePalette module -# 2001-12-18 fl Fixed palette loading (from Jean-Claude Rimbault) -# -# Copyright (c) 1997-2001 by Secret Labs AB -# Copyright (c) 1995-1996 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from . import Image, ImageFile, ImagePalette -from ._binary import i32be as i32 - - -def _accept(prefix: bytes) -> bool: - return len(prefix) >= 4 and i32(prefix) == 0x59A66A95 - - -## -# Image plugin for Sun raster files. - - -class SunImageFile(ImageFile.ImageFile): - format = "SUN" - format_description = "Sun Raster File" - - def _open(self) -> None: - # The Sun Raster file header is 32 bytes in length - # and has the following format: - - # typedef struct _SunRaster - # { - # DWORD MagicNumber; /* Magic (identification) number */ - # DWORD Width; /* Width of image in pixels */ - # DWORD Height; /* Height of image in pixels */ - # DWORD Depth; /* Number of bits per pixel */ - # DWORD Length; /* Size of image data in bytes */ - # DWORD Type; /* Type of raster file */ - # DWORD ColorMapType; /* Type of color map */ - # DWORD ColorMapLength; /* Size of the color map in bytes */ - # } SUNRASTER; - - assert self.fp is not None - - # HEAD - s = self.fp.read(32) - if not _accept(s): - msg = "not an SUN raster file" - raise SyntaxError(msg) - - offset = 32 - - self._size = i32(s, 4), i32(s, 8) - - depth = i32(s, 12) - # data_length = i32(s, 16) # unreliable, ignore. - file_type = i32(s, 20) - palette_type = i32(s, 24) # 0: None, 1: RGB, 2: Raw/arbitrary - palette_length = i32(s, 28) - - if depth == 1: - self._mode, rawmode = "1", "1;I" - elif depth == 4: - self._mode, rawmode = "L", "L;4" - elif depth == 8: - self._mode = rawmode = "L" - elif depth == 24: - if file_type == 3: - self._mode, rawmode = "RGB", "RGB" - else: - self._mode, rawmode = "RGB", "BGR" - elif depth == 32: - if file_type == 3: - self._mode, rawmode = "RGB", "RGBX" - else: - self._mode, rawmode = "RGB", "BGRX" - else: - msg = "Unsupported Mode/Bit Depth" - raise SyntaxError(msg) - - if palette_length: - if palette_length > 1024: - msg = "Unsupported Color Palette Length" - raise SyntaxError(msg) - - if palette_type != 1: - msg = "Unsupported Palette Type" - raise SyntaxError(msg) - - offset = offset + palette_length - self.palette = ImagePalette.raw("RGB;L", self.fp.read(palette_length)) - if self.mode == "L": - self._mode = "P" - rawmode = rawmode.replace("L", "P") - - # 16 bit boundaries on stride - stride = ((self.size[0] * depth + 15) // 16) * 2 - - # file type: Type is the version (or flavor) of the bitmap - # file. The following values are typically found in the Type - # field: - # 0000h Old - # 0001h Standard - # 0002h Byte-encoded - # 0003h RGB format - # 0004h TIFF format - # 0005h IFF format - # FFFFh Experimental - - # Old and standard are the same, except for the length tag. - # byte-encoded is run-length-encoded - # RGB looks similar to standard, but RGB byte order - # TIFF and IFF mean that they were converted from T/IFF - # Experimental means that it's something else. - # (https://www.fileformat.info/format/sunraster/egff.htm) - - if file_type in (0, 1, 3, 4, 5): - self.tile = [ - ImageFile._Tile("raw", (0, 0) + self.size, offset, (rawmode, stride)) - ] - elif file_type == 2: - self.tile = [ - ImageFile._Tile("sun_rle", (0, 0) + self.size, offset, rawmode) - ] - else: - msg = "Unsupported Sun Raster file type" - raise SyntaxError(msg) - - -# -# registry - - -Image.register_open(SunImageFile.format, SunImageFile, _accept) - -Image.register_extension(SunImageFile.format, ".ras") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/TarIO.py b/.venv-docs/lib/python3.12/site-packages/PIL/TarIO.py deleted file mode 100644 index 86490a49..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/TarIO.py +++ /dev/null @@ -1,61 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# read files from within a tar file -# -# History: -# 95-06-18 fl Created -# 96-05-28 fl Open files in binary mode -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1995-96. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io - -from . import ContainerIO - - -class TarIO(ContainerIO.ContainerIO[bytes]): - """A file object that provides read access to a given member of a TAR file.""" - - def __init__(self, tarfile: str, file: str) -> None: - """ - Create file object. - - :param tarfile: Name of TAR file. - :param file: Name of member file. - """ - self.fh = open(tarfile, "rb") - - while True: - s = self.fh.read(512) - if len(s) != 512: - self.fh.close() - - msg = "unexpected end of tar file" - raise OSError(msg) - - name = s[:100].decode("utf-8") - i = name.find("\0") - if i == 0: - self.fh.close() - - msg = "cannot find subfile" - raise OSError(msg) - if i > 0: - name = name[:i] - - size = int(s[124:135], 8) - - if file == name: - break - - self.fh.seek((size + 511) & (~511), io.SEEK_CUR) - - # Open region - super().__init__(self.fh, self.fh.tell(), size) diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/TgaImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/TgaImagePlugin.py deleted file mode 100644 index 90d5b5cf..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/TgaImagePlugin.py +++ /dev/null @@ -1,264 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# TGA file handling -# -# History: -# 95-09-01 fl created (reads 24-bit files only) -# 97-01-04 fl support more TGA versions, including compressed images -# 98-07-04 fl fixed orientation and alpha layer bugs -# 98-09-11 fl fixed orientation for runlength decoder -# -# Copyright (c) Secret Labs AB 1997-98. -# Copyright (c) Fredrik Lundh 1995-97. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import warnings -from typing import IO - -from . import Image, ImageFile, ImagePalette -from ._binary import i16le as i16 -from ._binary import o8 -from ._binary import o16le as o16 - -# -# -------------------------------------------------------------------- -# Read RGA file - - -MODES = { - # map imagetype/depth to rawmode - (1, 8): "P", - (3, 1): "1", - (3, 8): "L", - (3, 16): "LA", - (2, 16): "BGRA;15Z", - (2, 24): "BGR", - (2, 32): "BGRA", -} - - -## -# Image plugin for Targa files. - - -class TgaImageFile(ImageFile.ImageFile): - format = "TGA" - format_description = "Targa" - - def _open(self) -> None: - # process header - assert self.fp is not None - - s = self.fp.read(18) - - id_len = s[0] - - colormaptype = s[1] - imagetype = s[2] - - depth = s[16] - - flags = s[17] - - self._size = i16(s, 12), i16(s, 14) - - # validate header fields - if ( - colormaptype not in (0, 1) - or self.size[0] <= 0 - or self.size[1] <= 0 - or depth not in (1, 8, 16, 24, 32) - ): - msg = "not a TGA file" - raise SyntaxError(msg) - - # image mode - if imagetype in (3, 11): - self._mode = "L" - if depth == 1: - self._mode = "1" # ??? - elif depth == 16: - self._mode = "LA" - elif imagetype in (1, 9): - self._mode = "P" if colormaptype else "L" - elif imagetype in (2, 10): - self._mode = "RGB" if depth == 24 else "RGBA" - else: - msg = "unknown TGA mode" - raise SyntaxError(msg) - - # orientation - orientation = flags & 0x30 - self._flip_horizontally = orientation in [0x10, 0x30] - if orientation in [0x20, 0x30]: - orientation = 1 - elif orientation in [0, 0x10]: - orientation = -1 - else: - msg = "unknown TGA orientation" - raise SyntaxError(msg) - - self.info["orientation"] = orientation - - if imagetype & 8: - self.info["compression"] = "tga_rle" - - if id_len: - self.info["id_section"] = self.fp.read(id_len) - - if colormaptype: - # read palette - start, size, mapdepth = i16(s, 3), i16(s, 5), s[7] - if mapdepth == 16: - self.palette = ImagePalette.raw( - "BGRA;15Z", bytes(2 * start) + self.fp.read(2 * size) - ) - self.palette.mode = "RGBA" - elif mapdepth == 24: - self.palette = ImagePalette.raw( - "BGR", bytes(3 * start) + self.fp.read(3 * size) - ) - elif mapdepth == 32: - self.palette = ImagePalette.raw( - "BGRA", bytes(4 * start) + self.fp.read(4 * size) - ) - else: - msg = "unknown TGA map depth" - raise SyntaxError(msg) - - # setup tile descriptor - try: - rawmode = MODES[(imagetype & 7, depth)] - if imagetype & 8: - # compressed - self.tile = [ - ImageFile._Tile( - "tga_rle", - (0, 0) + self.size, - self.fp.tell(), - (rawmode, orientation, depth), - ) - ] - else: - self.tile = [ - ImageFile._Tile( - "raw", - (0, 0) + self.size, - self.fp.tell(), - (rawmode, 0, orientation), - ) - ] - except KeyError: - pass # cannot decode - - def load_end(self) -> None: - if self._flip_horizontally: - self.im = self.im.transpose(Image.Transpose.FLIP_LEFT_RIGHT) - - -# -# -------------------------------------------------------------------- -# Write TGA file - - -SAVE = { - "1": ("1", 1, 0, 3), - "L": ("L", 8, 0, 3), - "LA": ("LA", 16, 0, 3), - "P": ("P", 8, 1, 1), - "RGB": ("BGR", 24, 0, 2), - "RGBA": ("BGRA", 32, 0, 2), -} - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - try: - rawmode, bits, colormaptype, imagetype = SAVE[im.mode] - except KeyError as e: - msg = f"cannot write mode {im.mode} as TGA" - raise OSError(msg) from e - - if "rle" in im.encoderinfo: - rle = im.encoderinfo["rle"] - else: - compression = im.encoderinfo.get("compression", im.info.get("compression")) - rle = compression == "tga_rle" - if rle: - imagetype += 8 - - id_section = im.encoderinfo.get("id_section", im.info.get("id_section", "")) - id_len = len(id_section) - if id_len > 255: - id_len = 255 - id_section = id_section[:255] - warnings.warn("id_section has been trimmed to 255 characters") - - if colormaptype: - palette = im.im.getpalette("RGB", "BGR") - colormaplength, colormapentry = len(palette) // 3, 24 - else: - colormaplength, colormapentry = 0, 0 - - if im.mode in ("LA", "RGBA"): - flags = 8 - else: - flags = 0 - - orientation = im.encoderinfo.get("orientation", im.info.get("orientation", -1)) - if orientation > 0: - flags = flags | 0x20 - - fp.write( - o8(id_len) - + o8(colormaptype) - + o8(imagetype) - + o16(0) # colormapfirst - + o16(colormaplength) - + o8(colormapentry) - + o16(0) - + o16(0) - + o16(im.size[0]) - + o16(im.size[1]) - + o8(bits) - + o8(flags) - ) - - if id_section: - fp.write(id_section) - - if colormaptype: - fp.write(palette) - - if rle: - ImageFile._save( - im, - fp, - [ImageFile._Tile("tga_rle", (0, 0) + im.size, 0, (rawmode, orientation))], - ) - else: - ImageFile._save( - im, - fp, - [ImageFile._Tile("raw", (0, 0) + im.size, 0, (rawmode, 0, orientation))], - ) - - # write targa version 2 footer - fp.write(b"\000" * 8 + b"TRUEVISION-XFILE." + b"\000") - - -# -# -------------------------------------------------------------------- -# Registry - - -Image.register_open(TgaImageFile.format, TgaImageFile) -Image.register_save(TgaImageFile.format, _save) - -Image.register_extensions(TgaImageFile.format, [".tga", ".icb", ".vda", ".vst"]) - -Image.register_mime(TgaImageFile.format, "image/x-tga") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/TiffImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/TiffImagePlugin.py deleted file mode 100644 index de2ce066..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/TiffImagePlugin.py +++ /dev/null @@ -1,2338 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# TIFF file handling -# -# TIFF is a flexible, if somewhat aged, image file format originally -# defined by Aldus. Although TIFF supports a wide variety of pixel -# layouts and compression methods, the name doesn't really stand for -# "thousands of incompatible file formats," it just feels that way. -# -# To read TIFF data from a stream, the stream must be seekable. For -# progressive decoding, make sure to use TIFF files where the tag -# directory is placed first in the file. -# -# History: -# 1995-09-01 fl Created -# 1996-05-04 fl Handle JPEGTABLES tag -# 1996-05-18 fl Fixed COLORMAP support -# 1997-01-05 fl Fixed PREDICTOR support -# 1997-08-27 fl Added support for rational tags (from Perry Stoll) -# 1998-01-10 fl Fixed seek/tell (from Jan Blom) -# 1998-07-15 fl Use private names for internal variables -# 1999-06-13 fl Rewritten for PIL 1.0 (1.0) -# 2000-10-11 fl Additional fixes for Python 2.0 (1.1) -# 2001-04-17 fl Fixed rewind support (seek to frame 0) (1.2) -# 2001-05-12 fl Added write support for more tags (from Greg Couch) (1.3) -# 2001-12-18 fl Added workaround for broken Matrox library -# 2002-01-18 fl Don't mess up if photometric tag is missing (D. Alan Stewart) -# 2003-05-19 fl Check FILLORDER tag -# 2003-09-26 fl Added RGBa support -# 2004-02-24 fl Added DPI support; fixed rational write support -# 2005-02-07 fl Added workaround for broken Corel Draw 10 files -# 2006-01-09 fl Added support for float/double tags (from Russell Nelson) -# -# Copyright (c) 1997-2006 by Secret Labs AB. All rights reserved. -# Copyright (c) 1995-1997 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io -import itertools -import logging -import math -import os -import struct -import warnings -from collections.abc import Callable, MutableMapping -from fractions import Fraction -from numbers import Number, Rational -from typing import IO, Any, cast - -from . import ExifTags, Image, ImageFile, ImageOps, ImagePalette, TiffTags -from ._binary import i16be as i16 -from ._binary import i32be as i32 -from ._binary import o8 -from ._util import DeferredError, is_path -from .TiffTags import TYPES - -TYPE_CHECKING = False -if TYPE_CHECKING: - from collections.abc import Iterator - from typing import NoReturn - - from ._typing import Buffer, IntegralLike, StrOrBytesPath - -logger = logging.getLogger(__name__) - -# Set these to true to force use of libtiff for reading or writing. -READ_LIBTIFF = False -WRITE_LIBTIFF = False -STRIP_SIZE = 65536 - -II = b"II" # little-endian (Intel style) -MM = b"MM" # big-endian (Motorola style) - -# -# -------------------------------------------------------------------- -# Read TIFF files - -# a few tag names, just to make the code below a bit more readable -OSUBFILETYPE = 255 -IMAGEWIDTH = 256 -IMAGELENGTH = 257 -BITSPERSAMPLE = 258 -COMPRESSION = 259 -PHOTOMETRIC_INTERPRETATION = 262 -FILLORDER = 266 -IMAGEDESCRIPTION = 270 -STRIPOFFSETS = 273 -SAMPLESPERPIXEL = 277 -ROWSPERSTRIP = 278 -STRIPBYTECOUNTS = 279 -X_RESOLUTION = 282 -Y_RESOLUTION = 283 -PLANAR_CONFIGURATION = 284 -RESOLUTION_UNIT = 296 -TRANSFERFUNCTION = 301 -SOFTWARE = 305 -DATE_TIME = 306 -ARTIST = 315 -PREDICTOR = 317 -COLORMAP = 320 -TILEWIDTH = 322 -TILELENGTH = 323 -TILEOFFSETS = 324 -TILEBYTECOUNTS = 325 -SUBIFD = 330 -EXTRASAMPLES = 338 -SAMPLEFORMAT = 339 -JPEGTABLES = 347 -YCBCRSUBSAMPLING = 530 -REFERENCEBLACKWHITE = 532 -COPYRIGHT = 33432 -IPTC_NAA_CHUNK = 33723 # newsphoto properties -PHOTOSHOP_CHUNK = 34377 # photoshop properties -ICCPROFILE = 34675 -EXIFIFD = 34665 -XMP = 700 -JPEGQUALITY = 65537 # pseudo-tag by libtiff - -# https://github.com/imagej/ImageJA/blob/master/src/main/java/ij/io/TiffDecoder.java -IMAGEJ_META_DATA_BYTE_COUNTS = 50838 -IMAGEJ_META_DATA = 50839 - -COMPRESSION_INFO = { - # Compression => pil compression name - 1: "raw", - 2: "tiff_ccitt", - 3: "group3", - 4: "group4", - 5: "tiff_lzw", - 6: "tiff_jpeg", # obsolete - 7: "jpeg", - 8: "tiff_adobe_deflate", - 32771: "tiff_raw_16", # 16-bit padding - 32773: "packbits", - 32809: "tiff_thunderscan", - 32946: "tiff_deflate", - 34676: "tiff_sgilog", - 34677: "tiff_sgilog24", - 34925: "lzma", - 50000: "zstd", - 50001: "webp", -} - -COMPRESSION_INFO_REV = {v: k for k, v in COMPRESSION_INFO.items()} - -OPEN_INFO = { - # (ByteOrder, PhotoInterpretation, SampleFormat, FillOrder, BitsPerSample, - # ExtraSamples) => mode, rawmode - (II, 0, (1,), 1, (1,), ()): ("1", "1;I"), - (MM, 0, (1,), 1, (1,), ()): ("1", "1;I"), - (II, 0, (1,), 2, (1,), ()): ("1", "1;IR"), - (MM, 0, (1,), 2, (1,), ()): ("1", "1;IR"), - (II, 1, (1,), 1, (1,), ()): ("1", "1"), - (MM, 1, (1,), 1, (1,), ()): ("1", "1"), - (II, 1, (1,), 2, (1,), ()): ("1", "1;R"), - (MM, 1, (1,), 2, (1,), ()): ("1", "1;R"), - (II, 0, (1,), 1, (2,), ()): ("L", "L;2I"), - (MM, 0, (1,), 1, (2,), ()): ("L", "L;2I"), - (II, 0, (1,), 2, (2,), ()): ("L", "L;2IR"), - (MM, 0, (1,), 2, (2,), ()): ("L", "L;2IR"), - (II, 1, (1,), 1, (2,), ()): ("L", "L;2"), - (MM, 1, (1,), 1, (2,), ()): ("L", "L;2"), - (II, 1, (1,), 2, (2,), ()): ("L", "L;2R"), - (MM, 1, (1,), 2, (2,), ()): ("L", "L;2R"), - (II, 0, (1,), 1, (4,), ()): ("L", "L;4I"), - (MM, 0, (1,), 1, (4,), ()): ("L", "L;4I"), - (II, 0, (1,), 2, (4,), ()): ("L", "L;4IR"), - (MM, 0, (1,), 2, (4,), ()): ("L", "L;4IR"), - (II, 1, (1,), 1, (4,), ()): ("L", "L;4"), - (MM, 1, (1,), 1, (4,), ()): ("L", "L;4"), - (II, 1, (1,), 2, (4,), ()): ("L", "L;4R"), - (MM, 1, (1,), 2, (4,), ()): ("L", "L;4R"), - (II, 0, (1,), 1, (8,), ()): ("L", "L;I"), - (MM, 0, (1,), 1, (8,), ()): ("L", "L;I"), - (II, 0, (1,), 2, (8,), ()): ("L", "L;IR"), - (MM, 0, (1,), 2, (8,), ()): ("L", "L;IR"), - (II, 1, (1,), 1, (8,), ()): ("L", "L"), - (MM, 1, (1,), 1, (8,), ()): ("L", "L"), - (II, 1, (2,), 1, (8,), ()): ("L", "L"), - (MM, 1, (2,), 1, (8,), ()): ("L", "L"), - (II, 1, (1,), 2, (8,), ()): ("L", "L;R"), - (MM, 1, (1,), 2, (8,), ()): ("L", "L;R"), - (II, 1, (1,), 1, (12,), ()): ("I;16", "I;12"), - (II, 0, (1,), 1, (16,), ()): ("I;16", "I;16"), - (II, 1, (1,), 1, (16,), ()): ("I;16", "I;16"), - (MM, 1, (1,), 1, (16,), ()): ("I;16B", "I;16B"), - (II, 1, (1,), 2, (16,), ()): ("I;16", "I;16R"), - (II, 1, (2,), 1, (16,), ()): ("I", "I;16S"), - (MM, 1, (2,), 1, (16,), ()): ("I", "I;16BS"), - (II, 0, (3,), 1, (32,), ()): ("F", "F;32F"), - (MM, 0, (3,), 1, (32,), ()): ("F", "F;32BF"), - (II, 1, (1,), 1, (32,), ()): ("I", "I;32N"), - (II, 1, (2,), 1, (32,), ()): ("I", "I;32S"), - (MM, 1, (2,), 1, (32,), ()): ("I", "I;32BS"), - (II, 1, (3,), 1, (32,), ()): ("F", "F;32F"), - (MM, 1, (3,), 1, (32,), ()): ("F", "F;32BF"), - (II, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"), - (MM, 1, (1,), 1, (8, 8), (2,)): ("LA", "LA"), - (II, 2, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"), - (MM, 2, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"), - (II, 2, (1,), 2, (8, 8, 8), ()): ("RGB", "RGB;R"), - (MM, 2, (1,), 2, (8, 8, 8), ()): ("RGB", "RGB;R"), - (II, 2, (1,), 1, (8, 8, 8, 8), ()): ("RGBA", "RGBA"), # missing ExtraSamples - (MM, 2, (1,), 1, (8, 8, 8, 8), ()): ("RGBA", "RGBA"), # missing ExtraSamples - (II, 2, (1,), 1, (8, 8, 8, 8), (0,)): ("RGB", "RGBX"), - (MM, 2, (1,), 1, (8, 8, 8, 8), (0,)): ("RGB", "RGBX"), - (II, 2, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("RGB", "RGBXX"), - (MM, 2, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("RGB", "RGBXX"), - (II, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0, 0)): ("RGB", "RGBXXX"), - (MM, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0, 0)): ("RGB", "RGBXXX"), - (II, 2, (1,), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"), - (MM, 2, (1,), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"), - (II, 2, (1,), 1, (8, 8, 8, 8, 8), (1, 0)): ("RGBA", "RGBaX"), - (MM, 2, (1,), 1, (8, 8, 8, 8, 8), (1, 0)): ("RGBA", "RGBaX"), - (II, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (1, 0, 0)): ("RGBA", "RGBaXX"), - (MM, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (1, 0, 0)): ("RGBA", "RGBaXX"), - (II, 2, (1,), 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"), - (MM, 2, (1,), 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"), - (II, 2, (1,), 1, (8, 8, 8, 8, 8), (2, 0)): ("RGBA", "RGBAX"), - (MM, 2, (1,), 1, (8, 8, 8, 8, 8), (2, 0)): ("RGBA", "RGBAX"), - (II, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (2, 0, 0)): ("RGBA", "RGBAXX"), - (MM, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (2, 0, 0)): ("RGBA", "RGBAXX"), - (II, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10 - (MM, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10 - (II, 2, (1,), 1, (16, 16, 16), ()): ("RGB", "RGB;16L"), - (MM, 2, (1,), 1, (16, 16, 16), ()): ("RGB", "RGB;16B"), - (II, 2, (1,), 1, (16, 16, 16, 16), ()): ("RGBA", "RGBA;16L"), - (MM, 2, (1,), 1, (16, 16, 16, 16), ()): ("RGBA", "RGBA;16B"), - (II, 2, (1,), 1, (16, 16, 16, 16), (0,)): ("RGB", "RGBX;16L"), - (MM, 2, (1,), 1, (16, 16, 16, 16), (0,)): ("RGB", "RGBX;16B"), - (II, 2, (1,), 1, (16, 16, 16, 16), (1,)): ("RGBA", "RGBa;16L"), - (MM, 2, (1,), 1, (16, 16, 16, 16), (1,)): ("RGBA", "RGBa;16B"), - (II, 2, (1,), 1, (16, 16, 16, 16), (2,)): ("RGBA", "RGBA;16L"), - (MM, 2, (1,), 1, (16, 16, 16, 16), (2,)): ("RGBA", "RGBA;16B"), - (II, 3, (1,), 1, (1,), ()): ("P", "P;1"), - (MM, 3, (1,), 1, (1,), ()): ("P", "P;1"), - (II, 3, (1,), 2, (1,), ()): ("P", "P;1R"), - (MM, 3, (1,), 2, (1,), ()): ("P", "P;1R"), - (II, 3, (1,), 1, (2,), ()): ("P", "P;2"), - (MM, 3, (1,), 1, (2,), ()): ("P", "P;2"), - (II, 3, (1,), 2, (2,), ()): ("P", "P;2R"), - (MM, 3, (1,), 2, (2,), ()): ("P", "P;2R"), - (II, 3, (1,), 1, (4,), ()): ("P", "P;4"), - (MM, 3, (1,), 1, (4,), ()): ("P", "P;4"), - (II, 3, (1,), 2, (4,), ()): ("P", "P;4R"), - (MM, 3, (1,), 2, (4,), ()): ("P", "P;4R"), - (II, 3, (1,), 1, (8,), ()): ("P", "P"), - (MM, 3, (1,), 1, (8,), ()): ("P", "P"), - (II, 3, (1,), 1, (8, 8), (0,)): ("P", "PX"), - (MM, 3, (1,), 1, (8, 8), (0,)): ("P", "PX"), - (II, 3, (1,), 1, (8, 8), (2,)): ("PA", "PA"), - (MM, 3, (1,), 1, (8, 8), (2,)): ("PA", "PA"), - (II, 3, (1,), 2, (8,), ()): ("P", "P;R"), - (MM, 3, (1,), 2, (8,), ()): ("P", "P;R"), - (II, 5, (1,), 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"), - (MM, 5, (1,), 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"), - (II, 5, (1,), 1, (8, 8, 8, 8, 8), (0,)): ("CMYK", "CMYKX"), - (MM, 5, (1,), 1, (8, 8, 8, 8, 8), (0,)): ("CMYK", "CMYKX"), - (II, 5, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0)): ("CMYK", "CMYKXX"), - (MM, 5, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0)): ("CMYK", "CMYKXX"), - (II, 5, (1,), 1, (16, 16, 16, 16), ()): ("CMYK", "CMYK;16L"), - (MM, 5, (1,), 1, (16, 16, 16, 16), ()): ("CMYK", "CMYK;16B"), - (II, 6, (1,), 1, (8,), ()): ("L", "L"), - (MM, 6, (1,), 1, (8,), ()): ("L", "L"), - # JPEG compressed images handled by LibTiff and auto-converted to RGBX - # Minimal Baseline TIFF requires YCbCr images to have 3 SamplesPerPixel - (II, 6, (1,), 1, (8, 8, 8), ()): ("RGB", "RGBX"), - (MM, 6, (1,), 1, (8, 8, 8), ()): ("RGB", "RGBX"), - (II, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"), - (MM, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"), -} - -MAX_SAMPLESPERPIXEL = max(len(key_tp[4]) for key_tp in OPEN_INFO) - -PREFIXES = [ - b"MM\x00\x2a", # Valid TIFF header with big-endian byte order - b"II\x2a\x00", # Valid TIFF header with little-endian byte order - b"MM\x2a\x00", # Invalid TIFF header, assume big-endian - b"II\x00\x2a", # Invalid TIFF header, assume little-endian - b"MM\x00\x2b", # BigTIFF with big-endian byte order - b"II\x2b\x00", # BigTIFF with little-endian byte order -] - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(tuple(PREFIXES)) - - -def _limit_rational( - val: float | Fraction | IFDRational, max_val: int -) -> tuple[IntegralLike, IntegralLike]: - inv = abs(val) > 1 - n_d = IFDRational(1 / val if inv else val).limit_rational(max_val) - return n_d[::-1] if inv else n_d - - -def _limit_signed_rational( - val: IFDRational, max_val: int, min_val: int -) -> tuple[IntegralLike, IntegralLike]: - frac = Fraction(val) - n_d: tuple[IntegralLike, IntegralLike] = frac.numerator, frac.denominator - - if min(float(i) for i in n_d) < min_val: - n_d = _limit_rational(val, abs(min_val)) - - n_d_float = tuple(float(i) for i in n_d) - if max(n_d_float) > max_val: - n_d = _limit_rational(n_d_float[0] / n_d_float[1], max_val) - - return n_d - - -## -# Wrapper for TIFF IFDs. - -_load_dispatch = {} -_write_dispatch = {} - - -def _delegate(op: str) -> Any: - def delegate( - self: IFDRational, *args: tuple[float, ...] - ) -> bool | float | Fraction: - return getattr(self._val, op)(*args) - - return delegate - - -class IFDRational(Rational): - """Implements a rational class where 0/0 is a legal value to match - the in the wild use of exif rationals. - - e.g., DigitalZoomRatio - 0.00/0.00 indicates that no digital zoom was used - """ - - """ If the denominator is 0, store this as a float('nan'), otherwise store - as a fractions.Fraction(). Delegate as appropriate - - """ - - __slots__ = ("_numerator", "_denominator", "_val") - - def __init__( - self, value: float | Fraction | IFDRational, denominator: int = 1 - ) -> None: - """ - :param value: either an integer numerator, a - float/rational/other number, or an IFDRational - :param denominator: Optional integer denominator - """ - self._val: Fraction | float - if isinstance(value, IFDRational): - self._numerator = value.numerator - self._denominator = value.denominator - self._val = value._val - return - - if isinstance(value, Fraction): - self._numerator = value.numerator - self._denominator = value.denominator - else: - if TYPE_CHECKING: - self._numerator = cast(IntegralLike, value) - else: - self._numerator = value - self._denominator = denominator - - if denominator == 0: - self._val = float("nan") - elif denominator == 1: - self._val = Fraction(value) - elif int(value) == value: - self._val = Fraction(int(value), denominator) - else: - self._val = Fraction(value / denominator) - - @property - def numerator(self) -> IntegralLike: - return self._numerator - - @property - def denominator(self) -> int: - return self._denominator - - def limit_rational(self, max_denominator: int) -> tuple[IntegralLike, int]: - """ - - :param max_denominator: Integer, the maximum denominator value - :returns: Tuple of (numerator, denominator) - """ - - if self.denominator == 0: - return self.numerator, self.denominator - - assert isinstance(self._val, Fraction) - f = self._val.limit_denominator(max_denominator) - return f.numerator, f.denominator - - def __repr__(self) -> str: - return str(float(self._val)) - - def __hash__(self) -> int: # type: ignore[override] - return self._val.__hash__() - - def __eq__(self, other: object) -> bool: - val = self._val - if isinstance(other, IFDRational): - other = other._val - if isinstance(other, float): - val = float(val) - return val == other - - def __getstate__(self) -> list[float | Fraction | IntegralLike]: - return [self._val, self._numerator, self._denominator] - - def __setstate__(self, state: list[float | Fraction | IntegralLike]) -> None: - IFDRational.__init__(self, 0) - _val, _numerator, _denominator = state - assert isinstance(_val, (float, Fraction)) - self._val = _val - if TYPE_CHECKING: - self._numerator = cast(IntegralLike, _numerator) - else: - self._numerator = _numerator - assert isinstance(_denominator, int) - self._denominator = _denominator - - """ a = ['add','radd', 'sub', 'rsub', 'mul', 'rmul', - 'truediv', 'rtruediv', 'floordiv', 'rfloordiv', - 'mod','rmod', 'pow','rpow', 'pos', 'neg', - 'abs', 'trunc', 'lt', 'gt', 'le', 'ge', 'bool', - 'ceil', 'floor', 'round'] - print("\n".join("__%s__ = _delegate('__%s__')" % (s,s) for s in a)) - """ - - __add__ = _delegate("__add__") - __radd__ = _delegate("__radd__") - __sub__ = _delegate("__sub__") - __rsub__ = _delegate("__rsub__") - __mul__ = _delegate("__mul__") - __rmul__ = _delegate("__rmul__") - __truediv__ = _delegate("__truediv__") - __rtruediv__ = _delegate("__rtruediv__") - __floordiv__ = _delegate("__floordiv__") - __rfloordiv__ = _delegate("__rfloordiv__") - __mod__ = _delegate("__mod__") - __rmod__ = _delegate("__rmod__") - __pow__ = _delegate("__pow__") - __rpow__ = _delegate("__rpow__") - __pos__ = _delegate("__pos__") - __neg__ = _delegate("__neg__") - __abs__ = _delegate("__abs__") - __trunc__ = _delegate("__trunc__") - __lt__ = _delegate("__lt__") - __gt__ = _delegate("__gt__") - __le__ = _delegate("__le__") - __ge__ = _delegate("__ge__") - __bool__ = _delegate("__bool__") - __ceil__ = _delegate("__ceil__") - __floor__ = _delegate("__floor__") - __round__ = _delegate("__round__") - # Python >= 3.11 - if hasattr(Fraction, "__int__"): - __int__ = _delegate("__int__") - - -_LoaderFunc = Callable[["ImageFileDirectory_v2", bytes, bool], Any] - - -def _register_loader(idx: int, size: int) -> Callable[[_LoaderFunc], _LoaderFunc]: - def decorator(func: _LoaderFunc) -> _LoaderFunc: - from .TiffTags import TYPES - - if func.__name__.startswith("load_"): - TYPES[idx] = func.__name__[5:].replace("_", " ") - _load_dispatch[idx] = size, func # noqa: F821 - return func - - return decorator - - -def _register_writer(idx: int) -> Callable[[Callable[..., Any]], Callable[..., Any]]: - def decorator(func: Callable[..., Any]) -> Callable[..., Any]: - _write_dispatch[idx] = func # noqa: F821 - return func - - return decorator - - -def _register_basic(idx_fmt_name: tuple[int, str, str]) -> None: - from .TiffTags import TYPES - - idx, fmt, name = idx_fmt_name - TYPES[idx] = name - size = struct.calcsize(f"={fmt}") - - def basic_handler( - self: ImageFileDirectory_v2, data: bytes, legacy_api: bool = True - ) -> tuple[Any, ...]: - return self._unpack(f"{len(data) // size}{fmt}", data) - - _load_dispatch[idx] = size, basic_handler # noqa: F821 - _write_dispatch[idx] = lambda self, *values: ( # noqa: F821 - b"".join(self._pack(fmt, value) for value in values) - ) - - -if TYPE_CHECKING: - _IFDv2Base = MutableMapping[int, Any] -else: - _IFDv2Base = MutableMapping - - -class ImageFileDirectory_v2(_IFDv2Base): - """This class represents a TIFF tag directory. To speed things up, we - don't decode tags unless they're asked for. - - Exposes a dictionary interface of the tags in the directory:: - - ifd = ImageFileDirectory_v2() - ifd[key] = 'Some Data' - ifd.tagtype[key] = TiffTags.ASCII - print(ifd[key]) - 'Some Data' - - Individual values are returned as the strings or numbers, sequences are - returned as tuples of the values. - - The tiff metadata type of each item is stored in a dictionary of - tag types in - :attr:`~PIL.TiffImagePlugin.ImageFileDirectory_v2.tagtype`. The types - are read from a tiff file, guessed from the type added, or added - manually. - - Data Structures: - - * ``self.tagtype = {}`` - - * Key: numerical TIFF tag number - * Value: integer corresponding to the data type from - :py:data:`.TiffTags.TYPES` - - .. versionadded:: 3.0.0 - - 'Internal' data structures: - - * ``self._tags_v2 = {}`` - - * Key: numerical TIFF tag number - * Value: decoded data, as tuple for multiple values - - * ``self._tagdata = {}`` - - * Key: numerical TIFF tag number - * Value: undecoded byte string from file - - * ``self._tags_v1 = {}`` - - * Key: numerical TIFF tag number - * Value: decoded data in the v1 format - - Tags will be found in the private attributes ``self._tagdata``, and in - ``self._tags_v2`` once decoded. - - ``self.legacy_api`` is a value for internal use, and shouldn't be changed - from outside code. In cooperation with - :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1`, if ``legacy_api`` - is true, then decoded tags will be populated into both ``_tags_v1`` and - ``_tags_v2``. ``_tags_v2`` will be used if this IFD is used in the TIFF - save routine. Tags should be read from ``_tags_v1`` if - ``legacy_api == true``. - - """ - - _load_dispatch: dict[int, tuple[int, _LoaderFunc]] = {} - _write_dispatch: dict[int, Callable[..., Any]] = {} - - def __init__( - self, - ifh: bytes = b"II\x2a\x00\x00\x00\x00\x00", - prefix: bytes | None = None, - group: int | None = None, - ) -> None: - """Initialize an ImageFileDirectory. - - To construct an ImageFileDirectory from a real file, pass the 8-byte - magic header to the constructor. To only set the endianness, pass it - as the 'prefix' keyword argument. - - :param ifh: One of the accepted magic headers (cf. PREFIXES); also sets - endianness. - :param prefix: Override the endianness of the file. - """ - if not _accept(ifh): - msg = f"not a TIFF file (header {repr(ifh)} not valid)" - raise SyntaxError(msg) - self._prefix = prefix if prefix is not None else ifh[:2] - if self._prefix == MM: - self._endian = ">" - elif self._prefix == II: - self._endian = "<" - else: - msg = "not a TIFF IFD" - raise SyntaxError(msg) - self._bigtiff = ifh[2] == 43 - self.group = group - self.tagtype: dict[int, int] = {} - """ Dictionary of tag types """ - self.reset() - self.next = ( - self._unpack("Q", ifh[8:])[0] - if self._bigtiff - else self._unpack("L", ifh[4:])[0] - ) - self._legacy_api = False - - prefix = property(lambda self: self._prefix) - offset = property(lambda self: self._offset) - - @property - def legacy_api(self) -> bool: - return self._legacy_api - - @legacy_api.setter - def legacy_api(self, value: bool) -> NoReturn: - msg = "Not allowing setting of legacy api" - raise Exception(msg) - - def reset(self) -> None: - self._tags_v1: dict[int, Any] = {} # will remain empty if legacy_api is false - self._tags_v2: dict[int, Any] = {} # main tag storage - self._tagdata: dict[int, bytes] = {} - self.tagtype = {} # added 2008-06-05 by Florian Hoech - self._next = None - self._offset: int | None = None - - def __str__(self) -> str: - return str(dict(self)) - - def named(self) -> dict[str, Any]: - """ - :returns: dict of name|key: value - - Returns the complete tag dictionary, with named tags where possible. - """ - return { - TiffTags.lookup(code, self.group).name: value - for code, value in self.items() - } - - def __len__(self) -> int: - return len(set(self._tagdata) | set(self._tags_v2)) - - def __getitem__(self, tag: int) -> Any: - if tag not in self._tags_v2: # unpack on the fly - data = self._tagdata[tag] - typ = self.tagtype[tag] - size, handler = self._load_dispatch[typ] - self[tag] = handler(self, data, self.legacy_api) # check type - val = self._tags_v2[tag] - if self.legacy_api and not isinstance(val, (tuple, bytes)): - val = (val,) - return val - - def __contains__(self, tag: object) -> bool: - return tag in self._tags_v2 or tag in self._tagdata - - def __setitem__(self, tag: int, value: Any) -> None: - self._setitem(tag, value, self.legacy_api) - - def _setitem(self, tag: int, value: Any, legacy_api: bool) -> None: - basetypes = (Number, bytes, str) - - info = TiffTags.lookup(tag, self.group) - values = [value] if isinstance(value, basetypes) else value - - if tag not in self.tagtype: - if info.type: - self.tagtype[tag] = info.type - else: - self.tagtype[tag] = TiffTags.UNDEFINED - if all(isinstance(v, IFDRational) for v in values): - for v in values: - assert isinstance(v, IFDRational) - if v < 0: - self.tagtype[tag] = TiffTags.SIGNED_RATIONAL - break - else: - self.tagtype[tag] = TiffTags.RATIONAL - elif all(isinstance(v, int) for v in values): - short = True - signed_short = True - long = True - for v in values: - assert isinstance(v, int) - if short and not (0 <= v < 2**16): - short = False - if signed_short and not (-(2**15) < v < 2**15): - signed_short = False - if long and v < 0: - long = False - if short: - self.tagtype[tag] = TiffTags.SHORT - elif signed_short: - self.tagtype[tag] = TiffTags.SIGNED_SHORT - elif long: - self.tagtype[tag] = TiffTags.LONG - else: - self.tagtype[tag] = TiffTags.SIGNED_LONG - elif all(isinstance(v, float) for v in values): - self.tagtype[tag] = TiffTags.DOUBLE - elif all(isinstance(v, str) for v in values): - self.tagtype[tag] = TiffTags.ASCII - elif all(isinstance(v, bytes) for v in values): - self.tagtype[tag] = TiffTags.BYTE - - if self.tagtype[tag] == TiffTags.UNDEFINED: - values = [ - v.encode("ascii", "replace") if isinstance(v, str) else v - for v in values - ] - elif self.tagtype[tag] == TiffTags.RATIONAL: - values = [float(v) if isinstance(v, int) else v for v in values] - - is_ifd = self.tagtype[tag] == TiffTags.LONG and isinstance(values, dict) - if not is_ifd: - values = tuple( - info.cvt_enum(value) if isinstance(value, str) else value - for value in values - ) - - dest = self._tags_v1 if legacy_api else self._tags_v2 - - # Three branches: - # Spec'd length == 1, Actual length 1, store as element - # Spec'd length == 1, Actual > 1, Warn and truncate. Formerly barfed. - # No Spec, Actual length 1, Formerly (<4.2) returned a 1 element tuple. - # Don't mess with the legacy api, since it's frozen. - if not is_ifd and ( - (info.length == 1) - or self.tagtype[tag] == TiffTags.BYTE - or (info.length is None and len(values) == 1 and not legacy_api) - ): - # Don't mess with the legacy api, since it's frozen. - if legacy_api and self.tagtype[tag] in [ - TiffTags.RATIONAL, - TiffTags.SIGNED_RATIONAL, - ]: # rationals - values = (values,) - try: - (dest[tag],) = values - except ValueError: - # We've got a builtin tag with 1 expected entry - warnings.warn( - f"Metadata Warning, tag {tag} had too many entries: " - f"{len(values)}, expected 1" - ) - dest[tag] = values[0] - - else: - # Spec'd length > 1 or undefined - # Unspec'd, and length > 1 - dest[tag] = values - - def __delitem__(self, tag: int) -> None: - self._tags_v2.pop(tag, None) - self._tags_v1.pop(tag, None) - self._tagdata.pop(tag, None) - - def __iter__(self) -> Iterator[int]: - return iter(set(self._tagdata) | set(self._tags_v2)) - - def _unpack(self, fmt: str, data: bytes) -> tuple[Any, ...]: - return struct.unpack(self._endian + fmt, data) - - def _pack(self, fmt: str, *values: Any) -> bytes: - return struct.pack(self._endian + fmt, *values) - - list( - map( - _register_basic, - [ - (TiffTags.SHORT, "H", "short"), - (TiffTags.LONG, "L", "long"), - (TiffTags.SIGNED_BYTE, "b", "signed byte"), - (TiffTags.SIGNED_SHORT, "h", "signed short"), - (TiffTags.SIGNED_LONG, "l", "signed long"), - (TiffTags.FLOAT, "f", "float"), - (TiffTags.DOUBLE, "d", "double"), - (TiffTags.IFD, "L", "long"), - (TiffTags.LONG8, "Q", "long8"), - ], - ) - ) - - @_register_loader(1, 1) # Basic type, except for the legacy API. - def load_byte(self, data: bytes, legacy_api: bool = True) -> bytes: - return data - - @_register_writer(1) # Basic type, except for the legacy API. - def write_byte(self, data: bytes | int | IFDRational) -> bytes: - if isinstance(data, IFDRational): - data = int(data) - if isinstance(data, int): - data = bytes((data,)) - return data - - @_register_loader(2, 1) - def load_string(self, data: bytes, legacy_api: bool = True) -> str: - if data.endswith(b"\0"): - data = data[:-1] - return data.decode("latin-1", "replace") - - @_register_writer(2) - def write_string(self, value: str | bytes | int) -> bytes: - # remerge of https://github.com/python-pillow/Pillow/pull/1416 - if isinstance(value, int): - value = str(value) - if not isinstance(value, bytes): - value = value.encode("ascii", "replace") - return value + b"\0" - - @_register_loader(5, 8) - def load_rational( - self, data: bytes, legacy_api: bool = True - ) -> tuple[tuple[int, int] | IFDRational, ...]: - vals = self._unpack(f"{len(data) // 4}L", data) - - def combine(a: int, b: int) -> tuple[int, int] | IFDRational: - return (a, b) if legacy_api else IFDRational(a, b) - - return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2])) - - @_register_writer(5) - def write_rational(self, *values: IFDRational) -> bytes: - return b"".join( - self._pack("2L", *_limit_rational(frac, 2**32 - 1)) for frac in values - ) - - @_register_loader(7, 1) - def load_undefined(self, data: bytes, legacy_api: bool = True) -> bytes: - return data - - @_register_writer(7) - def write_undefined(self, value: bytes | int | IFDRational) -> bytes: - if isinstance(value, IFDRational): - value = int(value) - if isinstance(value, int): - value = str(value).encode("ascii", "replace") - return value - - @_register_loader(10, 8) - def load_signed_rational( - self, data: bytes, legacy_api: bool = True - ) -> tuple[tuple[int, int] | IFDRational, ...]: - vals = self._unpack(f"{len(data) // 4}l", data) - - def combine(a: int, b: int) -> tuple[int, int] | IFDRational: - return (a, b) if legacy_api else IFDRational(a, b) - - return tuple(combine(num, denom) for num, denom in zip(vals[::2], vals[1::2])) - - @_register_writer(10) - def write_signed_rational(self, *values: IFDRational) -> bytes: - return b"".join( - self._pack("2l", *_limit_signed_rational(frac, 2**31 - 1, -(2**31))) - for frac in values - ) - - def _ensure_read(self, fp: IO[bytes], size: int) -> bytes: - ret = fp.read(size) - if len(ret) != size: - msg = ( - "Corrupt EXIF data. " - f"Expecting to read {size} bytes but only got {len(ret)}. " - ) - raise OSError(msg) - return ret - - def load(self, fp: IO[bytes]) -> None: - self.reset() - self._offset = fp.tell() - - try: - tag_count = ( - self._unpack("Q", self._ensure_read(fp, 8)) - if self._bigtiff - else self._unpack("H", self._ensure_read(fp, 2)) - )[0] - for i in range(tag_count): - tag, typ, count, data = ( - self._unpack("HHQ8s", self._ensure_read(fp, 20)) - if self._bigtiff - else self._unpack("HHL4s", self._ensure_read(fp, 12)) - ) - - tagname = TiffTags.lookup(tag, self.group).name - typname = TYPES.get(typ, "unknown") - msg = f"tag: {tagname} ({tag}) - type: {typname} ({typ})" - - try: - unit_size, handler = self._load_dispatch[typ] - except KeyError: - logger.debug("%s - unsupported type %s", msg, typ) - continue # ignore unsupported type - size = count * unit_size - if size > (8 if self._bigtiff else 4): - here = fp.tell() - (offset,) = self._unpack("Q" if self._bigtiff else "L", data) - msg += f" Tag Location: {here} - Data Location: {offset}" - fp.seek(offset) - data = ImageFile._safe_read(fp, size) - fp.seek(here) - else: - data = data[:size] - - if len(data) != size: - warnings.warn( - "Possibly corrupt EXIF data. " - f"Expecting to read {size} bytes but only got {len(data)}." - f" Skipping tag {tag}" - ) - logger.debug(msg) - continue - - if not data: - logger.debug(msg) - continue - - self._tagdata[tag] = data - self.tagtype[tag] = typ - - msg += " - value: " - msg += f"" if size > 32 else repr(data) - - logger.debug(msg) - - (self.next,) = ( - self._unpack("Q", self._ensure_read(fp, 8)) - if self._bigtiff - else self._unpack("L", self._ensure_read(fp, 4)) - ) - except OSError as msg: - warnings.warn(str(msg)) - return - - def _get_ifh(self) -> bytes: - ifh = self._prefix + self._pack("H", 43 if self._bigtiff else 42) - if self._bigtiff: - ifh += self._pack("HH", 8, 0) - ifh += self._pack("Q", 16) if self._bigtiff else self._pack("L", 8) - - return ifh - - def tobytes(self, offset: int = 0) -> bytes: - # FIXME What about tagdata? - result = self._pack("Q" if self._bigtiff else "H", len(self._tags_v2)) - - entries: list[tuple[int, int, int, bytes, bytes]] = [] - - fmt = "Q" if self._bigtiff else "L" - fmt_size = 8 if self._bigtiff else 4 - offset += ( - len(result) + len(self._tags_v2) * (20 if self._bigtiff else 12) + fmt_size - ) - stripoffsets = None - - # pass 1: convert tags to binary format - # always write tags in ascending order - for tag, value in sorted(self._tags_v2.items()): - if tag == STRIPOFFSETS: - stripoffsets = len(entries) - typ = self.tagtype[tag] - logger.debug("Tag %s, Type: %s, Value: %s", tag, typ, repr(value)) - is_ifd = typ == TiffTags.LONG and isinstance(value, dict) - if is_ifd: - ifd = ImageFileDirectory_v2(self._get_ifh(), group=tag) - values = self._tags_v2[tag] - for ifd_tag, ifd_value in values.items(): - ifd[ifd_tag] = ifd_value - data = ifd.tobytes(offset) - else: - values = value if isinstance(value, tuple) else (value,) - data = self._write_dispatch[typ](self, *values) - - tagname = TiffTags.lookup(tag, self.group).name - typname = "ifd" if is_ifd else TYPES.get(typ, "unknown") - msg = f"save: {tagname} ({tag}) - type: {typname} ({typ}) - value: " - msg += f"" if len(data) >= 16 else str(values) - logger.debug(msg) - - # count is sum of lengths for string and arbitrary data - if is_ifd: - count = 1 - elif typ in [TiffTags.BYTE, TiffTags.ASCII, TiffTags.UNDEFINED]: - count = len(data) - else: - count = len(values) - # figure out if data fits into the entry - if len(data) <= fmt_size: - entries.append((tag, typ, count, data.ljust(fmt_size, b"\0"), b"")) - else: - entries.append((tag, typ, count, self._pack(fmt, offset), data)) - offset += (len(data) + 1) // 2 * 2 # pad to word - - # update strip offset data to point beyond auxiliary data - if stripoffsets is not None: - tag, typ, count, value, data = entries[stripoffsets] - if data: - size, handler = self._load_dispatch[typ] - values = [val + offset for val in handler(self, data, self.legacy_api)] - data = self._write_dispatch[typ](self, *values) - else: - value = self._pack(fmt, self._unpack(fmt, value)[0] + offset) - entries[stripoffsets] = tag, typ, count, value, data - - # pass 2: write entries to file - for tag, typ, count, value, data in entries: - logger.debug("%s %s %s %s %s", tag, typ, count, repr(value), repr(data)) - result += self._pack( - "HHQ8s" if self._bigtiff else "HHL4s", tag, typ, count, value - ) - - # -- overwrite here for multi-page -- - result += self._pack(fmt, 0) # end of entries - - # pass 3: write auxiliary data to file - for tag, typ, count, value, data in entries: - result += data - if len(data) & 1: - result += b"\0" - - return result - - def save(self, fp: IO[bytes]) -> int: - if fp.tell() == 0: # skip TIFF header on subsequent pages - fp.write(self._get_ifh()) - - offset = fp.tell() - result = self.tobytes(offset) - fp.write(result) - return offset + len(result) - - -ImageFileDirectory_v2._load_dispatch = _load_dispatch -ImageFileDirectory_v2._write_dispatch = _write_dispatch -for idx, name in TYPES.items(): - name = name.replace(" ", "_") - setattr(ImageFileDirectory_v2, f"load_{name}", _load_dispatch[idx][1]) - setattr(ImageFileDirectory_v2, f"write_{name}", _write_dispatch[idx]) -del _load_dispatch, _write_dispatch, idx, name - - -# Legacy ImageFileDirectory support. -class ImageFileDirectory_v1(ImageFileDirectory_v2): - """This class represents the **legacy** interface to a TIFF tag directory. - - Exposes a dictionary interface of the tags in the directory:: - - ifd = ImageFileDirectory_v1() - ifd[key] = 'Some Data' - ifd.tagtype[key] = TiffTags.ASCII - print(ifd[key]) - ('Some Data',) - - Also contains a dictionary of tag types as read from the tiff image file, - :attr:`~PIL.TiffImagePlugin.ImageFileDirectory_v1.tagtype`. - - Values are returned as a tuple. - - .. deprecated:: 3.0.0 - """ - - def __init__(self, *args: Any, **kwargs: Any) -> None: - super().__init__(*args, **kwargs) - self._legacy_api = True - - tags = property(lambda self: self._tags_v1) - tagdata = property(lambda self: self._tagdata) - - # defined in ImageFileDirectory_v2 - tagtype: dict[int, int] - """Dictionary of tag types""" - - @classmethod - def from_v2(cls, original: ImageFileDirectory_v2) -> ImageFileDirectory_v1: - """Returns an - :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1` - instance with the same data as is contained in the original - :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2` - instance. - - :returns: :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1` - - """ - - ifd = cls(prefix=original.prefix) - ifd._tagdata = original._tagdata - ifd.tagtype = original.tagtype - ifd.next = original.next # an indicator for multipage tiffs - return ifd - - def to_v2(self) -> ImageFileDirectory_v2: - """Returns an - :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2` - instance with the same data as is contained in the original - :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v1` - instance. - - :returns: :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory_v2` - - """ - - ifd = ImageFileDirectory_v2(prefix=self.prefix) - ifd._tagdata = dict(self._tagdata) - ifd.tagtype = dict(self.tagtype) - ifd._tags_v2 = dict(self._tags_v2) - return ifd - - def __contains__(self, tag: object) -> bool: - return tag in self._tags_v1 or tag in self._tagdata - - def __len__(self) -> int: - return len(set(self._tagdata) | set(self._tags_v1)) - - def __iter__(self) -> Iterator[int]: - return iter(set(self._tagdata) | set(self._tags_v1)) - - def __setitem__(self, tag: int, value: Any) -> None: - for legacy_api in (False, True): - self._setitem(tag, value, legacy_api) - - def __getitem__(self, tag: int) -> Any: - if tag not in self._tags_v1: # unpack on the fly - data = self._tagdata[tag] - typ = self.tagtype[tag] - size, handler = self._load_dispatch[typ] - for legacy in (False, True): - self._setitem(tag, handler(self, data, legacy), legacy) - val = self._tags_v1[tag] - if not isinstance(val, (tuple, bytes)): - val = (val,) - return val - - -# undone -- switch this pointer -ImageFileDirectory = ImageFileDirectory_v1 - - -## -# Image plugin for TIFF files. - - -class TiffImageFile(ImageFile.ImageFile): - format = "TIFF" - format_description = "Adobe TIFF" - _close_exclusive_fp_after_loading = False - - def __init__( - self, - fp: StrOrBytesPath | IO[bytes], - filename: str | bytes | None = None, - ) -> None: - self.tag_v2: ImageFileDirectory_v2 - """ Image file directory (tag dictionary) """ - - self.tag: ImageFileDirectory_v1 - """ Legacy tag entries """ - - super().__init__(fp, filename) - - def _open(self) -> None: - """Open the first image in a TIFF file""" - - # Header - assert self.fp is not None - ifh = self.fp.read(8) - if ifh[2] == 43: - ifh += self.fp.read(8) - - self.tag_v2 = ImageFileDirectory_v2(ifh) - - # setup frame pointers - self.__first = self.__next = self.tag_v2.next - self.__frame = -1 - self._fp = self.fp - self._frame_pos: list[int] = [] - self._n_frames: int | None = None - - logger.debug("*** TiffImageFile._open ***") - logger.debug("- __first: %s", self.__first) - logger.debug("- ifh: %s", repr(ifh)) # Use repr to avoid str(bytes) - - # and load the first frame - self._seek(0) - - @property - def n_frames(self) -> int: - current_n_frames = self._n_frames - if current_n_frames is None: - current = self.tell() - self._seek(len(self._frame_pos)) - while self._n_frames is None: - self._seek(self.tell() + 1) - self.seek(current) - assert self._n_frames is not None - return self._n_frames - - def seek(self, frame: int) -> None: - """Select a given frame as current image""" - if not self._seek_check(frame): - return - self._seek(frame) - if self._im is not None and ( - self.im.size != self._tile_size - or self.im.mode != self.mode - or self.readonly - ): - self._im = None - - def _seek(self, frame: int) -> None: - if isinstance(self._fp, DeferredError): - raise self._fp.ex - self.fp = self._fp - - while len(self._frame_pos) <= frame: - if not self.__next: - msg = "no more images in TIFF file" - raise EOFError(msg) - logger.debug( - "Seeking to frame %s, on frame %s, __next %s, location: %s", - frame, - self.__frame, - self.__next, - self.fp.tell(), - ) - if self.__next >= 2**63: - msg = "Unable to seek to frame" - raise ValueError(msg) - self.fp.seek(self.__next) - self._frame_pos.append(self.__next) - logger.debug("Loading tags, location: %s", self.fp.tell()) - self.tag_v2.load(self.fp) - if self.tag_v2.next in self._frame_pos: - # This IFD has already been processed - # Declare this to be the end of the image - self.__next = 0 - else: - self.__next = self.tag_v2.next - if self.__next == 0: - self._n_frames = frame + 1 - if len(self._frame_pos) == 1: - self.is_animated = self.__next != 0 - self.__frame += 1 - self.fp.seek(self._frame_pos[frame]) - self.tag_v2.load(self.fp) - if XMP in self.tag_v2: - xmp = self.tag_v2[XMP] - if isinstance(xmp, tuple) and len(xmp) == 1: - xmp = xmp[0] - self.info["xmp"] = xmp - elif "xmp" in self.info: - del self.info["xmp"] - self._reload_exif() - # fill the legacy tag/ifd entries - self.tag = self.ifd = ImageFileDirectory_v1.from_v2(self.tag_v2) - self.__frame = frame - self._setup() - - def tell(self) -> int: - """Return the current frame number""" - return self.__frame - - def get_photoshop_blocks(self) -> dict[int, dict[str, bytes]]: - """ - Returns a dictionary of Photoshop "Image Resource Blocks". - The keys are the image resource ID. For more information, see - https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_pgfId-1037727 - - :returns: Photoshop "Image Resource Blocks" in a dictionary. - """ - blocks = {} - val = self.tag_v2.get(ExifTags.Base.ImageResources) - if val: - while val.startswith(b"8BIM"): - id = i16(val[4:6]) - n = math.ceil((val[6] + 1) / 2) * 2 - size = i32(val[6 + n : 10 + n]) - data = val[10 + n : 10 + n + size] - blocks[id] = {"data": data} - - val = val[math.ceil((10 + n + size) / 2) * 2 :] - return blocks - - def load(self) -> Image.core.PixelAccess | None: - if self.tile and self.use_load_libtiff: - return self._load_libtiff() - return super().load() - - def load_prepare(self) -> None: - if self._im is None: - Image._decompression_bomb_check(self._tile_size) - self.im = Image.core.new(self.mode, self._tile_size) - ImageFile.ImageFile.load_prepare(self) - - def load_end(self) -> None: - # allow closing if we're on the first frame, there's no next - # This is the ImageFile.load path only, libtiff specific below. - if not self.is_animated: - self._close_exclusive_fp_after_loading = True - - # load IFD data from fp before it is closed - exif = self.getexif() - for key in TiffTags.TAGS_V2_GROUPS: - if key not in exif: - continue - exif.get_ifd(key) - - ImageOps.exif_transpose(self, in_place=True) - if ExifTags.Base.Orientation in self.tag_v2: - del self.tag_v2[ExifTags.Base.Orientation] - - def _load_libtiff(self) -> Image.core.PixelAccess | None: - """Overload method triggered when we detect a compressed tiff - Calls out to libtiff""" - - Image.Image.load(self) - - self.load_prepare() - - if not len(self.tile) == 1: - msg = "Not exactly one tile" - raise OSError(msg) - - # (self._compression, (extents tuple), - # 0, (rawmode, self._compression, fp)) - extents = self.tile[0][1] - args = self.tile[0][3] - - # To be nice on memory footprint, if there's a - # file descriptor, use that instead of reading - # into a string in python. - assert self.fp is not None - try: - fp = hasattr(self.fp, "fileno") and self.fp.fileno() - # flush the file descriptor, prevents error on pypy 2.4+ - # should also eliminate the need for fp.tell - # in _seek - if hasattr(self.fp, "flush"): - self.fp.flush() - except OSError: - # io.BytesIO have a fileno, but returns an OSError if - # it doesn't use a file descriptor. - fp = False - - if fp: - assert isinstance(args, tuple) - args_list = list(args) - args_list[2] = fp - args = tuple(args_list) - - decoder = Image._getdecoder(self.mode, "libtiff", args, self.decoderconfig) - try: - decoder.setimage(self.im, extents) - except ValueError as e: - msg = "Couldn't set the image" - raise OSError(msg) from e - - close_self_fp = self._exclusive_fp and not self.is_animated - if hasattr(self.fp, "getvalue"): - # We've got a stringio like thing passed in. Yay for all in memory. - # The decoder needs the entire file in one shot, so there's not - # a lot we can do here other than give it the entire file. - # unless we could do something like get the address of the - # underlying string for stringio. - # - # Rearranging for supporting byteio items, since they have a fileno - # that returns an OSError if there's no underlying fp. Easier to - # deal with here by reordering. - logger.debug("have getvalue. just sending in a string from getvalue") - n, err = decoder.decode(self.fp.getvalue()) - elif fp: - # we've got a actual file on disk, pass in the fp. - logger.debug("have fileno, calling fileno version of the decoder.") - if not close_self_fp: - self.fp.seek(0) - # Save and restore the file position, because libtiff will move it - # outside of the Python runtime, and that will confuse - # io.BufferedReader and possible others. - # NOTE: This must use os.lseek(), and not fp.tell()/fp.seek(), - # because the buffer read head already may not equal the actual - # file position, and fp.seek() may just adjust it's internal - # pointer and not actually seek the OS file handle. - pos = os.lseek(fp, 0, os.SEEK_CUR) - # 4 bytes, otherwise the trace might error out - n, err = decoder.decode(b"fpfp") - os.lseek(fp, pos, os.SEEK_SET) - else: - # we have something else. - logger.debug("don't have fileno or getvalue. just reading") - self.fp.seek(0) - # UNDONE -- so much for that buffer size thing. - n, err = decoder.decode(self.fp.read()) - - self.tile = [] - self.readonly = 0 - - self.load_end() - - if close_self_fp: - self.fp.close() - self.fp = None # might be shared - - if err < 0: - msg = f"decoder error {err}" - raise OSError(msg) - - return Image.Image.load(self) - - def _setup(self) -> None: - """Setup this image object based on current tags""" - - if 0xBC01 in self.tag_v2: - msg = "Windows Media Photo files not yet supported" - raise OSError(msg) - - # extract relevant tags - self._compression = COMPRESSION_INFO[self.tag_v2.get(COMPRESSION, 1)] - self._planar_configuration = self.tag_v2.get(PLANAR_CONFIGURATION, 1) - - # photometric is a required tag, but not everyone is reading - # the specification - photo = self.tag_v2.get(PHOTOMETRIC_INTERPRETATION, 0) - - # old style jpeg compression images most certainly are YCbCr - if self._compression == "tiff_jpeg": - photo = 6 - - fillorder = self.tag_v2.get(FILLORDER, 1) - - logger.debug("*** Summary ***") - logger.debug("- compression: %s", self._compression) - logger.debug("- photometric_interpretation: %s", photo) - logger.debug("- planar_configuration: %s", self._planar_configuration) - logger.debug("- fill_order: %s", fillorder) - logger.debug("- YCbCr subsampling: %s", self.tag_v2.get(YCBCRSUBSAMPLING)) - - # size - try: - xsize = self.tag_v2[IMAGEWIDTH] - ysize = self.tag_v2[IMAGELENGTH] - except KeyError as e: - msg = "Missing dimensions" - raise TypeError(msg) from e - if not isinstance(xsize, int) or not isinstance(ysize, int): - msg = "Invalid dimensions" - raise ValueError(msg) - self._tile_size = xsize, ysize - orientation = self.tag_v2.get(ExifTags.Base.Orientation) - if orientation in (5, 6, 7, 8): - self._size = ysize, xsize - else: - self._size = xsize, ysize - - logger.debug("- size: %s", self.size) - - sample_format = self.tag_v2.get(SAMPLEFORMAT, (1,)) - if len(sample_format) > 1 and max(sample_format) == min(sample_format) == 1: - # SAMPLEFORMAT is properly per band, so an RGB image will - # be (1,1,1). But, we don't support per band pixel types, - # and anything more than one band is a uint8. So, just - # take the first element. Revisit this if adding support - # for more exotic images. - sample_format = (1,) - - bps_tuple = self.tag_v2.get(BITSPERSAMPLE, (1,)) - extra_tuple = self.tag_v2.get(EXTRASAMPLES, ()) - if photo in (2, 6, 8): # RGB, YCbCr, LAB - bps_count = 3 - elif photo == 5: # CMYK - bps_count = 4 - else: - bps_count = 1 - bps_count += len(extra_tuple) - bps_actual_count = len(bps_tuple) - samples_per_pixel = self.tag_v2.get( - SAMPLESPERPIXEL, - 3 if self._compression == "tiff_jpeg" and photo in (2, 6) else 1, - ) - - if samples_per_pixel > MAX_SAMPLESPERPIXEL: - # DOS check, samples_per_pixel can be a Long, and we extend the tuple below - logger.error( - "More samples per pixel than can be decoded: %s", samples_per_pixel - ) - msg = "Invalid value for samples per pixel" - raise SyntaxError(msg) - - if samples_per_pixel < bps_actual_count: - # If a file has more values in bps_tuple than expected, - # remove the excess. - bps_tuple = bps_tuple[:samples_per_pixel] - elif samples_per_pixel > bps_actual_count and bps_actual_count == 1: - # If a file has only one value in bps_tuple, when it should have more, - # presume it is the same number of bits for all of the samples. - bps_tuple = bps_tuple * samples_per_pixel - - if len(bps_tuple) != samples_per_pixel: - msg = "unknown data organization" - raise SyntaxError(msg) - - # mode: check photometric interpretation and bits per pixel - key = ( - self.tag_v2.prefix, - photo, - sample_format, - fillorder, - bps_tuple, - extra_tuple, - ) - logger.debug("format key: %s", key) - try: - self._mode, rawmode = OPEN_INFO[key] - except KeyError as e: - logger.debug("- unsupported format") - msg = "unknown pixel mode" - raise SyntaxError(msg) from e - - logger.debug("- raw mode: %s", rawmode) - logger.debug("- pil mode: %s", self.mode) - - self.info["compression"] = self._compression - - xres = self.tag_v2.get(X_RESOLUTION, 1) - yres = self.tag_v2.get(Y_RESOLUTION, 1) - - if xres and yres: - resunit = self.tag_v2.get(RESOLUTION_UNIT) - if resunit == 2: # dots per inch - self.info["dpi"] = (xres, yres) - elif resunit == 3: # dots per centimeter. convert to dpi - self.info["dpi"] = (xres * 2.54, yres * 2.54) - elif resunit is None: # used to default to 1, but now 2) - self.info["dpi"] = (xres, yres) - # For backward compatibility, - # we also preserve the old behavior - self.info["resolution"] = xres, yres - else: # No absolute unit of measurement - self.info["resolution"] = xres, yres - - # build tile descriptors - x = y = layer = 0 - self.tile = [] - self.use_load_libtiff = READ_LIBTIFF or self._compression != "raw" - if self.use_load_libtiff: - # Decoder expects entire file as one tile. - # There's a buffer size limit in load (64k) - # so large g4 images will fail if we use that - # function. - # - # Setup the one tile for the whole image, then - # use the _load_libtiff function. - - # libtiff handles the fillmode for us, so 1;IR should - # actually be 1;I. Including the R double reverses the - # bits, so stripes of the image are reversed. See - # https://github.com/python-pillow/Pillow/issues/279 - if fillorder == 2: - # Replace fillorder with fillorder=1 - key = key[:3] + (1,) + key[4:] - logger.debug("format key: %s", key) - # this should always work, since all the - # fillorder==2 modes have a corresponding - # fillorder=1 mode - self._mode, rawmode = OPEN_INFO[key] - # YCbCr images with new jpeg compression with pixels in one plane - # unpacked straight into RGB values - if ( - photo == 6 - and self._compression == "jpeg" - and self._planar_configuration == 1 - ): - rawmode = "RGB" - # libtiff always returns the bytes in native order. - # we're expecting image byte order. So, if the rawmode - # contains I;16, we need to convert from native to image - # byte order. - elif rawmode == "I;16": - rawmode = "I;16N" - elif rawmode.endswith((";16B", ";16L")): - rawmode = rawmode[:-1] + "N" - - # Offset in the tile tuple is 0, we go from 0,0 to - # w,h, and we only do this once -- eds - a = (rawmode, self._compression, False, self.tag_v2.offset) - self.tile.append(ImageFile._Tile("libtiff", (0, 0, xsize, ysize), 0, a)) - - elif STRIPOFFSETS in self.tag_v2 or TILEOFFSETS in self.tag_v2: - # striped image - if STRIPOFFSETS in self.tag_v2: - offsets = self.tag_v2[STRIPOFFSETS] - h = self.tag_v2.get(ROWSPERSTRIP, ysize) - w = xsize - else: - # tiled image - offsets = self.tag_v2[TILEOFFSETS] - tilewidth = self.tag_v2.get(TILEWIDTH) - h = self.tag_v2.get(TILELENGTH) - if not isinstance(tilewidth, int) or not isinstance(h, int): - msg = "Invalid tile dimensions" - raise ValueError(msg) - w = tilewidth - - if w == xsize and h == ysize and self._planar_configuration != 2: - # Every tile covers the image. Only use the last offset - offsets = offsets[-1:] - - for offset in offsets: - if x + w > xsize: - stride = w * sum(bps_tuple) / 8 # bytes per line - else: - stride = 0 - - tile_rawmode = rawmode - if self._planar_configuration == 2: - # each band on it's own layer - tile_rawmode = rawmode[layer] - # adjust stride width accordingly - stride /= bps_count - - args = (tile_rawmode, int(stride), 1) - self.tile.append( - ImageFile._Tile( - self._compression, - (x, y, min(x + w, xsize), min(y + h, ysize)), - offset, - args, - ) - ) - x += w - if x >= xsize: - x, y = 0, y + h - if y >= ysize: - y = 0 - layer += 1 - else: - logger.debug("- unsupported data organization") - msg = "unknown data organization" - raise SyntaxError(msg) - - # Fix up info. - if ICCPROFILE in self.tag_v2: - self.info["icc_profile"] = self.tag_v2[ICCPROFILE] - - # fixup palette descriptor - - if self.mode in ["P", "PA"]: - palette = [o8(b // 256) for b in self.tag_v2[COLORMAP]] - self.palette = ImagePalette.raw("RGB;L", b"".join(palette)) - - -# -# -------------------------------------------------------------------- -# Write TIFF files - -# little endian is default except for image modes with -# explicit big endian byte-order - -SAVE_INFO = { - # mode => rawmode, byteorder, photometrics, - # sampleformat, bitspersample, extra - "1": ("1", II, 1, 1, (1,), None), - "L": ("L", II, 1, 1, (8,), None), - "LA": ("LA", II, 1, 1, (8, 8), 2), - "P": ("P", II, 3, 1, (8,), None), - "PA": ("PA", II, 3, 1, (8, 8), 2), - "I": ("I;32S", II, 1, 2, (32,), None), - "I;16": ("I;16", II, 1, 1, (16,), None), - "I;16L": ("I;16L", II, 1, 1, (16,), None), - "F": ("F;32F", II, 1, 3, (32,), None), - "RGB": ("RGB", II, 2, 1, (8, 8, 8), None), - "RGBX": ("RGBX", II, 2, 1, (8, 8, 8, 8), 0), - "RGBA": ("RGBA", II, 2, 1, (8, 8, 8, 8), 2), - "CMYK": ("CMYK", II, 5, 1, (8, 8, 8, 8), None), - "YCbCr": ("YCbCr", II, 6, 1, (8, 8, 8), None), - "LAB": ("LAB", II, 8, 1, (8, 8, 8), None), - "I;16B": ("I;16B", MM, 1, 1, (16,), None), -} - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - try: - rawmode, prefix, photo, format, bits, extra = SAVE_INFO[im.mode] - except KeyError as e: - msg = f"cannot write mode {im.mode} as TIFF" - raise OSError(msg) from e - - encoderinfo = im.encoderinfo - encoderconfig = im.encoderconfig - - ifd = ImageFileDirectory_v2(prefix=prefix) - if encoderinfo.get("big_tiff"): - ifd._bigtiff = True - - try: - compression = encoderinfo["compression"] - except KeyError: - compression = im.info.get("compression") - if isinstance(compression, int): - # compression value may be from BMP. Ignore it - compression = None - if compression is None: - compression = "raw" - elif compression == "tiff_jpeg": - # OJPEG is obsolete, so use new-style JPEG compression instead - compression = "jpeg" - elif compression == "tiff_deflate": - compression = "tiff_adobe_deflate" - - libtiff = WRITE_LIBTIFF or compression != "raw" - - # required for color libtiff images - ifd[PLANAR_CONFIGURATION] = 1 - - ifd[IMAGEWIDTH] = im.size[0] - ifd[IMAGELENGTH] = im.size[1] - - # write any arbitrary tags passed in as an ImageFileDirectory - if "tiffinfo" in encoderinfo: - info = encoderinfo["tiffinfo"] - elif "exif" in encoderinfo: - info = encoderinfo["exif"] - if isinstance(info, bytes): - exif = Image.Exif() - exif.load(info) - info = exif - else: - info = {} - logger.debug("Tiffinfo Keys: %s", list(info)) - if isinstance(info, ImageFileDirectory_v1): - info = info.to_v2() - for key in info: - if isinstance(info, Image.Exif) and key in TiffTags.TAGS_V2_GROUPS: - ifd[key] = info.get_ifd(key) - else: - ifd[key] = info.get(key) - try: - ifd.tagtype[key] = info.tagtype[key] - except Exception: - pass # might not be an IFD. Might not have populated type - - legacy_ifd = {} - if hasattr(im, "tag"): - legacy_ifd = im.tag.to_v2() - - supplied_tags = {**legacy_ifd, **getattr(im, "tag_v2", {})} - for tag in ( - # IFD offset that may not be correct in the saved image - EXIFIFD, - # Determined by the image format and should not be copied from legacy_ifd. - SAMPLEFORMAT, - ): - if tag in supplied_tags: - del supplied_tags[tag] - - # additions written by Greg Couch, gregc@cgl.ucsf.edu - # inspired by image-sig posting from Kevin Cazabon, kcazabon@home.com - if hasattr(im, "tag_v2"): - # preserve tags from original TIFF image file - for key in ( - RESOLUTION_UNIT, - X_RESOLUTION, - Y_RESOLUTION, - IPTC_NAA_CHUNK, - PHOTOSHOP_CHUNK, - XMP, - ): - if key in im.tag_v2: - if key == IPTC_NAA_CHUNK and im.tag_v2.tagtype[key] not in ( - TiffTags.BYTE, - TiffTags.UNDEFINED, - ): - del supplied_tags[key] - else: - ifd[key] = im.tag_v2[key] - ifd.tagtype[key] = im.tag_v2.tagtype[key] - - # preserve ICC profile (should also work when saving other formats - # which support profiles as TIFF) -- 2008-06-06 Florian Hoech - icc = encoderinfo.get("icc_profile", im.info.get("icc_profile")) - if icc: - ifd[ICCPROFILE] = icc - - for key, name in [ - (IMAGEDESCRIPTION, "description"), - (X_RESOLUTION, "resolution"), - (Y_RESOLUTION, "resolution"), - (X_RESOLUTION, "x_resolution"), - (Y_RESOLUTION, "y_resolution"), - (RESOLUTION_UNIT, "resolution_unit"), - (SOFTWARE, "software"), - (DATE_TIME, "date_time"), - (ARTIST, "artist"), - (COPYRIGHT, "copyright"), - ]: - if name in encoderinfo: - ifd[key] = encoderinfo[name] - - dpi = encoderinfo.get("dpi") - if dpi: - ifd[RESOLUTION_UNIT] = 2 - ifd[X_RESOLUTION] = dpi[0] - ifd[Y_RESOLUTION] = dpi[1] - - if bits != (1,): - ifd[BITSPERSAMPLE] = bits - if len(bits) != 1: - ifd[SAMPLESPERPIXEL] = len(bits) - if extra is not None: - ifd[EXTRASAMPLES] = extra - if format != 1: - ifd[SAMPLEFORMAT] = format - - if PHOTOMETRIC_INTERPRETATION not in ifd: - ifd[PHOTOMETRIC_INTERPRETATION] = photo - elif im.mode in ("1", "L") and ifd[PHOTOMETRIC_INTERPRETATION] == 0: - if im.mode == "1": - inverted_im = im.copy() - px = inverted_im.load() - if px is not None: - for y in range(inverted_im.height): - for x in range(inverted_im.width): - px[x, y] = 0 if px[x, y] == 255 else 255 - im = inverted_im - else: - im = ImageOps.invert(im) - - if im.mode in ["P", "PA"]: - lut = im.im.getpalette("RGB", "RGB;L") - colormap = [] - colors = len(lut) // 3 - for i in range(3): - colormap += [v * 256 for v in lut[colors * i : colors * (i + 1)]] - colormap += [0] * (256 - colors) - ifd[COLORMAP] = colormap - # data orientation - w, h = ifd[IMAGEWIDTH], ifd[IMAGELENGTH] - stride = len(bits) * ((w * bits[0] + 7) // 8) - if ROWSPERSTRIP not in ifd: - # aim for given strip size (64 KB by default) when using libtiff writer - if libtiff: - im_strip_size = encoderinfo.get("strip_size", STRIP_SIZE) - rows_per_strip = 1 if stride == 0 else min(im_strip_size // stride, h) - # JPEG encoder expects multiple of 8 rows - if compression == "jpeg": - rows_per_strip = min(((rows_per_strip + 7) // 8) * 8, h) - else: - rows_per_strip = h - if rows_per_strip == 0: - rows_per_strip = 1 - ifd[ROWSPERSTRIP] = rows_per_strip - strip_byte_counts = 1 if stride == 0 else stride * ifd[ROWSPERSTRIP] - strips_per_image = (h + ifd[ROWSPERSTRIP] - 1) // ifd[ROWSPERSTRIP] - if strip_byte_counts >= 2**16: - ifd.tagtype[STRIPBYTECOUNTS] = TiffTags.LONG - ifd[STRIPBYTECOUNTS] = (strip_byte_counts,) * (strips_per_image - 1) + ( - stride * h - strip_byte_counts * (strips_per_image - 1), - ) - ifd[STRIPOFFSETS] = tuple( - range(0, strip_byte_counts * strips_per_image, strip_byte_counts) - ) # this is adjusted by IFD writer - # no compression by default: - ifd[COMPRESSION] = COMPRESSION_INFO_REV.get(compression, 1) - - if im.mode == "YCbCr": - for tag, default_value in { - YCBCRSUBSAMPLING: (1, 1), - REFERENCEBLACKWHITE: (0, 255, 128, 255, 128, 255), - }.items(): - ifd.setdefault(tag, default_value) - - blocklist = [TILEWIDTH, TILELENGTH, TILEOFFSETS, TILEBYTECOUNTS] - if libtiff: - if "quality" in encoderinfo: - quality = encoderinfo["quality"] - if not isinstance(quality, int) or quality < 0 or quality > 100: - msg = "Invalid quality setting" - raise ValueError(msg) - if compression != "jpeg": - msg = "quality setting only supported for 'jpeg' compression" - raise ValueError(msg) - ifd[JPEGQUALITY] = quality - - logger.debug("Saving using libtiff encoder") - logger.debug("Items: %s", sorted(ifd.items())) - _fp = 0 - if hasattr(fp, "fileno"): - try: - fp.seek(0) - _fp = fp.fileno() - except io.UnsupportedOperation: - pass - - # optional types for non core tags - types = {} - # STRIPOFFSETS and STRIPBYTECOUNTS are added by the library - # based on the data in the strip. - # OSUBFILETYPE is deprecated. - # The other tags expect arrays with a certain length (fixed or depending on - # BITSPERSAMPLE, etc), passing arrays with a different length will result in - # segfaults. Block these tags until we add extra validation. - # SUBIFD may also cause a segfault. - blocklist += [ - OSUBFILETYPE, - REFERENCEBLACKWHITE, - STRIPBYTECOUNTS, - STRIPOFFSETS, - TRANSFERFUNCTION, - SUBIFD, - ] - - # bits per sample is a single short in the tiff directory, not a list. - atts: dict[int, Any] = {BITSPERSAMPLE: bits[0]} - # Merge the ones that we have with (optional) more bits from - # the original file, e.g x,y resolution so that we can - # save(load('')) == original file. - for tag, value in itertools.chain(ifd.items(), supplied_tags.items()): - # Libtiff can only process certain core items without adding - # them to the custom dictionary. - # Custom items are supported for int, float, unicode, string and byte - # values. Other types and tuples require a tagtype. - if tag not in TiffTags.LIBTIFF_CORE: - if tag in TiffTags.TAGS_V2_GROUPS: - types[tag] = TiffTags.LONG8 - elif tag in ifd.tagtype: - types[tag] = ifd.tagtype[tag] - elif isinstance(value, (int, float, str, bytes)) or ( - isinstance(value, tuple) - and all(isinstance(v, (int, float, IFDRational)) for v in value) - ): - type = TiffTags.lookup(tag).type - if type: - types[tag] = type - if tag not in atts and tag not in blocklist: - if isinstance(value, str): - atts[tag] = value.encode("ascii", "replace") + b"\0" - elif isinstance(value, IFDRational): - atts[tag] = float(value) - else: - atts[tag] = value - - if SAMPLEFORMAT in atts and len(atts[SAMPLEFORMAT]) == 1: - atts[SAMPLEFORMAT] = atts[SAMPLEFORMAT][0] - - logger.debug("Converted items: %s", sorted(atts.items())) - - # libtiff always expects the bytes in native order. - # we're storing image byte order. So, if the rawmode - # contains I;16, we need to convert from native to image - # byte order. - if im.mode in ("I;16", "I;16B", "I;16L"): - rawmode = "I;16N" - - # Pass tags as sorted list so that the tags are set in a fixed order. - # This is required by libtiff for some tags. For example, the JPEGQUALITY - # pseudo tag requires that the COMPRESS tag was already set. - tags = list(atts.items()) - tags.sort() - a = (rawmode, compression, _fp, filename, tags, types) - encoder = Image._getencoder(im.mode, "libtiff", a, encoderconfig) - encoder.setimage(im.im, (0, 0) + im.size) - while True: - errcode, data = encoder.encode(ImageFile.MAXBLOCK)[1:] - if not _fp: - fp.write(data) - if errcode: - break - if errcode < 0: - msg = f"encoder error {errcode} when writing image file" - raise OSError(msg) - - else: - for tag in blocklist: - del ifd[tag] - offset = ifd.save(fp) - - ImageFile._save( - im, - fp, - [ImageFile._Tile("raw", (0, 0) + im.size, offset, (rawmode, stride, 1))], - ) - - # -- helper for multi-page save -- - if "_debug_multipage" in encoderinfo: - # just to access o32 and o16 (using correct byte order) - setattr(im, "_debug_multipage", ifd) - - -class AppendingTiffWriter(io.BytesIO): - fieldSizes = [ - 0, # None - 1, # byte - 1, # ascii - 2, # short - 4, # long - 8, # rational - 1, # sbyte - 1, # undefined - 2, # sshort - 4, # slong - 8, # srational - 4, # float - 8, # double - 4, # ifd - 2, # unicode - 4, # complex - 8, # long8 - ] - - Tags = { - 273, # StripOffsets - 288, # FreeOffsets - 324, # TileOffsets - 519, # JPEGQTables - 520, # JPEGDCTables - 521, # JPEGACTables - } - - def __init__(self, fn: StrOrBytesPath | IO[bytes], new: bool = False) -> None: - self.f: IO[bytes] - if is_path(fn): - self.name = fn - self.close_fp = True - try: - self.f = open(fn, "w+b" if new else "r+b") - except OSError: - self.f = open(fn, "w+b") - else: - self.f = cast(IO[bytes], fn) - self.close_fp = False - self.beginning = self.f.tell() - self.setup() - - def setup(self) -> None: - # Reset everything. - self.f.seek(self.beginning, os.SEEK_SET) - - self.whereToWriteNewIFDOffset: int | None = None - self.offsetOfNewPage = 0 - - self.IIMM = iimm = self.f.read(4) - self._bigtiff = b"\x2b" in iimm - if not iimm: - # empty file - first page - self.isFirst = True - return - - self.isFirst = False - if iimm not in PREFIXES: - msg = "Invalid TIFF file header" - raise RuntimeError(msg) - - self.setEndian("<" if iimm.startswith(II) else ">") - - if self._bigtiff: - self.f.seek(4, os.SEEK_CUR) - self.skipIFDs() - self.goToEnd() - - def finalize(self) -> None: - if self.isFirst: - return - - # fix offsets - self.f.seek(self.offsetOfNewPage) - - iimm = self.f.read(4) - if not iimm: - # Make it easy to finish a frame without committing to a new one. - return - - if iimm != self.IIMM: - msg = "IIMM of new page doesn't match IIMM of first page" - raise RuntimeError(msg) - - if self._bigtiff: - self.f.seek(4, os.SEEK_CUR) - ifd_offset = self._read(8 if self._bigtiff else 4) - ifd_offset += self.offsetOfNewPage - assert self.whereToWriteNewIFDOffset is not None - self.f.seek(self.whereToWriteNewIFDOffset) - self._write(ifd_offset, 8 if self._bigtiff else 4) - self.f.seek(ifd_offset) - self.fixIFD() - - def newFrame(self) -> None: - # Call this to finish a frame. - self.finalize() - self.setup() - - def __enter__(self) -> AppendingTiffWriter: - return self - - def __exit__(self, *args: object) -> None: - if self.close_fp: - self.close() - - def tell(self) -> int: - return self.f.tell() - self.offsetOfNewPage - - def seek(self, offset: int, whence: int = io.SEEK_SET) -> int: - """ - :param offset: Distance to seek. - :param whence: Whether the distance is relative to the start, - end or current position. - :returns: The resulting position, relative to the start. - """ - if whence == os.SEEK_SET: - offset += self.offsetOfNewPage - - self.f.seek(offset, whence) - return self.tell() - - def goToEnd(self) -> None: - self.f.seek(0, os.SEEK_END) - pos = self.f.tell() - - # pad to 16 byte boundary - pad_bytes = 16 - pos % 16 - if 0 < pad_bytes < 16: - self.f.write(bytes(pad_bytes)) - self.offsetOfNewPage = self.f.tell() - - def setEndian(self, endian: str) -> None: - self.endian = endian - self.longFmt = f"{self.endian}L" - self.shortFmt = f"{self.endian}H" - self.tagFormat = f"{self.endian}HH" + ("Q" if self._bigtiff else "L") - - def skipIFDs(self) -> None: - while True: - ifd_offset = self._read(8 if self._bigtiff else 4) - if ifd_offset == 0: - self.whereToWriteNewIFDOffset = self.f.tell() - ( - 8 if self._bigtiff else 4 - ) - break - - self.f.seek(ifd_offset) - num_tags = self._read(8 if self._bigtiff else 2) - self.f.seek(num_tags * (20 if self._bigtiff else 12), os.SEEK_CUR) - - def write(self, data: Buffer, /) -> int: - return self.f.write(data) - - def _fmt(self, field_size: int) -> str: - try: - return {2: "H", 4: "L", 8: "Q"}[field_size] - except KeyError: - msg = "offset is not supported" - raise RuntimeError(msg) - - def _read(self, field_size: int) -> int: - (value,) = struct.unpack( - self.endian + self._fmt(field_size), self.f.read(field_size) - ) - return value - - def readShort(self) -> int: - return self._read(2) - - def readLong(self) -> int: - return self._read(4) - - @staticmethod - def _verify_bytes_written(bytes_written: int | None, expected: int) -> None: - if bytes_written is not None and bytes_written != expected: - msg = f"wrote only {bytes_written} bytes but wanted {expected}" - raise RuntimeError(msg) - - def _rewriteLast( - self, value: int, field_size: int, new_field_size: int = 0 - ) -> None: - self.f.seek(-field_size, os.SEEK_CUR) - if not new_field_size: - new_field_size = field_size - bytes_written = self.f.write( - struct.pack(self.endian + self._fmt(new_field_size), value) - ) - self._verify_bytes_written(bytes_written, new_field_size) - - def rewriteLastShortToLong(self, value: int) -> None: - self._rewriteLast(value, 2, 4) - - def rewriteLastShort(self, value: int) -> None: - return self._rewriteLast(value, 2) - - def rewriteLastLong(self, value: int) -> None: - return self._rewriteLast(value, 4) - - def _write(self, value: int, field_size: int) -> None: - bytes_written = self.f.write( - struct.pack(self.endian + self._fmt(field_size), value) - ) - self._verify_bytes_written(bytes_written, field_size) - - def writeShort(self, value: int) -> None: - self._write(value, 2) - - def writeLong(self, value: int) -> None: - self._write(value, 4) - - def close(self) -> None: - self.finalize() - if self.close_fp: - self.f.close() - - def fixIFD(self) -> None: - num_tags = self._read(8 if self._bigtiff else 2) - - for i in range(num_tags): - tag, field_type, count = struct.unpack( - self.tagFormat, self.f.read(12 if self._bigtiff else 8) - ) - - field_size = self.fieldSizes[field_type] - total_size = field_size * count - fmt_size = 8 if self._bigtiff else 4 - is_local = total_size <= fmt_size - if not is_local: - offset = self._read(fmt_size) + self.offsetOfNewPage - self._rewriteLast(offset, fmt_size) - - if tag in self.Tags: - cur_pos = self.f.tell() - - logger.debug( - "fixIFD: %s (%d) - type: %s (%d) - type size: %d - count: %d", - TiffTags.lookup(tag).name, - tag, - TYPES.get(field_type, "unknown"), - field_type, - field_size, - count, - ) - - if is_local: - self._fixOffsets(count, field_size) - self.f.seek(cur_pos + fmt_size) - else: - self.f.seek(offset) - self._fixOffsets(count, field_size) - self.f.seek(cur_pos) - - elif is_local: - # skip the locally stored value that is not an offset - self.f.seek(fmt_size, os.SEEK_CUR) - - def _fixOffsets(self, count: int, field_size: int) -> None: - for i in range(count): - offset = self._read(field_size) - offset += self.offsetOfNewPage - - new_field_size = 0 - if self._bigtiff and field_size in (2, 4) and offset >= 2**32: - # offset is now too large - we must convert long to long8 - new_field_size = 8 - elif field_size == 2 and offset >= 2**16: - # offset is now too large - we must convert short to long - new_field_size = 4 - if new_field_size: - if count != 1: - msg = "not implemented" - raise RuntimeError(msg) # XXX TODO - - # simple case - the offset is just one and therefore it is - # local (not referenced with another offset) - self._rewriteLast(offset, field_size, new_field_size) - # Move back past the new offset, past 'count', and before 'field_type' - rewind = -new_field_size - 4 - 2 - self.f.seek(rewind, os.SEEK_CUR) - self.writeShort(new_field_size) # rewrite the type - self.f.seek(2 - rewind, os.SEEK_CUR) - else: - self._rewriteLast(offset, field_size) - - def fixOffsets( - self, count: int, isShort: bool = False, isLong: bool = False - ) -> None: - if isShort: - field_size = 2 - elif isLong: - field_size = 4 - else: - field_size = 0 - return self._fixOffsets(count, field_size) - - -def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - append_images = list(im.encoderinfo.get("append_images", [])) - if not hasattr(im, "n_frames") and not append_images: - return _save(im, fp, filename) - - cur_idx = im.tell() - try: - with AppendingTiffWriter(fp) as tf: - for ims in [im] + append_images: - encoderinfo = ims._attach_default_encoderinfo(im) - if not hasattr(ims, "encoderconfig"): - ims.encoderconfig = () - nfr = getattr(ims, "n_frames", 1) - - for idx in range(nfr): - ims.seek(idx) - ims.load() - _save(ims, tf, filename) - tf.newFrame() - ims.encoderinfo = encoderinfo - finally: - im.seek(cur_idx) - - -# -# -------------------------------------------------------------------- -# Register - -Image.register_open(TiffImageFile.format, TiffImageFile, _accept) -Image.register_save(TiffImageFile.format, _save) -Image.register_save_all(TiffImageFile.format, _save_all) - -Image.register_extensions(TiffImageFile.format, [".tif", ".tiff"]) - -Image.register_mime(TiffImageFile.format, "image/tiff") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/TiffTags.py b/.venv-docs/lib/python3.12/site-packages/PIL/TiffTags.py deleted file mode 100644 index 761aa3f6..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/TiffTags.py +++ /dev/null @@ -1,567 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# TIFF tags -# -# This module provides clear-text names for various well-known -# TIFF tags. the TIFF codec works just fine without it. -# -# Copyright (c) Secret Labs AB 1999. -# -# See the README file for information on usage and redistribution. -# - -## -# This module provides constants and clear-text names for various -# well-known TIFF tags. -## -from __future__ import annotations - -from typing import NamedTuple - - -class _TagInfo(NamedTuple): - value: int | None - name: str - type: int | None - length: int | None - enum: dict[str, int] - - -class TagInfo(_TagInfo): - __slots__: list[str] = [] - - def __new__( - cls, - value: int | None = None, - name: str = "unknown", - type: int | None = None, - length: int | None = None, - enum: dict[str, int] | None = None, - ) -> TagInfo: - return super().__new__(cls, value, name, type, length, enum or {}) - - def cvt_enum(self, value: str) -> int | str: - # Using get will call hash(value), which can be expensive - # for some types (e.g. Fraction). Since self.enum is rarely - # used, it's usually better to test it first. - return self.enum.get(value, value) if self.enum else value - - -def lookup(tag: int, group: int | None = None) -> TagInfo: - """ - :param tag: Integer tag number - :param group: Which :py:data:`~PIL.TiffTags.TAGS_V2_GROUPS` to look in - - .. versionadded:: 8.3.0 - - :returns: Taginfo namedtuple, From the ``TAGS_V2`` info if possible, - otherwise just populating the value and name from ``TAGS``. - If the tag is not recognized, "unknown" is returned for the name - - """ - - if group is not None: - info = TAGS_V2_GROUPS[group].get(tag) if group in TAGS_V2_GROUPS else None - else: - info = TAGS_V2.get(tag) - return info or TagInfo(tag, TAGS.get(tag, "unknown")) - - -## -# Map tag numbers to tag info. -# -# id: (Name, Type, Length[, enum_values]) -# -# The length here differs from the length in the tiff spec. For -# numbers, the tiff spec is for the number of fields returned. We -# agree here. For string-like types, the tiff spec uses the length of -# field in bytes. In Pillow, we are using the number of expected -# fields, in general 1 for string-like types. - - -BYTE = 1 -ASCII = 2 -SHORT = 3 -LONG = 4 -RATIONAL = 5 -SIGNED_BYTE = 6 -UNDEFINED = 7 -SIGNED_SHORT = 8 -SIGNED_LONG = 9 -SIGNED_RATIONAL = 10 -FLOAT = 11 -DOUBLE = 12 -IFD = 13 -LONG8 = 16 - -_tags_v2: dict[int, tuple[str, int, int] | tuple[str, int, int, dict[str, int]]] = { - 254: ("NewSubfileType", LONG, 1), - 255: ("SubfileType", SHORT, 1), - 256: ("ImageWidth", LONG, 1), - 257: ("ImageLength", LONG, 1), - 258: ("BitsPerSample", SHORT, 0), - 259: ( - "Compression", - SHORT, - 1, - { - "Uncompressed": 1, - "CCITT 1d": 2, - "Group 3 Fax": 3, - "Group 4 Fax": 4, - "LZW": 5, - "JPEG": 6, - "PackBits": 32773, - }, - ), - 262: ( - "PhotometricInterpretation", - SHORT, - 1, - { - "WhiteIsZero": 0, - "BlackIsZero": 1, - "RGB": 2, - "RGB Palette": 3, - "Transparency Mask": 4, - "CMYK": 5, - "YCbCr": 6, - "CieLAB": 8, - "CFA": 32803, # TIFF/EP, Adobe DNG - "LinearRaw": 32892, # Adobe DNG - }, - ), - 263: ("Threshholding", SHORT, 1), - 264: ("CellWidth", SHORT, 1), - 265: ("CellLength", SHORT, 1), - 266: ("FillOrder", SHORT, 1), - 269: ("DocumentName", ASCII, 1), - 270: ("ImageDescription", ASCII, 1), - 271: ("Make", ASCII, 1), - 272: ("Model", ASCII, 1), - 273: ("StripOffsets", LONG, 0), - 274: ("Orientation", SHORT, 1), - 277: ("SamplesPerPixel", SHORT, 1), - 278: ("RowsPerStrip", LONG, 1), - 279: ("StripByteCounts", LONG, 0), - 280: ("MinSampleValue", SHORT, 0), - 281: ("MaxSampleValue", SHORT, 0), - 282: ("XResolution", RATIONAL, 1), - 283: ("YResolution", RATIONAL, 1), - 284: ("PlanarConfiguration", SHORT, 1, {"Contiguous": 1, "Separate": 2}), - 285: ("PageName", ASCII, 1), - 286: ("XPosition", RATIONAL, 1), - 287: ("YPosition", RATIONAL, 1), - 288: ("FreeOffsets", LONG, 1), - 289: ("FreeByteCounts", LONG, 1), - 290: ("GrayResponseUnit", SHORT, 1), - 291: ("GrayResponseCurve", SHORT, 0), - 292: ("T4Options", LONG, 1), - 293: ("T6Options", LONG, 1), - 296: ("ResolutionUnit", SHORT, 1, {"none": 1, "inch": 2, "cm": 3}), - 297: ("PageNumber", SHORT, 2), - 301: ("TransferFunction", SHORT, 0), - 305: ("Software", ASCII, 1), - 306: ("DateTime", ASCII, 1), - 315: ("Artist", ASCII, 1), - 316: ("HostComputer", ASCII, 1), - 317: ("Predictor", SHORT, 1, {"none": 1, "Horizontal Differencing": 2}), - 318: ("WhitePoint", RATIONAL, 2), - 319: ("PrimaryChromaticities", RATIONAL, 6), - 320: ("ColorMap", SHORT, 0), - 321: ("HalftoneHints", SHORT, 2), - 322: ("TileWidth", LONG, 1), - 323: ("TileLength", LONG, 1), - 324: ("TileOffsets", LONG, 0), - 325: ("TileByteCounts", LONG, 0), - 330: ("SubIFDs", LONG, 0), - 332: ("InkSet", SHORT, 1), - 333: ("InkNames", ASCII, 1), - 334: ("NumberOfInks", SHORT, 1), - 336: ("DotRange", SHORT, 0), - 337: ("TargetPrinter", ASCII, 1), - 338: ("ExtraSamples", SHORT, 0), - 339: ("SampleFormat", SHORT, 0), - 340: ("SMinSampleValue", DOUBLE, 0), - 341: ("SMaxSampleValue", DOUBLE, 0), - 342: ("TransferRange", SHORT, 6), - 347: ("JPEGTables", UNDEFINED, 1), - # obsolete JPEG tags - 512: ("JPEGProc", SHORT, 1), - 513: ("JPEGInterchangeFormat", LONG, 1), - 514: ("JPEGInterchangeFormatLength", LONG, 1), - 515: ("JPEGRestartInterval", SHORT, 1), - 517: ("JPEGLosslessPredictors", SHORT, 0), - 518: ("JPEGPointTransforms", SHORT, 0), - 519: ("JPEGQTables", LONG, 0), - 520: ("JPEGDCTables", LONG, 0), - 521: ("JPEGACTables", LONG, 0), - 529: ("YCbCrCoefficients", RATIONAL, 3), - 530: ("YCbCrSubSampling", SHORT, 2), - 531: ("YCbCrPositioning", SHORT, 1), - 532: ("ReferenceBlackWhite", RATIONAL, 6), - 700: ("XMP", BYTE, 0), - # Four private SGI tags - 32995: ("Matteing", SHORT, 1), - 32996: ("DataType", SHORT, 0), - 32997: ("ImageDepth", LONG, 1), - 32998: ("TileDepth", LONG, 1), - 33432: ("Copyright", ASCII, 1), - 33723: ("IptcNaaInfo", UNDEFINED, 1), - 34377: ("PhotoshopInfo", BYTE, 0), - # FIXME add more tags here - 34665: ("ExifIFD", LONG, 1), - 34675: ("ICCProfile", UNDEFINED, 1), - 34853: ("GPSInfoIFD", LONG, 1), - 36864: ("ExifVersion", UNDEFINED, 1), - 37724: ("ImageSourceData", UNDEFINED, 1), - 40965: ("InteroperabilityIFD", LONG, 1), - 41730: ("CFAPattern", UNDEFINED, 1), - # MPInfo - 45056: ("MPFVersion", UNDEFINED, 1), - 45057: ("NumberOfImages", LONG, 1), - 45058: ("MPEntry", UNDEFINED, 1), - 45059: ("ImageUIDList", UNDEFINED, 0), # UNDONE, check - 45060: ("TotalFrames", LONG, 1), - 45313: ("MPIndividualNum", LONG, 1), - 45569: ("PanOrientation", LONG, 1), - 45570: ("PanOverlap_H", RATIONAL, 1), - 45571: ("PanOverlap_V", RATIONAL, 1), - 45572: ("BaseViewpointNum", LONG, 1), - 45573: ("ConvergenceAngle", SIGNED_RATIONAL, 1), - 45574: ("BaselineLength", RATIONAL, 1), - 45575: ("VerticalDivergence", SIGNED_RATIONAL, 1), - 45576: ("AxisDistance_X", SIGNED_RATIONAL, 1), - 45577: ("AxisDistance_Y", SIGNED_RATIONAL, 1), - 45578: ("AxisDistance_Z", SIGNED_RATIONAL, 1), - 45579: ("YawAngle", SIGNED_RATIONAL, 1), - 45580: ("PitchAngle", SIGNED_RATIONAL, 1), - 45581: ("RollAngle", SIGNED_RATIONAL, 1), - 40960: ("FlashPixVersion", UNDEFINED, 1), - 50741: ("MakerNoteSafety", SHORT, 1, {"Unsafe": 0, "Safe": 1}), - 50780: ("BestQualityScale", RATIONAL, 1), - 50838: ("ImageJMetaDataByteCounts", LONG, 0), # Can be more than one - 50839: ("ImageJMetaData", UNDEFINED, 1), # see Issue #2006 -} -_tags_v2_groups = { - # ExifIFD - 34665: { - 36864: ("ExifVersion", UNDEFINED, 1), - 40960: ("FlashPixVersion", UNDEFINED, 1), - 40965: ("InteroperabilityIFD", LONG, 1), - 41730: ("CFAPattern", UNDEFINED, 1), - }, - # GPSInfoIFD - 34853: { - 0: ("GPSVersionID", BYTE, 4), - 1: ("GPSLatitudeRef", ASCII, 2), - 2: ("GPSLatitude", RATIONAL, 3), - 3: ("GPSLongitudeRef", ASCII, 2), - 4: ("GPSLongitude", RATIONAL, 3), - 5: ("GPSAltitudeRef", BYTE, 1), - 6: ("GPSAltitude", RATIONAL, 1), - 7: ("GPSTimeStamp", RATIONAL, 3), - 8: ("GPSSatellites", ASCII, 0), - 9: ("GPSStatus", ASCII, 2), - 10: ("GPSMeasureMode", ASCII, 2), - 11: ("GPSDOP", RATIONAL, 1), - 12: ("GPSSpeedRef", ASCII, 2), - 13: ("GPSSpeed", RATIONAL, 1), - 14: ("GPSTrackRef", ASCII, 2), - 15: ("GPSTrack", RATIONAL, 1), - 16: ("GPSImgDirectionRef", ASCII, 2), - 17: ("GPSImgDirection", RATIONAL, 1), - 18: ("GPSMapDatum", ASCII, 0), - 19: ("GPSDestLatitudeRef", ASCII, 2), - 20: ("GPSDestLatitude", RATIONAL, 3), - 21: ("GPSDestLongitudeRef", ASCII, 2), - 22: ("GPSDestLongitude", RATIONAL, 3), - 23: ("GPSDestBearingRef", ASCII, 2), - 24: ("GPSDestBearing", RATIONAL, 1), - 25: ("GPSDestDistanceRef", ASCII, 2), - 26: ("GPSDestDistance", RATIONAL, 1), - 27: ("GPSProcessingMethod", UNDEFINED, 0), - 28: ("GPSAreaInformation", UNDEFINED, 0), - 29: ("GPSDateStamp", ASCII, 11), - 30: ("GPSDifferential", SHORT, 1), - }, - # InteroperabilityIFD - 40965: {1: ("InteropIndex", ASCII, 1), 2: ("InteropVersion", UNDEFINED, 1)}, -} - -# Legacy Tags structure -# these tags aren't included above, but were in the previous versions -TAGS: dict[int | tuple[int, int], str] = { - 347: "JPEGTables", - 700: "XMP", - # Additional Exif Info - 32932: "Wang Annotation", - 33434: "ExposureTime", - 33437: "FNumber", - 33445: "MD FileTag", - 33446: "MD ScalePixel", - 33447: "MD ColorTable", - 33448: "MD LabName", - 33449: "MD SampleInfo", - 33450: "MD PrepDate", - 33451: "MD PrepTime", - 33452: "MD FileUnits", - 33550: "ModelPixelScaleTag", - 33723: "IptcNaaInfo", - 33918: "INGR Packet Data Tag", - 33919: "INGR Flag Registers", - 33920: "IrasB Transformation Matrix", - 33922: "ModelTiepointTag", - 34264: "ModelTransformationTag", - 34377: "PhotoshopInfo", - 34735: "GeoKeyDirectoryTag", - 34736: "GeoDoubleParamsTag", - 34737: "GeoAsciiParamsTag", - 34850: "ExposureProgram", - 34852: "SpectralSensitivity", - 34855: "ISOSpeedRatings", - 34856: "OECF", - 34864: "SensitivityType", - 34865: "StandardOutputSensitivity", - 34866: "RecommendedExposureIndex", - 34867: "ISOSpeed", - 34868: "ISOSpeedLatitudeyyy", - 34869: "ISOSpeedLatitudezzz", - 34908: "HylaFAX FaxRecvParams", - 34909: "HylaFAX FaxSubAddress", - 34910: "HylaFAX FaxRecvTime", - 36864: "ExifVersion", - 36867: "DateTimeOriginal", - 36868: "DateTimeDigitized", - 37121: "ComponentsConfiguration", - 37122: "CompressedBitsPerPixel", - 37724: "ImageSourceData", - 37377: "ShutterSpeedValue", - 37378: "ApertureValue", - 37379: "BrightnessValue", - 37380: "ExposureBiasValue", - 37381: "MaxApertureValue", - 37382: "SubjectDistance", - 37383: "MeteringMode", - 37384: "LightSource", - 37385: "Flash", - 37386: "FocalLength", - 37396: "SubjectArea", - 37500: "MakerNote", - 37510: "UserComment", - 37520: "SubSec", - 37521: "SubSecTimeOriginal", - 37522: "SubsecTimeDigitized", - 40960: "FlashPixVersion", - 40961: "ColorSpace", - 40962: "PixelXDimension", - 40963: "PixelYDimension", - 40964: "RelatedSoundFile", - 40965: "InteroperabilityIFD", - 41483: "FlashEnergy", - 41484: "SpatialFrequencyResponse", - 41486: "FocalPlaneXResolution", - 41487: "FocalPlaneYResolution", - 41488: "FocalPlaneResolutionUnit", - 41492: "SubjectLocation", - 41493: "ExposureIndex", - 41495: "SensingMethod", - 41728: "FileSource", - 41729: "SceneType", - 41730: "CFAPattern", - 41985: "CustomRendered", - 41986: "ExposureMode", - 41987: "WhiteBalance", - 41988: "DigitalZoomRatio", - 41989: "FocalLengthIn35mmFilm", - 41990: "SceneCaptureType", - 41991: "GainControl", - 41992: "Contrast", - 41993: "Saturation", - 41994: "Sharpness", - 41995: "DeviceSettingDescription", - 41996: "SubjectDistanceRange", - 42016: "ImageUniqueID", - 42032: "CameraOwnerName", - 42033: "BodySerialNumber", - 42034: "LensSpecification", - 42035: "LensMake", - 42036: "LensModel", - 42037: "LensSerialNumber", - 42112: "GDAL_METADATA", - 42113: "GDAL_NODATA", - 42240: "Gamma", - 50215: "Oce Scanjob Description", - 50216: "Oce Application Selector", - 50217: "Oce Identification Number", - 50218: "Oce ImageLogic Characteristics", - # Adobe DNG - 50706: "DNGVersion", - 50707: "DNGBackwardVersion", - 50708: "UniqueCameraModel", - 50709: "LocalizedCameraModel", - 50710: "CFAPlaneColor", - 50711: "CFALayout", - 50712: "LinearizationTable", - 50713: "BlackLevelRepeatDim", - 50714: "BlackLevel", - 50715: "BlackLevelDeltaH", - 50716: "BlackLevelDeltaV", - 50717: "WhiteLevel", - 50718: "DefaultScale", - 50719: "DefaultCropOrigin", - 50720: "DefaultCropSize", - 50721: "ColorMatrix1", - 50722: "ColorMatrix2", - 50723: "CameraCalibration1", - 50724: "CameraCalibration2", - 50725: "ReductionMatrix1", - 50726: "ReductionMatrix2", - 50727: "AnalogBalance", - 50728: "AsShotNeutral", - 50729: "AsShotWhiteXY", - 50730: "BaselineExposure", - 50731: "BaselineNoise", - 50732: "BaselineSharpness", - 50733: "BayerGreenSplit", - 50734: "LinearResponseLimit", - 50735: "CameraSerialNumber", - 50736: "LensInfo", - 50737: "ChromaBlurRadius", - 50738: "AntiAliasStrength", - 50740: "DNGPrivateData", - 50778: "CalibrationIlluminant1", - 50779: "CalibrationIlluminant2", - 50784: "Alias Layer Metadata", -} - -TAGS_V2: dict[int, TagInfo] = {} -TAGS_V2_GROUPS: dict[int, dict[int, TagInfo]] = {} - - -def _populate() -> None: - for k, v in _tags_v2.items(): - # Populate legacy structure. - TAGS[k] = v[0] - if len(v) == 4: - for sk, sv in v[3].items(): - TAGS[(k, sv)] = sk - - TAGS_V2[k] = TagInfo(k, *v) - - for group, tags in _tags_v2_groups.items(): - TAGS_V2_GROUPS[group] = {k: TagInfo(k, *v) for k, v in tags.items()} - - -_populate() -## -# Map type numbers to type names -- defined in ImageFileDirectory. - -TYPES: dict[int, str] = {} - -# -# These tags are handled by default in libtiff, without -# adding to the custom dictionary. From tif_dir.c, searching for -# case TIFFTAG in the _TIFFVSetField function: -# Line: item. -# 148: case TIFFTAG_SUBFILETYPE: -# 151: case TIFFTAG_IMAGEWIDTH: -# 154: case TIFFTAG_IMAGELENGTH: -# 157: case TIFFTAG_BITSPERSAMPLE: -# 181: case TIFFTAG_COMPRESSION: -# 202: case TIFFTAG_PHOTOMETRIC: -# 205: case TIFFTAG_THRESHHOLDING: -# 208: case TIFFTAG_FILLORDER: -# 214: case TIFFTAG_ORIENTATION: -# 221: case TIFFTAG_SAMPLESPERPIXEL: -# 228: case TIFFTAG_ROWSPERSTRIP: -# 238: case TIFFTAG_MINSAMPLEVALUE: -# 241: case TIFFTAG_MAXSAMPLEVALUE: -# 244: case TIFFTAG_SMINSAMPLEVALUE: -# 247: case TIFFTAG_SMAXSAMPLEVALUE: -# 250: case TIFFTAG_XRESOLUTION: -# 256: case TIFFTAG_YRESOLUTION: -# 262: case TIFFTAG_PLANARCONFIG: -# 268: case TIFFTAG_XPOSITION: -# 271: case TIFFTAG_YPOSITION: -# 274: case TIFFTAG_RESOLUTIONUNIT: -# 280: case TIFFTAG_PAGENUMBER: -# 284: case TIFFTAG_HALFTONEHINTS: -# 288: case TIFFTAG_COLORMAP: -# 294: case TIFFTAG_EXTRASAMPLES: -# 298: case TIFFTAG_MATTEING: -# 305: case TIFFTAG_TILEWIDTH: -# 316: case TIFFTAG_TILELENGTH: -# 327: case TIFFTAG_TILEDEPTH: -# 333: case TIFFTAG_DATATYPE: -# 344: case TIFFTAG_SAMPLEFORMAT: -# 361: case TIFFTAG_IMAGEDEPTH: -# 364: case TIFFTAG_SUBIFD: -# 376: case TIFFTAG_YCBCRPOSITIONING: -# 379: case TIFFTAG_YCBCRSUBSAMPLING: -# 383: case TIFFTAG_TRANSFERFUNCTION: -# 389: case TIFFTAG_REFERENCEBLACKWHITE: -# 393: case TIFFTAG_INKNAMES: - -# Following pseudo-tags are also handled by default in libtiff: -# TIFFTAG_JPEGQUALITY 65537 - -# some of these are not in our TAGS_V2 dict and were included from tiff.h - -# This list also exists in encode.c -LIBTIFF_CORE = { - 255, - 256, - 257, - 258, - 259, - 262, - 263, - 266, - 274, - 277, - 278, - 280, - 281, - 340, - 341, - 282, - 283, - 284, - 286, - 287, - 296, - 297, - 321, - 320, - 338, - 32995, - 322, - 323, - 32998, - 32996, - 339, - 32997, - 330, - 531, - 530, - 301, - 532, - 333, - # as above - 269, # this has been in our tests forever, and works - 65537, -} - -LIBTIFF_CORE.remove(255) # We don't have support for subfiletypes -LIBTIFF_CORE.remove(322) # We don't have support for writing tiled images with libtiff -LIBTIFF_CORE.remove(323) # Tiled images -LIBTIFF_CORE.remove(333) # Ink Names either - -# Note to advanced users: There may be combinations of these -# parameters and values that when added properly, will work and -# produce valid tiff images that may work in your application. -# It is safe to add and remove tags from this set from Pillow's point -# of view so long as you test against libtiff. diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/WalImageFile.py b/.venv-docs/lib/python3.12/site-packages/PIL/WalImageFile.py deleted file mode 100644 index 5494f62e..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/WalImageFile.py +++ /dev/null @@ -1,126 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# WAL file handling -# -# History: -# 2003-04-23 fl created -# -# Copyright (c) 2003 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# - -""" -This reader is based on the specification available from: -https://www.flipcode.com/archives/Quake_2_BSP_File_Format.shtml -and has been tested with a few sample files found using google. - -.. note:: - This format cannot be automatically recognized, so the reader - is not registered for use with :py:func:`PIL.Image.open()`. - To open a WAL file, use the :py:func:`PIL.WalImageFile.open()` function instead. -""" -from __future__ import annotations - -from typing import IO - -from . import Image, ImageFile -from ._binary import i32le as i32 -from ._typing import StrOrBytesPath - - -class WalImageFile(ImageFile.ImageFile): - format = "WAL" - format_description = "Quake2 Texture" - - def _open(self) -> None: - self._mode = "P" - - # read header fields - header = self.fp.read(32 + 24 + 32 + 12) - self._size = i32(header, 32), i32(header, 36) - Image._decompression_bomb_check(self.size) - - # load pixel data - offset = i32(header, 40) - self.fp.seek(offset) - - # strings are null-terminated - self.info["name"] = header[:32].split(b"\0", 1)[0] - if next_name := header[56 : 56 + 32].split(b"\0", 1)[0]: - self.info["next_name"] = next_name - - def load(self) -> Image.core.PixelAccess | None: - if self._im is None: - self.im = Image.core.new(self.mode, self.size) - self.frombytes(self.fp.read(self.size[0] * self.size[1])) - self.putpalette(quake2palette) - return Image.Image.load(self) - - -def open(filename: StrOrBytesPath | IO[bytes]) -> WalImageFile: - """ - Load texture from a Quake2 WAL texture file. - - By default, a Quake2 standard palette is attached to the texture. - To override the palette, use the :py:func:`PIL.Image.Image.putpalette()` method. - - :param filename: WAL file name, or an opened file handle. - :returns: An image instance. - """ - return WalImageFile(filename) - - -quake2palette = ( - # default palette taken from piffo 0.93 by Hans Häggström - b"\x01\x01\x01\x0b\x0b\x0b\x12\x12\x12\x17\x17\x17\x1b\x1b\x1b\x1e" - b"\x1e\x1e\x22\x22\x22\x26\x26\x26\x29\x29\x29\x2c\x2c\x2c\x2f\x2f" - b"\x2f\x32\x32\x32\x35\x35\x35\x37\x37\x37\x3a\x3a\x3a\x3c\x3c\x3c" - b"\x24\x1e\x13\x22\x1c\x12\x20\x1b\x12\x1f\x1a\x10\x1d\x19\x10\x1b" - b"\x17\x0f\x1a\x16\x0f\x18\x14\x0d\x17\x13\x0d\x16\x12\x0d\x14\x10" - b"\x0b\x13\x0f\x0b\x10\x0d\x0a\x0f\x0b\x0a\x0d\x0b\x07\x0b\x0a\x07" - b"\x23\x23\x26\x22\x22\x25\x22\x20\x23\x21\x1f\x22\x20\x1e\x20\x1f" - b"\x1d\x1e\x1d\x1b\x1c\x1b\x1a\x1a\x1a\x19\x19\x18\x17\x17\x17\x16" - b"\x16\x14\x14\x14\x13\x13\x13\x10\x10\x10\x0f\x0f\x0f\x0d\x0d\x0d" - b"\x2d\x28\x20\x29\x24\x1c\x27\x22\x1a\x25\x1f\x17\x38\x2e\x1e\x31" - b"\x29\x1a\x2c\x25\x17\x26\x20\x14\x3c\x30\x14\x37\x2c\x13\x33\x28" - b"\x12\x2d\x24\x10\x28\x1f\x0f\x22\x1a\x0b\x1b\x14\x0a\x13\x0f\x07" - b"\x31\x1a\x16\x30\x17\x13\x2e\x16\x10\x2c\x14\x0d\x2a\x12\x0b\x27" - b"\x0f\x0a\x25\x0f\x07\x21\x0d\x01\x1e\x0b\x01\x1c\x0b\x01\x1a\x0b" - b"\x01\x18\x0a\x01\x16\x0a\x01\x13\x0a\x01\x10\x07\x01\x0d\x07\x01" - b"\x29\x23\x1e\x27\x21\x1c\x26\x20\x1b\x25\x1f\x1a\x23\x1d\x19\x21" - b"\x1c\x18\x20\x1b\x17\x1e\x19\x16\x1c\x18\x14\x1b\x17\x13\x19\x14" - b"\x10\x17\x13\x0f\x14\x10\x0d\x12\x0f\x0b\x0f\x0b\x0a\x0b\x0a\x07" - b"\x26\x1a\x0f\x23\x19\x0f\x20\x17\x0f\x1c\x16\x0f\x19\x13\x0d\x14" - b"\x10\x0b\x10\x0d\x0a\x0b\x0a\x07\x33\x22\x1f\x35\x29\x26\x37\x2f" - b"\x2d\x39\x35\x34\x37\x39\x3a\x33\x37\x39\x30\x34\x36\x2b\x31\x34" - b"\x27\x2e\x31\x22\x2b\x2f\x1d\x28\x2c\x17\x25\x2a\x0f\x20\x26\x0d" - b"\x1e\x25\x0b\x1c\x22\x0a\x1b\x20\x07\x19\x1e\x07\x17\x1b\x07\x14" - b"\x18\x01\x12\x16\x01\x0f\x12\x01\x0b\x0d\x01\x07\x0a\x01\x01\x01" - b"\x2c\x21\x21\x2a\x1f\x1f\x29\x1d\x1d\x27\x1c\x1c\x26\x1a\x1a\x24" - b"\x18\x18\x22\x17\x17\x21\x16\x16\x1e\x13\x13\x1b\x12\x12\x18\x10" - b"\x10\x16\x0d\x0d\x12\x0b\x0b\x0d\x0a\x0a\x0a\x07\x07\x01\x01\x01" - b"\x2e\x30\x29\x2d\x2e\x27\x2b\x2c\x26\x2a\x2a\x24\x28\x29\x23\x27" - b"\x27\x21\x26\x26\x1f\x24\x24\x1d\x22\x22\x1c\x1f\x1f\x1a\x1c\x1c" - b"\x18\x19\x19\x16\x17\x17\x13\x13\x13\x10\x0f\x0f\x0d\x0b\x0b\x0a" - b"\x30\x1e\x1b\x2d\x1c\x19\x2c\x1a\x17\x2a\x19\x14\x28\x17\x13\x26" - b"\x16\x10\x24\x13\x0f\x21\x12\x0d\x1f\x10\x0b\x1c\x0f\x0a\x19\x0d" - b"\x0a\x16\x0b\x07\x12\x0a\x07\x0f\x07\x01\x0a\x01\x01\x01\x01\x01" - b"\x28\x29\x38\x26\x27\x36\x25\x26\x34\x24\x24\x31\x22\x22\x2f\x20" - b"\x21\x2d\x1e\x1f\x2a\x1d\x1d\x27\x1b\x1b\x25\x19\x19\x21\x17\x17" - b"\x1e\x14\x14\x1b\x13\x12\x17\x10\x0f\x13\x0d\x0b\x0f\x0a\x07\x07" - b"\x2f\x32\x29\x2d\x30\x26\x2b\x2e\x24\x29\x2c\x21\x27\x2a\x1e\x25" - b"\x28\x1c\x23\x26\x1a\x21\x25\x18\x1e\x22\x14\x1b\x1f\x10\x19\x1c" - b"\x0d\x17\x1a\x0a\x13\x17\x07\x10\x13\x01\x0d\x0f\x01\x0a\x0b\x01" - b"\x01\x3f\x01\x13\x3c\x0b\x1b\x39\x10\x20\x35\x14\x23\x31\x17\x23" - b"\x2d\x18\x23\x29\x18\x3f\x3f\x3f\x3f\x3f\x39\x3f\x3f\x31\x3f\x3f" - b"\x2a\x3f\x3f\x20\x3f\x3f\x14\x3f\x3c\x12\x3f\x39\x0f\x3f\x35\x0b" - b"\x3f\x32\x07\x3f\x2d\x01\x3d\x2a\x01\x3b\x26\x01\x39\x21\x01\x37" - b"\x1d\x01\x34\x1a\x01\x32\x16\x01\x2f\x12\x01\x2d\x0f\x01\x2a\x0b" - b"\x01\x27\x07\x01\x23\x01\x01\x1d\x01\x01\x17\x01\x01\x10\x01\x01" - b"\x3d\x01\x01\x19\x19\x3f\x3f\x01\x01\x01\x01\x3f\x16\x16\x13\x10" - b"\x10\x0f\x0d\x0d\x0b\x3c\x2e\x2a\x36\x27\x20\x30\x21\x18\x29\x1b" - b"\x10\x3c\x39\x37\x37\x32\x2f\x31\x2c\x28\x2b\x26\x21\x30\x22\x20" -) diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/WebPImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/WebPImagePlugin.py deleted file mode 100644 index 2847fed2..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/WebPImagePlugin.py +++ /dev/null @@ -1,322 +0,0 @@ -from __future__ import annotations - -from io import BytesIO - -from . import Image, ImageFile - -try: - from . import _webp - - SUPPORTED = True -except ImportError: - SUPPORTED = False - -TYPE_CHECKING = False -if TYPE_CHECKING: - from typing import IO, Any - -_VP8_MODES_BY_IDENTIFIER = { - b"VP8 ": "RGB", - b"VP8X": "RGBA", - b"VP8L": "RGBA", # lossless -} - - -def _accept(prefix: bytes) -> bool | str: - is_riff_file_format = prefix.startswith(b"RIFF") - is_webp_file = prefix[8:12] == b"WEBP" - is_valid_vp8_mode = prefix[12:16] in _VP8_MODES_BY_IDENTIFIER - - if is_riff_file_format and is_webp_file and is_valid_vp8_mode: - if not SUPPORTED: - return ( - "image file could not be identified because WEBP support not installed" - ) - return True - return False - - -class WebPImageFile(ImageFile.ImageFile): - format = "WEBP" - format_description = "WebP image" - __loaded = 0 - __logical_frame = 0 - - def _open(self) -> None: - # Use the newer AnimDecoder API to parse the (possibly) animated file, - # and access muxed chunks like ICC/EXIF/XMP. - self._decoder = _webp.WebPAnimDecoder(self.fp.read()) - - # Get info from decoder - self._size, loop_count, bgcolor, frame_count, mode = self._decoder.get_info() - self.info["loop"] = loop_count - bg_a, bg_r, bg_g, bg_b = ( - (bgcolor >> 24) & 0xFF, - (bgcolor >> 16) & 0xFF, - (bgcolor >> 8) & 0xFF, - bgcolor & 0xFF, - ) - self.info["background"] = (bg_r, bg_g, bg_b, bg_a) - self.n_frames = frame_count - self.is_animated = self.n_frames > 1 - self._mode = "RGB" if mode == "RGBX" else mode - self.rawmode = mode - - # Attempt to read ICC / EXIF / XMP chunks from file - icc_profile = self._decoder.get_chunk("ICCP") - exif = self._decoder.get_chunk("EXIF") - xmp = self._decoder.get_chunk("XMP ") - if icc_profile: - self.info["icc_profile"] = icc_profile - if exif: - self.info["exif"] = exif - if xmp: - self.info["xmp"] = xmp - - # Initialize seek state - self._reset(reset=False) - - def _getexif(self) -> dict[int, Any] | None: - if "exif" not in self.info: - return None - return self.getexif()._get_merged_dict() - - def seek(self, frame: int) -> None: - if not self._seek_check(frame): - return - - # Set logical frame to requested position - self.__logical_frame = frame - - def _reset(self, reset: bool = True) -> None: - if reset: - self._decoder.reset() - self.__physical_frame = 0 - self.__loaded = -1 - self.__timestamp = 0 - - def _get_next(self) -> tuple[bytes, int, int]: - # Get next frame - ret = self._decoder.get_next() - self.__physical_frame += 1 - - # Check if an error occurred - if ret is None: - self._reset() # Reset just to be safe - self.seek(0) - msg = "failed to decode next frame in WebP file" - raise EOFError(msg) - - # Compute duration - data, timestamp = ret - duration = timestamp - self.__timestamp - self.__timestamp = timestamp - - # libwebp gives frame end, adjust to start of frame - timestamp -= duration - return data, timestamp, duration - - def _seek(self, frame: int) -> None: - if self.__physical_frame == frame: - return # Nothing to do - if frame < self.__physical_frame: - self._reset() # Rewind to beginning - while self.__physical_frame < frame: - self._get_next() # Advance to the requested frame - - def load(self) -> Image.core.PixelAccess | None: - if self.__loaded != self.__logical_frame: - self._seek(self.__logical_frame) - - # We need to load the image data for this frame - data, timestamp, duration = self._get_next() - self.info["timestamp"] = timestamp - self.info["duration"] = duration - self.__loaded = self.__logical_frame - - # Set tile - if self.fp and self._exclusive_fp: - self.fp.close() - self.fp = BytesIO(data) - self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 0, self.rawmode)] - - return super().load() - - def load_seek(self, pos: int) -> None: - pass - - def tell(self) -> int: - return self.__logical_frame - - -def _convert_frame(im: Image.Image) -> Image.Image: - # Make sure image mode is supported - if im.mode not in ("RGBX", "RGBA", "RGB"): - im = im.convert("RGBA" if im.has_transparency_data else "RGB") - return im - - -def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - encoderinfo = im.encoderinfo.copy() - append_images = list(encoderinfo.get("append_images", [])) - - # If total frame count is 1, then save using the legacy API, which - # will preserve non-alpha modes - total = 0 - for ims in [im] + append_images: - total += getattr(ims, "n_frames", 1) - if total == 1: - _save(im, fp, filename) - return - - background: int | tuple[int, ...] = (0, 0, 0, 0) - if "background" in encoderinfo: - background = encoderinfo["background"] - elif "background" in im.info: - background = im.info["background"] - if isinstance(background, int): - # GifImagePlugin stores a global color table index in - # info["background"]. So it must be converted to an RGBA value - palette = im.getpalette() - if palette: - r, g, b = palette[background * 3 : (background + 1) * 3] - background = (r, g, b, 255) - else: - background = (background, background, background, 255) - - duration = im.encoderinfo.get("duration", im.info.get("duration", 0)) - loop = im.encoderinfo.get("loop", 0) - minimize_size = im.encoderinfo.get("minimize_size", False) - kmin = im.encoderinfo.get("kmin", None) - kmax = im.encoderinfo.get("kmax", None) - allow_mixed = im.encoderinfo.get("allow_mixed", False) - verbose = False - lossless = im.encoderinfo.get("lossless", False) - quality = im.encoderinfo.get("quality", 80) - alpha_quality = im.encoderinfo.get("alpha_quality", 100) - method = im.encoderinfo.get("method", 0) - icc_profile = im.encoderinfo.get("icc_profile") or "" - exif = im.encoderinfo.get("exif", "") - if isinstance(exif, Image.Exif): - exif = exif.tobytes() - xmp = im.encoderinfo.get("xmp", "") - if allow_mixed: - lossless = False - - # Sensible keyframe defaults are from gif2webp.c script - if kmin is None: - kmin = 9 if lossless else 3 - if kmax is None: - kmax = 17 if lossless else 5 - - # Validate background color - if ( - not isinstance(background, (list, tuple)) - or len(background) != 4 - or not all(0 <= v < 256 for v in background) - ): - msg = f"Background color is not an RGBA tuple clamped to (0-255): {background}" - raise OSError(msg) - - # Convert to packed uint - bg_r, bg_g, bg_b, bg_a = background - background = (bg_a << 24) | (bg_r << 16) | (bg_g << 8) | (bg_b << 0) - - # Setup the WebP animation encoder - enc = _webp.WebPAnimEncoder( - im.size, - background, - loop, - minimize_size, - kmin, - kmax, - allow_mixed, - verbose, - ) - - # Add each frame - frame_idx = 0 - timestamp = 0 - cur_idx = im.tell() - try: - for ims in [im] + append_images: - # Get number of frames in this image - nfr = getattr(ims, "n_frames", 1) - - for idx in range(nfr): - ims.seek(idx) - - frame = _convert_frame(ims) - - # Append the frame to the animation encoder - enc.add( - frame.getim(), - round(timestamp), - lossless, - quality, - alpha_quality, - method, - ) - - # Update timestamp and frame index - if isinstance(duration, (list, tuple)): - timestamp += duration[frame_idx] - else: - timestamp += duration - frame_idx += 1 - - finally: - im.seek(cur_idx) - - # Force encoder to flush frames - enc.add(None, round(timestamp), lossless, quality, alpha_quality, 0) - - # Get the final output from the encoder - data = enc.assemble(icc_profile, exif, xmp) - if data is None: - msg = "cannot write file as WebP (encoder returned None)" - raise OSError(msg) - - fp.write(data) - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - lossless = im.encoderinfo.get("lossless", False) - quality = im.encoderinfo.get("quality", 80) - alpha_quality = im.encoderinfo.get("alpha_quality", 100) - icc_profile = im.encoderinfo.get("icc_profile") or "" - exif = im.encoderinfo.get("exif", b"") - if isinstance(exif, Image.Exif): - exif = exif.tobytes() - if exif.startswith(b"Exif\x00\x00"): - exif = exif[6:] - xmp = im.encoderinfo.get("xmp", "") - method = im.encoderinfo.get("method", 4) - exact = 1 if im.encoderinfo.get("exact") else 0 - - im = _convert_frame(im) - - data = _webp.WebPEncode( - im.getim(), - lossless, - float(quality), - float(alpha_quality), - icc_profile, - method, - exact, - exif, - xmp, - ) - if data is None: - msg = "cannot write file as WebP (encoder returned None)" - raise OSError(msg) - - fp.write(data) - - -Image.register_open(WebPImageFile.format, WebPImageFile, _accept) -if SUPPORTED: - Image.register_save(WebPImageFile.format, _save) - Image.register_save_all(WebPImageFile.format, _save_all) - Image.register_extension(WebPImageFile.format, ".webp") - Image.register_mime(WebPImageFile.format, "image/webp") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/WmfImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/WmfImagePlugin.py deleted file mode 100644 index de714d33..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/WmfImagePlugin.py +++ /dev/null @@ -1,186 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# WMF stub codec -# -# history: -# 1996-12-14 fl Created -# 2004-02-22 fl Turned into a stub driver -# 2004-02-23 fl Added EMF support -# -# Copyright (c) Secret Labs AB 1997-2004. All rights reserved. -# Copyright (c) Fredrik Lundh 1996. -# -# See the README file for information on usage and redistribution. -# -# WMF/EMF reference documentation: -# https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-WMF/[MS-WMF].pdf -# http://wvware.sourceforge.net/caolan/index.html -# http://wvware.sourceforge.net/caolan/ora-wmf.html -from __future__ import annotations - -from typing import IO - -from . import Image, ImageFile -from ._binary import i16le as word -from ._binary import si16le as short -from ._binary import si32le as _long - -_handler = None - - -def register_handler(handler: ImageFile.StubHandler | None) -> None: - """ - Install application-specific WMF image handler. - - :param handler: Handler object. - """ - global _handler - _handler = handler - - -if hasattr(Image.core, "drawwmf"): - # install default handler (windows only) - - class WmfHandler(ImageFile.StubHandler): - def open(self, im: ImageFile.StubImageFile) -> None: - im._mode = "RGB" - self.bbox = im.info["wmf_bbox"] - - def load(self, im: ImageFile.StubImageFile) -> Image.Image: - im.fp.seek(0) # rewind - return Image.frombytes( - "RGB", - im.size, - Image.core.drawwmf(im.fp.read(), im.size, self.bbox), - "raw", - "BGR", - (im.size[0] * 3 + 3) & -4, - -1, - ) - - register_handler(WmfHandler()) - -# -# -------------------------------------------------------------------- -# Read WMF file - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith((b"\xd7\xcd\xc6\x9a\x00\x00", b"\x01\x00\x00\x00")) - - -## -# Image plugin for Windows metafiles. - - -class WmfStubImageFile(ImageFile.StubImageFile): - format = "WMF" - format_description = "Windows Metafile" - - def _open(self) -> None: - # check placeable header - s = self.fp.read(44) - - if s.startswith(b"\xd7\xcd\xc6\x9a\x00\x00"): - # placeable windows metafile - - # get units per inch - inch = word(s, 14) - if inch == 0: - msg = "Invalid inch" - raise ValueError(msg) - self._inch: tuple[float, float] = inch, inch - - # get bounding box - x0 = short(s, 6) - y0 = short(s, 8) - x1 = short(s, 10) - y1 = short(s, 12) - - # normalize size to 72 dots per inch - self.info["dpi"] = 72 - size = ( - (x1 - x0) * self.info["dpi"] // inch, - (y1 - y0) * self.info["dpi"] // inch, - ) - - self.info["wmf_bbox"] = x0, y0, x1, y1 - - # sanity check (standard metafile header) - if s[22:26] != b"\x01\x00\t\x00": - msg = "Unsupported WMF file format" - raise SyntaxError(msg) - - elif s.startswith(b"\x01\x00\x00\x00") and s[40:44] == b" EMF": - # enhanced metafile - - # get bounding box - x0 = _long(s, 8) - y0 = _long(s, 12) - x1 = _long(s, 16) - y1 = _long(s, 20) - - # get frame (in 0.01 millimeter units) - frame = _long(s, 24), _long(s, 28), _long(s, 32), _long(s, 36) - - size = x1 - x0, y1 - y0 - - # calculate dots per inch from bbox and frame - xdpi = 2540.0 * (x1 - x0) / (frame[2] - frame[0]) - ydpi = 2540.0 * (y1 - y0) / (frame[3] - frame[1]) - - self.info["wmf_bbox"] = x0, y0, x1, y1 - - if xdpi == ydpi: - self.info["dpi"] = xdpi - else: - self.info["dpi"] = xdpi, ydpi - self._inch = xdpi, ydpi - - else: - msg = "Unsupported file format" - raise SyntaxError(msg) - - self._mode = "RGB" - self._size = size - - loader = self._load() - if loader: - loader.open(self) - - def _load(self) -> ImageFile.StubHandler | None: - return _handler - - def load( - self, dpi: float | tuple[float, float] | None = None - ) -> Image.core.PixelAccess | None: - if dpi is not None: - self.info["dpi"] = dpi - x0, y0, x1, y1 = self.info["wmf_bbox"] - if not isinstance(dpi, tuple): - dpi = dpi, dpi - self._size = ( - int((x1 - x0) * dpi[0] / self._inch[0]), - int((y1 - y0) * dpi[1] / self._inch[1]), - ) - return super().load() - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - if _handler is None or not hasattr(_handler, "save"): - msg = "WMF save handler not installed" - raise OSError(msg) - _handler.save(im, fp, filename) - - -# -# -------------------------------------------------------------------- -# Registry stuff - - -Image.register_open(WmfStubImageFile.format, WmfStubImageFile, _accept) -Image.register_save(WmfStubImageFile.format, _save) - -Image.register_extensions(WmfStubImageFile.format, [".wmf", ".emf"]) diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/XVThumbImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/XVThumbImagePlugin.py deleted file mode 100644 index cde28388..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/XVThumbImagePlugin.py +++ /dev/null @@ -1,83 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# XV Thumbnail file handler by Charles E. "Gene" Cash -# (gcash@magicnet.net) -# -# see xvcolor.c and xvbrowse.c in the sources to John Bradley's XV, -# available from ftp://ftp.cis.upenn.edu/pub/xv/ -# -# history: -# 98-08-15 cec created (b/w only) -# 98-12-09 cec added color palette -# 98-12-28 fl added to PIL (with only a few very minor modifications) -# -# To do: -# FIXME: make save work (this requires quantization support) -# -from __future__ import annotations - -from . import Image, ImageFile, ImagePalette -from ._binary import o8 - -_MAGIC = b"P7 332" - -# standard color palette for thumbnails (RGB332) -PALETTE = b"" -for r in range(8): - for g in range(8): - for b in range(4): - PALETTE = PALETTE + ( - o8((r * 255) // 7) + o8((g * 255) // 7) + o8((b * 255) // 3) - ) - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(_MAGIC) - - -## -# Image plugin for XV thumbnail images. - - -class XVThumbImageFile(ImageFile.ImageFile): - format = "XVThumb" - format_description = "XV thumbnail image" - - def _open(self) -> None: - # check magic - assert self.fp is not None - - if not _accept(self.fp.read(6)): - msg = "not an XV thumbnail file" - raise SyntaxError(msg) - - # Skip to beginning of next line - self.fp.readline() - - # skip info comments - while True: - s = self.fp.readline() - if not s: - msg = "Unexpected EOF reading XV thumbnail file" - raise SyntaxError(msg) - if s[0] != 35: # ie. when not a comment: '#' - break - - # parse header line (already read) - s = s.strip().split() - - self._mode = "P" - self._size = int(s[0]), int(s[1]) - - self.palette = ImagePalette.raw("RGB", PALETTE) - - self.tile = [ - ImageFile._Tile("raw", (0, 0) + self.size, self.fp.tell(), self.mode) - ] - - -# -------------------------------------------------------------------- - -Image.register_open(XVThumbImageFile.format, XVThumbImageFile, _accept) diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/XbmImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/XbmImagePlugin.py deleted file mode 100644 index 1e57aa16..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/XbmImagePlugin.py +++ /dev/null @@ -1,98 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# XBM File handling -# -# History: -# 1995-09-08 fl Created -# 1996-11-01 fl Added save support -# 1997-07-07 fl Made header parser more tolerant -# 1997-07-22 fl Fixed yet another parser bug -# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.4) -# 2001-05-13 fl Added hotspot handling (based on code from Bernhard Herzog) -# 2004-02-24 fl Allow some whitespace before first #define -# -# Copyright (c) 1997-2004 by Secret Labs AB -# Copyright (c) 1996-1997 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import re -from typing import IO - -from . import Image, ImageFile - -# XBM header -xbm_head = re.compile( - rb"\s*#define[ \t]+.*_width[ \t]+(?P[0-9]+)[\r\n]+" - b"#define[ \t]+.*_height[ \t]+(?P[0-9]+)[\r\n]+" - b"(?P" - b"#define[ \t]+[^_]*_x_hot[ \t]+(?P[0-9]+)[\r\n]+" - b"#define[ \t]+[^_]*_y_hot[ \t]+(?P[0-9]+)[\r\n]+" - b")?" - rb"[\000-\377]*_bits\[]" -) - - -def _accept(prefix: bytes) -> bool: - return prefix.lstrip().startswith(b"#define") - - -## -# Image plugin for X11 bitmaps. - - -class XbmImageFile(ImageFile.ImageFile): - format = "XBM" - format_description = "X11 Bitmap" - - def _open(self) -> None: - assert self.fp is not None - - m = xbm_head.match(self.fp.read(512)) - - if not m: - msg = "not a XBM file" - raise SyntaxError(msg) - - xsize = int(m.group("width")) - ysize = int(m.group("height")) - - if m.group("hotspot"): - self.info["hotspot"] = (int(m.group("xhot")), int(m.group("yhot"))) - - self._mode = "1" - self._size = xsize, ysize - - self.tile = [ImageFile._Tile("xbm", (0, 0) + self.size, m.end())] - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - if im.mode != "1": - msg = f"cannot write mode {im.mode} as XBM" - raise OSError(msg) - - fp.write(f"#define im_width {im.size[0]}\n".encode("ascii")) - fp.write(f"#define im_height {im.size[1]}\n".encode("ascii")) - - hotspot = im.encoderinfo.get("hotspot") - if hotspot: - fp.write(f"#define im_x_hot {hotspot[0]}\n".encode("ascii")) - fp.write(f"#define im_y_hot {hotspot[1]}\n".encode("ascii")) - - fp.write(b"static char im_bits[] = {\n") - - ImageFile._save(im, fp, [ImageFile._Tile("xbm", (0, 0) + im.size)]) - - fp.write(b"};\n") - - -Image.register_open(XbmImageFile.format, XbmImageFile, _accept) -Image.register_save(XbmImageFile.format, _save) - -Image.register_extension(XbmImageFile.format, ".xbm") - -Image.register_mime(XbmImageFile.format, "image/xbm") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/XpmImagePlugin.py b/.venv-docs/lib/python3.12/site-packages/PIL/XpmImagePlugin.py deleted file mode 100644 index 3be240fb..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/XpmImagePlugin.py +++ /dev/null @@ -1,157 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# XPM File handling -# -# History: -# 1996-12-29 fl Created -# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.7) -# -# Copyright (c) Secret Labs AB 1997-2001. -# Copyright (c) Fredrik Lundh 1996-2001. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import re - -from . import Image, ImageFile, ImagePalette -from ._binary import o8 - -# XPM header -xpm_head = re.compile(b'"([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*)') - - -def _accept(prefix: bytes) -> bool: - return prefix.startswith(b"/* XPM */") - - -## -# Image plugin for X11 pixel maps. - - -class XpmImageFile(ImageFile.ImageFile): - format = "XPM" - format_description = "X11 Pixel Map" - - def _open(self) -> None: - assert self.fp is not None - if not _accept(self.fp.read(9)): - msg = "not an XPM file" - raise SyntaxError(msg) - - # skip forward to next string - while True: - line = self.fp.readline() - if not line: - msg = "broken XPM file" - raise SyntaxError(msg) - m = xpm_head.match(line) - if m: - break - - self._size = int(m.group(1)), int(m.group(2)) - - palette_length = int(m.group(3)) - bpp = int(m.group(4)) - - # - # load palette description - - palette = {} - - for _ in range(palette_length): - line = self.fp.readline().rstrip() - - c = line[1 : bpp + 1] - s = line[bpp + 1 : -2].split() - - for i in range(0, len(s), 2): - if s[i] == b"c": - # process colour key - rgb = s[i + 1] - if rgb == b"None": - self.info["transparency"] = c - elif rgb.startswith(b"#"): - rgb_int = int(rgb[1:], 16) - palette[c] = ( - o8((rgb_int >> 16) & 255) - + o8((rgb_int >> 8) & 255) - + o8(rgb_int & 255) - ) - else: - # unknown colour - msg = "cannot read this XPM file" - raise ValueError(msg) - break - - else: - # missing colour key - msg = "cannot read this XPM file" - raise ValueError(msg) - - args: tuple[int, dict[bytes, bytes] | tuple[bytes, ...]] - if palette_length > 256: - self._mode = "RGB" - args = (bpp, palette) - else: - self._mode = "P" - self.palette = ImagePalette.raw("RGB", b"".join(palette.values())) - args = (bpp, tuple(palette.keys())) - - self.tile = [ImageFile._Tile("xpm", (0, 0) + self.size, self.fp.tell(), args)] - - def load_read(self, read_bytes: int) -> bytes: - # - # load all image data in one chunk - - xsize, ysize = self.size - - assert self.fp is not None - s = [self.fp.readline()[1 : xsize + 1].ljust(xsize) for i in range(ysize)] - - return b"".join(s) - - -class XpmDecoder(ImageFile.PyDecoder): - _pulls_fd = True - - def decode(self, buffer: bytes | Image.SupportsArrayInterface) -> tuple[int, int]: - assert self.fd is not None - - data = bytearray() - bpp, palette = self.args - dest_length = self.state.xsize * self.state.ysize - if self.mode == "RGB": - dest_length *= 3 - pixel_header = False - while len(data) < dest_length: - line = self.fd.readline() - if not line: - break - if line.rstrip() == b"/* pixels */" and not pixel_header: - pixel_header = True - continue - line = b'"'.join(line.split(b'"')[1:-1]) - for i in range(0, len(line), bpp): - key = line[i : i + bpp] - if self.mode == "RGB": - data += palette[key] - else: - data += o8(palette.index(key)) - self.set_as_raw(bytes(data)) - return -1, 0 - - -# -# Registry - - -Image.register_open(XpmImageFile.format, XpmImageFile, _accept) -Image.register_decoder("xpm", XpmDecoder) - -Image.register_extension(XpmImageFile.format, ".xpm") - -Image.register_mime(XpmImageFile.format, "image/xpm") diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__init__.py b/.venv-docs/lib/python3.12/site-packages/PIL/__init__.py deleted file mode 100644 index 6e4c23f8..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/__init__.py +++ /dev/null @@ -1,87 +0,0 @@ -"""Pillow (Fork of the Python Imaging Library) - -Pillow is the friendly PIL fork by Jeffrey A. Clark and contributors. - https://github.com/python-pillow/Pillow/ - -Pillow is forked from PIL 1.1.7. - -PIL is the Python Imaging Library by Fredrik Lundh and contributors. -Copyright (c) 1999 by Secret Labs AB. - -Use PIL.__version__ for this Pillow version. - -;-) -""" - -from __future__ import annotations - -from . import _version - -# VERSION was removed in Pillow 6.0.0. -# PILLOW_VERSION was removed in Pillow 9.0.0. -# Use __version__ instead. -__version__ = _version.__version__ -del _version - - -_plugins = [ - "AvifImagePlugin", - "BlpImagePlugin", - "BmpImagePlugin", - "BufrStubImagePlugin", - "CurImagePlugin", - "DcxImagePlugin", - "DdsImagePlugin", - "EpsImagePlugin", - "FitsImagePlugin", - "FliImagePlugin", - "FpxImagePlugin", - "FtexImagePlugin", - "GbrImagePlugin", - "GifImagePlugin", - "GribStubImagePlugin", - "Hdf5StubImagePlugin", - "IcnsImagePlugin", - "IcoImagePlugin", - "ImImagePlugin", - "ImtImagePlugin", - "IptcImagePlugin", - "JpegImagePlugin", - "Jpeg2KImagePlugin", - "McIdasImagePlugin", - "MicImagePlugin", - "MpegImagePlugin", - "MpoImagePlugin", - "MspImagePlugin", - "PalmImagePlugin", - "PcdImagePlugin", - "PcxImagePlugin", - "PdfImagePlugin", - "PixarImagePlugin", - "PngImagePlugin", - "PpmImagePlugin", - "PsdImagePlugin", - "QoiImagePlugin", - "SgiImagePlugin", - "SpiderImagePlugin", - "SunImagePlugin", - "TgaImagePlugin", - "TiffImagePlugin", - "WebPImagePlugin", - "WmfImagePlugin", - "XbmImagePlugin", - "XpmImagePlugin", - "XVThumbImagePlugin", -] - - -class UnidentifiedImageError(OSError): - """ - Raised in :py:meth:`PIL.Image.open` if an image cannot be opened and identified. - - If a PNG image raises this error, setting :data:`.ImageFile.LOAD_TRUNCATED_IMAGES` - to true may allow the image to be opened after all. The setting will ignore missing - data and checksum failures. - """ - - pass diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__main__.py b/.venv-docs/lib/python3.12/site-packages/PIL/__main__.py deleted file mode 100644 index 043156e8..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/__main__.py +++ /dev/null @@ -1,7 +0,0 @@ -from __future__ import annotations - -import sys - -from .features import pilinfo - -pilinfo(supported_formats="--report" not in sys.argv) diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/AvifImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/AvifImagePlugin.cpython-312.pyc deleted file mode 100644 index e3a4fefb..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/AvifImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/BdfFontFile.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/BdfFontFile.cpython-312.pyc deleted file mode 100644 index b984534e..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/BdfFontFile.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/BlpImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/BlpImagePlugin.cpython-312.pyc deleted file mode 100644 index 2cf78df6..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/BlpImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/BmpImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/BmpImagePlugin.cpython-312.pyc deleted file mode 100644 index 1d4e9fee..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/BmpImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/BufrStubImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/BufrStubImagePlugin.cpython-312.pyc deleted file mode 100644 index d4494f15..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/BufrStubImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ContainerIO.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ContainerIO.cpython-312.pyc deleted file mode 100644 index c61b6556..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ContainerIO.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/CurImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/CurImagePlugin.cpython-312.pyc deleted file mode 100644 index 7ff47ca0..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/CurImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/DcxImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/DcxImagePlugin.cpython-312.pyc deleted file mode 100644 index 66e1ae14..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/DcxImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/DdsImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/DdsImagePlugin.cpython-312.pyc deleted file mode 100644 index 66febe39..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/DdsImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/EpsImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/EpsImagePlugin.cpython-312.pyc deleted file mode 100644 index e6db916e..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/EpsImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ExifTags.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ExifTags.cpython-312.pyc deleted file mode 100644 index e479f9a0..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ExifTags.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/FitsImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/FitsImagePlugin.cpython-312.pyc deleted file mode 100644 index 6395bebd..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/FitsImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/FliImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/FliImagePlugin.cpython-312.pyc deleted file mode 100644 index 62e7b9cf..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/FliImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/FontFile.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/FontFile.cpython-312.pyc deleted file mode 100644 index 2ef6fd5d..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/FontFile.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/FpxImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/FpxImagePlugin.cpython-312.pyc deleted file mode 100644 index fbf44e08..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/FpxImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/FtexImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/FtexImagePlugin.cpython-312.pyc deleted file mode 100644 index a9ff9cdc..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/FtexImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/GbrImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/GbrImagePlugin.cpython-312.pyc deleted file mode 100644 index 2eab62e2..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/GbrImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/GdImageFile.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/GdImageFile.cpython-312.pyc deleted file mode 100644 index 58f42b36..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/GdImageFile.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/GifImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/GifImagePlugin.cpython-312.pyc deleted file mode 100644 index 9cf9c352..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/GifImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/GimpGradientFile.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/GimpGradientFile.cpython-312.pyc deleted file mode 100644 index 8e6c7f91..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/GimpGradientFile.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/GimpPaletteFile.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/GimpPaletteFile.cpython-312.pyc deleted file mode 100644 index ad966a6c..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/GimpPaletteFile.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/GribStubImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/GribStubImagePlugin.cpython-312.pyc deleted file mode 100644 index 98783ddf..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/GribStubImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/Hdf5StubImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/Hdf5StubImagePlugin.cpython-312.pyc deleted file mode 100644 index cb639633..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/Hdf5StubImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/IcnsImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/IcnsImagePlugin.cpython-312.pyc deleted file mode 100644 index 42de465c..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/IcnsImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/IcoImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/IcoImagePlugin.cpython-312.pyc deleted file mode 100644 index a7a7dd03..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/IcoImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImImagePlugin.cpython-312.pyc deleted file mode 100644 index 5ff04df8..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/Image.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/Image.cpython-312.pyc deleted file mode 100644 index ef863706..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/Image.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageChops.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageChops.cpython-312.pyc deleted file mode 100644 index 0dce0c5e..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageChops.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageCms.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageCms.cpython-312.pyc deleted file mode 100644 index dd6369d9..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageCms.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageColor.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageColor.cpython-312.pyc deleted file mode 100644 index 79dfe2eb..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageColor.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageDraw.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageDraw.cpython-312.pyc deleted file mode 100644 index eddccb9d..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageDraw.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageDraw2.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageDraw2.cpython-312.pyc deleted file mode 100644 index 5449c05b..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageDraw2.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageEnhance.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageEnhance.cpython-312.pyc deleted file mode 100644 index 7d6a0779..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageEnhance.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageFile.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageFile.cpython-312.pyc deleted file mode 100644 index 8a7e1c37..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageFile.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageFilter.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageFilter.cpython-312.pyc deleted file mode 100644 index b97f01ac..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageFilter.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageFont.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageFont.cpython-312.pyc deleted file mode 100644 index 0e81a1ee..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageFont.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageGrab.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageGrab.cpython-312.pyc deleted file mode 100644 index d69d60c6..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageGrab.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageMath.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageMath.cpython-312.pyc deleted file mode 100644 index 1ce2901e..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageMath.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageMode.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageMode.cpython-312.pyc deleted file mode 100644 index 8f8548dc..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageMode.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageMorph.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageMorph.cpython-312.pyc deleted file mode 100644 index 05736126..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageMorph.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageOps.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageOps.cpython-312.pyc deleted file mode 100644 index 46942481..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageOps.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImagePalette.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImagePalette.cpython-312.pyc deleted file mode 100644 index 847d08b4..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImagePalette.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImagePath.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImagePath.cpython-312.pyc deleted file mode 100644 index 19c7de68..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImagePath.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageQt.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageQt.cpython-312.pyc deleted file mode 100644 index e5e595a4..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageQt.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageSequence.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageSequence.cpython-312.pyc deleted file mode 100644 index ccaddfcc..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageSequence.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageShow.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageShow.cpython-312.pyc deleted file mode 100644 index 30377360..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageShow.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageStat.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageStat.cpython-312.pyc deleted file mode 100644 index db0277ec..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageStat.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageText.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageText.cpython-312.pyc deleted file mode 100644 index 16990df7..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageText.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageTk.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageTk.cpython-312.pyc deleted file mode 100644 index 94de98d6..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageTk.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageTransform.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageTransform.cpython-312.pyc deleted file mode 100644 index 66a570b7..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageTransform.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageWin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageWin.cpython-312.pyc deleted file mode 100644 index 4d7092f8..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImageWin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImtImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImtImagePlugin.cpython-312.pyc deleted file mode 100644 index f59464c5..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/ImtImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/IptcImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/IptcImagePlugin.cpython-312.pyc deleted file mode 100644 index ac1a0577..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/IptcImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/Jpeg2KImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/Jpeg2KImagePlugin.cpython-312.pyc deleted file mode 100644 index f1c7666a..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/Jpeg2KImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/JpegImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/JpegImagePlugin.cpython-312.pyc deleted file mode 100644 index 414f5e44..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/JpegImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/JpegPresets.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/JpegPresets.cpython-312.pyc deleted file mode 100644 index 626d7424..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/JpegPresets.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/McIdasImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/McIdasImagePlugin.cpython-312.pyc deleted file mode 100644 index fa8bb69e..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/McIdasImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/MicImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/MicImagePlugin.cpython-312.pyc deleted file mode 100644 index 3855ef62..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/MicImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/MpegImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/MpegImagePlugin.cpython-312.pyc deleted file mode 100644 index e6ebdc93..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/MpegImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/MpoImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/MpoImagePlugin.cpython-312.pyc deleted file mode 100644 index 0815572a..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/MpoImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/MspImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/MspImagePlugin.cpython-312.pyc deleted file mode 100644 index a9e558f9..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/MspImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PSDraw.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PSDraw.cpython-312.pyc deleted file mode 100644 index d64c73ee..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PSDraw.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PaletteFile.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PaletteFile.cpython-312.pyc deleted file mode 100644 index 237043e9..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PaletteFile.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PalmImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PalmImagePlugin.cpython-312.pyc deleted file mode 100644 index c5150d98..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PalmImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PcdImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PcdImagePlugin.cpython-312.pyc deleted file mode 100644 index 31b2e240..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PcdImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PcfFontFile.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PcfFontFile.cpython-312.pyc deleted file mode 100644 index f32c1317..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PcfFontFile.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PcxImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PcxImagePlugin.cpython-312.pyc deleted file mode 100644 index fd1aee9b..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PcxImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PdfImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PdfImagePlugin.cpython-312.pyc deleted file mode 100644 index d851810b..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PdfImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PdfParser.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PdfParser.cpython-312.pyc deleted file mode 100644 index 3166bf46..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PdfParser.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PixarImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PixarImagePlugin.cpython-312.pyc deleted file mode 100644 index 4caeaa8e..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PixarImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PngImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PngImagePlugin.cpython-312.pyc deleted file mode 100644 index 51b4adf5..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PngImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PpmImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PpmImagePlugin.cpython-312.pyc deleted file mode 100644 index 0d01bd58..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PpmImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PsdImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PsdImagePlugin.cpython-312.pyc deleted file mode 100644 index 6d97f719..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/PsdImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/QoiImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/QoiImagePlugin.cpython-312.pyc deleted file mode 100644 index b3b891d5..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/QoiImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/SgiImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/SgiImagePlugin.cpython-312.pyc deleted file mode 100644 index 3780e7e4..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/SgiImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/SpiderImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/SpiderImagePlugin.cpython-312.pyc deleted file mode 100644 index 5883798d..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/SpiderImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/SunImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/SunImagePlugin.cpython-312.pyc deleted file mode 100644 index edd8944a..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/SunImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/TarIO.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/TarIO.cpython-312.pyc deleted file mode 100644 index 855108fb..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/TarIO.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/TgaImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/TgaImagePlugin.cpython-312.pyc deleted file mode 100644 index 9b9cdded..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/TgaImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/TiffImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/TiffImagePlugin.cpython-312.pyc deleted file mode 100644 index 40488661..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/TiffImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/TiffTags.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/TiffTags.cpython-312.pyc deleted file mode 100644 index 9559e3ef..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/TiffTags.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/WalImageFile.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/WalImageFile.cpython-312.pyc deleted file mode 100644 index 61a0a493..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/WalImageFile.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/WebPImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/WebPImagePlugin.cpython-312.pyc deleted file mode 100644 index 887a0502..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/WebPImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/WmfImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/WmfImagePlugin.cpython-312.pyc deleted file mode 100644 index b628610c..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/WmfImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/XVThumbImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/XVThumbImagePlugin.cpython-312.pyc deleted file mode 100644 index c355cc4d..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/XVThumbImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/XbmImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/XbmImagePlugin.cpython-312.pyc deleted file mode 100644 index e062f064..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/XbmImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/XpmImagePlugin.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/XpmImagePlugin.cpython-312.pyc deleted file mode 100644 index e35c6569..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/XpmImagePlugin.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/__init__.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index d308b5d1..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/__main__.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/__main__.cpython-312.pyc deleted file mode 100644 index 8369fd09..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/__main__.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/_binary.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/_binary.cpython-312.pyc deleted file mode 100644 index 3555d2ce..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/_binary.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/_deprecate.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/_deprecate.cpython-312.pyc deleted file mode 100644 index 8494951c..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/_deprecate.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/_tkinter_finder.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/_tkinter_finder.cpython-312.pyc deleted file mode 100644 index 771f90be..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/_tkinter_finder.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/_typing.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/_typing.cpython-312.pyc deleted file mode 100644 index 1fff296a..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/_typing.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/_util.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/_util.cpython-312.pyc deleted file mode 100644 index 39a52d9c..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/_util.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/_version.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/_version.cpython-312.pyc deleted file mode 100644 index 731be9c1..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/_version.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/features.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/features.cpython-312.pyc deleted file mode 100644 index b1b49052..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/features.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/report.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/report.cpython-312.pyc deleted file mode 100644 index 6d7d8262..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/__pycache__/report.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/_avif.cpython-312-x86_64-linux-gnu.so b/.venv-docs/lib/python3.12/site-packages/PIL/_avif.cpython-312-x86_64-linux-gnu.so deleted file mode 100755 index c83a451a..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/_avif.cpython-312-x86_64-linux-gnu.so and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/_avif.pyi b/.venv-docs/lib/python3.12/site-packages/PIL/_avif.pyi deleted file mode 100644 index e27843e5..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/_avif.pyi +++ /dev/null @@ -1,3 +0,0 @@ -from typing import Any - -def __getattr__(name: str) -> Any: ... diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/_binary.py b/.venv-docs/lib/python3.12/site-packages/PIL/_binary.py deleted file mode 100644 index 4594ccce..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/_binary.py +++ /dev/null @@ -1,112 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# Binary input/output support routines. -# -# Copyright (c) 1997-2003 by Secret Labs AB -# Copyright (c) 1995-2003 by Fredrik Lundh -# Copyright (c) 2012 by Brian Crowell -# -# See the README file for information on usage and redistribution. -# - - -"""Binary input/output support routines.""" -from __future__ import annotations - -from struct import pack, unpack_from - - -def i8(c: bytes) -> int: - return c[0] - - -def o8(i: int) -> bytes: - return bytes((i & 255,)) - - -# Input, le = little endian, be = big endian -def i16le(c: bytes, o: int = 0) -> int: - """ - Converts a 2-bytes (16 bits) string to an unsigned integer. - - :param c: string containing bytes to convert - :param o: offset of bytes to convert in string - """ - return unpack_from(" int: - """ - Converts a 2-bytes (16 bits) string to a signed integer. - - :param c: string containing bytes to convert - :param o: offset of bytes to convert in string - """ - return unpack_from(" int: - """ - Converts a 2-bytes (16 bits) string to a signed integer, big endian. - - :param c: string containing bytes to convert - :param o: offset of bytes to convert in string - """ - return unpack_from(">h", c, o)[0] - - -def i32le(c: bytes, o: int = 0) -> int: - """ - Converts a 4-bytes (32 bits) string to an unsigned integer. - - :param c: string containing bytes to convert - :param o: offset of bytes to convert in string - """ - return unpack_from(" int: - """ - Converts a 4-bytes (32 bits) string to a signed integer. - - :param c: string containing bytes to convert - :param o: offset of bytes to convert in string - """ - return unpack_from(" int: - """ - Converts a 4-bytes (32 bits) string to a signed integer, big endian. - - :param c: string containing bytes to convert - :param o: offset of bytes to convert in string - """ - return unpack_from(">i", c, o)[0] - - -def i16be(c: bytes, o: int = 0) -> int: - return unpack_from(">H", c, o)[0] - - -def i32be(c: bytes, o: int = 0) -> int: - return unpack_from(">I", c, o)[0] - - -# Output, le = little endian, be = big endian -def o16le(i: int) -> bytes: - return pack(" bytes: - return pack(" bytes: - return pack(">H", i) - - -def o32be(i: int) -> bytes: - return pack(">I", i) diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/_deprecate.py b/.venv-docs/lib/python3.12/site-packages/PIL/_deprecate.py deleted file mode 100644 index 616a9aac..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/_deprecate.py +++ /dev/null @@ -1,70 +0,0 @@ -from __future__ import annotations - -import warnings - -from . import __version__ - - -def deprecate( - deprecated: str, - when: int | None, - replacement: str | None = None, - *, - action: str | None = None, - plural: bool = False, - stacklevel: int = 3, -) -> None: - """ - Deprecations helper. - - :param deprecated: Name of thing to be deprecated. - :param when: Pillow major version to be removed in. - :param replacement: Name of replacement. - :param action: Instead of "replacement", give a custom call to action - e.g. "Upgrade to new thing". - :param plural: if the deprecated thing is plural, needing "are" instead of "is". - - Usually of the form: - - "[deprecated] is deprecated and will be removed in Pillow [when] (yyyy-mm-dd). - Use [replacement] instead." - - You can leave out the replacement sentence: - - "[deprecated] is deprecated and will be removed in Pillow [when] (yyyy-mm-dd)" - - Or with another call to action: - - "[deprecated] is deprecated and will be removed in Pillow [when] (yyyy-mm-dd). - [action]." - """ - - is_ = "are" if plural else "is" - - if when is None: - removed = "a future version" - elif when <= int(__version__.split(".")[0]): - msg = f"{deprecated} {is_} deprecated and should be removed." - raise RuntimeError(msg) - elif when == 13: - removed = "Pillow 13 (2026-10-15)" - else: - msg = f"Unknown removal version: {when}. Update {__name__}?" - raise ValueError(msg) - - if replacement and action: - msg = "Use only one of 'replacement' and 'action'" - raise ValueError(msg) - - if replacement: - action = f". Use {replacement} instead." - elif action: - action = f". {action.rstrip('.')}." - else: - action = "" - - warnings.warn( - f"{deprecated} {is_} deprecated and will be removed in {removed}{action}", - DeprecationWarning, - stacklevel=stacklevel, - ) diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/_imaging.cpython-312-x86_64-linux-gnu.so b/.venv-docs/lib/python3.12/site-packages/PIL/_imaging.cpython-312-x86_64-linux-gnu.so deleted file mode 100755 index faa2f9d4..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/_imaging.cpython-312-x86_64-linux-gnu.so and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/_imaging.pyi b/.venv-docs/lib/python3.12/site-packages/PIL/_imaging.pyi deleted file mode 100644 index 998bc52e..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/_imaging.pyi +++ /dev/null @@ -1,31 +0,0 @@ -from typing import Any - -class ImagingCore: - def __getitem__(self, index: int) -> float: ... - def __getattr__(self, name: str) -> Any: ... - -class ImagingFont: - def __getattr__(self, name: str) -> Any: ... - -class ImagingDraw: - def __getattr__(self, name: str) -> Any: ... - -class PixelAccess: - def __getitem__(self, xy: tuple[int, int]) -> float | tuple[int, ...]: ... - def __setitem__( - self, xy: tuple[int, int], color: float | tuple[int, ...] - ) -> None: ... - -class ImagingDecoder: - def __getattr__(self, name: str) -> Any: ... - -class ImagingEncoder: - def __getattr__(self, name: str) -> Any: ... - -class _Outline: - def close(self) -> None: ... - def __getattr__(self, name: str) -> Any: ... - -def font(image: ImagingCore, glyphdata: bytes) -> ImagingFont: ... -def outline() -> _Outline: ... -def __getattr__(name: str) -> Any: ... diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/_imagingcms.cpython-312-x86_64-linux-gnu.so b/.venv-docs/lib/python3.12/site-packages/PIL/_imagingcms.cpython-312-x86_64-linux-gnu.so deleted file mode 100755 index 2bcf0744..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/_imagingcms.cpython-312-x86_64-linux-gnu.so and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/_imagingcms.pyi b/.venv-docs/lib/python3.12/site-packages/PIL/_imagingcms.pyi deleted file mode 100644 index 4fc0d60a..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/_imagingcms.pyi +++ /dev/null @@ -1,143 +0,0 @@ -import datetime -import sys -from typing import Literal, SupportsFloat, TypeAlias, TypedDict - -from ._typing import CapsuleType - -littlecms_version: str | None - -_Tuple3f: TypeAlias = tuple[float, float, float] -_Tuple2x3f: TypeAlias = tuple[_Tuple3f, _Tuple3f] -_Tuple3x3f: TypeAlias = tuple[_Tuple3f, _Tuple3f, _Tuple3f] - -class _IccMeasurementCondition(TypedDict): - observer: int - backing: _Tuple3f - geo: str - flare: float - illuminant_type: str - -class _IccViewingCondition(TypedDict): - illuminant: _Tuple3f - surround: _Tuple3f - illuminant_type: str - -class CmsProfile: - @property - def rendering_intent(self) -> int: ... - @property - def creation_date(self) -> datetime.datetime | None: ... - @property - def copyright(self) -> str | None: ... - @property - def target(self) -> str | None: ... - @property - def manufacturer(self) -> str | None: ... - @property - def model(self) -> str | None: ... - @property - def profile_description(self) -> str | None: ... - @property - def screening_description(self) -> str | None: ... - @property - def viewing_condition(self) -> str | None: ... - @property - def version(self) -> float: ... - @property - def icc_version(self) -> int: ... - @property - def attributes(self) -> int: ... - @property - def header_flags(self) -> int: ... - @property - def header_manufacturer(self) -> str: ... - @property - def header_model(self) -> str: ... - @property - def device_class(self) -> str: ... - @property - def connection_space(self) -> str: ... - @property - def xcolor_space(self) -> str: ... - @property - def profile_id(self) -> bytes: ... - @property - def is_matrix_shaper(self) -> bool: ... - @property - def technology(self) -> str | None: ... - @property - def colorimetric_intent(self) -> str | None: ... - @property - def perceptual_rendering_intent_gamut(self) -> str | None: ... - @property - def saturation_rendering_intent_gamut(self) -> str | None: ... - @property - def red_colorant(self) -> _Tuple2x3f | None: ... - @property - def green_colorant(self) -> _Tuple2x3f | None: ... - @property - def blue_colorant(self) -> _Tuple2x3f | None: ... - @property - def red_primary(self) -> _Tuple2x3f | None: ... - @property - def green_primary(self) -> _Tuple2x3f | None: ... - @property - def blue_primary(self) -> _Tuple2x3f | None: ... - @property - def media_white_point_temperature(self) -> float | None: ... - @property - def media_white_point(self) -> _Tuple2x3f | None: ... - @property - def media_black_point(self) -> _Tuple2x3f | None: ... - @property - def luminance(self) -> _Tuple2x3f | None: ... - @property - def chromatic_adaptation(self) -> tuple[_Tuple3x3f, _Tuple3x3f] | None: ... - @property - def chromaticity(self) -> _Tuple3x3f | None: ... - @property - def colorant_table(self) -> list[str] | None: ... - @property - def colorant_table_out(self) -> list[str] | None: ... - @property - def intent_supported(self) -> dict[int, tuple[bool, bool, bool]] | None: ... - @property - def clut(self) -> dict[int, tuple[bool, bool, bool]] | None: ... - @property - def icc_measurement_condition(self) -> _IccMeasurementCondition | None: ... - @property - def icc_viewing_condition(self) -> _IccViewingCondition | None: ... - def is_intent_supported(self, intent: int, direction: int, /) -> int: ... - -class CmsTransform: - def apply(self, id_in: CapsuleType, id_out: CapsuleType) -> int: ... - -def profile_open(profile: str, /) -> CmsProfile: ... -def profile_frombytes(profile: bytes, /) -> CmsProfile: ... -def profile_tobytes(profile: CmsProfile, /) -> bytes: ... -def buildTransform( - input_profile: CmsProfile, - output_profile: CmsProfile, - in_mode: str, - out_mode: str, - rendering_intent: int = 0, - cms_flags: int = 0, - /, -) -> CmsTransform: ... -def buildProofTransform( - input_profile: CmsProfile, - output_profile: CmsProfile, - proof_profile: CmsProfile, - in_mode: str, - out_mode: str, - rendering_intent: int = 0, - proof_intent: int = 0, - cms_flags: int = 0, - /, -) -> CmsTransform: ... -def createProfile( - color_space: Literal["LAB", "XYZ", "sRGB"], color_temp: SupportsFloat = 0.0, / -) -> CmsProfile: ... - -if sys.platform == "win32": - def get_display_profile_win32(handle: int = 0, is_dc: int = 0, /) -> str | None: ... diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/_imagingft.cpython-312-x86_64-linux-gnu.so b/.venv-docs/lib/python3.12/site-packages/PIL/_imagingft.cpython-312-x86_64-linux-gnu.so deleted file mode 100755 index ed93a351..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/_imagingft.cpython-312-x86_64-linux-gnu.so and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/_imagingft.pyi b/.venv-docs/lib/python3.12/site-packages/PIL/_imagingft.pyi deleted file mode 100644 index 2136810b..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/_imagingft.pyi +++ /dev/null @@ -1,70 +0,0 @@ -from collections.abc import Callable -from typing import Any - -from . import ImageFont, _imaging - -class Font: - @property - def family(self) -> str | None: ... - @property - def style(self) -> str | None: ... - @property - def ascent(self) -> int: ... - @property - def descent(self) -> int: ... - @property - def height(self) -> int: ... - @property - def x_ppem(self) -> int: ... - @property - def y_ppem(self) -> int: ... - @property - def glyphs(self) -> int: ... - def render( - self, - string: str | bytes, - fill: Callable[[int, int], _imaging.ImagingCore], - mode: str, - dir: str | None, - features: list[str] | None, - lang: str | None, - stroke_width: float, - stroke_filled: bool, - anchor: str | None, - foreground_ink_long: int, - start: tuple[float, float], - /, - ) -> tuple[_imaging.ImagingCore, tuple[int, int]]: ... - def getsize( - self, - string: str | bytes | bytearray, - mode: str, - dir: str | None, - features: list[str] | None, - lang: str | None, - anchor: str | None, - /, - ) -> tuple[tuple[int, int], tuple[int, int]]: ... - def getlength( - self, - string: str | bytes, - mode: str, - dir: str | None, - features: list[str] | None, - lang: str | None, - /, - ) -> float: ... - def getvarnames(self) -> list[bytes]: ... - def getvaraxes(self) -> list[ImageFont.Axis]: ... - def setvarname(self, instance_index: int, /) -> None: ... - def setvaraxes(self, axes: list[float], /) -> None: ... - -def getfont( - filename: str | bytes, - size: float, - index: int, - encoding: str, - font_bytes: bytes, - layout_engine: int, -) -> Font: ... -def __getattr__(name: str) -> Any: ... diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/_imagingmath.cpython-312-x86_64-linux-gnu.so b/.venv-docs/lib/python3.12/site-packages/PIL/_imagingmath.cpython-312-x86_64-linux-gnu.so deleted file mode 100755 index d8e9029b..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/_imagingmath.cpython-312-x86_64-linux-gnu.so and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/_imagingmath.pyi b/.venv-docs/lib/python3.12/site-packages/PIL/_imagingmath.pyi deleted file mode 100644 index e27843e5..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/_imagingmath.pyi +++ /dev/null @@ -1,3 +0,0 @@ -from typing import Any - -def __getattr__(name: str) -> Any: ... diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/_imagingmorph.cpython-312-x86_64-linux-gnu.so b/.venv-docs/lib/python3.12/site-packages/PIL/_imagingmorph.cpython-312-x86_64-linux-gnu.so deleted file mode 100755 index db855ee0..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/_imagingmorph.cpython-312-x86_64-linux-gnu.so and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/_imagingmorph.pyi b/.venv-docs/lib/python3.12/site-packages/PIL/_imagingmorph.pyi deleted file mode 100644 index e27843e5..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/_imagingmorph.pyi +++ /dev/null @@ -1,3 +0,0 @@ -from typing import Any - -def __getattr__(name: str) -> Any: ... diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/_imagingtk.cpython-312-x86_64-linux-gnu.so b/.venv-docs/lib/python3.12/site-packages/PIL/_imagingtk.cpython-312-x86_64-linux-gnu.so deleted file mode 100755 index 5ca935fc..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/_imagingtk.cpython-312-x86_64-linux-gnu.so and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/_imagingtk.pyi b/.venv-docs/lib/python3.12/site-packages/PIL/_imagingtk.pyi deleted file mode 100644 index e27843e5..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/_imagingtk.pyi +++ /dev/null @@ -1,3 +0,0 @@ -from typing import Any - -def __getattr__(name: str) -> Any: ... diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/_tkinter_finder.py b/.venv-docs/lib/python3.12/site-packages/PIL/_tkinter_finder.py deleted file mode 100644 index 9c014300..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/_tkinter_finder.py +++ /dev/null @@ -1,20 +0,0 @@ -"""Find compiled module linking to Tcl / Tk libraries""" - -from __future__ import annotations - -import sys -import tkinter - -tk = getattr(tkinter, "_tkinter") - -try: - if hasattr(sys, "pypy_find_executable"): - TKINTER_LIB = tk.tklib_cffi.__file__ - else: - TKINTER_LIB = tk.__file__ -except AttributeError: - # _tkinter may be compiled directly into Python, in which case __file__ is - # not available. load_tkinter_funcs will check the binary first in any case. - TKINTER_LIB = None - -tk_version = str(tkinter.TkVersion) diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/_typing.py b/.venv-docs/lib/python3.12/site-packages/PIL/_typing.py deleted file mode 100644 index a941f898..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/_typing.py +++ /dev/null @@ -1,45 +0,0 @@ -from __future__ import annotations - -import os -import sys -from collections.abc import Sequence -from typing import Any, Protocol, TypeVar - -TYPE_CHECKING = False -if TYPE_CHECKING: - from numbers import _IntegralLike as IntegralLike - - try: - import numpy.typing as npt - - NumpyArray = npt.NDArray[Any] - except ImportError: - pass - -if sys.version_info >= (3, 13): - from types import CapsuleType -else: - CapsuleType = object - -if sys.version_info >= (3, 12): - from collections.abc import Buffer -else: - Buffer = Any - - -_Ink = float | tuple[int, ...] | str - -Coords = Sequence[float] | Sequence[Sequence[float]] - - -_T_co = TypeVar("_T_co", covariant=True) - - -class SupportsRead(Protocol[_T_co]): - def read(self, length: int = ..., /) -> _T_co: ... - - -StrOrBytesPath = str | bytes | os.PathLike[str] | os.PathLike[bytes] - - -__all__ = ["Buffer", "IntegralLike", "StrOrBytesPath", "SupportsRead"] diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/_util.py b/.venv-docs/lib/python3.12/site-packages/PIL/_util.py deleted file mode 100644 index b1fa6a0f..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/_util.py +++ /dev/null @@ -1,29 +0,0 @@ -from __future__ import annotations - -import os - -TYPE_CHECKING = False -if TYPE_CHECKING: - from typing import Any, NoReturn, TypeGuard - - from ._typing import StrOrBytesPath - - -def is_path(f: Any) -> TypeGuard[StrOrBytesPath]: - return isinstance(f, (bytes, str, os.PathLike)) - - -class DeferredError: - def __init__(self, ex: BaseException): - self.ex = ex - - def __getattr__(self, elt: str) -> NoReturn: - raise self.ex - - @staticmethod - def new(ex: BaseException) -> Any: - """ - Creates an object that raises the wrapped exception ``ex`` when used, - and casts it to :py:obj:`~typing.Any` type. - """ - return DeferredError(ex) diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/_version.py b/.venv-docs/lib/python3.12/site-packages/PIL/_version.py deleted file mode 100644 index 79ce194c..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/_version.py +++ /dev/null @@ -1,4 +0,0 @@ -# Master version for Pillow -from __future__ import annotations - -__version__ = "12.0.0" diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/_webp.cpython-312-x86_64-linux-gnu.so b/.venv-docs/lib/python3.12/site-packages/PIL/_webp.cpython-312-x86_64-linux-gnu.so deleted file mode 100755 index 9fc096b3..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/PIL/_webp.cpython-312-x86_64-linux-gnu.so and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/_webp.pyi b/.venv-docs/lib/python3.12/site-packages/PIL/_webp.pyi deleted file mode 100644 index e27843e5..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/_webp.pyi +++ /dev/null @@ -1,3 +0,0 @@ -from typing import Any - -def __getattr__(name: str) -> Any: ... diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/features.py b/.venv-docs/lib/python3.12/site-packages/PIL/features.py deleted file mode 100644 index ff32c251..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/features.py +++ /dev/null @@ -1,343 +0,0 @@ -from __future__ import annotations - -import collections -import os -import sys -import warnings -from typing import IO - -import PIL - -from . import Image - -modules = { - "pil": ("PIL._imaging", "PILLOW_VERSION"), - "tkinter": ("PIL._tkinter_finder", "tk_version"), - "freetype2": ("PIL._imagingft", "freetype2_version"), - "littlecms2": ("PIL._imagingcms", "littlecms_version"), - "webp": ("PIL._webp", "webpdecoder_version"), - "avif": ("PIL._avif", "libavif_version"), -} - - -def check_module(feature: str) -> bool: - """ - Checks if a module is available. - - :param feature: The module to check for. - :returns: ``True`` if available, ``False`` otherwise. - :raises ValueError: If the module is not defined in this version of Pillow. - """ - if feature not in modules: - msg = f"Unknown module {feature}" - raise ValueError(msg) - - module, ver = modules[feature] - - try: - __import__(module) - return True - except ModuleNotFoundError: - return False - except ImportError as ex: - warnings.warn(str(ex)) - return False - - -def version_module(feature: str) -> str | None: - """ - :param feature: The module to check for. - :returns: - The loaded version number as a string, or ``None`` if unknown or not available. - :raises ValueError: If the module is not defined in this version of Pillow. - """ - if not check_module(feature): - return None - - module, ver = modules[feature] - - return getattr(__import__(module, fromlist=[ver]), ver) - - -def get_supported_modules() -> list[str]: - """ - :returns: A list of all supported modules. - """ - return [f for f in modules if check_module(f)] - - -codecs = { - "jpg": ("jpeg", "jpeglib"), - "jpg_2000": ("jpeg2k", "jp2klib"), - "zlib": ("zip", "zlib"), - "libtiff": ("libtiff", "libtiff"), -} - - -def check_codec(feature: str) -> bool: - """ - Checks if a codec is available. - - :param feature: The codec to check for. - :returns: ``True`` if available, ``False`` otherwise. - :raises ValueError: If the codec is not defined in this version of Pillow. - """ - if feature not in codecs: - msg = f"Unknown codec {feature}" - raise ValueError(msg) - - codec, lib = codecs[feature] - - return f"{codec}_encoder" in dir(Image.core) - - -def version_codec(feature: str) -> str | None: - """ - :param feature: The codec to check for. - :returns: - The version number as a string, or ``None`` if not available. - Checked at compile time for ``jpg``, run-time otherwise. - :raises ValueError: If the codec is not defined in this version of Pillow. - """ - if not check_codec(feature): - return None - - codec, lib = codecs[feature] - - version = getattr(Image.core, f"{lib}_version") - - if feature == "libtiff": - return version.split("\n")[0].split("Version ")[1] - - return version - - -def get_supported_codecs() -> list[str]: - """ - :returns: A list of all supported codecs. - """ - return [f for f in codecs if check_codec(f)] - - -features: dict[str, tuple[str, str, str | None]] = { - "raqm": ("PIL._imagingft", "HAVE_RAQM", "raqm_version"), - "fribidi": ("PIL._imagingft", "HAVE_FRIBIDI", "fribidi_version"), - "harfbuzz": ("PIL._imagingft", "HAVE_HARFBUZZ", "harfbuzz_version"), - "libjpeg_turbo": ("PIL._imaging", "HAVE_LIBJPEGTURBO", "libjpeg_turbo_version"), - "mozjpeg": ("PIL._imaging", "HAVE_MOZJPEG", "libjpeg_turbo_version"), - "zlib_ng": ("PIL._imaging", "HAVE_ZLIBNG", "zlib_ng_version"), - "libimagequant": ("PIL._imaging", "HAVE_LIBIMAGEQUANT", "imagequant_version"), - "xcb": ("PIL._imaging", "HAVE_XCB", None), -} - - -def check_feature(feature: str) -> bool | None: - """ - Checks if a feature is available. - - :param feature: The feature to check for. - :returns: ``True`` if available, ``False`` if unavailable, ``None`` if unknown. - :raises ValueError: If the feature is not defined in this version of Pillow. - """ - if feature not in features: - msg = f"Unknown feature {feature}" - raise ValueError(msg) - - module, flag, ver = features[feature] - - try: - imported_module = __import__(module, fromlist=["PIL"]) - return getattr(imported_module, flag) - except ModuleNotFoundError: - return None - except ImportError as ex: - warnings.warn(str(ex)) - return None - - -def version_feature(feature: str) -> str | None: - """ - :param feature: The feature to check for. - :returns: The version number as a string, or ``None`` if not available. - :raises ValueError: If the feature is not defined in this version of Pillow. - """ - if not check_feature(feature): - return None - - module, flag, ver = features[feature] - - if ver is None: - return None - - return getattr(__import__(module, fromlist=[ver]), ver) - - -def get_supported_features() -> list[str]: - """ - :returns: A list of all supported features. - """ - return [f for f in features if check_feature(f)] - - -def check(feature: str) -> bool | None: - """ - :param feature: A module, codec, or feature name. - :returns: - ``True`` if the module, codec, or feature is available, - ``False`` or ``None`` otherwise. - """ - - if feature in modules: - return check_module(feature) - if feature in codecs: - return check_codec(feature) - if feature in features: - return check_feature(feature) - warnings.warn(f"Unknown feature '{feature}'.", stacklevel=2) - return False - - -def version(feature: str) -> str | None: - """ - :param feature: - The module, codec, or feature to check for. - :returns: - The version number as a string, or ``None`` if unknown or not available. - """ - if feature in modules: - return version_module(feature) - if feature in codecs: - return version_codec(feature) - if feature in features: - return version_feature(feature) - return None - - -def get_supported() -> list[str]: - """ - :returns: A list of all supported modules, features, and codecs. - """ - - ret = get_supported_modules() - ret.extend(get_supported_features()) - ret.extend(get_supported_codecs()) - return ret - - -def pilinfo(out: IO[str] | None = None, supported_formats: bool = True) -> None: - """ - Prints information about this installation of Pillow. - This function can be called with ``python3 -m PIL``. - It can also be called with ``python3 -m PIL.report`` or ``python3 -m PIL --report`` - to have "supported_formats" set to ``False``, omitting the list of all supported - image file formats. - - :param out: - The output stream to print to. Defaults to ``sys.stdout`` if ``None``. - :param supported_formats: - If ``True``, a list of all supported image file formats will be printed. - """ - - if out is None: - out = sys.stdout - - Image.init() - - print("-" * 68, file=out) - print(f"Pillow {PIL.__version__}", file=out) - py_version_lines = sys.version.splitlines() - print(f"Python {py_version_lines[0].strip()}", file=out) - for py_version in py_version_lines[1:]: - print(f" {py_version.strip()}", file=out) - print("-" * 68, file=out) - print(f"Python executable is {sys.executable or 'unknown'}", file=out) - if sys.prefix != sys.base_prefix: - print(f"Environment Python files loaded from {sys.prefix}", file=out) - print(f"System Python files loaded from {sys.base_prefix}", file=out) - print("-" * 68, file=out) - print( - f"Python Pillow modules loaded from {os.path.dirname(Image.__file__)}", - file=out, - ) - print( - f"Binary Pillow modules loaded from {os.path.dirname(Image.core.__file__)}", - file=out, - ) - print("-" * 68, file=out) - - for name, feature in [ - ("pil", "PIL CORE"), - ("tkinter", "TKINTER"), - ("freetype2", "FREETYPE2"), - ("littlecms2", "LITTLECMS2"), - ("webp", "WEBP"), - ("avif", "AVIF"), - ("jpg", "JPEG"), - ("jpg_2000", "OPENJPEG (JPEG2000)"), - ("zlib", "ZLIB (PNG/ZIP)"), - ("libtiff", "LIBTIFF"), - ("raqm", "RAQM (Bidirectional Text)"), - ("libimagequant", "LIBIMAGEQUANT (Quantization method)"), - ("xcb", "XCB (X protocol)"), - ]: - if check(name): - v: str | None = None - if name == "jpg": - libjpeg_turbo_version = version_feature("libjpeg_turbo") - if libjpeg_turbo_version is not None: - v = "mozjpeg" if check_feature("mozjpeg") else "libjpeg-turbo" - v += " " + libjpeg_turbo_version - if v is None: - v = version(name) - if v is not None: - version_static = name in ("pil", "jpg") - if name == "littlecms2": - # this check is also in src/_imagingcms.c:setup_module() - version_static = tuple(int(x) for x in v.split(".")) < (2, 7) - t = "compiled for" if version_static else "loaded" - if name == "zlib": - zlib_ng_version = version_feature("zlib_ng") - if zlib_ng_version is not None: - v += ", compiled for zlib-ng " + zlib_ng_version - elif name == "raqm": - for f in ("fribidi", "harfbuzz"): - v2 = version_feature(f) - if v2 is not None: - v += f", {f} {v2}" - print("---", feature, "support ok,", t, v, file=out) - else: - print("---", feature, "support ok", file=out) - else: - print("***", feature, "support not installed", file=out) - print("-" * 68, file=out) - - if supported_formats: - extensions = collections.defaultdict(list) - for ext, i in Image.EXTENSION.items(): - extensions[i].append(ext) - - for i in sorted(Image.ID): - line = f"{i}" - if i in Image.MIME: - line = f"{line} {Image.MIME[i]}" - print(line, file=out) - - if i in extensions: - print( - "Extensions: {}".format(", ".join(sorted(extensions[i]))), file=out - ) - - features = [] - if i in Image.OPEN: - features.append("open") - if i in Image.SAVE: - features.append("save") - if i in Image.SAVE_ALL: - features.append("save_all") - if i in Image.DECODERS: - features.append("decode") - if i in Image.ENCODERS: - features.append("encode") - - print("Features: {}".format(", ".join(features)), file=out) - print("-" * 68, file=out) diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/py.typed b/.venv-docs/lib/python3.12/site-packages/PIL/py.typed deleted file mode 100644 index e69de29b..00000000 diff --git a/.venv-docs/lib/python3.12/site-packages/PIL/report.py b/.venv-docs/lib/python3.12/site-packages/PIL/report.py deleted file mode 100644 index d2815e84..00000000 --- a/.venv-docs/lib/python3.12/site-packages/PIL/report.py +++ /dev/null @@ -1,5 +0,0 @@ -from __future__ import annotations - -from .features import pilinfo - -pilinfo(supported_formats=False) diff --git a/.venv-docs/lib/python3.12/site-packages/__pycache__/brotli.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/__pycache__/brotli.cpython-312.pyc deleted file mode 100644 index 89280ffc..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/__pycache__/brotli.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/__pycache__/typing_extensions.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/__pycache__/typing_extensions.cpython-312.pyc deleted file mode 100644 index ed0e0162..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/__pycache__/typing_extensions.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/_brotli.cpython-312-x86_64-linux-gnu.so b/.venv-docs/lib/python3.12/site-packages/_brotli.cpython-312-x86_64-linux-gnu.so deleted file mode 100755 index 5eba5f84..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/_brotli.cpython-312-x86_64-linux-gnu.so and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/_cffi_backend.cpython-312-x86_64-linux-gnu.so b/.venv-docs/lib/python3.12/site-packages/_cffi_backend.cpython-312-x86_64-linux-gnu.so deleted file mode 100755 index 156ee431..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/_cffi_backend.cpython-312-x86_64-linux-gnu.so and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/brotli.py b/.venv-docs/lib/python3.12/site-packages/brotli.py deleted file mode 100644 index 9be4ed4b..00000000 --- a/.venv-docs/lib/python3.12/site-packages/brotli.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright 2016 The Brotli Authors. All rights reserved. -# -# Distributed under MIT license. -# See file LICENSE for detail or copy at https://opensource.org/licenses/MIT - -"""Functions to compress and decompress data using the Brotli library.""" - -import _brotli - -# The library version. -version = __version__ = _brotli.__version__ - -# The compression mode. -MODE_GENERIC = _brotli.MODE_GENERIC -MODE_TEXT = _brotli.MODE_TEXT -MODE_FONT = _brotli.MODE_FONT - -# The Compressor object. -Compressor = _brotli.Compressor - -# The Decompressor object. -Decompressor = _brotli.Decompressor - -# Compress a byte string. -def compress(string, mode=MODE_GENERIC, quality=11, lgwin=22, lgblock=0): - """Compress a byte string. - - Args: - string (bytes): The input data. - mode (int, optional): The compression mode can be MODE_GENERIC (default), - MODE_TEXT (for UTF-8 format text input) or MODE_FONT (for WOFF 2.0). - quality (int, optional): Controls the compression-speed vs compression- - density tradeoff. The higher the quality, the slower the compression. - Range is 0 to 11. Defaults to 11. - lgwin (int, optional): Base 2 logarithm of the sliding window size. Range - is 10 to 24. Defaults to 22. - lgblock (int, optional): Base 2 logarithm of the maximum input block size. - Range is 16 to 24. If set to 0, the value will be set based on the - quality. Defaults to 0. - - Returns: - The compressed byte string. - - Raises: - brotli.error: If arguments are invalid, or compressor fails. - """ - compressor = Compressor(mode=mode, quality=quality, lgwin=lgwin, - lgblock=lgblock) - return compressor.process(string) + compressor.finish() - -# Decompress a compressed byte string. -decompress = _brotli.decompress - -# Raised if compression or decompression fails. -error = _brotli.error diff --git a/.venv-docs/lib/python3.12/site-packages/cffi-2.0.0.dist-info/INSTALLER b/.venv-docs/lib/python3.12/site-packages/cffi-2.0.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e3..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi-2.0.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv-docs/lib/python3.12/site-packages/cffi-2.0.0.dist-info/METADATA b/.venv-docs/lib/python3.12/site-packages/cffi-2.0.0.dist-info/METADATA deleted file mode 100644 index 67508e56..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi-2.0.0.dist-info/METADATA +++ /dev/null @@ -1,68 +0,0 @@ -Metadata-Version: 2.4 -Name: cffi -Version: 2.0.0 -Summary: Foreign Function Interface for Python calling C code. -Author: Armin Rigo, Maciej Fijalkowski -Maintainer: Matt Davis, Matt Clay, Matti Picus -License-Expression: MIT -Project-URL: Documentation, https://cffi.readthedocs.io/ -Project-URL: Changelog, https://cffi.readthedocs.io/en/latest/whatsnew.html -Project-URL: Downloads, https://github.com/python-cffi/cffi/releases -Project-URL: Contact, https://groups.google.com/forum/#!forum/python-cffi -Project-URL: Source Code, https://github.com/python-cffi/cffi -Project-URL: Issue Tracker, https://github.com/python-cffi/cffi/issues -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Programming Language :: Python :: 3.13 -Classifier: Programming Language :: Python :: 3.14 -Classifier: Programming Language :: Python :: Free Threading :: 2 - Beta -Classifier: Programming Language :: Python :: Implementation :: CPython -Requires-Python: >=3.9 -Description-Content-Type: text/markdown -License-File: LICENSE -License-File: AUTHORS -Requires-Dist: pycparser; implementation_name != "PyPy" -Dynamic: license-file - -[![GitHub Actions Status](https://github.com/python-cffi/cffi/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/python-cffi/cffi/actions/workflows/ci.yaml?query=branch%3Amain++) -[![PyPI version](https://img.shields.io/pypi/v/cffi.svg)](https://pypi.org/project/cffi) -[![Read the Docs](https://img.shields.io/badge/docs-latest-blue.svg)][Documentation] - - -CFFI -==== - -Foreign Function Interface for Python calling C code. - -Please see the [Documentation] or uncompiled in the `doc/` subdirectory. - -Download --------- - -[Download page](https://github.com/python-cffi/cffi/releases) - -Source Code ------------ - -Source code is publicly available on -[GitHub](https://github.com/python-cffi/cffi). - -Contact -------- - -[Mailing list](https://groups.google.com/forum/#!forum/python-cffi) - -Testing/development tips ------------------------- - -After `git clone` or `wget && tar`, we will get a directory called `cffi` or `cffi-x.x.x`. we call it `repo-directory`. To run tests under CPython, run the following in the `repo-directory`: - - pip install pytest - pip install -e . # editable install of CFFI for local development - pytest src/c/ testing/ - -[Documentation]: http://cffi.readthedocs.org/ diff --git a/.venv-docs/lib/python3.12/site-packages/cffi-2.0.0.dist-info/RECORD b/.venv-docs/lib/python3.12/site-packages/cffi-2.0.0.dist-info/RECORD deleted file mode 100644 index 6f822989..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi-2.0.0.dist-info/RECORD +++ /dev/null @@ -1,49 +0,0 @@ -_cffi_backend.cpython-312-x86_64-linux-gnu.so,sha256=AGLtw5fn9u4Cmwk3BbGlsXG7VZEvQekABMyEGuRZmcE,348808 -cffi-2.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -cffi-2.0.0.dist-info/METADATA,sha256=uYzn40F68Im8EtXHNBLZs7FoPM-OxzyYbDWsjJvhujk,2559 -cffi-2.0.0.dist-info/RECORD,, -cffi-2.0.0.dist-info/WHEEL,sha256=aSgG0F4rGPZtV0iTEIfy6dtHq6g67Lze3uLfk0vWn88,151 -cffi-2.0.0.dist-info/entry_points.txt,sha256=y6jTxnyeuLnL-XJcDv8uML3n6wyYiGRg8MTp_QGJ9Ho,75 -cffi-2.0.0.dist-info/licenses/AUTHORS,sha256=KmemC7-zN1nWfWRf8TG45ta8TK_CMtdR_Kw-2k0xTMg,208 -cffi-2.0.0.dist-info/licenses/LICENSE,sha256=W6JN3FcGf5JJrdZEw6_EGl1tw34jQz73Wdld83Cwr2M,1123 -cffi-2.0.0.dist-info/top_level.txt,sha256=rE7WR3rZfNKxWI9-jn6hsHCAl7MDkB-FmuQbxWjFehQ,19 -cffi/__init__.py,sha256=-ksBQ7MfDzVvbBlV_ftYBWAmEqfA86ljIzMxzaZeAlI,511 -cffi/__pycache__/__init__.cpython-312.pyc,, -cffi/__pycache__/_imp_emulation.cpython-312.pyc,, -cffi/__pycache__/_shimmed_dist_utils.cpython-312.pyc,, -cffi/__pycache__/api.cpython-312.pyc,, -cffi/__pycache__/backend_ctypes.cpython-312.pyc,, -cffi/__pycache__/cffi_opcode.cpython-312.pyc,, -cffi/__pycache__/commontypes.cpython-312.pyc,, -cffi/__pycache__/cparser.cpython-312.pyc,, -cffi/__pycache__/error.cpython-312.pyc,, -cffi/__pycache__/ffiplatform.cpython-312.pyc,, -cffi/__pycache__/lock.cpython-312.pyc,, -cffi/__pycache__/model.cpython-312.pyc,, -cffi/__pycache__/pkgconfig.cpython-312.pyc,, -cffi/__pycache__/recompiler.cpython-312.pyc,, -cffi/__pycache__/setuptools_ext.cpython-312.pyc,, -cffi/__pycache__/vengine_cpy.cpython-312.pyc,, -cffi/__pycache__/vengine_gen.cpython-312.pyc,, -cffi/__pycache__/verifier.cpython-312.pyc,, -cffi/_cffi_errors.h,sha256=zQXt7uR_m8gUW-fI2hJg0KoSkJFwXv8RGUkEDZ177dQ,3908 -cffi/_cffi_include.h,sha256=Exhmgm9qzHWzWivjfTe0D7Xp4rPUkVxdNuwGhMTMzbw,15055 -cffi/_embedding.h,sha256=Ai33FHblE7XSpHOCp8kPcWwN5_9BV14OvN0JVa6ITpw,18786 -cffi/_imp_emulation.py,sha256=RxREG8zAbI2RPGBww90u_5fi8sWdahpdipOoPzkp7C0,2960 -cffi/_shimmed_dist_utils.py,sha256=Bjj2wm8yZbvFvWEx5AEfmqaqZyZFhYfoyLLQHkXZuao,2230 -cffi/api.py,sha256=alBv6hZQkjpmZplBphdaRn2lPO9-CORs_M7ixabvZWI,42169 -cffi/backend_ctypes.py,sha256=h5ZIzLc6BFVXnGyc9xPqZWUS7qGy7yFSDqXe68Sa8z4,42454 -cffi/cffi_opcode.py,sha256=JDV5l0R0_OadBX_uE7xPPTYtMdmpp8I9UYd6av7aiDU,5731 -cffi/commontypes.py,sha256=7N6zPtCFlvxXMWhHV08psUjdYIK2XgsN3yo5dgua_v4,2805 -cffi/cparser.py,sha256=QUTfmlL-aO-MYR8bFGlvAUHc36OQr7XYLe0WLkGFjRo,44790 -cffi/error.py,sha256=v6xTiS4U0kvDcy4h_BDRo5v39ZQuj-IMRYLv5ETddZs,877 -cffi/ffiplatform.py,sha256=avxFjdikYGJoEtmJO7ewVmwG_VEVl6EZ_WaNhZYCqv4,3584 -cffi/lock.py,sha256=l9TTdwMIMpi6jDkJGnQgE9cvTIR7CAntIJr8EGHt3pY,747 -cffi/model.py,sha256=W30UFQZE73jL5Mx5N81YT77us2W2iJjTm0XYfnwz1cg,21797 -cffi/parse_c_type.h,sha256=OdwQfwM9ktq6vlCB43exFQmxDBtj2MBNdK8LYl15tjw,5976 -cffi/pkgconfig.py,sha256=LP1w7vmWvmKwyqLaU1Z243FOWGNQMrgMUZrvgFuOlco,4374 -cffi/recompiler.py,sha256=78J6lMEEOygXNmjN9-fOFFO3j7eW-iFxSrxfvQb54bY,65509 -cffi/setuptools_ext.py,sha256=0rCwBJ1W7FHWtiMKfNXsSST88V8UXrui5oeXFlDNLG8,9411 -cffi/vengine_cpy.py,sha256=oyQKD23kpE0aChUKA8Jg0e723foPiYzLYEdb-J0MiNs,43881 -cffi/vengine_gen.py,sha256=DUlEIrDiVin1Pnhn1sfoamnS5NLqfJcOdhRoeSNeJRg,26939 -cffi/verifier.py,sha256=oX8jpaohg2Qm3aHcznidAdvrVm5N4sQYG0a3Eo5mIl4,11182 diff --git a/.venv-docs/lib/python3.12/site-packages/cffi-2.0.0.dist-info/WHEEL b/.venv-docs/lib/python3.12/site-packages/cffi-2.0.0.dist-info/WHEEL deleted file mode 100644 index e21e9f2f..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi-2.0.0.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: setuptools (80.9.0) -Root-Is-Purelib: false -Tag: cp312-cp312-manylinux_2_17_x86_64 -Tag: cp312-cp312-manylinux2014_x86_64 - diff --git a/.venv-docs/lib/python3.12/site-packages/cffi-2.0.0.dist-info/entry_points.txt b/.venv-docs/lib/python3.12/site-packages/cffi-2.0.0.dist-info/entry_points.txt deleted file mode 100644 index 4b0274f2..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi-2.0.0.dist-info/entry_points.txt +++ /dev/null @@ -1,2 +0,0 @@ -[distutils.setup_keywords] -cffi_modules = cffi.setuptools_ext:cffi_modules diff --git a/.venv-docs/lib/python3.12/site-packages/cffi-2.0.0.dist-info/licenses/AUTHORS b/.venv-docs/lib/python3.12/site-packages/cffi-2.0.0.dist-info/licenses/AUTHORS deleted file mode 100644 index 370a25d3..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi-2.0.0.dist-info/licenses/AUTHORS +++ /dev/null @@ -1,8 +0,0 @@ -This package has been mostly done by Armin Rigo with help from -Maciej Fijałkowski. The idea is heavily based (although not directly -copied) from LuaJIT ffi by Mike Pall. - - -Other contributors: - - Google Inc. diff --git a/.venv-docs/lib/python3.12/site-packages/cffi-2.0.0.dist-info/licenses/LICENSE b/.venv-docs/lib/python3.12/site-packages/cffi-2.0.0.dist-info/licenses/LICENSE deleted file mode 100644 index 0a1dbfb0..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi-2.0.0.dist-info/licenses/LICENSE +++ /dev/null @@ -1,23 +0,0 @@ - -Except when otherwise stated (look for LICENSE files in directories or -information at the beginning of each file) all software and -documentation is licensed as follows: - - MIT No Attribution - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation - files (the "Software"), to deal in the Software without - restriction, including without limitation the rights to use, - copy, modify, merge, publish, distribute, sublicense, and/or - sell copies of the Software, and to permit persons to whom the - Software is furnished to do so. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - DEALINGS IN THE SOFTWARE. - diff --git a/.venv-docs/lib/python3.12/site-packages/cffi-2.0.0.dist-info/top_level.txt b/.venv-docs/lib/python3.12/site-packages/cffi-2.0.0.dist-info/top_level.txt deleted file mode 100644 index f6457795..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi-2.0.0.dist-info/top_level.txt +++ /dev/null @@ -1,2 +0,0 @@ -_cffi_backend -cffi diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/__init__.py b/.venv-docs/lib/python3.12/site-packages/cffi/__init__.py deleted file mode 100644 index c99ec3d4..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -__all__ = ['FFI', 'VerificationError', 'VerificationMissing', 'CDefError', - 'FFIError'] - -from .api import FFI -from .error import CDefError, FFIError, VerificationError, VerificationMissing -from .error import PkgConfigError - -__version__ = "2.0.0" -__version_info__ = (2, 0, 0) - -# The verifier module file names are based on the CRC32 of a string that -# contains the following version number. It may be older than __version__ -# if nothing is clearly incompatible. -__version_verifier_modules__ = "0.8.6" diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/__init__.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 2c380a05..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/_imp_emulation.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/_imp_emulation.cpython-312.pyc deleted file mode 100644 index 59703288..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/_imp_emulation.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/_shimmed_dist_utils.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/_shimmed_dist_utils.cpython-312.pyc deleted file mode 100644 index a97c0638..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/_shimmed_dist_utils.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/api.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/api.cpython-312.pyc deleted file mode 100644 index 366f75a4..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/api.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/backend_ctypes.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/backend_ctypes.cpython-312.pyc deleted file mode 100644 index ca370ddd..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/backend_ctypes.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/cffi_opcode.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/cffi_opcode.cpython-312.pyc deleted file mode 100644 index 2495af03..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/cffi_opcode.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/commontypes.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/commontypes.cpython-312.pyc deleted file mode 100644 index 72b44cde..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/commontypes.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/cparser.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/cparser.cpython-312.pyc deleted file mode 100644 index 4ec7ca71..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/cparser.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/error.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/error.cpython-312.pyc deleted file mode 100644 index 9204fd6e..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/error.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/ffiplatform.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/ffiplatform.cpython-312.pyc deleted file mode 100644 index 1c7c31c7..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/ffiplatform.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/lock.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/lock.cpython-312.pyc deleted file mode 100644 index 8ff968c6..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/lock.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/model.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/model.cpython-312.pyc deleted file mode 100644 index 13a64bb3..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/model.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/pkgconfig.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/pkgconfig.cpython-312.pyc deleted file mode 100644 index 9b853c25..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/pkgconfig.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/recompiler.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/recompiler.cpython-312.pyc deleted file mode 100644 index 3cfd4ce5..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/recompiler.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/setuptools_ext.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/setuptools_ext.cpython-312.pyc deleted file mode 100644 index f765d524..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/setuptools_ext.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/vengine_cpy.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/vengine_cpy.cpython-312.pyc deleted file mode 100644 index 5fb27373..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/vengine_cpy.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/vengine_gen.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/vengine_gen.cpython-312.pyc deleted file mode 100644 index 3a731cb6..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/vengine_gen.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/verifier.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/verifier.cpython-312.pyc deleted file mode 100644 index fe8cfa58..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/cffi/__pycache__/verifier.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/_cffi_errors.h b/.venv-docs/lib/python3.12/site-packages/cffi/_cffi_errors.h deleted file mode 100644 index 158e0590..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi/_cffi_errors.h +++ /dev/null @@ -1,149 +0,0 @@ -#ifndef CFFI_MESSAGEBOX -# ifdef _MSC_VER -# define CFFI_MESSAGEBOX 1 -# else -# define CFFI_MESSAGEBOX 0 -# endif -#endif - - -#if CFFI_MESSAGEBOX -/* Windows only: logic to take the Python-CFFI embedding logic - initialization errors and display them in a background thread - with MessageBox. The idea is that if the whole program closes - as a result of this problem, then likely it is already a console - program and you can read the stderr output in the console too. - If it is not a console program, then it will likely show its own - dialog to complain, or generally not abruptly close, and for this - case the background thread should stay alive. -*/ -static void *volatile _cffi_bootstrap_text; - -static PyObject *_cffi_start_error_capture(void) -{ - PyObject *result = NULL; - PyObject *x, *m, *bi; - - if (InterlockedCompareExchangePointer(&_cffi_bootstrap_text, - (void *)1, NULL) != NULL) - return (PyObject *)1; - - m = PyImport_AddModule("_cffi_error_capture"); - if (m == NULL) - goto error; - - result = PyModule_GetDict(m); - if (result == NULL) - goto error; - -#if PY_MAJOR_VERSION >= 3 - bi = PyImport_ImportModule("builtins"); -#else - bi = PyImport_ImportModule("__builtin__"); -#endif - if (bi == NULL) - goto error; - PyDict_SetItemString(result, "__builtins__", bi); - Py_DECREF(bi); - - x = PyRun_String( - "import sys\n" - "class FileLike:\n" - " def write(self, x):\n" - " try:\n" - " of.write(x)\n" - " except: pass\n" - " self.buf += x\n" - " def flush(self):\n" - " pass\n" - "fl = FileLike()\n" - "fl.buf = ''\n" - "of = sys.stderr\n" - "sys.stderr = fl\n" - "def done():\n" - " sys.stderr = of\n" - " return fl.buf\n", /* make sure the returned value stays alive */ - Py_file_input, - result, result); - Py_XDECREF(x); - - error: - if (PyErr_Occurred()) - { - PyErr_WriteUnraisable(Py_None); - PyErr_Clear(); - } - return result; -} - -#pragma comment(lib, "user32.lib") - -static DWORD WINAPI _cffi_bootstrap_dialog(LPVOID ignored) -{ - Sleep(666); /* may be interrupted if the whole process is closing */ -#if PY_MAJOR_VERSION >= 3 - MessageBoxW(NULL, (wchar_t *)_cffi_bootstrap_text, - L"Python-CFFI error", - MB_OK | MB_ICONERROR); -#else - MessageBoxA(NULL, (char *)_cffi_bootstrap_text, - "Python-CFFI error", - MB_OK | MB_ICONERROR); -#endif - _cffi_bootstrap_text = NULL; - return 0; -} - -static void _cffi_stop_error_capture(PyObject *ecap) -{ - PyObject *s; - void *text; - - if (ecap == (PyObject *)1) - return; - - if (ecap == NULL) - goto error; - - s = PyRun_String("done()", Py_eval_input, ecap, ecap); - if (s == NULL) - goto error; - - /* Show a dialog box, but in a background thread, and - never show multiple dialog boxes at once. */ -#if PY_MAJOR_VERSION >= 3 - text = PyUnicode_AsWideCharString(s, NULL); -#else - text = PyString_AsString(s); -#endif - - _cffi_bootstrap_text = text; - - if (text != NULL) - { - HANDLE h; - h = CreateThread(NULL, 0, _cffi_bootstrap_dialog, - NULL, 0, NULL); - if (h != NULL) - CloseHandle(h); - } - /* decref the string, but it should stay alive as 'fl.buf' - in the small module above. It will really be freed only if - we later get another similar error. So it's a leak of at - most one copy of the small module. That's fine for this - situation which is usually a "fatal error" anyway. */ - Py_DECREF(s); - PyErr_Clear(); - return; - - error: - _cffi_bootstrap_text = NULL; - PyErr_Clear(); -} - -#else - -static PyObject *_cffi_start_error_capture(void) { return NULL; } -static void _cffi_stop_error_capture(PyObject *ecap) { } - -#endif diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/_cffi_include.h b/.venv-docs/lib/python3.12/site-packages/cffi/_cffi_include.h deleted file mode 100644 index 908a1d73..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi/_cffi_include.h +++ /dev/null @@ -1,389 +0,0 @@ -#define _CFFI_ - -/* We try to define Py_LIMITED_API before including Python.h. - - Mess: we can only define it if Py_DEBUG, Py_TRACE_REFS and - Py_REF_DEBUG are not defined. This is a best-effort approximation: - we can learn about Py_DEBUG from pyconfig.h, but it is unclear if - the same works for the other two macros. Py_DEBUG implies them, - but not the other way around. - - The implementation is messy (issue #350): on Windows, with _MSC_VER, - we have to define Py_LIMITED_API even before including pyconfig.h. - In that case, we guess what pyconfig.h will do to the macros above, - and check our guess after the #include. - - Note that on Windows, with CPython 3.x, you need >= 3.5 and virtualenv - version >= 16.0.0. With older versions of either, you don't get a - copy of PYTHON3.DLL in the virtualenv. We can't check the version of - CPython *before* we even include pyconfig.h. ffi.set_source() puts - a ``#define _CFFI_NO_LIMITED_API'' at the start of this file if it is - running on Windows < 3.5, as an attempt at fixing it, but that's - arguably wrong because it may not be the target version of Python. - Still better than nothing I guess. As another workaround, you can - remove the definition of Py_LIMITED_API here. - - See also 'py_limited_api' in cffi/setuptools_ext.py. -*/ -#if !defined(_CFFI_USE_EMBEDDING) && !defined(Py_LIMITED_API) -# ifdef _MSC_VER -# if !defined(_DEBUG) && !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) && !defined(_CFFI_NO_LIMITED_API) -# define Py_LIMITED_API -# endif -# include - /* sanity-check: Py_LIMITED_API will cause crashes if any of these - are also defined. Normally, the Python file PC/pyconfig.h does not - cause any of these to be defined, with the exception that _DEBUG - causes Py_DEBUG. Double-check that. */ -# ifdef Py_LIMITED_API -# if defined(Py_DEBUG) -# error "pyconfig.h unexpectedly defines Py_DEBUG, but Py_LIMITED_API is set" -# endif -# if defined(Py_TRACE_REFS) -# error "pyconfig.h unexpectedly defines Py_TRACE_REFS, but Py_LIMITED_API is set" -# endif -# if defined(Py_REF_DEBUG) -# error "pyconfig.h unexpectedly defines Py_REF_DEBUG, but Py_LIMITED_API is set" -# endif -# endif -# else -# include -# if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) && !defined(_CFFI_NO_LIMITED_API) -# define Py_LIMITED_API -# endif -# endif -#endif - -#include -#ifdef __cplusplus -extern "C" { -#endif -#include -#include "parse_c_type.h" - -/* this block of #ifs should be kept exactly identical between - c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py - and cffi/_cffi_include.h */ -#if defined(_MSC_VER) -# include /* for alloca() */ -# if _MSC_VER < 1600 /* MSVC < 2010 */ - typedef __int8 int8_t; - typedef __int16 int16_t; - typedef __int32 int32_t; - typedef __int64 int64_t; - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; - typedef unsigned __int64 uint64_t; - typedef __int8 int_least8_t; - typedef __int16 int_least16_t; - typedef __int32 int_least32_t; - typedef __int64 int_least64_t; - typedef unsigned __int8 uint_least8_t; - typedef unsigned __int16 uint_least16_t; - typedef unsigned __int32 uint_least32_t; - typedef unsigned __int64 uint_least64_t; - typedef __int8 int_fast8_t; - typedef __int16 int_fast16_t; - typedef __int32 int_fast32_t; - typedef __int64 int_fast64_t; - typedef unsigned __int8 uint_fast8_t; - typedef unsigned __int16 uint_fast16_t; - typedef unsigned __int32 uint_fast32_t; - typedef unsigned __int64 uint_fast64_t; - typedef __int64 intmax_t; - typedef unsigned __int64 uintmax_t; -# else -# include -# endif -# if _MSC_VER < 1800 /* MSVC < 2013 */ -# ifndef __cplusplus - typedef unsigned char _Bool; -# endif -# endif -# define _cffi_float_complex_t _Fcomplex /* include for it */ -# define _cffi_double_complex_t _Dcomplex /* include for it */ -#else -# include -# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux) -# include -# endif -# define _cffi_float_complex_t float _Complex -# define _cffi_double_complex_t double _Complex -#endif - -#ifdef __GNUC__ -# define _CFFI_UNUSED_FN __attribute__((unused)) -#else -# define _CFFI_UNUSED_FN /* nothing */ -#endif - -#ifdef __cplusplus -# ifndef _Bool - typedef bool _Bool; /* semi-hackish: C++ has no _Bool; bool is builtin */ -# endif -#endif - -/********** CPython-specific section **********/ -#ifndef PYPY_VERSION - - -#if PY_MAJOR_VERSION >= 3 -# define PyInt_FromLong PyLong_FromLong -#endif - -#define _cffi_from_c_double PyFloat_FromDouble -#define _cffi_from_c_float PyFloat_FromDouble -#define _cffi_from_c_long PyInt_FromLong -#define _cffi_from_c_ulong PyLong_FromUnsignedLong -#define _cffi_from_c_longlong PyLong_FromLongLong -#define _cffi_from_c_ulonglong PyLong_FromUnsignedLongLong -#define _cffi_from_c__Bool PyBool_FromLong - -#define _cffi_to_c_double PyFloat_AsDouble -#define _cffi_to_c_float PyFloat_AsDouble - -#define _cffi_from_c_int(x, type) \ - (((type)-1) > 0 ? /* unsigned */ \ - (sizeof(type) < sizeof(long) ? \ - PyInt_FromLong((long)x) : \ - sizeof(type) == sizeof(long) ? \ - PyLong_FromUnsignedLong((unsigned long)x) : \ - PyLong_FromUnsignedLongLong((unsigned long long)x)) : \ - (sizeof(type) <= sizeof(long) ? \ - PyInt_FromLong((long)x) : \ - PyLong_FromLongLong((long long)x))) - -#define _cffi_to_c_int(o, type) \ - ((type)( \ - sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o) \ - : (type)_cffi_to_c_i8(o)) : \ - sizeof(type) == 2 ? (((type)-1) > 0 ? (type)_cffi_to_c_u16(o) \ - : (type)_cffi_to_c_i16(o)) : \ - sizeof(type) == 4 ? (((type)-1) > 0 ? (type)_cffi_to_c_u32(o) \ - : (type)_cffi_to_c_i32(o)) : \ - sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o) \ - : (type)_cffi_to_c_i64(o)) : \ - (Py_FatalError("unsupported size for type " #type), (type)0))) - -#define _cffi_to_c_i8 \ - ((int(*)(PyObject *))_cffi_exports[1]) -#define _cffi_to_c_u8 \ - ((int(*)(PyObject *))_cffi_exports[2]) -#define _cffi_to_c_i16 \ - ((int(*)(PyObject *))_cffi_exports[3]) -#define _cffi_to_c_u16 \ - ((int(*)(PyObject *))_cffi_exports[4]) -#define _cffi_to_c_i32 \ - ((int(*)(PyObject *))_cffi_exports[5]) -#define _cffi_to_c_u32 \ - ((unsigned int(*)(PyObject *))_cffi_exports[6]) -#define _cffi_to_c_i64 \ - ((long long(*)(PyObject *))_cffi_exports[7]) -#define _cffi_to_c_u64 \ - ((unsigned long long(*)(PyObject *))_cffi_exports[8]) -#define _cffi_to_c_char \ - ((int(*)(PyObject *))_cffi_exports[9]) -#define _cffi_from_c_pointer \ - ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[10]) -#define _cffi_to_c_pointer \ - ((char *(*)(PyObject *, struct _cffi_ctypedescr *))_cffi_exports[11]) -#define _cffi_get_struct_layout \ - not used any more -#define _cffi_restore_errno \ - ((void(*)(void))_cffi_exports[13]) -#define _cffi_save_errno \ - ((void(*)(void))_cffi_exports[14]) -#define _cffi_from_c_char \ - ((PyObject *(*)(char))_cffi_exports[15]) -#define _cffi_from_c_deref \ - ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[16]) -#define _cffi_to_c \ - ((int(*)(char *, struct _cffi_ctypedescr *, PyObject *))_cffi_exports[17]) -#define _cffi_from_c_struct \ - ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[18]) -#define _cffi_to_c_wchar_t \ - ((_cffi_wchar_t(*)(PyObject *))_cffi_exports[19]) -#define _cffi_from_c_wchar_t \ - ((PyObject *(*)(_cffi_wchar_t))_cffi_exports[20]) -#define _cffi_to_c_long_double \ - ((long double(*)(PyObject *))_cffi_exports[21]) -#define _cffi_to_c__Bool \ - ((_Bool(*)(PyObject *))_cffi_exports[22]) -#define _cffi_prepare_pointer_call_argument \ - ((Py_ssize_t(*)(struct _cffi_ctypedescr *, \ - PyObject *, char **))_cffi_exports[23]) -#define _cffi_convert_array_from_object \ - ((int(*)(char *, struct _cffi_ctypedescr *, PyObject *))_cffi_exports[24]) -#define _CFFI_CPIDX 25 -#define _cffi_call_python \ - ((void(*)(struct _cffi_externpy_s *, char *))_cffi_exports[_CFFI_CPIDX]) -#define _cffi_to_c_wchar3216_t \ - ((int(*)(PyObject *))_cffi_exports[26]) -#define _cffi_from_c_wchar3216_t \ - ((PyObject *(*)(int))_cffi_exports[27]) -#define _CFFI_NUM_EXPORTS 28 - -struct _cffi_ctypedescr; - -static void *_cffi_exports[_CFFI_NUM_EXPORTS]; - -#define _cffi_type(index) ( \ - assert((((uintptr_t)_cffi_types[index]) & 1) == 0), \ - (struct _cffi_ctypedescr *)_cffi_types[index]) - -static PyObject *_cffi_init(const char *module_name, Py_ssize_t version, - const struct _cffi_type_context_s *ctx) -{ - PyObject *module, *o_arg, *new_module; - void *raw[] = { - (void *)module_name, - (void *)version, - (void *)_cffi_exports, - (void *)ctx, - }; - - module = PyImport_ImportModule("_cffi_backend"); - if (module == NULL) - goto failure; - - o_arg = PyLong_FromVoidPtr((void *)raw); - if (o_arg == NULL) - goto failure; - - new_module = PyObject_CallMethod( - module, (char *)"_init_cffi_1_0_external_module", (char *)"O", o_arg); - - Py_DECREF(o_arg); - Py_DECREF(module); - return new_module; - - failure: - Py_XDECREF(module); - return NULL; -} - - -#ifdef HAVE_WCHAR_H -typedef wchar_t _cffi_wchar_t; -#else -typedef uint16_t _cffi_wchar_t; /* same random pick as _cffi_backend.c */ -#endif - -_CFFI_UNUSED_FN static uint16_t _cffi_to_c_char16_t(PyObject *o) -{ - if (sizeof(_cffi_wchar_t) == 2) - return (uint16_t)_cffi_to_c_wchar_t(o); - else - return (uint16_t)_cffi_to_c_wchar3216_t(o); -} - -_CFFI_UNUSED_FN static PyObject *_cffi_from_c_char16_t(uint16_t x) -{ - if (sizeof(_cffi_wchar_t) == 2) - return _cffi_from_c_wchar_t((_cffi_wchar_t)x); - else - return _cffi_from_c_wchar3216_t((int)x); -} - -_CFFI_UNUSED_FN static int _cffi_to_c_char32_t(PyObject *o) -{ - if (sizeof(_cffi_wchar_t) == 4) - return (int)_cffi_to_c_wchar_t(o); - else - return (int)_cffi_to_c_wchar3216_t(o); -} - -_CFFI_UNUSED_FN static PyObject *_cffi_from_c_char32_t(unsigned int x) -{ - if (sizeof(_cffi_wchar_t) == 4) - return _cffi_from_c_wchar_t((_cffi_wchar_t)x); - else - return _cffi_from_c_wchar3216_t((int)x); -} - -union _cffi_union_alignment_u { - unsigned char m_char; - unsigned short m_short; - unsigned int m_int; - unsigned long m_long; - unsigned long long m_longlong; - float m_float; - double m_double; - long double m_longdouble; -}; - -struct _cffi_freeme_s { - struct _cffi_freeme_s *next; - union _cffi_union_alignment_u alignment; -}; - -_CFFI_UNUSED_FN static int -_cffi_convert_array_argument(struct _cffi_ctypedescr *ctptr, PyObject *arg, - char **output_data, Py_ssize_t datasize, - struct _cffi_freeme_s **freeme) -{ - char *p; - if (datasize < 0) - return -1; - - p = *output_data; - if (p == NULL) { - struct _cffi_freeme_s *fp = (struct _cffi_freeme_s *)PyObject_Malloc( - offsetof(struct _cffi_freeme_s, alignment) + (size_t)datasize); - if (fp == NULL) - return -1; - fp->next = *freeme; - *freeme = fp; - p = *output_data = (char *)&fp->alignment; - } - memset((void *)p, 0, (size_t)datasize); - return _cffi_convert_array_from_object(p, ctptr, arg); -} - -_CFFI_UNUSED_FN static void -_cffi_free_array_arguments(struct _cffi_freeme_s *freeme) -{ - do { - void *p = (void *)freeme; - freeme = freeme->next; - PyObject_Free(p); - } while (freeme != NULL); -} - -/********** end CPython-specific section **********/ -#else -_CFFI_UNUSED_FN -static void (*_cffi_call_python_org)(struct _cffi_externpy_s *, char *); -# define _cffi_call_python _cffi_call_python_org -#endif - - -#define _cffi_array_len(array) (sizeof(array) / sizeof((array)[0])) - -#define _cffi_prim_int(size, sign) \ - ((size) == 1 ? ((sign) ? _CFFI_PRIM_INT8 : _CFFI_PRIM_UINT8) : \ - (size) == 2 ? ((sign) ? _CFFI_PRIM_INT16 : _CFFI_PRIM_UINT16) : \ - (size) == 4 ? ((sign) ? _CFFI_PRIM_INT32 : _CFFI_PRIM_UINT32) : \ - (size) == 8 ? ((sign) ? _CFFI_PRIM_INT64 : _CFFI_PRIM_UINT64) : \ - _CFFI__UNKNOWN_PRIM) - -#define _cffi_prim_float(size) \ - ((size) == sizeof(float) ? _CFFI_PRIM_FLOAT : \ - (size) == sizeof(double) ? _CFFI_PRIM_DOUBLE : \ - (size) == sizeof(long double) ? _CFFI__UNKNOWN_LONG_DOUBLE : \ - _CFFI__UNKNOWN_FLOAT_PRIM) - -#define _cffi_check_int(got, got_nonpos, expected) \ - ((got_nonpos) == (expected <= 0) && \ - (got) == (unsigned long long)expected) - -#ifdef MS_WIN32 -# define _cffi_stdcall __stdcall -#else -# define _cffi_stdcall /* nothing */ -#endif - -#ifdef __cplusplus -} -#endif diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/_embedding.h b/.venv-docs/lib/python3.12/site-packages/cffi/_embedding.h deleted file mode 100644 index 64c04f67..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi/_embedding.h +++ /dev/null @@ -1,550 +0,0 @@ - -/***** Support code for embedding *****/ - -#ifdef __cplusplus -extern "C" { -#endif - - -#if defined(_WIN32) -# define CFFI_DLLEXPORT __declspec(dllexport) -#elif defined(__GNUC__) -# define CFFI_DLLEXPORT __attribute__((visibility("default"))) -#else -# define CFFI_DLLEXPORT /* nothing */ -#endif - - -/* There are two global variables of type _cffi_call_python_fnptr: - - * _cffi_call_python, which we declare just below, is the one called - by ``extern "Python"`` implementations. - - * _cffi_call_python_org, which on CPython is actually part of the - _cffi_exports[] array, is the function pointer copied from - _cffi_backend. If _cffi_start_python() fails, then this is set - to NULL; otherwise, it should never be NULL. - - After initialization is complete, both are equal. However, the - first one remains equal to &_cffi_start_and_call_python until the - very end of initialization, when we are (or should be) sure that - concurrent threads also see a completely initialized world, and - only then is it changed. -*/ -#undef _cffi_call_python -typedef void (*_cffi_call_python_fnptr)(struct _cffi_externpy_s *, char *); -static void _cffi_start_and_call_python(struct _cffi_externpy_s *, char *); -static _cffi_call_python_fnptr _cffi_call_python = &_cffi_start_and_call_python; - - -#ifndef _MSC_VER - /* --- Assuming a GCC not infinitely old --- */ -# define cffi_compare_and_swap(l,o,n) __sync_bool_compare_and_swap(l,o,n) -# define cffi_write_barrier() __sync_synchronize() -# if !defined(__amd64__) && !defined(__x86_64__) && \ - !defined(__i386__) && !defined(__i386) -# define cffi_read_barrier() __sync_synchronize() -# else -# define cffi_read_barrier() (void)0 -# endif -#else - /* --- Windows threads version --- */ -# include -# define cffi_compare_and_swap(l,o,n) \ - (InterlockedCompareExchangePointer(l,n,o) == (o)) -# define cffi_write_barrier() InterlockedCompareExchange(&_cffi_dummy,0,0) -# define cffi_read_barrier() (void)0 -static volatile LONG _cffi_dummy; -#endif - -#ifdef WITH_THREAD -# ifndef _MSC_VER -# include - static pthread_mutex_t _cffi_embed_startup_lock; -# else - static CRITICAL_SECTION _cffi_embed_startup_lock; -# endif - static char _cffi_embed_startup_lock_ready = 0; -#endif - -static void _cffi_acquire_reentrant_mutex(void) -{ - static void *volatile lock = NULL; - - while (!cffi_compare_and_swap(&lock, NULL, (void *)1)) { - /* should ideally do a spin loop instruction here, but - hard to do it portably and doesn't really matter I - think: pthread_mutex_init() should be very fast, and - this is only run at start-up anyway. */ - } - -#ifdef WITH_THREAD - if (!_cffi_embed_startup_lock_ready) { -# ifndef _MSC_VER - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&_cffi_embed_startup_lock, &attr); -# else - InitializeCriticalSection(&_cffi_embed_startup_lock); -# endif - _cffi_embed_startup_lock_ready = 1; - } -#endif - - while (!cffi_compare_and_swap(&lock, (void *)1, NULL)) - ; - -#ifndef _MSC_VER - pthread_mutex_lock(&_cffi_embed_startup_lock); -#else - EnterCriticalSection(&_cffi_embed_startup_lock); -#endif -} - -static void _cffi_release_reentrant_mutex(void) -{ -#ifndef _MSC_VER - pthread_mutex_unlock(&_cffi_embed_startup_lock); -#else - LeaveCriticalSection(&_cffi_embed_startup_lock); -#endif -} - - -/********** CPython-specific section **********/ -#ifndef PYPY_VERSION - -#include "_cffi_errors.h" - - -#define _cffi_call_python_org _cffi_exports[_CFFI_CPIDX] - -PyMODINIT_FUNC _CFFI_PYTHON_STARTUP_FUNC(void); /* forward */ - -static void _cffi_py_initialize(void) -{ - /* XXX use initsigs=0, which "skips initialization registration of - signal handlers, which might be useful when Python is - embedded" according to the Python docs. But review and think - if it should be a user-controllable setting. - - XXX we should also give a way to write errors to a buffer - instead of to stderr. - - XXX if importing 'site' fails, CPython (any version) calls - exit(). Should we try to work around this behavior here? - */ - Py_InitializeEx(0); -} - -static int _cffi_initialize_python(void) -{ - /* This initializes Python, imports _cffi_backend, and then the - present .dll/.so is set up as a CPython C extension module. - */ - int result; - PyGILState_STATE state; - PyObject *pycode=NULL, *global_dict=NULL, *x; - PyObject *builtins; - - state = PyGILState_Ensure(); - - /* Call the initxxx() function from the present module. It will - create and initialize us as a CPython extension module, instead - of letting the startup Python code do it---it might reimport - the same .dll/.so and get maybe confused on some platforms. - It might also have troubles locating the .dll/.so again for all - I know. - */ - (void)_CFFI_PYTHON_STARTUP_FUNC(); - if (PyErr_Occurred()) - goto error; - - /* Now run the Python code provided to ffi.embedding_init_code(). - */ - pycode = Py_CompileString(_CFFI_PYTHON_STARTUP_CODE, - "", - Py_file_input); - if (pycode == NULL) - goto error; - global_dict = PyDict_New(); - if (global_dict == NULL) - goto error; - builtins = PyEval_GetBuiltins(); - if (builtins == NULL) - goto error; - if (PyDict_SetItemString(global_dict, "__builtins__", builtins) < 0) - goto error; - x = PyEval_EvalCode( -#if PY_MAJOR_VERSION < 3 - (PyCodeObject *) -#endif - pycode, global_dict, global_dict); - if (x == NULL) - goto error; - Py_DECREF(x); - - /* Done! Now if we've been called from - _cffi_start_and_call_python() in an ``extern "Python"``, we can - only hope that the Python code did correctly set up the - corresponding @ffi.def_extern() function. Otherwise, the - general logic of ``extern "Python"`` functions (inside the - _cffi_backend module) will find that the reference is still - missing and print an error. - */ - result = 0; - done: - Py_XDECREF(pycode); - Py_XDECREF(global_dict); - PyGILState_Release(state); - return result; - - error:; - { - /* Print as much information as potentially useful. - Debugging load-time failures with embedding is not fun - */ - PyObject *ecap; - PyObject *exception, *v, *tb, *f, *modules, *mod; - PyErr_Fetch(&exception, &v, &tb); - ecap = _cffi_start_error_capture(); - f = PySys_GetObject((char *)"stderr"); - if (f != NULL && f != Py_None) { - PyFile_WriteString( - "Failed to initialize the Python-CFFI embedding logic:\n\n", f); - } - - if (exception != NULL) { - PyErr_NormalizeException(&exception, &v, &tb); - PyErr_Display(exception, v, tb); - } - Py_XDECREF(exception); - Py_XDECREF(v); - Py_XDECREF(tb); - - if (f != NULL && f != Py_None) { - PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME - "\ncompiled with cffi version: 2.0.0" - "\n_cffi_backend module: ", f); - modules = PyImport_GetModuleDict(); - mod = PyDict_GetItemString(modules, "_cffi_backend"); - if (mod == NULL) { - PyFile_WriteString("not loaded", f); - } - else { - v = PyObject_GetAttrString(mod, "__file__"); - PyFile_WriteObject(v, f, 0); - Py_XDECREF(v); - } - PyFile_WriteString("\nsys.path: ", f); - PyFile_WriteObject(PySys_GetObject((char *)"path"), f, 0); - PyFile_WriteString("\n\n", f); - } - _cffi_stop_error_capture(ecap); - } - result = -1; - goto done; -} - -#if PY_VERSION_HEX < 0x03080000 -PyAPI_DATA(char *) _PyParser_TokenNames[]; /* from CPython */ -#endif - -static int _cffi_carefully_make_gil(void) -{ - /* This does the basic initialization of Python. It can be called - completely concurrently from unrelated threads. It assumes - that we don't hold the GIL before (if it exists), and we don't - hold it afterwards. - - (What it really does used to be completely different in Python 2 - and Python 3, with the Python 2 solution avoiding the spin-lock - around the Py_InitializeEx() call. However, after recent changes - to CPython 2.7 (issue #358) it no longer works. So we use the - Python 3 solution everywhere.) - - This initializes Python by calling Py_InitializeEx(). - Important: this must not be called concurrently at all. - So we use a global variable as a simple spin lock. This global - variable must be from 'libpythonX.Y.so', not from this - cffi-based extension module, because it must be shared from - different cffi-based extension modules. - - In Python < 3.8, we choose - _PyParser_TokenNames[0] as a completely arbitrary pointer value - that is never written to. The default is to point to the - string "ENDMARKER". We change it temporarily to point to the - next character in that string. (Yes, I know it's REALLY - obscure.) - - In Python >= 3.8, this string array is no longer writable, so - instead we pick PyCapsuleType.tp_version_tag. We can't change - Python < 3.8 because someone might use a mixture of cffi - embedded modules, some of which were compiled before this file - changed. - - In Python >= 3.12, this stopped working because that particular - tp_version_tag gets modified during interpreter startup. It's - arguably a bad idea before 3.12 too, but again we can't change - that because someone might use a mixture of cffi embedded - modules, and no-one reported a bug so far. In Python >= 3.12 - we go instead for PyCapsuleType.tp_as_buffer, which is supposed - to always be NULL. We write to it temporarily a pointer to - a struct full of NULLs, which is semantically the same. - */ - -#ifdef WITH_THREAD -# if PY_VERSION_HEX < 0x03080000 - char *volatile *lock = (char *volatile *)_PyParser_TokenNames; - char *old_value, *locked_value; - - while (1) { /* spin loop */ - old_value = *lock; - locked_value = old_value + 1; - if (old_value[0] == 'E') { - assert(old_value[1] == 'N'); - if (cffi_compare_and_swap(lock, old_value, locked_value)) - break; - } - else { - assert(old_value[0] == 'N'); - /* should ideally do a spin loop instruction here, but - hard to do it portably and doesn't really matter I - think: PyEval_InitThreads() should be very fast, and - this is only run at start-up anyway. */ - } - } -# else -# if PY_VERSION_HEX < 0x030C0000 - int volatile *lock = (int volatile *)&PyCapsule_Type.tp_version_tag; - int old_value, locked_value = -42; - assert(!(PyCapsule_Type.tp_flags & Py_TPFLAGS_HAVE_VERSION_TAG)); -# else - static struct ebp_s { PyBufferProcs buf; int mark; } empty_buffer_procs; - empty_buffer_procs.mark = -42; - PyBufferProcs *volatile *lock = (PyBufferProcs *volatile *) - &PyCapsule_Type.tp_as_buffer; - PyBufferProcs *old_value, *locked_value = &empty_buffer_procs.buf; -# endif - - while (1) { /* spin loop */ - old_value = *lock; - if (old_value == 0) { - if (cffi_compare_and_swap(lock, old_value, locked_value)) - break; - } - else { -# if PY_VERSION_HEX < 0x030C0000 - assert(old_value == locked_value); -# else - /* The pointer should point to a possibly different - empty_buffer_procs from another C extension module */ - assert(((struct ebp_s *)old_value)->mark == -42); -# endif - /* should ideally do a spin loop instruction here, but - hard to do it portably and doesn't really matter I - think: PyEval_InitThreads() should be very fast, and - this is only run at start-up anyway. */ - } - } -# endif -#endif - - /* call Py_InitializeEx() */ - if (!Py_IsInitialized()) { - _cffi_py_initialize(); -#if PY_VERSION_HEX < 0x03070000 - PyEval_InitThreads(); -#endif - PyEval_SaveThread(); /* release the GIL */ - /* the returned tstate must be the one that has been stored into the - autoTLSkey by _PyGILState_Init() called from Py_Initialize(). */ - } - else { -#if PY_VERSION_HEX < 0x03070000 - /* PyEval_InitThreads() is always a no-op from CPython 3.7 */ - PyGILState_STATE state = PyGILState_Ensure(); - PyEval_InitThreads(); - PyGILState_Release(state); -#endif - } - -#ifdef WITH_THREAD - /* release the lock */ - while (!cffi_compare_and_swap(lock, locked_value, old_value)) - ; -#endif - - return 0; -} - -/********** end CPython-specific section **********/ - - -#else - - -/********** PyPy-specific section **********/ - -PyMODINIT_FUNC _CFFI_PYTHON_STARTUP_FUNC(const void *[]); /* forward */ - -static struct _cffi_pypy_init_s { - const char *name; - void *func; /* function pointer */ - const char *code; -} _cffi_pypy_init = { - _CFFI_MODULE_NAME, - _CFFI_PYTHON_STARTUP_FUNC, - _CFFI_PYTHON_STARTUP_CODE, -}; - -extern int pypy_carefully_make_gil(const char *); -extern int pypy_init_embedded_cffi_module(int, struct _cffi_pypy_init_s *); - -static int _cffi_carefully_make_gil(void) -{ - return pypy_carefully_make_gil(_CFFI_MODULE_NAME); -} - -static int _cffi_initialize_python(void) -{ - return pypy_init_embedded_cffi_module(0xB011, &_cffi_pypy_init); -} - -/********** end PyPy-specific section **********/ - - -#endif - - -#ifdef __GNUC__ -__attribute__((noinline)) -#endif -static _cffi_call_python_fnptr _cffi_start_python(void) -{ - /* Delicate logic to initialize Python. This function can be - called multiple times concurrently, e.g. when the process calls - its first ``extern "Python"`` functions in multiple threads at - once. It can also be called recursively, in which case we must - ignore it. We also have to consider what occurs if several - different cffi-based extensions reach this code in parallel - threads---it is a different copy of the code, then, and we - can't have any shared global variable unless it comes from - 'libpythonX.Y.so'. - - Idea: - - * _cffi_carefully_make_gil(): "carefully" call - PyEval_InitThreads() (possibly with Py_InitializeEx() first). - - * then we use a (local) custom lock to make sure that a call to this - cffi-based extension will wait if another call to the *same* - extension is running the initialization in another thread. - It is reentrant, so that a recursive call will not block, but - only one from a different thread. - - * then we grab the GIL and (Python 2) we call Py_InitializeEx(). - At this point, concurrent calls to Py_InitializeEx() are not - possible: we have the GIL. - - * do the rest of the specific initialization, which may - temporarily release the GIL but not the custom lock. - Only release the custom lock when we are done. - */ - static char called = 0; - - if (_cffi_carefully_make_gil() != 0) - return NULL; - - _cffi_acquire_reentrant_mutex(); - - /* Here the GIL exists, but we don't have it. We're only protected - from concurrency by the reentrant mutex. */ - - /* This file only initializes the embedded module once, the first - time this is called, even if there are subinterpreters. */ - if (!called) { - called = 1; /* invoke _cffi_initialize_python() only once, - but don't set '_cffi_call_python' right now, - otherwise concurrent threads won't call - this function at all (we need them to wait) */ - if (_cffi_initialize_python() == 0) { - /* now initialization is finished. Switch to the fast-path. */ - - /* We would like nobody to see the new value of - '_cffi_call_python' without also seeing the rest of the - data initialized. However, this is not possible. But - the new value of '_cffi_call_python' is the function - 'cffi_call_python()' from _cffi_backend. So: */ - cffi_write_barrier(); - /* ^^^ we put a write barrier here, and a corresponding - read barrier at the start of cffi_call_python(). This - ensures that after that read barrier, we see everything - done here before the write barrier. - */ - - assert(_cffi_call_python_org != NULL); - _cffi_call_python = (_cffi_call_python_fnptr)_cffi_call_python_org; - } - else { - /* initialization failed. Reset this to NULL, even if it was - already set to some other value. Future calls to - _cffi_start_python() are still forced to occur, and will - always return NULL from now on. */ - _cffi_call_python_org = NULL; - } - } - - _cffi_release_reentrant_mutex(); - - return (_cffi_call_python_fnptr)_cffi_call_python_org; -} - -static -void _cffi_start_and_call_python(struct _cffi_externpy_s *externpy, char *args) -{ - _cffi_call_python_fnptr fnptr; - int current_err = errno; -#ifdef _MSC_VER - int current_lasterr = GetLastError(); -#endif - fnptr = _cffi_start_python(); - if (fnptr == NULL) { - fprintf(stderr, "function %s() called, but initialization code " - "failed. Returning 0.\n", externpy->name); - memset(args, 0, externpy->size_of_result); - } -#ifdef _MSC_VER - SetLastError(current_lasterr); -#endif - errno = current_err; - - if (fnptr != NULL) - fnptr(externpy, args); -} - - -/* The cffi_start_python() function makes sure Python is initialized - and our cffi module is set up. It can be called manually from the - user C code. The same effect is obtained automatically from any - dll-exported ``extern "Python"`` function. This function returns - -1 if initialization failed, 0 if all is OK. */ -_CFFI_UNUSED_FN -static int cffi_start_python(void) -{ - if (_cffi_call_python == &_cffi_start_and_call_python) { - if (_cffi_start_python() == NULL) - return -1; - } - cffi_read_barrier(); - return 0; -} - -#undef cffi_compare_and_swap -#undef cffi_write_barrier -#undef cffi_read_barrier - -#ifdef __cplusplus -} -#endif diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/_imp_emulation.py b/.venv-docs/lib/python3.12/site-packages/cffi/_imp_emulation.py deleted file mode 100644 index 136abddd..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi/_imp_emulation.py +++ /dev/null @@ -1,83 +0,0 @@ - -try: - # this works on Python < 3.12 - from imp import * - -except ImportError: - # this is a limited emulation for Python >= 3.12. - # Note that this is used only for tests or for the old ffi.verify(). - # This is copied from the source code of Python 3.11. - - from _imp import (acquire_lock, release_lock, - is_builtin, is_frozen) - - from importlib._bootstrap import _load - - from importlib import machinery - import os - import sys - import tokenize - - SEARCH_ERROR = 0 - PY_SOURCE = 1 - PY_COMPILED = 2 - C_EXTENSION = 3 - PY_RESOURCE = 4 - PKG_DIRECTORY = 5 - C_BUILTIN = 6 - PY_FROZEN = 7 - PY_CODERESOURCE = 8 - IMP_HOOK = 9 - - def get_suffixes(): - extensions = [(s, 'rb', C_EXTENSION) - for s in machinery.EXTENSION_SUFFIXES] - source = [(s, 'r', PY_SOURCE) for s in machinery.SOURCE_SUFFIXES] - bytecode = [(s, 'rb', PY_COMPILED) for s in machinery.BYTECODE_SUFFIXES] - return extensions + source + bytecode - - def find_module(name, path=None): - if not isinstance(name, str): - raise TypeError("'name' must be a str, not {}".format(type(name))) - elif not isinstance(path, (type(None), list)): - # Backwards-compatibility - raise RuntimeError("'path' must be None or a list, " - "not {}".format(type(path))) - - if path is None: - if is_builtin(name): - return None, None, ('', '', C_BUILTIN) - elif is_frozen(name): - return None, None, ('', '', PY_FROZEN) - else: - path = sys.path - - for entry in path: - package_directory = os.path.join(entry, name) - for suffix in ['.py', machinery.BYTECODE_SUFFIXES[0]]: - package_file_name = '__init__' + suffix - file_path = os.path.join(package_directory, package_file_name) - if os.path.isfile(file_path): - return None, package_directory, ('', '', PKG_DIRECTORY) - for suffix, mode, type_ in get_suffixes(): - file_name = name + suffix - file_path = os.path.join(entry, file_name) - if os.path.isfile(file_path): - break - else: - continue - break # Break out of outer loop when breaking out of inner loop. - else: - raise ImportError(name, name=name) - - encoding = None - if 'b' not in mode: - with open(file_path, 'rb') as file: - encoding = tokenize.detect_encoding(file.readline)[0] - file = open(file_path, mode, encoding=encoding) - return file, file_path, (suffix, mode, type_) - - def load_dynamic(name, path, file=None): - loader = machinery.ExtensionFileLoader(name, path) - spec = machinery.ModuleSpec(name=name, loader=loader, origin=path) - return _load(spec) diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/_shimmed_dist_utils.py b/.venv-docs/lib/python3.12/site-packages/cffi/_shimmed_dist_utils.py deleted file mode 100644 index c3d23128..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi/_shimmed_dist_utils.py +++ /dev/null @@ -1,45 +0,0 @@ -""" -Temporary shim module to indirect the bits of distutils we need from setuptools/distutils while providing useful -error messages beyond `No module named 'distutils' on Python >= 3.12, or when setuptools' vendored distutils is broken. - -This is a compromise to avoid a hard-dep on setuptools for Python >= 3.12, since many users don't need runtime compilation support from CFFI. -""" -import sys - -try: - # import setuptools first; this is the most robust way to ensure its embedded distutils is available - # (the .pth shim should usually work, but this is even more robust) - import setuptools -except Exception as ex: - if sys.version_info >= (3, 12): - # Python 3.12 has no built-in distutils to fall back on, so any import problem is fatal - raise Exception("This CFFI feature requires setuptools on Python >= 3.12. The setuptools module is missing or non-functional.") from ex - - # silently ignore on older Pythons (support fallback to stdlib distutils where available) -else: - del setuptools - -try: - # bring in just the bits of distutils we need, whether they really came from setuptools or stdlib-embedded distutils - from distutils import log, sysconfig - from distutils.ccompiler import CCompiler - from distutils.command.build_ext import build_ext - from distutils.core import Distribution, Extension - from distutils.dir_util import mkpath - from distutils.errors import DistutilsSetupError, CompileError, LinkError - from distutils.log import set_threshold, set_verbosity - - if sys.platform == 'win32': - try: - # FUTURE: msvc9compiler module was removed in setuptools 74; consider removing, as it's only used by an ancient patch in `recompiler` - from distutils.msvc9compiler import MSVCCompiler - except ImportError: - MSVCCompiler = None -except Exception as ex: - if sys.version_info >= (3, 12): - raise Exception("This CFFI feature requires setuptools on Python >= 3.12. Please install the setuptools package.") from ex - - # anything older, just let the underlying distutils import error fly - raise Exception("This CFFI feature requires distutils. Please install the distutils or setuptools package.") from ex - -del sys diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/api.py b/.venv-docs/lib/python3.12/site-packages/cffi/api.py deleted file mode 100644 index 5a474f3d..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi/api.py +++ /dev/null @@ -1,967 +0,0 @@ -import sys, types -from .lock import allocate_lock -from .error import CDefError -from . import model - -try: - callable -except NameError: - # Python 3.1 - from collections import Callable - callable = lambda x: isinstance(x, Callable) - -try: - basestring -except NameError: - # Python 3.x - basestring = str - -_unspecified = object() - - - -class FFI(object): - r''' - The main top-level class that you instantiate once, or once per module. - - Example usage: - - ffi = FFI() - ffi.cdef(""" - int printf(const char *, ...); - """) - - C = ffi.dlopen(None) # standard library - -or- - C = ffi.verify() # use a C compiler: verify the decl above is right - - C.printf("hello, %s!\n", ffi.new("char[]", "world")) - ''' - - def __init__(self, backend=None): - """Create an FFI instance. The 'backend' argument is used to - select a non-default backend, mostly for tests. - """ - if backend is None: - # You need PyPy (>= 2.0 beta), or a CPython (>= 2.6) with - # _cffi_backend.so compiled. - import _cffi_backend as backend - from . import __version__ - if backend.__version__ != __version__: - # bad version! Try to be as explicit as possible. - if hasattr(backend, '__file__'): - # CPython - raise Exception("Version mismatch: this is the 'cffi' package version %s, located in %r. When we import the top-level '_cffi_backend' extension module, we get version %s, located in %r. The two versions should be equal; check your installation." % ( - __version__, __file__, - backend.__version__, backend.__file__)) - else: - # PyPy - raise Exception("Version mismatch: this is the 'cffi' package version %s, located in %r. This interpreter comes with a built-in '_cffi_backend' module, which is version %s. The two versions should be equal; check your installation." % ( - __version__, __file__, backend.__version__)) - # (If you insist you can also try to pass the option - # 'backend=backend_ctypes.CTypesBackend()', but don't - # rely on it! It's probably not going to work well.) - - from . import cparser - self._backend = backend - self._lock = allocate_lock() - self._parser = cparser.Parser() - self._cached_btypes = {} - self._parsed_types = types.ModuleType('parsed_types').__dict__ - self._new_types = types.ModuleType('new_types').__dict__ - self._function_caches = [] - self._libraries = [] - self._cdefsources = [] - self._included_ffis = [] - self._windows_unicode = None - self._init_once_cache = {} - self._cdef_version = None - self._embedding = None - self._typecache = model.get_typecache(backend) - if hasattr(backend, 'set_ffi'): - backend.set_ffi(self) - for name in list(backend.__dict__): - if name.startswith('RTLD_'): - setattr(self, name, getattr(backend, name)) - # - with self._lock: - self.BVoidP = self._get_cached_btype(model.voidp_type) - self.BCharA = self._get_cached_btype(model.char_array_type) - if isinstance(backend, types.ModuleType): - # _cffi_backend: attach these constants to the class - if not hasattr(FFI, 'NULL'): - FFI.NULL = self.cast(self.BVoidP, 0) - FFI.CData, FFI.CType = backend._get_types() - else: - # ctypes backend: attach these constants to the instance - self.NULL = self.cast(self.BVoidP, 0) - self.CData, self.CType = backend._get_types() - self.buffer = backend.buffer - - def cdef(self, csource, override=False, packed=False, pack=None): - """Parse the given C source. This registers all declared functions, - types, and global variables. The functions and global variables can - then be accessed via either 'ffi.dlopen()' or 'ffi.verify()'. - The types can be used in 'ffi.new()' and other functions. - If 'packed' is specified as True, all structs declared inside this - cdef are packed, i.e. laid out without any field alignment at all. - Alternatively, 'pack' can be a small integer, and requests for - alignment greater than that are ignored (pack=1 is equivalent to - packed=True). - """ - self._cdef(csource, override=override, packed=packed, pack=pack) - - def embedding_api(self, csource, packed=False, pack=None): - self._cdef(csource, packed=packed, pack=pack, dllexport=True) - if self._embedding is None: - self._embedding = '' - - def _cdef(self, csource, override=False, **options): - if not isinstance(csource, str): # unicode, on Python 2 - if not isinstance(csource, basestring): - raise TypeError("cdef() argument must be a string") - csource = csource.encode('ascii') - with self._lock: - self._cdef_version = object() - self._parser.parse(csource, override=override, **options) - self._cdefsources.append(csource) - if override: - for cache in self._function_caches: - cache.clear() - finishlist = self._parser._recomplete - if finishlist: - self._parser._recomplete = [] - for tp in finishlist: - tp.finish_backend_type(self, finishlist) - - def dlopen(self, name, flags=0): - """Load and return a dynamic library identified by 'name'. - The standard C library can be loaded by passing None. - Note that functions and types declared by 'ffi.cdef()' are not - linked to a particular library, just like C headers; in the - library we only look for the actual (untyped) symbols. - """ - if not (isinstance(name, basestring) or - name is None or - isinstance(name, self.CData)): - raise TypeError("dlopen(name): name must be a file name, None, " - "or an already-opened 'void *' handle") - with self._lock: - lib, function_cache = _make_ffi_library(self, name, flags) - self._function_caches.append(function_cache) - self._libraries.append(lib) - return lib - - def dlclose(self, lib): - """Close a library obtained with ffi.dlopen(). After this call, - access to functions or variables from the library will fail - (possibly with a segmentation fault). - """ - type(lib).__cffi_close__(lib) - - def _typeof_locked(self, cdecl): - # call me with the lock! - key = cdecl - if key in self._parsed_types: - return self._parsed_types[key] - # - if not isinstance(cdecl, str): # unicode, on Python 2 - cdecl = cdecl.encode('ascii') - # - type = self._parser.parse_type(cdecl) - really_a_function_type = type.is_raw_function - if really_a_function_type: - type = type.as_function_pointer() - btype = self._get_cached_btype(type) - result = btype, really_a_function_type - self._parsed_types[key] = result - return result - - def _typeof(self, cdecl, consider_function_as_funcptr=False): - # string -> ctype object - try: - result = self._parsed_types[cdecl] - except KeyError: - with self._lock: - result = self._typeof_locked(cdecl) - # - btype, really_a_function_type = result - if really_a_function_type and not consider_function_as_funcptr: - raise CDefError("the type %r is a function type, not a " - "pointer-to-function type" % (cdecl,)) - return btype - - def typeof(self, cdecl): - """Parse the C type given as a string and return the - corresponding object. - It can also be used on 'cdata' instance to get its C type. - """ - if isinstance(cdecl, basestring): - return self._typeof(cdecl) - if isinstance(cdecl, self.CData): - return self._backend.typeof(cdecl) - if isinstance(cdecl, types.BuiltinFunctionType): - res = _builtin_function_type(cdecl) - if res is not None: - return res - if (isinstance(cdecl, types.FunctionType) - and hasattr(cdecl, '_cffi_base_type')): - with self._lock: - return self._get_cached_btype(cdecl._cffi_base_type) - raise TypeError(type(cdecl)) - - def sizeof(self, cdecl): - """Return the size in bytes of the argument. It can be a - string naming a C type, or a 'cdata' instance. - """ - if isinstance(cdecl, basestring): - BType = self._typeof(cdecl) - return self._backend.sizeof(BType) - else: - return self._backend.sizeof(cdecl) - - def alignof(self, cdecl): - """Return the natural alignment size in bytes of the C type - given as a string. - """ - if isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl) - return self._backend.alignof(cdecl) - - def offsetof(self, cdecl, *fields_or_indexes): - """Return the offset of the named field inside the given - structure or array, which must be given as a C type name. - You can give several field names in case of nested structures. - You can also give numeric values which correspond to array - items, in case of an array type. - """ - if isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl) - return self._typeoffsetof(cdecl, *fields_or_indexes)[1] - - def new(self, cdecl, init=None): - """Allocate an instance according to the specified C type and - return a pointer to it. The specified C type must be either a - pointer or an array: ``new('X *')`` allocates an X and returns - a pointer to it, whereas ``new('X[n]')`` allocates an array of - n X'es and returns an array referencing it (which works - mostly like a pointer, like in C). You can also use - ``new('X[]', n)`` to allocate an array of a non-constant - length n. - - The memory is initialized following the rules of declaring a - global variable in C: by default it is zero-initialized, but - an explicit initializer can be given which can be used to - fill all or part of the memory. - - When the returned object goes out of scope, the memory - is freed. In other words the returned object has - ownership of the value of type 'cdecl' that it points to. This - means that the raw data can be used as long as this object is - kept alive, but must not be used for a longer time. Be careful - about that when copying the pointer to the memory somewhere - else, e.g. into another structure. - """ - if isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl) - return self._backend.newp(cdecl, init) - - def new_allocator(self, alloc=None, free=None, - should_clear_after_alloc=True): - """Return a new allocator, i.e. a function that behaves like ffi.new() - but uses the provided low-level 'alloc' and 'free' functions. - - 'alloc' is called with the size as argument. If it returns NULL, a - MemoryError is raised. 'free' is called with the result of 'alloc' - as argument. Both can be either Python function or directly C - functions. If 'free' is None, then no free function is called. - If both 'alloc' and 'free' are None, the default is used. - - If 'should_clear_after_alloc' is set to False, then the memory - returned by 'alloc' is assumed to be already cleared (or you are - fine with garbage); otherwise CFFI will clear it. - """ - compiled_ffi = self._backend.FFI() - allocator = compiled_ffi.new_allocator(alloc, free, - should_clear_after_alloc) - def allocate(cdecl, init=None): - if isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl) - return allocator(cdecl, init) - return allocate - - def cast(self, cdecl, source): - """Similar to a C cast: returns an instance of the named C - type initialized with the given 'source'. The source is - casted between integers or pointers of any type. - """ - if isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl) - return self._backend.cast(cdecl, source) - - def string(self, cdata, maxlen=-1): - """Return a Python string (or unicode string) from the 'cdata'. - If 'cdata' is a pointer or array of characters or bytes, returns - the null-terminated string. The returned string extends until - the first null character, or at most 'maxlen' characters. If - 'cdata' is an array then 'maxlen' defaults to its length. - - If 'cdata' is a pointer or array of wchar_t, returns a unicode - string following the same rules. - - If 'cdata' is a single character or byte or a wchar_t, returns - it as a string or unicode string. - - If 'cdata' is an enum, returns the value of the enumerator as a - string, or 'NUMBER' if the value is out of range. - """ - return self._backend.string(cdata, maxlen) - - def unpack(self, cdata, length): - """Unpack an array of C data of the given length, - returning a Python string/unicode/list. - - If 'cdata' is a pointer to 'char', returns a byte string. - It does not stop at the first null. This is equivalent to: - ffi.buffer(cdata, length)[:] - - If 'cdata' is a pointer to 'wchar_t', returns a unicode string. - 'length' is measured in wchar_t's; it is not the size in bytes. - - If 'cdata' is a pointer to anything else, returns a list of - 'length' items. This is a faster equivalent to: - [cdata[i] for i in range(length)] - """ - return self._backend.unpack(cdata, length) - - #def buffer(self, cdata, size=-1): - # """Return a read-write buffer object that references the raw C data - # pointed to by the given 'cdata'. The 'cdata' must be a pointer or - # an array. Can be passed to functions expecting a buffer, or directly - # manipulated with: - # - # buf[:] get a copy of it in a regular string, or - # buf[idx] as a single character - # buf[:] = ... - # buf[idx] = ... change the content - # """ - # note that 'buffer' is a type, set on this instance by __init__ - - def from_buffer(self, cdecl, python_buffer=_unspecified, - require_writable=False): - """Return a cdata of the given type pointing to the data of the - given Python object, which must support the buffer interface. - Note that this is not meant to be used on the built-in types - str or unicode (you can build 'char[]' arrays explicitly) - but only on objects containing large quantities of raw data - in some other format, like 'array.array' or numpy arrays. - - The first argument is optional and default to 'char[]'. - """ - if python_buffer is _unspecified: - cdecl, python_buffer = self.BCharA, cdecl - elif isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl) - return self._backend.from_buffer(cdecl, python_buffer, - require_writable) - - def memmove(self, dest, src, n): - """ffi.memmove(dest, src, n) copies n bytes of memory from src to dest. - - Like the C function memmove(), the memory areas may overlap; - apart from that it behaves like the C function memcpy(). - - 'src' can be any cdata ptr or array, or any Python buffer object. - 'dest' can be any cdata ptr or array, or a writable Python buffer - object. The size to copy, 'n', is always measured in bytes. - - Unlike other methods, this one supports all Python buffer including - byte strings and bytearrays---but it still does not support - non-contiguous buffers. - """ - return self._backend.memmove(dest, src, n) - - def callback(self, cdecl, python_callable=None, error=None, onerror=None): - """Return a callback object or a decorator making such a - callback object. 'cdecl' must name a C function pointer type. - The callback invokes the specified 'python_callable' (which may - be provided either directly or via a decorator). Important: the - callback object must be manually kept alive for as long as the - callback may be invoked from the C level. - """ - def callback_decorator_wrap(python_callable): - if not callable(python_callable): - raise TypeError("the 'python_callable' argument " - "is not callable") - return self._backend.callback(cdecl, python_callable, - error, onerror) - if isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl, consider_function_as_funcptr=True) - if python_callable is None: - return callback_decorator_wrap # decorator mode - else: - return callback_decorator_wrap(python_callable) # direct mode - - def getctype(self, cdecl, replace_with=''): - """Return a string giving the C type 'cdecl', which may be itself - a string or a object. If 'replace_with' is given, it gives - extra text to append (or insert for more complicated C types), like - a variable name, or '*' to get actually the C type 'pointer-to-cdecl'. - """ - if isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl) - replace_with = replace_with.strip() - if (replace_with.startswith('*') - and '&[' in self._backend.getcname(cdecl, '&')): - replace_with = '(%s)' % replace_with - elif replace_with and not replace_with[0] in '[(': - replace_with = ' ' + replace_with - return self._backend.getcname(cdecl, replace_with) - - def gc(self, cdata, destructor, size=0): - """Return a new cdata object that points to the same - data. Later, when this new cdata object is garbage-collected, - 'destructor(old_cdata_object)' will be called. - - The optional 'size' gives an estimate of the size, used to - trigger the garbage collection more eagerly. So far only used - on PyPy. It tells the GC that the returned object keeps alive - roughly 'size' bytes of external memory. - """ - return self._backend.gcp(cdata, destructor, size) - - def _get_cached_btype(self, type): - assert self._lock.acquire(False) is False - # call me with the lock! - try: - BType = self._cached_btypes[type] - except KeyError: - finishlist = [] - BType = type.get_cached_btype(self, finishlist) - for type in finishlist: - type.finish_backend_type(self, finishlist) - return BType - - def verify(self, source='', tmpdir=None, **kwargs): - """Verify that the current ffi signatures compile on this - machine, and return a dynamic library object. The dynamic - library can be used to call functions and access global - variables declared in this 'ffi'. The library is compiled - by the C compiler: it gives you C-level API compatibility - (including calling macros). This is unlike 'ffi.dlopen()', - which requires binary compatibility in the signatures. - """ - from .verifier import Verifier, _caller_dir_pycache - # - # If set_unicode(True) was called, insert the UNICODE and - # _UNICODE macro declarations - if self._windows_unicode: - self._apply_windows_unicode(kwargs) - # - # Set the tmpdir here, and not in Verifier.__init__: it picks - # up the caller's directory, which we want to be the caller of - # ffi.verify(), as opposed to the caller of Veritier(). - tmpdir = tmpdir or _caller_dir_pycache() - # - # Make a Verifier() and use it to load the library. - self.verifier = Verifier(self, source, tmpdir, **kwargs) - lib = self.verifier.load_library() - # - # Save the loaded library for keep-alive purposes, even - # if the caller doesn't keep it alive itself (it should). - self._libraries.append(lib) - return lib - - def _get_errno(self): - return self._backend.get_errno() - def _set_errno(self, errno): - self._backend.set_errno(errno) - errno = property(_get_errno, _set_errno, None, - "the value of 'errno' from/to the C calls") - - def getwinerror(self, code=-1): - return self._backend.getwinerror(code) - - def _pointer_to(self, ctype): - with self._lock: - return model.pointer_cache(self, ctype) - - def addressof(self, cdata, *fields_or_indexes): - """Return the address of a . - If 'fields_or_indexes' are given, returns the address of that - field or array item in the structure or array, recursively in - case of nested structures. - """ - try: - ctype = self._backend.typeof(cdata) - except TypeError: - if '__addressof__' in type(cdata).__dict__: - return type(cdata).__addressof__(cdata, *fields_or_indexes) - raise - if fields_or_indexes: - ctype, offset = self._typeoffsetof(ctype, *fields_or_indexes) - else: - if ctype.kind == "pointer": - raise TypeError("addressof(pointer)") - offset = 0 - ctypeptr = self._pointer_to(ctype) - return self._backend.rawaddressof(ctypeptr, cdata, offset) - - def _typeoffsetof(self, ctype, field_or_index, *fields_or_indexes): - ctype, offset = self._backend.typeoffsetof(ctype, field_or_index) - for field1 in fields_or_indexes: - ctype, offset1 = self._backend.typeoffsetof(ctype, field1, 1) - offset += offset1 - return ctype, offset - - def include(self, ffi_to_include): - """Includes the typedefs, structs, unions and enums defined - in another FFI instance. Usage is similar to a #include in C, - where a part of the program might include types defined in - another part for its own usage. Note that the include() - method has no effect on functions, constants and global - variables, which must anyway be accessed directly from the - lib object returned by the original FFI instance. - """ - if not isinstance(ffi_to_include, FFI): - raise TypeError("ffi.include() expects an argument that is also of" - " type cffi.FFI, not %r" % ( - type(ffi_to_include).__name__,)) - if ffi_to_include is self: - raise ValueError("self.include(self)") - with ffi_to_include._lock: - with self._lock: - self._parser.include(ffi_to_include._parser) - self._cdefsources.append('[') - self._cdefsources.extend(ffi_to_include._cdefsources) - self._cdefsources.append(']') - self._included_ffis.append(ffi_to_include) - - def new_handle(self, x): - return self._backend.newp_handle(self.BVoidP, x) - - def from_handle(self, x): - return self._backend.from_handle(x) - - def release(self, x): - self._backend.release(x) - - def set_unicode(self, enabled_flag): - """Windows: if 'enabled_flag' is True, enable the UNICODE and - _UNICODE defines in C, and declare the types like TCHAR and LPTCSTR - to be (pointers to) wchar_t. If 'enabled_flag' is False, - declare these types to be (pointers to) plain 8-bit characters. - This is mostly for backward compatibility; you usually want True. - """ - if self._windows_unicode is not None: - raise ValueError("set_unicode() can only be called once") - enabled_flag = bool(enabled_flag) - if enabled_flag: - self.cdef("typedef wchar_t TBYTE;" - "typedef wchar_t TCHAR;" - "typedef const wchar_t *LPCTSTR;" - "typedef const wchar_t *PCTSTR;" - "typedef wchar_t *LPTSTR;" - "typedef wchar_t *PTSTR;" - "typedef TBYTE *PTBYTE;" - "typedef TCHAR *PTCHAR;") - else: - self.cdef("typedef char TBYTE;" - "typedef char TCHAR;" - "typedef const char *LPCTSTR;" - "typedef const char *PCTSTR;" - "typedef char *LPTSTR;" - "typedef char *PTSTR;" - "typedef TBYTE *PTBYTE;" - "typedef TCHAR *PTCHAR;") - self._windows_unicode = enabled_flag - - def _apply_windows_unicode(self, kwds): - defmacros = kwds.get('define_macros', ()) - if not isinstance(defmacros, (list, tuple)): - raise TypeError("'define_macros' must be a list or tuple") - defmacros = list(defmacros) + [('UNICODE', '1'), - ('_UNICODE', '1')] - kwds['define_macros'] = defmacros - - def _apply_embedding_fix(self, kwds): - # must include an argument like "-lpython2.7" for the compiler - def ensure(key, value): - lst = kwds.setdefault(key, []) - if value not in lst: - lst.append(value) - # - if '__pypy__' in sys.builtin_module_names: - import os - if sys.platform == "win32": - # we need 'libpypy-c.lib'. Current distributions of - # pypy (>= 4.1) contain it as 'libs/python27.lib'. - pythonlib = "python{0[0]}{0[1]}".format(sys.version_info) - if hasattr(sys, 'prefix'): - ensure('library_dirs', os.path.join(sys.prefix, 'libs')) - else: - # we need 'libpypy-c.{so,dylib}', which should be by - # default located in 'sys.prefix/bin' for installed - # systems. - if sys.version_info < (3,): - pythonlib = "pypy-c" - else: - pythonlib = "pypy3-c" - if hasattr(sys, 'prefix'): - ensure('library_dirs', os.path.join(sys.prefix, 'bin')) - # On uninstalled pypy's, the libpypy-c is typically found in - # .../pypy/goal/. - if hasattr(sys, 'prefix'): - ensure('library_dirs', os.path.join(sys.prefix, 'pypy', 'goal')) - else: - if sys.platform == "win32": - template = "python%d%d" - if hasattr(sys, 'gettotalrefcount'): - template += '_d' - else: - try: - import sysconfig - except ImportError: # 2.6 - from cffi._shimmed_dist_utils import sysconfig - template = "python%d.%d" - if sysconfig.get_config_var('DEBUG_EXT'): - template += sysconfig.get_config_var('DEBUG_EXT') - pythonlib = (template % - (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) - if hasattr(sys, 'abiflags'): - pythonlib += sys.abiflags - ensure('libraries', pythonlib) - if sys.platform == "win32": - ensure('extra_link_args', '/MANIFEST') - - def set_source(self, module_name, source, source_extension='.c', **kwds): - import os - if hasattr(self, '_assigned_source'): - raise ValueError("set_source() cannot be called several times " - "per ffi object") - if not isinstance(module_name, basestring): - raise TypeError("'module_name' must be a string") - if os.sep in module_name or (os.altsep and os.altsep in module_name): - raise ValueError("'module_name' must not contain '/': use a dotted " - "name to make a 'package.module' location") - self._assigned_source = (str(module_name), source, - source_extension, kwds) - - def set_source_pkgconfig(self, module_name, pkgconfig_libs, source, - source_extension='.c', **kwds): - from . import pkgconfig - if not isinstance(pkgconfig_libs, list): - raise TypeError("the pkgconfig_libs argument must be a list " - "of package names") - kwds2 = pkgconfig.flags_from_pkgconfig(pkgconfig_libs) - pkgconfig.merge_flags(kwds, kwds2) - self.set_source(module_name, source, source_extension, **kwds) - - def distutils_extension(self, tmpdir='build', verbose=True): - from cffi._shimmed_dist_utils import mkpath - from .recompiler import recompile - # - if not hasattr(self, '_assigned_source'): - if hasattr(self, 'verifier'): # fallback, 'tmpdir' ignored - return self.verifier.get_extension() - raise ValueError("set_source() must be called before" - " distutils_extension()") - module_name, source, source_extension, kwds = self._assigned_source - if source is None: - raise TypeError("distutils_extension() is only for C extension " - "modules, not for dlopen()-style pure Python " - "modules") - mkpath(tmpdir) - ext, updated = recompile(self, module_name, - source, tmpdir=tmpdir, extradir=tmpdir, - source_extension=source_extension, - call_c_compiler=False, **kwds) - if verbose: - if updated: - sys.stderr.write("regenerated: %r\n" % (ext.sources[0],)) - else: - sys.stderr.write("not modified: %r\n" % (ext.sources[0],)) - return ext - - def emit_c_code(self, filename): - from .recompiler import recompile - # - if not hasattr(self, '_assigned_source'): - raise ValueError("set_source() must be called before emit_c_code()") - module_name, source, source_extension, kwds = self._assigned_source - if source is None: - raise TypeError("emit_c_code() is only for C extension modules, " - "not for dlopen()-style pure Python modules") - recompile(self, module_name, source, - c_file=filename, call_c_compiler=False, - uses_ffiplatform=False, **kwds) - - def emit_python_code(self, filename): - from .recompiler import recompile - # - if not hasattr(self, '_assigned_source'): - raise ValueError("set_source() must be called before emit_c_code()") - module_name, source, source_extension, kwds = self._assigned_source - if source is not None: - raise TypeError("emit_python_code() is only for dlopen()-style " - "pure Python modules, not for C extension modules") - recompile(self, module_name, source, - c_file=filename, call_c_compiler=False, - uses_ffiplatform=False, **kwds) - - def compile(self, tmpdir='.', verbose=0, target=None, debug=None): - """The 'target' argument gives the final file name of the - compiled DLL. Use '*' to force distutils' choice, suitable for - regular CPython C API modules. Use a file name ending in '.*' - to ask for the system's default extension for dynamic libraries - (.so/.dll/.dylib). - - The default is '*' when building a non-embedded C API extension, - and (module_name + '.*') when building an embedded library. - """ - from .recompiler import recompile - # - if not hasattr(self, '_assigned_source'): - raise ValueError("set_source() must be called before compile()") - module_name, source, source_extension, kwds = self._assigned_source - return recompile(self, module_name, source, tmpdir=tmpdir, - target=target, source_extension=source_extension, - compiler_verbose=verbose, debug=debug, **kwds) - - def init_once(self, func, tag): - # Read _init_once_cache[tag], which is either (False, lock) if - # we're calling the function now in some thread, or (True, result). - # Don't call setdefault() in most cases, to avoid allocating and - # immediately freeing a lock; but still use setdefaut() to avoid - # races. - try: - x = self._init_once_cache[tag] - except KeyError: - x = self._init_once_cache.setdefault(tag, (False, allocate_lock())) - # Common case: we got (True, result), so we return the result. - if x[0]: - return x[1] - # Else, it's a lock. Acquire it to serialize the following tests. - with x[1]: - # Read again from _init_once_cache the current status. - x = self._init_once_cache[tag] - if x[0]: - return x[1] - # Call the function and store the result back. - result = func() - self._init_once_cache[tag] = (True, result) - return result - - def embedding_init_code(self, pysource): - if self._embedding: - raise ValueError("embedding_init_code() can only be called once") - # fix 'pysource' before it gets dumped into the C file: - # - remove empty lines at the beginning, so it starts at "line 1" - # - dedent, if all non-empty lines are indented - # - check for SyntaxErrors - import re - match = re.match(r'\s*\n', pysource) - if match: - pysource = pysource[match.end():] - lines = pysource.splitlines() or [''] - prefix = re.match(r'\s*', lines[0]).group() - for i in range(1, len(lines)): - line = lines[i] - if line.rstrip(): - while not line.startswith(prefix): - prefix = prefix[:-1] - i = len(prefix) - lines = [line[i:]+'\n' for line in lines] - pysource = ''.join(lines) - # - compile(pysource, "cffi_init", "exec") - # - self._embedding = pysource - - def def_extern(self, *args, **kwds): - raise ValueError("ffi.def_extern() is only available on API-mode FFI " - "objects") - - def list_types(self): - """Returns the user type names known to this FFI instance. - This returns a tuple containing three lists of names: - (typedef_names, names_of_structs, names_of_unions) - """ - typedefs = [] - structs = [] - unions = [] - for key in self._parser._declarations: - if key.startswith('typedef '): - typedefs.append(key[8:]) - elif key.startswith('struct '): - structs.append(key[7:]) - elif key.startswith('union '): - unions.append(key[6:]) - typedefs.sort() - structs.sort() - unions.sort() - return (typedefs, structs, unions) - - -def _load_backend_lib(backend, name, flags): - import os - if not isinstance(name, basestring): - if sys.platform != "win32" or name is not None: - return backend.load_library(name, flags) - name = "c" # Windows: load_library(None) fails, but this works - # on Python 2 (backward compatibility hack only) - first_error = None - if '.' in name or '/' in name or os.sep in name: - try: - return backend.load_library(name, flags) - except OSError as e: - first_error = e - import ctypes.util - path = ctypes.util.find_library(name) - if path is None: - if name == "c" and sys.platform == "win32" and sys.version_info >= (3,): - raise OSError("dlopen(None) cannot work on Windows for Python 3 " - "(see http://bugs.python.org/issue23606)") - msg = ("ctypes.util.find_library() did not manage " - "to locate a library called %r" % (name,)) - if first_error is not None: - msg = "%s. Additionally, %s" % (first_error, msg) - raise OSError(msg) - return backend.load_library(path, flags) - -def _make_ffi_library(ffi, libname, flags): - backend = ffi._backend - backendlib = _load_backend_lib(backend, libname, flags) - # - def accessor_function(name): - key = 'function ' + name - tp, _ = ffi._parser._declarations[key] - BType = ffi._get_cached_btype(tp) - value = backendlib.load_function(BType, name) - library.__dict__[name] = value - # - def accessor_variable(name): - key = 'variable ' + name - tp, _ = ffi._parser._declarations[key] - BType = ffi._get_cached_btype(tp) - read_variable = backendlib.read_variable - write_variable = backendlib.write_variable - setattr(FFILibrary, name, property( - lambda self: read_variable(BType, name), - lambda self, value: write_variable(BType, name, value))) - # - def addressof_var(name): - try: - return addr_variables[name] - except KeyError: - with ffi._lock: - if name not in addr_variables: - key = 'variable ' + name - tp, _ = ffi._parser._declarations[key] - BType = ffi._get_cached_btype(tp) - if BType.kind != 'array': - BType = model.pointer_cache(ffi, BType) - p = backendlib.load_function(BType, name) - addr_variables[name] = p - return addr_variables[name] - # - def accessor_constant(name): - raise NotImplementedError("non-integer constant '%s' cannot be " - "accessed from a dlopen() library" % (name,)) - # - def accessor_int_constant(name): - library.__dict__[name] = ffi._parser._int_constants[name] - # - accessors = {} - accessors_version = [False] - addr_variables = {} - # - def update_accessors(): - if accessors_version[0] is ffi._cdef_version: - return - # - for key, (tp, _) in ffi._parser._declarations.items(): - if not isinstance(tp, model.EnumType): - tag, name = key.split(' ', 1) - if tag == 'function': - accessors[name] = accessor_function - elif tag == 'variable': - accessors[name] = accessor_variable - elif tag == 'constant': - accessors[name] = accessor_constant - else: - for i, enumname in enumerate(tp.enumerators): - def accessor_enum(name, tp=tp, i=i): - tp.check_not_partial() - library.__dict__[name] = tp.enumvalues[i] - accessors[enumname] = accessor_enum - for name in ffi._parser._int_constants: - accessors.setdefault(name, accessor_int_constant) - accessors_version[0] = ffi._cdef_version - # - def make_accessor(name): - with ffi._lock: - if name in library.__dict__ or name in FFILibrary.__dict__: - return # added by another thread while waiting for the lock - if name not in accessors: - update_accessors() - if name not in accessors: - raise AttributeError(name) - accessors[name](name) - # - class FFILibrary(object): - def __getattr__(self, name): - make_accessor(name) - return getattr(self, name) - def __setattr__(self, name, value): - try: - property = getattr(self.__class__, name) - except AttributeError: - make_accessor(name) - setattr(self, name, value) - else: - property.__set__(self, value) - def __dir__(self): - with ffi._lock: - update_accessors() - return accessors.keys() - def __addressof__(self, name): - if name in library.__dict__: - return library.__dict__[name] - if name in FFILibrary.__dict__: - return addressof_var(name) - make_accessor(name) - if name in library.__dict__: - return library.__dict__[name] - if name in FFILibrary.__dict__: - return addressof_var(name) - raise AttributeError("cffi library has no function or " - "global variable named '%s'" % (name,)) - def __cffi_close__(self): - backendlib.close_lib() - self.__dict__.clear() - # - if isinstance(libname, basestring): - try: - if not isinstance(libname, str): # unicode, on Python 2 - libname = libname.encode('utf-8') - FFILibrary.__name__ = 'FFILibrary_%s' % libname - except UnicodeError: - pass - library = FFILibrary() - return library, library.__dict__ - -def _builtin_function_type(func): - # a hack to make at least ffi.typeof(builtin_function) work, - # if the builtin function was obtained by 'vengine_cpy'. - import sys - try: - module = sys.modules[func.__module__] - ffi = module._cffi_original_ffi - types_of_builtin_funcs = module._cffi_types_of_builtin_funcs - tp = types_of_builtin_funcs[func] - except (KeyError, AttributeError, TypeError): - return None - else: - with ffi._lock: - return ffi._get_cached_btype(tp) diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/backend_ctypes.py b/.venv-docs/lib/python3.12/site-packages/cffi/backend_ctypes.py deleted file mode 100644 index e7956a79..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi/backend_ctypes.py +++ /dev/null @@ -1,1121 +0,0 @@ -import ctypes, ctypes.util, operator, sys -from . import model - -if sys.version_info < (3,): - bytechr = chr -else: - unicode = str - long = int - xrange = range - bytechr = lambda num: bytes([num]) - -class CTypesType(type): - pass - -class CTypesData(object): - __metaclass__ = CTypesType - __slots__ = ['__weakref__'] - __name__ = '' - - def __init__(self, *args): - raise TypeError("cannot instantiate %r" % (self.__class__,)) - - @classmethod - def _newp(cls, init): - raise TypeError("expected a pointer or array ctype, got '%s'" - % (cls._get_c_name(),)) - - @staticmethod - def _to_ctypes(value): - raise TypeError - - @classmethod - def _arg_to_ctypes(cls, *value): - try: - ctype = cls._ctype - except AttributeError: - raise TypeError("cannot create an instance of %r" % (cls,)) - if value: - res = cls._to_ctypes(*value) - if not isinstance(res, ctype): - res = cls._ctype(res) - else: - res = cls._ctype() - return res - - @classmethod - def _create_ctype_obj(cls, init): - if init is None: - return cls._arg_to_ctypes() - else: - return cls._arg_to_ctypes(init) - - @staticmethod - def _from_ctypes(ctypes_value): - raise TypeError - - @classmethod - def _get_c_name(cls, replace_with=''): - return cls._reftypename.replace(' &', replace_with) - - @classmethod - def _fix_class(cls): - cls.__name__ = 'CData<%s>' % (cls._get_c_name(),) - cls.__qualname__ = 'CData<%s>' % (cls._get_c_name(),) - cls.__module__ = 'ffi' - - def _get_own_repr(self): - raise NotImplementedError - - def _addr_repr(self, address): - if address == 0: - return 'NULL' - else: - if address < 0: - address += 1 << (8*ctypes.sizeof(ctypes.c_void_p)) - return '0x%x' % address - - def __repr__(self, c_name=None): - own = self._get_own_repr() - return '' % (c_name or self._get_c_name(), own) - - def _convert_to_address(self, BClass): - if BClass is None: - raise TypeError("cannot convert %r to an address" % ( - self._get_c_name(),)) - else: - raise TypeError("cannot convert %r to %r" % ( - self._get_c_name(), BClass._get_c_name())) - - @classmethod - def _get_size(cls): - return ctypes.sizeof(cls._ctype) - - def _get_size_of_instance(self): - return ctypes.sizeof(self._ctype) - - @classmethod - def _cast_from(cls, source): - raise TypeError("cannot cast to %r" % (cls._get_c_name(),)) - - def _cast_to_integer(self): - return self._convert_to_address(None) - - @classmethod - def _alignment(cls): - return ctypes.alignment(cls._ctype) - - def __iter__(self): - raise TypeError("cdata %r does not support iteration" % ( - self._get_c_name()),) - - def _make_cmp(name): - cmpfunc = getattr(operator, name) - def cmp(self, other): - v_is_ptr = not isinstance(self, CTypesGenericPrimitive) - w_is_ptr = (isinstance(other, CTypesData) and - not isinstance(other, CTypesGenericPrimitive)) - if v_is_ptr and w_is_ptr: - return cmpfunc(self._convert_to_address(None), - other._convert_to_address(None)) - elif v_is_ptr or w_is_ptr: - return NotImplemented - else: - if isinstance(self, CTypesGenericPrimitive): - self = self._value - if isinstance(other, CTypesGenericPrimitive): - other = other._value - return cmpfunc(self, other) - cmp.func_name = name - return cmp - - __eq__ = _make_cmp('__eq__') - __ne__ = _make_cmp('__ne__') - __lt__ = _make_cmp('__lt__') - __le__ = _make_cmp('__le__') - __gt__ = _make_cmp('__gt__') - __ge__ = _make_cmp('__ge__') - - def __hash__(self): - return hash(self._convert_to_address(None)) - - def _to_string(self, maxlen): - raise TypeError("string(): %r" % (self,)) - - -class CTypesGenericPrimitive(CTypesData): - __slots__ = [] - - def __hash__(self): - return hash(self._value) - - def _get_own_repr(self): - return repr(self._from_ctypes(self._value)) - - -class CTypesGenericArray(CTypesData): - __slots__ = [] - - @classmethod - def _newp(cls, init): - return cls(init) - - def __iter__(self): - for i in xrange(len(self)): - yield self[i] - - def _get_own_repr(self): - return self._addr_repr(ctypes.addressof(self._blob)) - - -class CTypesGenericPtr(CTypesData): - __slots__ = ['_address', '_as_ctype_ptr'] - _automatic_casts = False - kind = "pointer" - - @classmethod - def _newp(cls, init): - return cls(init) - - @classmethod - def _cast_from(cls, source): - if source is None: - address = 0 - elif isinstance(source, CTypesData): - address = source._cast_to_integer() - elif isinstance(source, (int, long)): - address = source - else: - raise TypeError("bad type for cast to %r: %r" % - (cls, type(source).__name__)) - return cls._new_pointer_at(address) - - @classmethod - def _new_pointer_at(cls, address): - self = cls.__new__(cls) - self._address = address - self._as_ctype_ptr = ctypes.cast(address, cls._ctype) - return self - - def _get_own_repr(self): - try: - return self._addr_repr(self._address) - except AttributeError: - return '???' - - def _cast_to_integer(self): - return self._address - - def __nonzero__(self): - return bool(self._address) - __bool__ = __nonzero__ - - @classmethod - def _to_ctypes(cls, value): - if not isinstance(value, CTypesData): - raise TypeError("unexpected %s object" % type(value).__name__) - address = value._convert_to_address(cls) - return ctypes.cast(address, cls._ctype) - - @classmethod - def _from_ctypes(cls, ctypes_ptr): - address = ctypes.cast(ctypes_ptr, ctypes.c_void_p).value or 0 - return cls._new_pointer_at(address) - - @classmethod - def _initialize(cls, ctypes_ptr, value): - if value: - ctypes_ptr.contents = cls._to_ctypes(value).contents - - def _convert_to_address(self, BClass): - if (BClass in (self.__class__, None) or BClass._automatic_casts - or self._automatic_casts): - return self._address - else: - return CTypesData._convert_to_address(self, BClass) - - -class CTypesBaseStructOrUnion(CTypesData): - __slots__ = ['_blob'] - - @classmethod - def _create_ctype_obj(cls, init): - # may be overridden - raise TypeError("cannot instantiate opaque type %s" % (cls,)) - - def _get_own_repr(self): - return self._addr_repr(ctypes.addressof(self._blob)) - - @classmethod - def _offsetof(cls, fieldname): - return getattr(cls._ctype, fieldname).offset - - def _convert_to_address(self, BClass): - if getattr(BClass, '_BItem', None) is self.__class__: - return ctypes.addressof(self._blob) - else: - return CTypesData._convert_to_address(self, BClass) - - @classmethod - def _from_ctypes(cls, ctypes_struct_or_union): - self = cls.__new__(cls) - self._blob = ctypes_struct_or_union - return self - - @classmethod - def _to_ctypes(cls, value): - return value._blob - - def __repr__(self, c_name=None): - return CTypesData.__repr__(self, c_name or self._get_c_name(' &')) - - -class CTypesBackend(object): - - PRIMITIVE_TYPES = { - 'char': ctypes.c_char, - 'short': ctypes.c_short, - 'int': ctypes.c_int, - 'long': ctypes.c_long, - 'long long': ctypes.c_longlong, - 'signed char': ctypes.c_byte, - 'unsigned char': ctypes.c_ubyte, - 'unsigned short': ctypes.c_ushort, - 'unsigned int': ctypes.c_uint, - 'unsigned long': ctypes.c_ulong, - 'unsigned long long': ctypes.c_ulonglong, - 'float': ctypes.c_float, - 'double': ctypes.c_double, - '_Bool': ctypes.c_bool, - } - - for _name in ['unsigned long long', 'unsigned long', - 'unsigned int', 'unsigned short', 'unsigned char']: - _size = ctypes.sizeof(PRIMITIVE_TYPES[_name]) - PRIMITIVE_TYPES['uint%d_t' % (8*_size)] = PRIMITIVE_TYPES[_name] - if _size == ctypes.sizeof(ctypes.c_void_p): - PRIMITIVE_TYPES['uintptr_t'] = PRIMITIVE_TYPES[_name] - if _size == ctypes.sizeof(ctypes.c_size_t): - PRIMITIVE_TYPES['size_t'] = PRIMITIVE_TYPES[_name] - - for _name in ['long long', 'long', 'int', 'short', 'signed char']: - _size = ctypes.sizeof(PRIMITIVE_TYPES[_name]) - PRIMITIVE_TYPES['int%d_t' % (8*_size)] = PRIMITIVE_TYPES[_name] - if _size == ctypes.sizeof(ctypes.c_void_p): - PRIMITIVE_TYPES['intptr_t'] = PRIMITIVE_TYPES[_name] - PRIMITIVE_TYPES['ptrdiff_t'] = PRIMITIVE_TYPES[_name] - if _size == ctypes.sizeof(ctypes.c_size_t): - PRIMITIVE_TYPES['ssize_t'] = PRIMITIVE_TYPES[_name] - - - def __init__(self): - self.RTLD_LAZY = 0 # not supported anyway by ctypes - self.RTLD_NOW = 0 - self.RTLD_GLOBAL = ctypes.RTLD_GLOBAL - self.RTLD_LOCAL = ctypes.RTLD_LOCAL - - def set_ffi(self, ffi): - self.ffi = ffi - - def _get_types(self): - return CTypesData, CTypesType - - def load_library(self, path, flags=0): - cdll = ctypes.CDLL(path, flags) - return CTypesLibrary(self, cdll) - - def new_void_type(self): - class CTypesVoid(CTypesData): - __slots__ = [] - _reftypename = 'void &' - @staticmethod - def _from_ctypes(novalue): - return None - @staticmethod - def _to_ctypes(novalue): - if novalue is not None: - raise TypeError("None expected, got %s object" % - (type(novalue).__name__,)) - return None - CTypesVoid._fix_class() - return CTypesVoid - - def new_primitive_type(self, name): - if name == 'wchar_t': - raise NotImplementedError(name) - ctype = self.PRIMITIVE_TYPES[name] - if name == 'char': - kind = 'char' - elif name in ('float', 'double'): - kind = 'float' - else: - if name in ('signed char', 'unsigned char'): - kind = 'byte' - elif name == '_Bool': - kind = 'bool' - else: - kind = 'int' - is_signed = (ctype(-1).value == -1) - # - def _cast_source_to_int(source): - if isinstance(source, (int, long, float)): - source = int(source) - elif isinstance(source, CTypesData): - source = source._cast_to_integer() - elif isinstance(source, bytes): - source = ord(source) - elif source is None: - source = 0 - else: - raise TypeError("bad type for cast to %r: %r" % - (CTypesPrimitive, type(source).__name__)) - return source - # - kind1 = kind - class CTypesPrimitive(CTypesGenericPrimitive): - __slots__ = ['_value'] - _ctype = ctype - _reftypename = '%s &' % name - kind = kind1 - - def __init__(self, value): - self._value = value - - @staticmethod - def _create_ctype_obj(init): - if init is None: - return ctype() - return ctype(CTypesPrimitive._to_ctypes(init)) - - if kind == 'int' or kind == 'byte': - @classmethod - def _cast_from(cls, source): - source = _cast_source_to_int(source) - source = ctype(source).value # cast within range - return cls(source) - def __int__(self): - return self._value - - if kind == 'bool': - @classmethod - def _cast_from(cls, source): - if not isinstance(source, (int, long, float)): - source = _cast_source_to_int(source) - return cls(bool(source)) - def __int__(self): - return int(self._value) - - if kind == 'char': - @classmethod - def _cast_from(cls, source): - source = _cast_source_to_int(source) - source = bytechr(source & 0xFF) - return cls(source) - def __int__(self): - return ord(self._value) - - if kind == 'float': - @classmethod - def _cast_from(cls, source): - if isinstance(source, float): - pass - elif isinstance(source, CTypesGenericPrimitive): - if hasattr(source, '__float__'): - source = float(source) - else: - source = int(source) - else: - source = _cast_source_to_int(source) - source = ctype(source).value # fix precision - return cls(source) - def __int__(self): - return int(self._value) - def __float__(self): - return self._value - - _cast_to_integer = __int__ - - if kind == 'int' or kind == 'byte' or kind == 'bool': - @staticmethod - def _to_ctypes(x): - if not isinstance(x, (int, long)): - if isinstance(x, CTypesData): - x = int(x) - else: - raise TypeError("integer expected, got %s" % - type(x).__name__) - if ctype(x).value != x: - if not is_signed and x < 0: - raise OverflowError("%s: negative integer" % name) - else: - raise OverflowError("%s: integer out of bounds" - % name) - return x - - if kind == 'char': - @staticmethod - def _to_ctypes(x): - if isinstance(x, bytes) and len(x) == 1: - return x - if isinstance(x, CTypesPrimitive): # > - return x._value - raise TypeError("character expected, got %s" % - type(x).__name__) - def __nonzero__(self): - return ord(self._value) != 0 - else: - def __nonzero__(self): - return self._value != 0 - __bool__ = __nonzero__ - - if kind == 'float': - @staticmethod - def _to_ctypes(x): - if not isinstance(x, (int, long, float, CTypesData)): - raise TypeError("float expected, got %s" % - type(x).__name__) - return ctype(x).value - - @staticmethod - def _from_ctypes(value): - return getattr(value, 'value', value) - - @staticmethod - def _initialize(blob, init): - blob.value = CTypesPrimitive._to_ctypes(init) - - if kind == 'char': - def _to_string(self, maxlen): - return self._value - if kind == 'byte': - def _to_string(self, maxlen): - return chr(self._value & 0xff) - # - CTypesPrimitive._fix_class() - return CTypesPrimitive - - def new_pointer_type(self, BItem): - getbtype = self.ffi._get_cached_btype - if BItem is getbtype(model.PrimitiveType('char')): - kind = 'charp' - elif BItem in (getbtype(model.PrimitiveType('signed char')), - getbtype(model.PrimitiveType('unsigned char'))): - kind = 'bytep' - elif BItem is getbtype(model.void_type): - kind = 'voidp' - else: - kind = 'generic' - # - class CTypesPtr(CTypesGenericPtr): - __slots__ = ['_own'] - if kind == 'charp': - __slots__ += ['__as_strbuf'] - _BItem = BItem - if hasattr(BItem, '_ctype'): - _ctype = ctypes.POINTER(BItem._ctype) - _bitem_size = ctypes.sizeof(BItem._ctype) - else: - _ctype = ctypes.c_void_p - if issubclass(BItem, CTypesGenericArray): - _reftypename = BItem._get_c_name('(* &)') - else: - _reftypename = BItem._get_c_name(' * &') - - def __init__(self, init): - ctypeobj = BItem._create_ctype_obj(init) - if kind == 'charp': - self.__as_strbuf = ctypes.create_string_buffer( - ctypeobj.value + b'\x00') - self._as_ctype_ptr = ctypes.cast( - self.__as_strbuf, self._ctype) - else: - self._as_ctype_ptr = ctypes.pointer(ctypeobj) - self._address = ctypes.cast(self._as_ctype_ptr, - ctypes.c_void_p).value - self._own = True - - def __add__(self, other): - if isinstance(other, (int, long)): - return self._new_pointer_at(self._address + - other * self._bitem_size) - else: - return NotImplemented - - def __sub__(self, other): - if isinstance(other, (int, long)): - return self._new_pointer_at(self._address - - other * self._bitem_size) - elif type(self) is type(other): - return (self._address - other._address) // self._bitem_size - else: - return NotImplemented - - def __getitem__(self, index): - if getattr(self, '_own', False) and index != 0: - raise IndexError - return BItem._from_ctypes(self._as_ctype_ptr[index]) - - def __setitem__(self, index, value): - self._as_ctype_ptr[index] = BItem._to_ctypes(value) - - if kind == 'charp' or kind == 'voidp': - @classmethod - def _arg_to_ctypes(cls, *value): - if value and isinstance(value[0], bytes): - return ctypes.c_char_p(value[0]) - else: - return super(CTypesPtr, cls)._arg_to_ctypes(*value) - - if kind == 'charp' or kind == 'bytep': - def _to_string(self, maxlen): - if maxlen < 0: - maxlen = sys.maxsize - p = ctypes.cast(self._as_ctype_ptr, - ctypes.POINTER(ctypes.c_char)) - n = 0 - while n < maxlen and p[n] != b'\x00': - n += 1 - return b''.join([p[i] for i in range(n)]) - - def _get_own_repr(self): - if getattr(self, '_own', False): - return 'owning %d bytes' % ( - ctypes.sizeof(self._as_ctype_ptr.contents),) - return super(CTypesPtr, self)._get_own_repr() - # - if (BItem is self.ffi._get_cached_btype(model.void_type) or - BItem is self.ffi._get_cached_btype(model.PrimitiveType('char'))): - CTypesPtr._automatic_casts = True - # - CTypesPtr._fix_class() - return CTypesPtr - - def new_array_type(self, CTypesPtr, length): - if length is None: - brackets = ' &[]' - else: - brackets = ' &[%d]' % length - BItem = CTypesPtr._BItem - getbtype = self.ffi._get_cached_btype - if BItem is getbtype(model.PrimitiveType('char')): - kind = 'char' - elif BItem in (getbtype(model.PrimitiveType('signed char')), - getbtype(model.PrimitiveType('unsigned char'))): - kind = 'byte' - else: - kind = 'generic' - # - class CTypesArray(CTypesGenericArray): - __slots__ = ['_blob', '_own'] - if length is not None: - _ctype = BItem._ctype * length - else: - __slots__.append('_ctype') - _reftypename = BItem._get_c_name(brackets) - _declared_length = length - _CTPtr = CTypesPtr - - def __init__(self, init): - if length is None: - if isinstance(init, (int, long)): - len1 = init - init = None - elif kind == 'char' and isinstance(init, bytes): - len1 = len(init) + 1 # extra null - else: - init = tuple(init) - len1 = len(init) - self._ctype = BItem._ctype * len1 - self._blob = self._ctype() - self._own = True - if init is not None: - self._initialize(self._blob, init) - - @staticmethod - def _initialize(blob, init): - if isinstance(init, bytes): - init = [init[i:i+1] for i in range(len(init))] - else: - if isinstance(init, CTypesGenericArray): - if (len(init) != len(blob) or - not isinstance(init, CTypesArray)): - raise TypeError("length/type mismatch: %s" % (init,)) - init = tuple(init) - if len(init) > len(blob): - raise IndexError("too many initializers") - addr = ctypes.cast(blob, ctypes.c_void_p).value - PTR = ctypes.POINTER(BItem._ctype) - itemsize = ctypes.sizeof(BItem._ctype) - for i, value in enumerate(init): - p = ctypes.cast(addr + i * itemsize, PTR) - BItem._initialize(p.contents, value) - - def __len__(self): - return len(self._blob) - - def __getitem__(self, index): - if not (0 <= index < len(self._blob)): - raise IndexError - return BItem._from_ctypes(self._blob[index]) - - def __setitem__(self, index, value): - if not (0 <= index < len(self._blob)): - raise IndexError - self._blob[index] = BItem._to_ctypes(value) - - if kind == 'char' or kind == 'byte': - def _to_string(self, maxlen): - if maxlen < 0: - maxlen = len(self._blob) - p = ctypes.cast(self._blob, - ctypes.POINTER(ctypes.c_char)) - n = 0 - while n < maxlen and p[n] != b'\x00': - n += 1 - return b''.join([p[i] for i in range(n)]) - - def _get_own_repr(self): - if getattr(self, '_own', False): - return 'owning %d bytes' % (ctypes.sizeof(self._blob),) - return super(CTypesArray, self)._get_own_repr() - - def _convert_to_address(self, BClass): - if BClass in (CTypesPtr, None) or BClass._automatic_casts: - return ctypes.addressof(self._blob) - else: - return CTypesData._convert_to_address(self, BClass) - - @staticmethod - def _from_ctypes(ctypes_array): - self = CTypesArray.__new__(CTypesArray) - self._blob = ctypes_array - return self - - @staticmethod - def _arg_to_ctypes(value): - return CTypesPtr._arg_to_ctypes(value) - - def __add__(self, other): - if isinstance(other, (int, long)): - return CTypesPtr._new_pointer_at( - ctypes.addressof(self._blob) + - other * ctypes.sizeof(BItem._ctype)) - else: - return NotImplemented - - @classmethod - def _cast_from(cls, source): - raise NotImplementedError("casting to %r" % ( - cls._get_c_name(),)) - # - CTypesArray._fix_class() - return CTypesArray - - def _new_struct_or_union(self, kind, name, base_ctypes_class): - # - class struct_or_union(base_ctypes_class): - pass - struct_or_union.__name__ = '%s_%s' % (kind, name) - kind1 = kind - # - class CTypesStructOrUnion(CTypesBaseStructOrUnion): - __slots__ = ['_blob'] - _ctype = struct_or_union - _reftypename = '%s &' % (name,) - _kind = kind = kind1 - # - CTypesStructOrUnion._fix_class() - return CTypesStructOrUnion - - def new_struct_type(self, name): - return self._new_struct_or_union('struct', name, ctypes.Structure) - - def new_union_type(self, name): - return self._new_struct_or_union('union', name, ctypes.Union) - - def complete_struct_or_union(self, CTypesStructOrUnion, fields, tp, - totalsize=-1, totalalignment=-1, sflags=0, - pack=0): - if totalsize >= 0 or totalalignment >= 0: - raise NotImplementedError("the ctypes backend of CFFI does not support " - "structures completed by verify(); please " - "compile and install the _cffi_backend module.") - struct_or_union = CTypesStructOrUnion._ctype - fnames = [fname for (fname, BField, bitsize) in fields] - btypes = [BField for (fname, BField, bitsize) in fields] - bitfields = [bitsize for (fname, BField, bitsize) in fields] - # - bfield_types = {} - cfields = [] - for (fname, BField, bitsize) in fields: - if bitsize < 0: - cfields.append((fname, BField._ctype)) - bfield_types[fname] = BField - else: - cfields.append((fname, BField._ctype, bitsize)) - bfield_types[fname] = Ellipsis - if sflags & 8: - struct_or_union._pack_ = 1 - elif pack: - struct_or_union._pack_ = pack - struct_or_union._fields_ = cfields - CTypesStructOrUnion._bfield_types = bfield_types - # - @staticmethod - def _create_ctype_obj(init): - result = struct_or_union() - if init is not None: - initialize(result, init) - return result - CTypesStructOrUnion._create_ctype_obj = _create_ctype_obj - # - def initialize(blob, init): - if is_union: - if len(init) > 1: - raise ValueError("union initializer: %d items given, but " - "only one supported (use a dict if needed)" - % (len(init),)) - if not isinstance(init, dict): - if isinstance(init, (bytes, unicode)): - raise TypeError("union initializer: got a str") - init = tuple(init) - if len(init) > len(fnames): - raise ValueError("too many values for %s initializer" % - CTypesStructOrUnion._get_c_name()) - init = dict(zip(fnames, init)) - addr = ctypes.addressof(blob) - for fname, value in init.items(): - BField, bitsize = name2fieldtype[fname] - assert bitsize < 0, \ - "not implemented: initializer with bit fields" - offset = CTypesStructOrUnion._offsetof(fname) - PTR = ctypes.POINTER(BField._ctype) - p = ctypes.cast(addr + offset, PTR) - BField._initialize(p.contents, value) - is_union = CTypesStructOrUnion._kind == 'union' - name2fieldtype = dict(zip(fnames, zip(btypes, bitfields))) - # - for fname, BField, bitsize in fields: - if fname == '': - raise NotImplementedError("nested anonymous structs/unions") - if hasattr(CTypesStructOrUnion, fname): - raise ValueError("the field name %r conflicts in " - "the ctypes backend" % fname) - if bitsize < 0: - def getter(self, fname=fname, BField=BField, - offset=CTypesStructOrUnion._offsetof(fname), - PTR=ctypes.POINTER(BField._ctype)): - addr = ctypes.addressof(self._blob) - p = ctypes.cast(addr + offset, PTR) - return BField._from_ctypes(p.contents) - def setter(self, value, fname=fname, BField=BField): - setattr(self._blob, fname, BField._to_ctypes(value)) - # - if issubclass(BField, CTypesGenericArray): - setter = None - if BField._declared_length == 0: - def getter(self, fname=fname, BFieldPtr=BField._CTPtr, - offset=CTypesStructOrUnion._offsetof(fname), - PTR=ctypes.POINTER(BField._ctype)): - addr = ctypes.addressof(self._blob) - p = ctypes.cast(addr + offset, PTR) - return BFieldPtr._from_ctypes(p) - # - else: - def getter(self, fname=fname, BField=BField): - return BField._from_ctypes(getattr(self._blob, fname)) - def setter(self, value, fname=fname, BField=BField): - # xxx obscure workaround - value = BField._to_ctypes(value) - oldvalue = getattr(self._blob, fname) - setattr(self._blob, fname, value) - if value != getattr(self._blob, fname): - setattr(self._blob, fname, oldvalue) - raise OverflowError("value too large for bitfield") - setattr(CTypesStructOrUnion, fname, property(getter, setter)) - # - CTypesPtr = self.ffi._get_cached_btype(model.PointerType(tp)) - for fname in fnames: - if hasattr(CTypesPtr, fname): - raise ValueError("the field name %r conflicts in " - "the ctypes backend" % fname) - def getter(self, fname=fname): - return getattr(self[0], fname) - def setter(self, value, fname=fname): - setattr(self[0], fname, value) - setattr(CTypesPtr, fname, property(getter, setter)) - - def new_function_type(self, BArgs, BResult, has_varargs): - nameargs = [BArg._get_c_name() for BArg in BArgs] - if has_varargs: - nameargs.append('...') - nameargs = ', '.join(nameargs) - # - class CTypesFunctionPtr(CTypesGenericPtr): - __slots__ = ['_own_callback', '_name'] - _ctype = ctypes.CFUNCTYPE(getattr(BResult, '_ctype', None), - *[BArg._ctype for BArg in BArgs], - use_errno=True) - _reftypename = BResult._get_c_name('(* &)(%s)' % (nameargs,)) - - def __init__(self, init, error=None): - # create a callback to the Python callable init() - import traceback - assert not has_varargs, "varargs not supported for callbacks" - if getattr(BResult, '_ctype', None) is not None: - error = BResult._from_ctypes( - BResult._create_ctype_obj(error)) - else: - error = None - def callback(*args): - args2 = [] - for arg, BArg in zip(args, BArgs): - args2.append(BArg._from_ctypes(arg)) - try: - res2 = init(*args2) - res2 = BResult._to_ctypes(res2) - except: - traceback.print_exc() - res2 = error - if issubclass(BResult, CTypesGenericPtr): - if res2: - res2 = ctypes.cast(res2, ctypes.c_void_p).value - # .value: http://bugs.python.org/issue1574593 - else: - res2 = None - #print repr(res2) - return res2 - if issubclass(BResult, CTypesGenericPtr): - # The only pointers callbacks can return are void*s: - # http://bugs.python.org/issue5710 - callback_ctype = ctypes.CFUNCTYPE( - ctypes.c_void_p, - *[BArg._ctype for BArg in BArgs], - use_errno=True) - else: - callback_ctype = CTypesFunctionPtr._ctype - self._as_ctype_ptr = callback_ctype(callback) - self._address = ctypes.cast(self._as_ctype_ptr, - ctypes.c_void_p).value - self._own_callback = init - - @staticmethod - def _initialize(ctypes_ptr, value): - if value: - raise NotImplementedError("ctypes backend: not supported: " - "initializers for function pointers") - - def __repr__(self): - c_name = getattr(self, '_name', None) - if c_name: - i = self._reftypename.index('(* &)') - if self._reftypename[i-1] not in ' )*': - c_name = ' ' + c_name - c_name = self._reftypename.replace('(* &)', c_name) - return CTypesData.__repr__(self, c_name) - - def _get_own_repr(self): - if getattr(self, '_own_callback', None) is not None: - return 'calling %r' % (self._own_callback,) - return super(CTypesFunctionPtr, self)._get_own_repr() - - def __call__(self, *args): - if has_varargs: - assert len(args) >= len(BArgs) - extraargs = args[len(BArgs):] - args = args[:len(BArgs)] - else: - assert len(args) == len(BArgs) - ctypes_args = [] - for arg, BArg in zip(args, BArgs): - ctypes_args.append(BArg._arg_to_ctypes(arg)) - if has_varargs: - for i, arg in enumerate(extraargs): - if arg is None: - ctypes_args.append(ctypes.c_void_p(0)) # NULL - continue - if not isinstance(arg, CTypesData): - raise TypeError( - "argument %d passed in the variadic part " - "needs to be a cdata object (got %s)" % - (1 + len(BArgs) + i, type(arg).__name__)) - ctypes_args.append(arg._arg_to_ctypes(arg)) - result = self._as_ctype_ptr(*ctypes_args) - return BResult._from_ctypes(result) - # - CTypesFunctionPtr._fix_class() - return CTypesFunctionPtr - - def new_enum_type(self, name, enumerators, enumvalues, CTypesInt): - assert isinstance(name, str) - reverse_mapping = dict(zip(reversed(enumvalues), - reversed(enumerators))) - # - class CTypesEnum(CTypesInt): - __slots__ = [] - _reftypename = '%s &' % name - - def _get_own_repr(self): - value = self._value - try: - return '%d: %s' % (value, reverse_mapping[value]) - except KeyError: - return str(value) - - def _to_string(self, maxlen): - value = self._value - try: - return reverse_mapping[value] - except KeyError: - return str(value) - # - CTypesEnum._fix_class() - return CTypesEnum - - def get_errno(self): - return ctypes.get_errno() - - def set_errno(self, value): - ctypes.set_errno(value) - - def string(self, b, maxlen=-1): - return b._to_string(maxlen) - - def buffer(self, bptr, size=-1): - raise NotImplementedError("buffer() with ctypes backend") - - def sizeof(self, cdata_or_BType): - if isinstance(cdata_or_BType, CTypesData): - return cdata_or_BType._get_size_of_instance() - else: - assert issubclass(cdata_or_BType, CTypesData) - return cdata_or_BType._get_size() - - def alignof(self, BType): - assert issubclass(BType, CTypesData) - return BType._alignment() - - def newp(self, BType, source): - if not issubclass(BType, CTypesData): - raise TypeError - return BType._newp(source) - - def cast(self, BType, source): - return BType._cast_from(source) - - def callback(self, BType, source, error, onerror): - assert onerror is None # XXX not implemented - return BType(source, error) - - _weakref_cache_ref = None - - def gcp(self, cdata, destructor, size=0): - if self._weakref_cache_ref is None: - import weakref - class MyRef(weakref.ref): - def __eq__(self, other): - myref = self() - return self is other or ( - myref is not None and myref is other()) - def __ne__(self, other): - return not (self == other) - def __hash__(self): - try: - return self._hash - except AttributeError: - self._hash = hash(self()) - return self._hash - self._weakref_cache_ref = {}, MyRef - weak_cache, MyRef = self._weakref_cache_ref - - if destructor is None: - try: - del weak_cache[MyRef(cdata)] - except KeyError: - raise TypeError("Can remove destructor only on a object " - "previously returned by ffi.gc()") - return None - - def remove(k): - cdata, destructor = weak_cache.pop(k, (None, None)) - if destructor is not None: - destructor(cdata) - - new_cdata = self.cast(self.typeof(cdata), cdata) - assert new_cdata is not cdata - weak_cache[MyRef(new_cdata, remove)] = (cdata, destructor) - return new_cdata - - typeof = type - - def getcname(self, BType, replace_with): - return BType._get_c_name(replace_with) - - def typeoffsetof(self, BType, fieldname, num=0): - if isinstance(fieldname, str): - if num == 0 and issubclass(BType, CTypesGenericPtr): - BType = BType._BItem - if not issubclass(BType, CTypesBaseStructOrUnion): - raise TypeError("expected a struct or union ctype") - BField = BType._bfield_types[fieldname] - if BField is Ellipsis: - raise TypeError("not supported for bitfields") - return (BField, BType._offsetof(fieldname)) - elif isinstance(fieldname, (int, long)): - if issubclass(BType, CTypesGenericArray): - BType = BType._CTPtr - if not issubclass(BType, CTypesGenericPtr): - raise TypeError("expected an array or ptr ctype") - BItem = BType._BItem - offset = BItem._get_size() * fieldname - if offset > sys.maxsize: - raise OverflowError - return (BItem, offset) - else: - raise TypeError(type(fieldname)) - - def rawaddressof(self, BTypePtr, cdata, offset=None): - if isinstance(cdata, CTypesBaseStructOrUnion): - ptr = ctypes.pointer(type(cdata)._to_ctypes(cdata)) - elif isinstance(cdata, CTypesGenericPtr): - if offset is None or not issubclass(type(cdata)._BItem, - CTypesBaseStructOrUnion): - raise TypeError("unexpected cdata type") - ptr = type(cdata)._to_ctypes(cdata) - elif isinstance(cdata, CTypesGenericArray): - ptr = type(cdata)._to_ctypes(cdata) - else: - raise TypeError("expected a ") - if offset: - ptr = ctypes.cast( - ctypes.c_void_p( - ctypes.cast(ptr, ctypes.c_void_p).value + offset), - type(ptr)) - return BTypePtr._from_ctypes(ptr) - - -class CTypesLibrary(object): - - def __init__(self, backend, cdll): - self.backend = backend - self.cdll = cdll - - def load_function(self, BType, name): - c_func = getattr(self.cdll, name) - funcobj = BType._from_ctypes(c_func) - funcobj._name = name - return funcobj - - def read_variable(self, BType, name): - try: - ctypes_obj = BType._ctype.in_dll(self.cdll, name) - except AttributeError as e: - raise NotImplementedError(e) - return BType._from_ctypes(ctypes_obj) - - def write_variable(self, BType, name, value): - new_ctypes_obj = BType._to_ctypes(value) - ctypes_obj = BType._ctype.in_dll(self.cdll, name) - ctypes.memmove(ctypes.addressof(ctypes_obj), - ctypes.addressof(new_ctypes_obj), - ctypes.sizeof(BType._ctype)) diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/cffi_opcode.py b/.venv-docs/lib/python3.12/site-packages/cffi/cffi_opcode.py deleted file mode 100644 index 6421df62..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi/cffi_opcode.py +++ /dev/null @@ -1,187 +0,0 @@ -from .error import VerificationError - -class CffiOp(object): - def __init__(self, op, arg): - self.op = op - self.arg = arg - - def as_c_expr(self): - if self.op is None: - assert isinstance(self.arg, str) - return '(_cffi_opcode_t)(%s)' % (self.arg,) - classname = CLASS_NAME[self.op] - return '_CFFI_OP(_CFFI_OP_%s, %s)' % (classname, self.arg) - - def as_python_bytes(self): - if self.op is None and self.arg.isdigit(): - value = int(self.arg) # non-negative: '-' not in self.arg - if value >= 2**31: - raise OverflowError("cannot emit %r: limited to 2**31-1" - % (self.arg,)) - return format_four_bytes(value) - if isinstance(self.arg, str): - raise VerificationError("cannot emit to Python: %r" % (self.arg,)) - return format_four_bytes((self.arg << 8) | self.op) - - def __str__(self): - classname = CLASS_NAME.get(self.op, self.op) - return '(%s %s)' % (classname, self.arg) - -def format_four_bytes(num): - return '\\x%02X\\x%02X\\x%02X\\x%02X' % ( - (num >> 24) & 0xFF, - (num >> 16) & 0xFF, - (num >> 8) & 0xFF, - (num ) & 0xFF) - -OP_PRIMITIVE = 1 -OP_POINTER = 3 -OP_ARRAY = 5 -OP_OPEN_ARRAY = 7 -OP_STRUCT_UNION = 9 -OP_ENUM = 11 -OP_FUNCTION = 13 -OP_FUNCTION_END = 15 -OP_NOOP = 17 -OP_BITFIELD = 19 -OP_TYPENAME = 21 -OP_CPYTHON_BLTN_V = 23 # varargs -OP_CPYTHON_BLTN_N = 25 # noargs -OP_CPYTHON_BLTN_O = 27 # O (i.e. a single arg) -OP_CONSTANT = 29 -OP_CONSTANT_INT = 31 -OP_GLOBAL_VAR = 33 -OP_DLOPEN_FUNC = 35 -OP_DLOPEN_CONST = 37 -OP_GLOBAL_VAR_F = 39 -OP_EXTERN_PYTHON = 41 - -PRIM_VOID = 0 -PRIM_BOOL = 1 -PRIM_CHAR = 2 -PRIM_SCHAR = 3 -PRIM_UCHAR = 4 -PRIM_SHORT = 5 -PRIM_USHORT = 6 -PRIM_INT = 7 -PRIM_UINT = 8 -PRIM_LONG = 9 -PRIM_ULONG = 10 -PRIM_LONGLONG = 11 -PRIM_ULONGLONG = 12 -PRIM_FLOAT = 13 -PRIM_DOUBLE = 14 -PRIM_LONGDOUBLE = 15 - -PRIM_WCHAR = 16 -PRIM_INT8 = 17 -PRIM_UINT8 = 18 -PRIM_INT16 = 19 -PRIM_UINT16 = 20 -PRIM_INT32 = 21 -PRIM_UINT32 = 22 -PRIM_INT64 = 23 -PRIM_UINT64 = 24 -PRIM_INTPTR = 25 -PRIM_UINTPTR = 26 -PRIM_PTRDIFF = 27 -PRIM_SIZE = 28 -PRIM_SSIZE = 29 -PRIM_INT_LEAST8 = 30 -PRIM_UINT_LEAST8 = 31 -PRIM_INT_LEAST16 = 32 -PRIM_UINT_LEAST16 = 33 -PRIM_INT_LEAST32 = 34 -PRIM_UINT_LEAST32 = 35 -PRIM_INT_LEAST64 = 36 -PRIM_UINT_LEAST64 = 37 -PRIM_INT_FAST8 = 38 -PRIM_UINT_FAST8 = 39 -PRIM_INT_FAST16 = 40 -PRIM_UINT_FAST16 = 41 -PRIM_INT_FAST32 = 42 -PRIM_UINT_FAST32 = 43 -PRIM_INT_FAST64 = 44 -PRIM_UINT_FAST64 = 45 -PRIM_INTMAX = 46 -PRIM_UINTMAX = 47 -PRIM_FLOATCOMPLEX = 48 -PRIM_DOUBLECOMPLEX = 49 -PRIM_CHAR16 = 50 -PRIM_CHAR32 = 51 - -_NUM_PRIM = 52 -_UNKNOWN_PRIM = -1 -_UNKNOWN_FLOAT_PRIM = -2 -_UNKNOWN_LONG_DOUBLE = -3 - -_IO_FILE_STRUCT = -1 - -PRIMITIVE_TO_INDEX = { - 'char': PRIM_CHAR, - 'short': PRIM_SHORT, - 'int': PRIM_INT, - 'long': PRIM_LONG, - 'long long': PRIM_LONGLONG, - 'signed char': PRIM_SCHAR, - 'unsigned char': PRIM_UCHAR, - 'unsigned short': PRIM_USHORT, - 'unsigned int': PRIM_UINT, - 'unsigned long': PRIM_ULONG, - 'unsigned long long': PRIM_ULONGLONG, - 'float': PRIM_FLOAT, - 'double': PRIM_DOUBLE, - 'long double': PRIM_LONGDOUBLE, - '_cffi_float_complex_t': PRIM_FLOATCOMPLEX, - '_cffi_double_complex_t': PRIM_DOUBLECOMPLEX, - '_Bool': PRIM_BOOL, - 'wchar_t': PRIM_WCHAR, - 'char16_t': PRIM_CHAR16, - 'char32_t': PRIM_CHAR32, - 'int8_t': PRIM_INT8, - 'uint8_t': PRIM_UINT8, - 'int16_t': PRIM_INT16, - 'uint16_t': PRIM_UINT16, - 'int32_t': PRIM_INT32, - 'uint32_t': PRIM_UINT32, - 'int64_t': PRIM_INT64, - 'uint64_t': PRIM_UINT64, - 'intptr_t': PRIM_INTPTR, - 'uintptr_t': PRIM_UINTPTR, - 'ptrdiff_t': PRIM_PTRDIFF, - 'size_t': PRIM_SIZE, - 'ssize_t': PRIM_SSIZE, - 'int_least8_t': PRIM_INT_LEAST8, - 'uint_least8_t': PRIM_UINT_LEAST8, - 'int_least16_t': PRIM_INT_LEAST16, - 'uint_least16_t': PRIM_UINT_LEAST16, - 'int_least32_t': PRIM_INT_LEAST32, - 'uint_least32_t': PRIM_UINT_LEAST32, - 'int_least64_t': PRIM_INT_LEAST64, - 'uint_least64_t': PRIM_UINT_LEAST64, - 'int_fast8_t': PRIM_INT_FAST8, - 'uint_fast8_t': PRIM_UINT_FAST8, - 'int_fast16_t': PRIM_INT_FAST16, - 'uint_fast16_t': PRIM_UINT_FAST16, - 'int_fast32_t': PRIM_INT_FAST32, - 'uint_fast32_t': PRIM_UINT_FAST32, - 'int_fast64_t': PRIM_INT_FAST64, - 'uint_fast64_t': PRIM_UINT_FAST64, - 'intmax_t': PRIM_INTMAX, - 'uintmax_t': PRIM_UINTMAX, - } - -F_UNION = 0x01 -F_CHECK_FIELDS = 0x02 -F_PACKED = 0x04 -F_EXTERNAL = 0x08 -F_OPAQUE = 0x10 - -G_FLAGS = dict([('_CFFI_' + _key, globals()[_key]) - for _key in ['F_UNION', 'F_CHECK_FIELDS', 'F_PACKED', - 'F_EXTERNAL', 'F_OPAQUE']]) - -CLASS_NAME = {} -for _name, _value in list(globals().items()): - if _name.startswith('OP_') and isinstance(_value, int): - CLASS_NAME[_value] = _name[3:] diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/commontypes.py b/.venv-docs/lib/python3.12/site-packages/cffi/commontypes.py deleted file mode 100644 index d4dae351..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi/commontypes.py +++ /dev/null @@ -1,82 +0,0 @@ -import sys -from . import model -from .error import FFIError - - -COMMON_TYPES = {} - -try: - # fetch "bool" and all simple Windows types - from _cffi_backend import _get_common_types - _get_common_types(COMMON_TYPES) -except ImportError: - pass - -COMMON_TYPES['FILE'] = model.unknown_type('FILE', '_IO_FILE') -COMMON_TYPES['bool'] = '_Bool' # in case we got ImportError above -COMMON_TYPES['float _Complex'] = '_cffi_float_complex_t' -COMMON_TYPES['double _Complex'] = '_cffi_double_complex_t' - -for _type in model.PrimitiveType.ALL_PRIMITIVE_TYPES: - if _type.endswith('_t'): - COMMON_TYPES[_type] = _type -del _type - -_CACHE = {} - -def resolve_common_type(parser, commontype): - try: - return _CACHE[commontype] - except KeyError: - cdecl = COMMON_TYPES.get(commontype, commontype) - if not isinstance(cdecl, str): - result, quals = cdecl, 0 # cdecl is already a BaseType - elif cdecl in model.PrimitiveType.ALL_PRIMITIVE_TYPES: - result, quals = model.PrimitiveType(cdecl), 0 - elif cdecl == 'set-unicode-needed': - raise FFIError("The Windows type %r is only available after " - "you call ffi.set_unicode()" % (commontype,)) - else: - if commontype == cdecl: - raise FFIError( - "Unsupported type: %r. Please look at " - "http://cffi.readthedocs.io/en/latest/cdef.html#ffi-cdef-limitations " - "and file an issue if you think this type should really " - "be supported." % (commontype,)) - result, quals = parser.parse_type_and_quals(cdecl) # recursive - - assert isinstance(result, model.BaseTypeByIdentity) - _CACHE[commontype] = result, quals - return result, quals - - -# ____________________________________________________________ -# extra types for Windows (most of them are in commontypes.c) - - -def win_common_types(): - return { - "UNICODE_STRING": model.StructType( - "_UNICODE_STRING", - ["Length", - "MaximumLength", - "Buffer"], - [model.PrimitiveType("unsigned short"), - model.PrimitiveType("unsigned short"), - model.PointerType(model.PrimitiveType("wchar_t"))], - [-1, -1, -1]), - "PUNICODE_STRING": "UNICODE_STRING *", - "PCUNICODE_STRING": "const UNICODE_STRING *", - - "TBYTE": "set-unicode-needed", - "TCHAR": "set-unicode-needed", - "LPCTSTR": "set-unicode-needed", - "PCTSTR": "set-unicode-needed", - "LPTSTR": "set-unicode-needed", - "PTSTR": "set-unicode-needed", - "PTBYTE": "set-unicode-needed", - "PTCHAR": "set-unicode-needed", - } - -if sys.platform == 'win32': - COMMON_TYPES.update(win_common_types()) diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/cparser.py b/.venv-docs/lib/python3.12/site-packages/cffi/cparser.py deleted file mode 100644 index dd590d87..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi/cparser.py +++ /dev/null @@ -1,1015 +0,0 @@ -from . import model -from .commontypes import COMMON_TYPES, resolve_common_type -from .error import FFIError, CDefError -try: - from . import _pycparser as pycparser -except ImportError: - import pycparser -import weakref, re, sys - -try: - if sys.version_info < (3,): - import thread as _thread - else: - import _thread - lock = _thread.allocate_lock() -except ImportError: - lock = None - -def _workaround_for_static_import_finders(): - # Issue #392: packaging tools like cx_Freeze can not find these - # because pycparser uses exec dynamic import. This is an obscure - # workaround. This function is never called. - import pycparser.yacctab - import pycparser.lextab - -CDEF_SOURCE_STRING = "" -_r_comment = re.compile(r"/\*.*?\*/|//([^\n\\]|\\.)*?$", - re.DOTALL | re.MULTILINE) -_r_define = re.compile(r"^\s*#\s*define\s+([A-Za-z_][A-Za-z_0-9]*)" - r"\b((?:[^\n\\]|\\.)*?)$", - re.DOTALL | re.MULTILINE) -_r_line_directive = re.compile(r"^[ \t]*#[ \t]*(?:line|\d+)\b.*$", re.MULTILINE) -_r_partial_enum = re.compile(r"=\s*\.\.\.\s*[,}]|\.\.\.\s*\}") -_r_enum_dotdotdot = re.compile(r"__dotdotdot\d+__$") -_r_partial_array = re.compile(r"\[\s*\.\.\.\s*\]") -_r_words = re.compile(r"\w+|\S") -_parser_cache = None -_r_int_literal = re.compile(r"-?0?x?[0-9a-f]+[lu]*$", re.IGNORECASE) -_r_stdcall1 = re.compile(r"\b(__stdcall|WINAPI)\b") -_r_stdcall2 = re.compile(r"[(]\s*(__stdcall|WINAPI)\b") -_r_cdecl = re.compile(r"\b__cdecl\b") -_r_extern_python = re.compile(r'\bextern\s*"' - r'(Python|Python\s*\+\s*C|C\s*\+\s*Python)"\s*.') -_r_star_const_space = re.compile( # matches "* const " - r"[*]\s*((const|volatile|restrict)\b\s*)+") -_r_int_dotdotdot = re.compile(r"(\b(int|long|short|signed|unsigned|char)\s*)+" - r"\.\.\.") -_r_float_dotdotdot = re.compile(r"\b(double|float)\s*\.\.\.") - -def _get_parser(): - global _parser_cache - if _parser_cache is None: - _parser_cache = pycparser.CParser() - return _parser_cache - -def _workaround_for_old_pycparser(csource): - # Workaround for a pycparser issue (fixed between pycparser 2.10 and - # 2.14): "char*const***" gives us a wrong syntax tree, the same as - # for "char***(*const)". This means we can't tell the difference - # afterwards. But "char(*const(***))" gives us the right syntax - # tree. The issue only occurs if there are several stars in - # sequence with no parenthesis in between, just possibly qualifiers. - # Attempt to fix it by adding some parentheses in the source: each - # time we see "* const" or "* const *", we add an opening - # parenthesis before each star---the hard part is figuring out where - # to close them. - parts = [] - while True: - match = _r_star_const_space.search(csource) - if not match: - break - #print repr(''.join(parts)+csource), '=>', - parts.append(csource[:match.start()]) - parts.append('('); closing = ')' - parts.append(match.group()) # e.g. "* const " - endpos = match.end() - if csource.startswith('*', endpos): - parts.append('('); closing += ')' - level = 0 - i = endpos - while i < len(csource): - c = csource[i] - if c == '(': - level += 1 - elif c == ')': - if level == 0: - break - level -= 1 - elif c in ',;=': - if level == 0: - break - i += 1 - csource = csource[endpos:i] + closing + csource[i:] - #print repr(''.join(parts)+csource) - parts.append(csource) - return ''.join(parts) - -def _preprocess_extern_python(csource): - # input: `extern "Python" int foo(int);` or - # `extern "Python" { int foo(int); }` - # output: - # void __cffi_extern_python_start; - # int foo(int); - # void __cffi_extern_python_stop; - # - # input: `extern "Python+C" int foo(int);` - # output: - # void __cffi_extern_python_plus_c_start; - # int foo(int); - # void __cffi_extern_python_stop; - parts = [] - while True: - match = _r_extern_python.search(csource) - if not match: - break - endpos = match.end() - 1 - #print - #print ''.join(parts)+csource - #print '=>' - parts.append(csource[:match.start()]) - if 'C' in match.group(1): - parts.append('void __cffi_extern_python_plus_c_start; ') - else: - parts.append('void __cffi_extern_python_start; ') - if csource[endpos] == '{': - # grouping variant - closing = csource.find('}', endpos) - if closing < 0: - raise CDefError("'extern \"Python\" {': no '}' found") - if csource.find('{', endpos + 1, closing) >= 0: - raise NotImplementedError("cannot use { } inside a block " - "'extern \"Python\" { ... }'") - parts.append(csource[endpos+1:closing]) - csource = csource[closing+1:] - else: - # non-grouping variant - semicolon = csource.find(';', endpos) - if semicolon < 0: - raise CDefError("'extern \"Python\": no ';' found") - parts.append(csource[endpos:semicolon+1]) - csource = csource[semicolon+1:] - parts.append(' void __cffi_extern_python_stop;') - #print ''.join(parts)+csource - #print - parts.append(csource) - return ''.join(parts) - -def _warn_for_string_literal(csource): - if '"' not in csource: - return - for line in csource.splitlines(): - if '"' in line and not line.lstrip().startswith('#'): - import warnings - warnings.warn("String literal found in cdef() or type source. " - "String literals are ignored here, but you should " - "remove them anyway because some character sequences " - "confuse pre-parsing.") - break - -def _warn_for_non_extern_non_static_global_variable(decl): - if not decl.storage: - import warnings - warnings.warn("Global variable '%s' in cdef(): for consistency " - "with C it should have a storage class specifier " - "(usually 'extern')" % (decl.name,)) - -def _remove_line_directives(csource): - # _r_line_directive matches whole lines, without the final \n, if they - # start with '#line' with some spacing allowed, or '#NUMBER'. This - # function stores them away and replaces them with exactly the string - # '#line@N', where N is the index in the list 'line_directives'. - line_directives = [] - def replace(m): - i = len(line_directives) - line_directives.append(m.group()) - return '#line@%d' % i - csource = _r_line_directive.sub(replace, csource) - return csource, line_directives - -def _put_back_line_directives(csource, line_directives): - def replace(m): - s = m.group() - if not s.startswith('#line@'): - raise AssertionError("unexpected #line directive " - "(should have been processed and removed") - return line_directives[int(s[6:])] - return _r_line_directive.sub(replace, csource) - -def _preprocess(csource): - # First, remove the lines of the form '#line N "filename"' because - # the "filename" part could confuse the rest - csource, line_directives = _remove_line_directives(csource) - # Remove comments. NOTE: this only work because the cdef() section - # should not contain any string literals (except in line directives)! - def replace_keeping_newlines(m): - return ' ' + m.group().count('\n') * '\n' - csource = _r_comment.sub(replace_keeping_newlines, csource) - # Remove the "#define FOO x" lines - macros = {} - for match in _r_define.finditer(csource): - macroname, macrovalue = match.groups() - macrovalue = macrovalue.replace('\\\n', '').strip() - macros[macroname] = macrovalue - csource = _r_define.sub('', csource) - # - if pycparser.__version__ < '2.14': - csource = _workaround_for_old_pycparser(csource) - # - # BIG HACK: replace WINAPI or __stdcall with "volatile const". - # It doesn't make sense for the return type of a function to be - # "volatile volatile const", so we abuse it to detect __stdcall... - # Hack number 2 is that "int(volatile *fptr)();" is not valid C - # syntax, so we place the "volatile" before the opening parenthesis. - csource = _r_stdcall2.sub(' volatile volatile const(', csource) - csource = _r_stdcall1.sub(' volatile volatile const ', csource) - csource = _r_cdecl.sub(' ', csource) - # - # Replace `extern "Python"` with start/end markers - csource = _preprocess_extern_python(csource) - # - # Now there should not be any string literal left; warn if we get one - _warn_for_string_literal(csource) - # - # Replace "[...]" with "[__dotdotdotarray__]" - csource = _r_partial_array.sub('[__dotdotdotarray__]', csource) - # - # Replace "...}" with "__dotdotdotNUM__}". This construction should - # occur only at the end of enums; at the end of structs we have "...;}" - # and at the end of vararg functions "...);". Also replace "=...[,}]" - # with ",__dotdotdotNUM__[,}]": this occurs in the enums too, when - # giving an unknown value. - matches = list(_r_partial_enum.finditer(csource)) - for number, match in enumerate(reversed(matches)): - p = match.start() - if csource[p] == '=': - p2 = csource.find('...', p, match.end()) - assert p2 > p - csource = '%s,__dotdotdot%d__ %s' % (csource[:p], number, - csource[p2+3:]) - else: - assert csource[p:p+3] == '...' - csource = '%s __dotdotdot%d__ %s' % (csource[:p], number, - csource[p+3:]) - # Replace "int ..." or "unsigned long int..." with "__dotdotdotint__" - csource = _r_int_dotdotdot.sub(' __dotdotdotint__ ', csource) - # Replace "float ..." or "double..." with "__dotdotdotfloat__" - csource = _r_float_dotdotdot.sub(' __dotdotdotfloat__ ', csource) - # Replace all remaining "..." with the same name, "__dotdotdot__", - # which is declared with a typedef for the purpose of C parsing. - csource = csource.replace('...', ' __dotdotdot__ ') - # Finally, put back the line directives - csource = _put_back_line_directives(csource, line_directives) - return csource, macros - -def _common_type_names(csource): - # Look in the source for what looks like usages of types from the - # list of common types. A "usage" is approximated here as the - # appearance of the word, minus a "definition" of the type, which - # is the last word in a "typedef" statement. Approximative only - # but should be fine for all the common types. - look_for_words = set(COMMON_TYPES) - look_for_words.add(';') - look_for_words.add(',') - look_for_words.add('(') - look_for_words.add(')') - look_for_words.add('typedef') - words_used = set() - is_typedef = False - paren = 0 - previous_word = '' - for word in _r_words.findall(csource): - if word in look_for_words: - if word == ';': - if is_typedef: - words_used.discard(previous_word) - look_for_words.discard(previous_word) - is_typedef = False - elif word == 'typedef': - is_typedef = True - paren = 0 - elif word == '(': - paren += 1 - elif word == ')': - paren -= 1 - elif word == ',': - if is_typedef and paren == 0: - words_used.discard(previous_word) - look_for_words.discard(previous_word) - else: # word in COMMON_TYPES - words_used.add(word) - previous_word = word - return words_used - - -class Parser(object): - - def __init__(self): - self._declarations = {} - self._included_declarations = set() - self._anonymous_counter = 0 - self._structnode2type = weakref.WeakKeyDictionary() - self._options = {} - self._int_constants = {} - self._recomplete = [] - self._uses_new_feature = None - - def _parse(self, csource): - csource, macros = _preprocess(csource) - # XXX: for more efficiency we would need to poke into the - # internals of CParser... the following registers the - # typedefs, because their presence or absence influences the - # parsing itself (but what they are typedef'ed to plays no role) - ctn = _common_type_names(csource) - typenames = [] - for name in sorted(self._declarations): - if name.startswith('typedef '): - name = name[8:] - typenames.append(name) - ctn.discard(name) - typenames += sorted(ctn) - # - csourcelines = [] - csourcelines.append('# 1 ""') - for typename in typenames: - csourcelines.append('typedef int %s;' % typename) - csourcelines.append('typedef int __dotdotdotint__, __dotdotdotfloat__,' - ' __dotdotdot__;') - # this forces pycparser to consider the following in the file - # called from line 1 - csourcelines.append('# 1 "%s"' % (CDEF_SOURCE_STRING,)) - csourcelines.append(csource) - csourcelines.append('') # see test_missing_newline_bug - fullcsource = '\n'.join(csourcelines) - if lock is not None: - lock.acquire() # pycparser is not thread-safe... - try: - ast = _get_parser().parse(fullcsource) - except pycparser.c_parser.ParseError as e: - self.convert_pycparser_error(e, csource) - finally: - if lock is not None: - lock.release() - # csource will be used to find buggy source text - return ast, macros, csource - - def _convert_pycparser_error(self, e, csource): - # xxx look for ":NUM:" at the start of str(e) - # and interpret that as a line number. This will not work if - # the user gives explicit ``# NUM "FILE"`` directives. - line = None - msg = str(e) - match = re.match(r"%s:(\d+):" % (CDEF_SOURCE_STRING,), msg) - if match: - linenum = int(match.group(1), 10) - csourcelines = csource.splitlines() - if 1 <= linenum <= len(csourcelines): - line = csourcelines[linenum-1] - return line - - def convert_pycparser_error(self, e, csource): - line = self._convert_pycparser_error(e, csource) - - msg = str(e) - if line: - msg = 'cannot parse "%s"\n%s' % (line.strip(), msg) - else: - msg = 'parse error\n%s' % (msg,) - raise CDefError(msg) - - def parse(self, csource, override=False, packed=False, pack=None, - dllexport=False): - if packed: - if packed != True: - raise ValueError("'packed' should be False or True; use " - "'pack' to give another value") - if pack: - raise ValueError("cannot give both 'pack' and 'packed'") - pack = 1 - elif pack: - if pack & (pack - 1): - raise ValueError("'pack' must be a power of two, not %r" % - (pack,)) - else: - pack = 0 - prev_options = self._options - try: - self._options = {'override': override, - 'packed': pack, - 'dllexport': dllexport} - self._internal_parse(csource) - finally: - self._options = prev_options - - def _internal_parse(self, csource): - ast, macros, csource = self._parse(csource) - # add the macros - self._process_macros(macros) - # find the first "__dotdotdot__" and use that as a separator - # between the repeated typedefs and the real csource - iterator = iter(ast.ext) - for decl in iterator: - if decl.name == '__dotdotdot__': - break - else: - assert 0 - current_decl = None - # - try: - self._inside_extern_python = '__cffi_extern_python_stop' - for decl in iterator: - current_decl = decl - if isinstance(decl, pycparser.c_ast.Decl): - self._parse_decl(decl) - elif isinstance(decl, pycparser.c_ast.Typedef): - if not decl.name: - raise CDefError("typedef does not declare any name", - decl) - quals = 0 - if (isinstance(decl.type.type, pycparser.c_ast.IdentifierType) and - decl.type.type.names[-1].startswith('__dotdotdot')): - realtype = self._get_unknown_type(decl) - elif (isinstance(decl.type, pycparser.c_ast.PtrDecl) and - isinstance(decl.type.type, pycparser.c_ast.TypeDecl) and - isinstance(decl.type.type.type, - pycparser.c_ast.IdentifierType) and - decl.type.type.type.names[-1].startswith('__dotdotdot')): - realtype = self._get_unknown_ptr_type(decl) - else: - realtype, quals = self._get_type_and_quals( - decl.type, name=decl.name, partial_length_ok=True, - typedef_example="*(%s *)0" % (decl.name,)) - self._declare('typedef ' + decl.name, realtype, quals=quals) - elif decl.__class__.__name__ == 'Pragma': - # skip pragma, only in pycparser 2.15 - import warnings - warnings.warn( - "#pragma in cdef() are entirely ignored. " - "They should be removed for now, otherwise your " - "code might behave differently in a future version " - "of CFFI if #pragma support gets added. Note that " - "'#pragma pack' needs to be replaced with the " - "'packed' keyword argument to cdef().") - else: - raise CDefError("unexpected <%s>: this construct is valid " - "C but not valid in cdef()" % - decl.__class__.__name__, decl) - except CDefError as e: - if len(e.args) == 1: - e.args = e.args + (current_decl,) - raise - except FFIError as e: - msg = self._convert_pycparser_error(e, csource) - if msg: - e.args = (e.args[0] + "\n *** Err: %s" % msg,) - raise - - def _add_constants(self, key, val): - if key in self._int_constants: - if self._int_constants[key] == val: - return # ignore identical double declarations - raise FFIError( - "multiple declarations of constant: %s" % (key,)) - self._int_constants[key] = val - - def _add_integer_constant(self, name, int_str): - int_str = int_str.lower().rstrip("ul") - neg = int_str.startswith('-') - if neg: - int_str = int_str[1:] - # "010" is not valid oct in py3 - if (int_str.startswith("0") and int_str != '0' - and not int_str.startswith("0x")): - int_str = "0o" + int_str[1:] - pyvalue = int(int_str, 0) - if neg: - pyvalue = -pyvalue - self._add_constants(name, pyvalue) - self._declare('macro ' + name, pyvalue) - - def _process_macros(self, macros): - for key, value in macros.items(): - value = value.strip() - if _r_int_literal.match(value): - self._add_integer_constant(key, value) - elif value == '...': - self._declare('macro ' + key, value) - else: - raise CDefError( - 'only supports one of the following syntax:\n' - ' #define %s ... (literally dot-dot-dot)\n' - ' #define %s NUMBER (with NUMBER an integer' - ' constant, decimal/hex/octal)\n' - 'got:\n' - ' #define %s %s' - % (key, key, key, value)) - - def _declare_function(self, tp, quals, decl): - tp = self._get_type_pointer(tp, quals) - if self._options.get('dllexport'): - tag = 'dllexport_python ' - elif self._inside_extern_python == '__cffi_extern_python_start': - tag = 'extern_python ' - elif self._inside_extern_python == '__cffi_extern_python_plus_c_start': - tag = 'extern_python_plus_c ' - else: - tag = 'function ' - self._declare(tag + decl.name, tp) - - def _parse_decl(self, decl): - node = decl.type - if isinstance(node, pycparser.c_ast.FuncDecl): - tp, quals = self._get_type_and_quals(node, name=decl.name) - assert isinstance(tp, model.RawFunctionType) - self._declare_function(tp, quals, decl) - else: - if isinstance(node, pycparser.c_ast.Struct): - self._get_struct_union_enum_type('struct', node) - elif isinstance(node, pycparser.c_ast.Union): - self._get_struct_union_enum_type('union', node) - elif isinstance(node, pycparser.c_ast.Enum): - self._get_struct_union_enum_type('enum', node) - elif not decl.name: - raise CDefError("construct does not declare any variable", - decl) - # - if decl.name: - tp, quals = self._get_type_and_quals(node, - partial_length_ok=True) - if tp.is_raw_function: - self._declare_function(tp, quals, decl) - elif (tp.is_integer_type() and - hasattr(decl, 'init') and - hasattr(decl.init, 'value') and - _r_int_literal.match(decl.init.value)): - self._add_integer_constant(decl.name, decl.init.value) - elif (tp.is_integer_type() and - isinstance(decl.init, pycparser.c_ast.UnaryOp) and - decl.init.op == '-' and - hasattr(decl.init.expr, 'value') and - _r_int_literal.match(decl.init.expr.value)): - self._add_integer_constant(decl.name, - '-' + decl.init.expr.value) - elif (tp is model.void_type and - decl.name.startswith('__cffi_extern_python_')): - # hack: `extern "Python"` in the C source is replaced - # with "void __cffi_extern_python_start;" and - # "void __cffi_extern_python_stop;" - self._inside_extern_python = decl.name - else: - if self._inside_extern_python !='__cffi_extern_python_stop': - raise CDefError( - "cannot declare constants or " - "variables with 'extern \"Python\"'") - if (quals & model.Q_CONST) and not tp.is_array_type: - self._declare('constant ' + decl.name, tp, quals=quals) - else: - _warn_for_non_extern_non_static_global_variable(decl) - self._declare('variable ' + decl.name, tp, quals=quals) - - def parse_type(self, cdecl): - return self.parse_type_and_quals(cdecl)[0] - - def parse_type_and_quals(self, cdecl): - ast, macros = self._parse('void __dummy(\n%s\n);' % cdecl)[:2] - assert not macros - exprnode = ast.ext[-1].type.args.params[0] - if isinstance(exprnode, pycparser.c_ast.ID): - raise CDefError("unknown identifier '%s'" % (exprnode.name,)) - return self._get_type_and_quals(exprnode.type) - - def _declare(self, name, obj, included=False, quals=0): - if name in self._declarations: - prevobj, prevquals = self._declarations[name] - if prevobj is obj and prevquals == quals: - return - if not self._options.get('override'): - raise FFIError( - "multiple declarations of %s (for interactive usage, " - "try cdef(xx, override=True))" % (name,)) - assert '__dotdotdot__' not in name.split() - self._declarations[name] = (obj, quals) - if included: - self._included_declarations.add(obj) - - def _extract_quals(self, type): - quals = 0 - if isinstance(type, (pycparser.c_ast.TypeDecl, - pycparser.c_ast.PtrDecl)): - if 'const' in type.quals: - quals |= model.Q_CONST - if 'volatile' in type.quals: - quals |= model.Q_VOLATILE - if 'restrict' in type.quals: - quals |= model.Q_RESTRICT - return quals - - def _get_type_pointer(self, type, quals, declname=None): - if isinstance(type, model.RawFunctionType): - return type.as_function_pointer() - if (isinstance(type, model.StructOrUnionOrEnum) and - type.name.startswith('$') and type.name[1:].isdigit() and - type.forcename is None and declname is not None): - return model.NamedPointerType(type, declname, quals) - return model.PointerType(type, quals) - - def _get_type_and_quals(self, typenode, name=None, partial_length_ok=False, - typedef_example=None): - # first, dereference typedefs, if we have it already parsed, we're good - if (isinstance(typenode, pycparser.c_ast.TypeDecl) and - isinstance(typenode.type, pycparser.c_ast.IdentifierType) and - len(typenode.type.names) == 1 and - ('typedef ' + typenode.type.names[0]) in self._declarations): - tp, quals = self._declarations['typedef ' + typenode.type.names[0]] - quals |= self._extract_quals(typenode) - return tp, quals - # - if isinstance(typenode, pycparser.c_ast.ArrayDecl): - # array type - if typenode.dim is None: - length = None - else: - length = self._parse_constant( - typenode.dim, partial_length_ok=partial_length_ok) - # a hack: in 'typedef int foo_t[...][...];', don't use '...' as - # the length but use directly the C expression that would be - # generated by recompiler.py. This lets the typedef be used in - # many more places within recompiler.py - if typedef_example is not None: - if length == '...': - length = '_cffi_array_len(%s)' % (typedef_example,) - typedef_example = "*" + typedef_example - # - tp, quals = self._get_type_and_quals(typenode.type, - partial_length_ok=partial_length_ok, - typedef_example=typedef_example) - return model.ArrayType(tp, length), quals - # - if isinstance(typenode, pycparser.c_ast.PtrDecl): - # pointer type - itemtype, itemquals = self._get_type_and_quals(typenode.type) - tp = self._get_type_pointer(itemtype, itemquals, declname=name) - quals = self._extract_quals(typenode) - return tp, quals - # - if isinstance(typenode, pycparser.c_ast.TypeDecl): - quals = self._extract_quals(typenode) - type = typenode.type - if isinstance(type, pycparser.c_ast.IdentifierType): - # assume a primitive type. get it from .names, but reduce - # synonyms to a single chosen combination - names = list(type.names) - if names != ['signed', 'char']: # keep this unmodified - prefixes = {} - while names: - name = names[0] - if name in ('short', 'long', 'signed', 'unsigned'): - prefixes[name] = prefixes.get(name, 0) + 1 - del names[0] - else: - break - # ignore the 'signed' prefix below, and reorder the others - newnames = [] - for prefix in ('unsigned', 'short', 'long'): - for i in range(prefixes.get(prefix, 0)): - newnames.append(prefix) - if not names: - names = ['int'] # implicitly - if names == ['int']: # but kill it if 'short' or 'long' - if 'short' in prefixes or 'long' in prefixes: - names = [] - names = newnames + names - ident = ' '.join(names) - if ident == 'void': - return model.void_type, quals - if ident == '__dotdotdot__': - raise FFIError(':%d: bad usage of "..."' % - typenode.coord.line) - tp0, quals0 = resolve_common_type(self, ident) - return tp0, (quals | quals0) - # - if isinstance(type, pycparser.c_ast.Struct): - # 'struct foobar' - tp = self._get_struct_union_enum_type('struct', type, name) - return tp, quals - # - if isinstance(type, pycparser.c_ast.Union): - # 'union foobar' - tp = self._get_struct_union_enum_type('union', type, name) - return tp, quals - # - if isinstance(type, pycparser.c_ast.Enum): - # 'enum foobar' - tp = self._get_struct_union_enum_type('enum', type, name) - return tp, quals - # - if isinstance(typenode, pycparser.c_ast.FuncDecl): - # a function type - return self._parse_function_type(typenode, name), 0 - # - # nested anonymous structs or unions end up here - if isinstance(typenode, pycparser.c_ast.Struct): - return self._get_struct_union_enum_type('struct', typenode, name, - nested=True), 0 - if isinstance(typenode, pycparser.c_ast.Union): - return self._get_struct_union_enum_type('union', typenode, name, - nested=True), 0 - # - raise FFIError(":%d: bad or unsupported type declaration" % - typenode.coord.line) - - def _parse_function_type(self, typenode, funcname=None): - params = list(getattr(typenode.args, 'params', [])) - for i, arg in enumerate(params): - if not hasattr(arg, 'type'): - raise CDefError("%s arg %d: unknown type '%s'" - " (if you meant to use the old C syntax of giving" - " untyped arguments, it is not supported)" - % (funcname or 'in expression', i + 1, - getattr(arg, 'name', '?'))) - ellipsis = ( - len(params) > 0 and - isinstance(params[-1].type, pycparser.c_ast.TypeDecl) and - isinstance(params[-1].type.type, - pycparser.c_ast.IdentifierType) and - params[-1].type.type.names == ['__dotdotdot__']) - if ellipsis: - params.pop() - if not params: - raise CDefError( - "%s: a function with only '(...)' as argument" - " is not correct C" % (funcname or 'in expression')) - args = [self._as_func_arg(*self._get_type_and_quals(argdeclnode.type)) - for argdeclnode in params] - if not ellipsis and args == [model.void_type]: - args = [] - result, quals = self._get_type_and_quals(typenode.type) - # the 'quals' on the result type are ignored. HACK: we absure them - # to detect __stdcall functions: we textually replace "__stdcall" - # with "volatile volatile const" above. - abi = None - if hasattr(typenode.type, 'quals'): # else, probable syntax error anyway - if typenode.type.quals[-3:] == ['volatile', 'volatile', 'const']: - abi = '__stdcall' - return model.RawFunctionType(tuple(args), result, ellipsis, abi) - - def _as_func_arg(self, type, quals): - if isinstance(type, model.ArrayType): - return model.PointerType(type.item, quals) - elif isinstance(type, model.RawFunctionType): - return type.as_function_pointer() - else: - return type - - def _get_struct_union_enum_type(self, kind, type, name=None, nested=False): - # First, a level of caching on the exact 'type' node of the AST. - # This is obscure, but needed because pycparser "unrolls" declarations - # such as "typedef struct { } foo_t, *foo_p" and we end up with - # an AST that is not a tree, but a DAG, with the "type" node of the - # two branches foo_t and foo_p of the trees being the same node. - # It's a bit silly but detecting "DAG-ness" in the AST tree seems - # to be the only way to distinguish this case from two independent - # structs. See test_struct_with_two_usages. - try: - return self._structnode2type[type] - except KeyError: - pass - # - # Note that this must handle parsing "struct foo" any number of - # times and always return the same StructType object. Additionally, - # one of these times (not necessarily the first), the fields of - # the struct can be specified with "struct foo { ...fields... }". - # If no name is given, then we have to create a new anonymous struct - # with no caching; in this case, the fields are either specified - # right now or never. - # - force_name = name - name = type.name - # - # get the type or create it if needed - if name is None: - # 'force_name' is used to guess a more readable name for - # anonymous structs, for the common case "typedef struct { } foo". - if force_name is not None: - explicit_name = '$%s' % force_name - else: - self._anonymous_counter += 1 - explicit_name = '$%d' % self._anonymous_counter - tp = None - else: - explicit_name = name - key = '%s %s' % (kind, name) - tp, _ = self._declarations.get(key, (None, None)) - # - if tp is None: - if kind == 'struct': - tp = model.StructType(explicit_name, None, None, None) - elif kind == 'union': - tp = model.UnionType(explicit_name, None, None, None) - elif kind == 'enum': - if explicit_name == '__dotdotdot__': - raise CDefError("Enums cannot be declared with ...") - tp = self._build_enum_type(explicit_name, type.values) - else: - raise AssertionError("kind = %r" % (kind,)) - if name is not None: - self._declare(key, tp) - else: - if kind == 'enum' and type.values is not None: - raise NotImplementedError( - "enum %s: the '{}' declaration should appear on the first " - "time the enum is mentioned, not later" % explicit_name) - if not tp.forcename: - tp.force_the_name(force_name) - if tp.forcename and '$' in tp.name: - self._declare('anonymous %s' % tp.forcename, tp) - # - self._structnode2type[type] = tp - # - # enums: done here - if kind == 'enum': - return tp - # - # is there a 'type.decls'? If yes, then this is the place in the - # C sources that declare the fields. If no, then just return the - # existing type, possibly still incomplete. - if type.decls is None: - return tp - # - if tp.fldnames is not None: - raise CDefError("duplicate declaration of struct %s" % name) - fldnames = [] - fldtypes = [] - fldbitsize = [] - fldquals = [] - for decl in type.decls: - if (isinstance(decl.type, pycparser.c_ast.IdentifierType) and - ''.join(decl.type.names) == '__dotdotdot__'): - # XXX pycparser is inconsistent: 'names' should be a list - # of strings, but is sometimes just one string. Use - # str.join() as a way to cope with both. - self._make_partial(tp, nested) - continue - if decl.bitsize is None: - bitsize = -1 - else: - bitsize = self._parse_constant(decl.bitsize) - self._partial_length = False - type, fqual = self._get_type_and_quals(decl.type, - partial_length_ok=True) - if self._partial_length: - self._make_partial(tp, nested) - if isinstance(type, model.StructType) and type.partial: - self._make_partial(tp, nested) - fldnames.append(decl.name or '') - fldtypes.append(type) - fldbitsize.append(bitsize) - fldquals.append(fqual) - tp.fldnames = tuple(fldnames) - tp.fldtypes = tuple(fldtypes) - tp.fldbitsize = tuple(fldbitsize) - tp.fldquals = tuple(fldquals) - if fldbitsize != [-1] * len(fldbitsize): - if isinstance(tp, model.StructType) and tp.partial: - raise NotImplementedError("%s: using both bitfields and '...;'" - % (tp,)) - tp.packed = self._options.get('packed') - if tp.completed: # must be re-completed: it is not opaque any more - tp.completed = 0 - self._recomplete.append(tp) - return tp - - def _make_partial(self, tp, nested): - if not isinstance(tp, model.StructOrUnion): - raise CDefError("%s cannot be partial" % (tp,)) - if not tp.has_c_name() and not nested: - raise NotImplementedError("%s is partial but has no C name" %(tp,)) - tp.partial = True - - def _parse_constant(self, exprnode, partial_length_ok=False): - # for now, limited to expressions that are an immediate number - # or positive/negative number - if isinstance(exprnode, pycparser.c_ast.Constant): - s = exprnode.value - if '0' <= s[0] <= '9': - s = s.rstrip('uUlL') - try: - if s.startswith('0'): - return int(s, 8) - else: - return int(s, 10) - except ValueError: - if len(s) > 1: - if s.lower()[0:2] == '0x': - return int(s, 16) - elif s.lower()[0:2] == '0b': - return int(s, 2) - raise CDefError("invalid constant %r" % (s,)) - elif s[0] == "'" and s[-1] == "'" and ( - len(s) == 3 or (len(s) == 4 and s[1] == "\\")): - return ord(s[-2]) - else: - raise CDefError("invalid constant %r" % (s,)) - # - if (isinstance(exprnode, pycparser.c_ast.UnaryOp) and - exprnode.op == '+'): - return self._parse_constant(exprnode.expr) - # - if (isinstance(exprnode, pycparser.c_ast.UnaryOp) and - exprnode.op == '-'): - return -self._parse_constant(exprnode.expr) - # load previously defined int constant - if (isinstance(exprnode, pycparser.c_ast.ID) and - exprnode.name in self._int_constants): - return self._int_constants[exprnode.name] - # - if (isinstance(exprnode, pycparser.c_ast.ID) and - exprnode.name == '__dotdotdotarray__'): - if partial_length_ok: - self._partial_length = True - return '...' - raise FFIError(":%d: unsupported '[...]' here, cannot derive " - "the actual array length in this context" - % exprnode.coord.line) - # - if isinstance(exprnode, pycparser.c_ast.BinaryOp): - left = self._parse_constant(exprnode.left) - right = self._parse_constant(exprnode.right) - if exprnode.op == '+': - return left + right - elif exprnode.op == '-': - return left - right - elif exprnode.op == '*': - return left * right - elif exprnode.op == '/': - return self._c_div(left, right) - elif exprnode.op == '%': - return left - self._c_div(left, right) * right - elif exprnode.op == '<<': - return left << right - elif exprnode.op == '>>': - return left >> right - elif exprnode.op == '&': - return left & right - elif exprnode.op == '|': - return left | right - elif exprnode.op == '^': - return left ^ right - # - raise FFIError(":%d: unsupported expression: expected a " - "simple numeric constant" % exprnode.coord.line) - - def _c_div(self, a, b): - result = a // b - if ((a < 0) ^ (b < 0)) and (a % b) != 0: - result += 1 - return result - - def _build_enum_type(self, explicit_name, decls): - if decls is not None: - partial = False - enumerators = [] - enumvalues = [] - nextenumvalue = 0 - for enum in decls.enumerators: - if _r_enum_dotdotdot.match(enum.name): - partial = True - continue - if enum.value is not None: - nextenumvalue = self._parse_constant(enum.value) - enumerators.append(enum.name) - enumvalues.append(nextenumvalue) - self._add_constants(enum.name, nextenumvalue) - nextenumvalue += 1 - enumerators = tuple(enumerators) - enumvalues = tuple(enumvalues) - tp = model.EnumType(explicit_name, enumerators, enumvalues) - tp.partial = partial - else: # opaque enum - tp = model.EnumType(explicit_name, (), ()) - return tp - - def include(self, other): - for name, (tp, quals) in other._declarations.items(): - if name.startswith('anonymous $enum_$'): - continue # fix for test_anonymous_enum_include - kind = name.split(' ', 1)[0] - if kind in ('struct', 'union', 'enum', 'anonymous', 'typedef'): - self._declare(name, tp, included=True, quals=quals) - for k, v in other._int_constants.items(): - self._add_constants(k, v) - - def _get_unknown_type(self, decl): - typenames = decl.type.type.names - if typenames == ['__dotdotdot__']: - return model.unknown_type(decl.name) - - if typenames == ['__dotdotdotint__']: - if self._uses_new_feature is None: - self._uses_new_feature = "'typedef int... %s'" % decl.name - return model.UnknownIntegerType(decl.name) - - if typenames == ['__dotdotdotfloat__']: - # note: not for 'long double' so far - if self._uses_new_feature is None: - self._uses_new_feature = "'typedef float... %s'" % decl.name - return model.UnknownFloatType(decl.name) - - raise FFIError(':%d: unsupported usage of "..." in typedef' - % decl.coord.line) - - def _get_unknown_ptr_type(self, decl): - if decl.type.type.type.names == ['__dotdotdot__']: - return model.unknown_ptr_type(decl.name) - raise FFIError(':%d: unsupported usage of "..." in typedef' - % decl.coord.line) diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/error.py b/.venv-docs/lib/python3.12/site-packages/cffi/error.py deleted file mode 100644 index 0a27247c..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi/error.py +++ /dev/null @@ -1,31 +0,0 @@ - -class FFIError(Exception): - __module__ = 'cffi' - -class CDefError(Exception): - __module__ = 'cffi' - def __str__(self): - try: - current_decl = self.args[1] - filename = current_decl.coord.file - linenum = current_decl.coord.line - prefix = '%s:%d: ' % (filename, linenum) - except (AttributeError, TypeError, IndexError): - prefix = '' - return '%s%s' % (prefix, self.args[0]) - -class VerificationError(Exception): - """ An error raised when verification fails - """ - __module__ = 'cffi' - -class VerificationMissing(Exception): - """ An error raised when incomplete structures are passed into - cdef, but no verification has been done - """ - __module__ = 'cffi' - -class PkgConfigError(Exception): - """ An error raised for missing modules in pkg-config - """ - __module__ = 'cffi' diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/ffiplatform.py b/.venv-docs/lib/python3.12/site-packages/cffi/ffiplatform.py deleted file mode 100644 index adca28f1..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi/ffiplatform.py +++ /dev/null @@ -1,113 +0,0 @@ -import sys, os -from .error import VerificationError - - -LIST_OF_FILE_NAMES = ['sources', 'include_dirs', 'library_dirs', - 'extra_objects', 'depends'] - -def get_extension(srcfilename, modname, sources=(), **kwds): - from cffi._shimmed_dist_utils import Extension - allsources = [srcfilename] - for src in sources: - allsources.append(os.path.normpath(src)) - return Extension(name=modname, sources=allsources, **kwds) - -def compile(tmpdir, ext, compiler_verbose=0, debug=None): - """Compile a C extension module using distutils.""" - - saved_environ = os.environ.copy() - try: - outputfilename = _build(tmpdir, ext, compiler_verbose, debug) - outputfilename = os.path.abspath(outputfilename) - finally: - # workaround for a distutils bugs where some env vars can - # become longer and longer every time it is used - for key, value in saved_environ.items(): - if os.environ.get(key) != value: - os.environ[key] = value - return outputfilename - -def _build(tmpdir, ext, compiler_verbose=0, debug=None): - # XXX compact but horrible :-( - from cffi._shimmed_dist_utils import Distribution, CompileError, LinkError, set_threshold, set_verbosity - - dist = Distribution({'ext_modules': [ext]}) - dist.parse_config_files() - options = dist.get_option_dict('build_ext') - if debug is None: - debug = sys.flags.debug - options['debug'] = ('ffiplatform', debug) - options['force'] = ('ffiplatform', True) - options['build_lib'] = ('ffiplatform', tmpdir) - options['build_temp'] = ('ffiplatform', tmpdir) - # - try: - old_level = set_threshold(0) or 0 - try: - set_verbosity(compiler_verbose) - dist.run_command('build_ext') - cmd_obj = dist.get_command_obj('build_ext') - [soname] = cmd_obj.get_outputs() - finally: - set_threshold(old_level) - except (CompileError, LinkError) as e: - raise VerificationError('%s: %s' % (e.__class__.__name__, e)) - # - return soname - -try: - from os.path import samefile -except ImportError: - def samefile(f1, f2): - return os.path.abspath(f1) == os.path.abspath(f2) - -def maybe_relative_path(path): - if not os.path.isabs(path): - return path # already relative - dir = path - names = [] - while True: - prevdir = dir - dir, name = os.path.split(prevdir) - if dir == prevdir or not dir: - return path # failed to make it relative - names.append(name) - try: - if samefile(dir, os.curdir): - names.reverse() - return os.path.join(*names) - except OSError: - pass - -# ____________________________________________________________ - -try: - int_or_long = (int, long) - import cStringIO -except NameError: - int_or_long = int # Python 3 - import io as cStringIO - -def _flatten(x, f): - if isinstance(x, str): - f.write('%ds%s' % (len(x), x)) - elif isinstance(x, dict): - keys = sorted(x.keys()) - f.write('%dd' % len(keys)) - for key in keys: - _flatten(key, f) - _flatten(x[key], f) - elif isinstance(x, (list, tuple)): - f.write('%dl' % len(x)) - for value in x: - _flatten(value, f) - elif isinstance(x, int_or_long): - f.write('%di' % (x,)) - else: - raise TypeError( - "the keywords to verify() contains unsupported object %r" % (x,)) - -def flatten(x): - f = cStringIO.StringIO() - _flatten(x, f) - return f.getvalue() diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/lock.py b/.venv-docs/lib/python3.12/site-packages/cffi/lock.py deleted file mode 100644 index db91b715..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi/lock.py +++ /dev/null @@ -1,30 +0,0 @@ -import sys - -if sys.version_info < (3,): - try: - from thread import allocate_lock - except ImportError: - from dummy_thread import allocate_lock -else: - try: - from _thread import allocate_lock - except ImportError: - from _dummy_thread import allocate_lock - - -##import sys -##l1 = allocate_lock - -##class allocate_lock(object): -## def __init__(self): -## self._real = l1() -## def __enter__(self): -## for i in range(4, 0, -1): -## print sys._getframe(i).f_code -## print -## return self._real.__enter__() -## def __exit__(self, *args): -## return self._real.__exit__(*args) -## def acquire(self, f): -## assert f is False -## return self._real.acquire(f) diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/model.py b/.venv-docs/lib/python3.12/site-packages/cffi/model.py deleted file mode 100644 index e5f4cae3..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi/model.py +++ /dev/null @@ -1,618 +0,0 @@ -import types -import weakref - -from .lock import allocate_lock -from .error import CDefError, VerificationError, VerificationMissing - -# type qualifiers -Q_CONST = 0x01 -Q_RESTRICT = 0x02 -Q_VOLATILE = 0x04 - -def qualify(quals, replace_with): - if quals & Q_CONST: - replace_with = ' const ' + replace_with.lstrip() - if quals & Q_VOLATILE: - replace_with = ' volatile ' + replace_with.lstrip() - if quals & Q_RESTRICT: - # It seems that __restrict is supported by gcc and msvc. - # If you hit some different compiler, add a #define in - # _cffi_include.h for it (and in its copies, documented there) - replace_with = ' __restrict ' + replace_with.lstrip() - return replace_with - - -class BaseTypeByIdentity(object): - is_array_type = False - is_raw_function = False - - def get_c_name(self, replace_with='', context='a C file', quals=0): - result = self.c_name_with_marker - assert result.count('&') == 1 - # some logic duplication with ffi.getctype()... :-( - replace_with = replace_with.strip() - if replace_with: - if replace_with.startswith('*') and '&[' in result: - replace_with = '(%s)' % replace_with - elif not replace_with[0] in '[(': - replace_with = ' ' + replace_with - replace_with = qualify(quals, replace_with) - result = result.replace('&', replace_with) - if '$' in result: - raise VerificationError( - "cannot generate '%s' in %s: unknown type name" - % (self._get_c_name(), context)) - return result - - def _get_c_name(self): - return self.c_name_with_marker.replace('&', '') - - def has_c_name(self): - return '$' not in self._get_c_name() - - def is_integer_type(self): - return False - - def get_cached_btype(self, ffi, finishlist, can_delay=False): - try: - BType = ffi._cached_btypes[self] - except KeyError: - BType = self.build_backend_type(ffi, finishlist) - BType2 = ffi._cached_btypes.setdefault(self, BType) - assert BType2 is BType - return BType - - def __repr__(self): - return '<%s>' % (self._get_c_name(),) - - def _get_items(self): - return [(name, getattr(self, name)) for name in self._attrs_] - - -class BaseType(BaseTypeByIdentity): - - def __eq__(self, other): - return (self.__class__ == other.__class__ and - self._get_items() == other._get_items()) - - def __ne__(self, other): - return not self == other - - def __hash__(self): - return hash((self.__class__, tuple(self._get_items()))) - - -class VoidType(BaseType): - _attrs_ = () - - def __init__(self): - self.c_name_with_marker = 'void&' - - def build_backend_type(self, ffi, finishlist): - return global_cache(self, ffi, 'new_void_type') - -void_type = VoidType() - - -class BasePrimitiveType(BaseType): - def is_complex_type(self): - return False - - -class PrimitiveType(BasePrimitiveType): - _attrs_ = ('name',) - - ALL_PRIMITIVE_TYPES = { - 'char': 'c', - 'short': 'i', - 'int': 'i', - 'long': 'i', - 'long long': 'i', - 'signed char': 'i', - 'unsigned char': 'i', - 'unsigned short': 'i', - 'unsigned int': 'i', - 'unsigned long': 'i', - 'unsigned long long': 'i', - 'float': 'f', - 'double': 'f', - 'long double': 'f', - '_cffi_float_complex_t': 'j', - '_cffi_double_complex_t': 'j', - '_Bool': 'i', - # the following types are not primitive in the C sense - 'wchar_t': 'c', - 'char16_t': 'c', - 'char32_t': 'c', - 'int8_t': 'i', - 'uint8_t': 'i', - 'int16_t': 'i', - 'uint16_t': 'i', - 'int32_t': 'i', - 'uint32_t': 'i', - 'int64_t': 'i', - 'uint64_t': 'i', - 'int_least8_t': 'i', - 'uint_least8_t': 'i', - 'int_least16_t': 'i', - 'uint_least16_t': 'i', - 'int_least32_t': 'i', - 'uint_least32_t': 'i', - 'int_least64_t': 'i', - 'uint_least64_t': 'i', - 'int_fast8_t': 'i', - 'uint_fast8_t': 'i', - 'int_fast16_t': 'i', - 'uint_fast16_t': 'i', - 'int_fast32_t': 'i', - 'uint_fast32_t': 'i', - 'int_fast64_t': 'i', - 'uint_fast64_t': 'i', - 'intptr_t': 'i', - 'uintptr_t': 'i', - 'intmax_t': 'i', - 'uintmax_t': 'i', - 'ptrdiff_t': 'i', - 'size_t': 'i', - 'ssize_t': 'i', - } - - def __init__(self, name): - assert name in self.ALL_PRIMITIVE_TYPES - self.name = name - self.c_name_with_marker = name + '&' - - def is_char_type(self): - return self.ALL_PRIMITIVE_TYPES[self.name] == 'c' - def is_integer_type(self): - return self.ALL_PRIMITIVE_TYPES[self.name] == 'i' - def is_float_type(self): - return self.ALL_PRIMITIVE_TYPES[self.name] == 'f' - def is_complex_type(self): - return self.ALL_PRIMITIVE_TYPES[self.name] == 'j' - - def build_backend_type(self, ffi, finishlist): - return global_cache(self, ffi, 'new_primitive_type', self.name) - - -class UnknownIntegerType(BasePrimitiveType): - _attrs_ = ('name',) - - def __init__(self, name): - self.name = name - self.c_name_with_marker = name + '&' - - def is_integer_type(self): - return True - - def build_backend_type(self, ffi, finishlist): - raise NotImplementedError("integer type '%s' can only be used after " - "compilation" % self.name) - -class UnknownFloatType(BasePrimitiveType): - _attrs_ = ('name', ) - - def __init__(self, name): - self.name = name - self.c_name_with_marker = name + '&' - - def build_backend_type(self, ffi, finishlist): - raise NotImplementedError("float type '%s' can only be used after " - "compilation" % self.name) - - -class BaseFunctionType(BaseType): - _attrs_ = ('args', 'result', 'ellipsis', 'abi') - - def __init__(self, args, result, ellipsis, abi=None): - self.args = args - self.result = result - self.ellipsis = ellipsis - self.abi = abi - # - reprargs = [arg._get_c_name() for arg in self.args] - if self.ellipsis: - reprargs.append('...') - reprargs = reprargs or ['void'] - replace_with = self._base_pattern % (', '.join(reprargs),) - if abi is not None: - replace_with = replace_with[:1] + abi + ' ' + replace_with[1:] - self.c_name_with_marker = ( - self.result.c_name_with_marker.replace('&', replace_with)) - - -class RawFunctionType(BaseFunctionType): - # Corresponds to a C type like 'int(int)', which is the C type of - # a function, but not a pointer-to-function. The backend has no - # notion of such a type; it's used temporarily by parsing. - _base_pattern = '(&)(%s)' - is_raw_function = True - - def build_backend_type(self, ffi, finishlist): - raise CDefError("cannot render the type %r: it is a function " - "type, not a pointer-to-function type" % (self,)) - - def as_function_pointer(self): - return FunctionPtrType(self.args, self.result, self.ellipsis, self.abi) - - -class FunctionPtrType(BaseFunctionType): - _base_pattern = '(*&)(%s)' - - def build_backend_type(self, ffi, finishlist): - result = self.result.get_cached_btype(ffi, finishlist) - args = [] - for tp in self.args: - args.append(tp.get_cached_btype(ffi, finishlist)) - abi_args = () - if self.abi == "__stdcall": - if not self.ellipsis: # __stdcall ignored for variadic funcs - try: - abi_args = (ffi._backend.FFI_STDCALL,) - except AttributeError: - pass - return global_cache(self, ffi, 'new_function_type', - tuple(args), result, self.ellipsis, *abi_args) - - def as_raw_function(self): - return RawFunctionType(self.args, self.result, self.ellipsis, self.abi) - - -class PointerType(BaseType): - _attrs_ = ('totype', 'quals') - - def __init__(self, totype, quals=0): - self.totype = totype - self.quals = quals - extra = " *&" - if totype.is_array_type: - extra = "(%s)" % (extra.lstrip(),) - extra = qualify(quals, extra) - self.c_name_with_marker = totype.c_name_with_marker.replace('&', extra) - - def build_backend_type(self, ffi, finishlist): - BItem = self.totype.get_cached_btype(ffi, finishlist, can_delay=True) - return global_cache(self, ffi, 'new_pointer_type', BItem) - -voidp_type = PointerType(void_type) - -def ConstPointerType(totype): - return PointerType(totype, Q_CONST) - -const_voidp_type = ConstPointerType(void_type) - - -class NamedPointerType(PointerType): - _attrs_ = ('totype', 'name') - - def __init__(self, totype, name, quals=0): - PointerType.__init__(self, totype, quals) - self.name = name - self.c_name_with_marker = name + '&' - - -class ArrayType(BaseType): - _attrs_ = ('item', 'length') - is_array_type = True - - def __init__(self, item, length): - self.item = item - self.length = length - # - if length is None: - brackets = '&[]' - elif length == '...': - brackets = '&[/*...*/]' - else: - brackets = '&[%s]' % length - self.c_name_with_marker = ( - self.item.c_name_with_marker.replace('&', brackets)) - - def length_is_unknown(self): - return isinstance(self.length, str) - - def resolve_length(self, newlength): - return ArrayType(self.item, newlength) - - def build_backend_type(self, ffi, finishlist): - if self.length_is_unknown(): - raise CDefError("cannot render the type %r: unknown length" % - (self,)) - self.item.get_cached_btype(ffi, finishlist) # force the item BType - BPtrItem = PointerType(self.item).get_cached_btype(ffi, finishlist) - return global_cache(self, ffi, 'new_array_type', BPtrItem, self.length) - -char_array_type = ArrayType(PrimitiveType('char'), None) - - -class StructOrUnionOrEnum(BaseTypeByIdentity): - _attrs_ = ('name',) - forcename = None - - def build_c_name_with_marker(self): - name = self.forcename or '%s %s' % (self.kind, self.name) - self.c_name_with_marker = name + '&' - - def force_the_name(self, forcename): - self.forcename = forcename - self.build_c_name_with_marker() - - def get_official_name(self): - assert self.c_name_with_marker.endswith('&') - return self.c_name_with_marker[:-1] - - -class StructOrUnion(StructOrUnionOrEnum): - fixedlayout = None - completed = 0 - partial = False - packed = 0 - - def __init__(self, name, fldnames, fldtypes, fldbitsize, fldquals=None): - self.name = name - self.fldnames = fldnames - self.fldtypes = fldtypes - self.fldbitsize = fldbitsize - self.fldquals = fldquals - self.build_c_name_with_marker() - - def anonymous_struct_fields(self): - if self.fldtypes is not None: - for name, type in zip(self.fldnames, self.fldtypes): - if name == '' and isinstance(type, StructOrUnion): - yield type - - def enumfields(self, expand_anonymous_struct_union=True): - fldquals = self.fldquals - if fldquals is None: - fldquals = (0,) * len(self.fldnames) - for name, type, bitsize, quals in zip(self.fldnames, self.fldtypes, - self.fldbitsize, fldquals): - if (name == '' and isinstance(type, StructOrUnion) - and expand_anonymous_struct_union): - # nested anonymous struct/union - for result in type.enumfields(): - yield result - else: - yield (name, type, bitsize, quals) - - def force_flatten(self): - # force the struct or union to have a declaration that lists - # directly all fields returned by enumfields(), flattening - # nested anonymous structs/unions. - names = [] - types = [] - bitsizes = [] - fldquals = [] - for name, type, bitsize, quals in self.enumfields(): - names.append(name) - types.append(type) - bitsizes.append(bitsize) - fldquals.append(quals) - self.fldnames = tuple(names) - self.fldtypes = tuple(types) - self.fldbitsize = tuple(bitsizes) - self.fldquals = tuple(fldquals) - - def get_cached_btype(self, ffi, finishlist, can_delay=False): - BType = StructOrUnionOrEnum.get_cached_btype(self, ffi, finishlist, - can_delay) - if not can_delay: - self.finish_backend_type(ffi, finishlist) - return BType - - def finish_backend_type(self, ffi, finishlist): - if self.completed: - if self.completed != 2: - raise NotImplementedError("recursive structure declaration " - "for '%s'" % (self.name,)) - return - BType = ffi._cached_btypes[self] - # - self.completed = 1 - # - if self.fldtypes is None: - pass # not completing it: it's an opaque struct - # - elif self.fixedlayout is None: - fldtypes = [tp.get_cached_btype(ffi, finishlist) - for tp in self.fldtypes] - lst = list(zip(self.fldnames, fldtypes, self.fldbitsize)) - extra_flags = () - if self.packed: - if self.packed == 1: - extra_flags = (8,) # SF_PACKED - else: - extra_flags = (0, self.packed) - ffi._backend.complete_struct_or_union(BType, lst, self, - -1, -1, *extra_flags) - # - else: - fldtypes = [] - fieldofs, fieldsize, totalsize, totalalignment = self.fixedlayout - for i in range(len(self.fldnames)): - fsize = fieldsize[i] - ftype = self.fldtypes[i] - # - if isinstance(ftype, ArrayType) and ftype.length_is_unknown(): - # fix the length to match the total size - BItemType = ftype.item.get_cached_btype(ffi, finishlist) - nlen, nrest = divmod(fsize, ffi.sizeof(BItemType)) - if nrest != 0: - self._verification_error( - "field '%s.%s' has a bogus size?" % ( - self.name, self.fldnames[i] or '{}')) - ftype = ftype.resolve_length(nlen) - self.fldtypes = (self.fldtypes[:i] + (ftype,) + - self.fldtypes[i+1:]) - # - BFieldType = ftype.get_cached_btype(ffi, finishlist) - if isinstance(ftype, ArrayType) and ftype.length is None: - assert fsize == 0 - else: - bitemsize = ffi.sizeof(BFieldType) - if bitemsize != fsize: - self._verification_error( - "field '%s.%s' is declared as %d bytes, but is " - "really %d bytes" % (self.name, - self.fldnames[i] or '{}', - bitemsize, fsize)) - fldtypes.append(BFieldType) - # - lst = list(zip(self.fldnames, fldtypes, self.fldbitsize, fieldofs)) - ffi._backend.complete_struct_or_union(BType, lst, self, - totalsize, totalalignment) - self.completed = 2 - - def _verification_error(self, msg): - raise VerificationError(msg) - - def check_not_partial(self): - if self.partial and self.fixedlayout is None: - raise VerificationMissing(self._get_c_name()) - - def build_backend_type(self, ffi, finishlist): - self.check_not_partial() - finishlist.append(self) - # - return global_cache(self, ffi, 'new_%s_type' % self.kind, - self.get_official_name(), key=self) - - -class StructType(StructOrUnion): - kind = 'struct' - - -class UnionType(StructOrUnion): - kind = 'union' - - -class EnumType(StructOrUnionOrEnum): - kind = 'enum' - partial = False - partial_resolved = False - - def __init__(self, name, enumerators, enumvalues, baseinttype=None): - self.name = name - self.enumerators = enumerators - self.enumvalues = enumvalues - self.baseinttype = baseinttype - self.build_c_name_with_marker() - - def force_the_name(self, forcename): - StructOrUnionOrEnum.force_the_name(self, forcename) - if self.forcename is None: - name = self.get_official_name() - self.forcename = '$' + name.replace(' ', '_') - - def check_not_partial(self): - if self.partial and not self.partial_resolved: - raise VerificationMissing(self._get_c_name()) - - def build_backend_type(self, ffi, finishlist): - self.check_not_partial() - base_btype = self.build_baseinttype(ffi, finishlist) - return global_cache(self, ffi, 'new_enum_type', - self.get_official_name(), - self.enumerators, self.enumvalues, - base_btype, key=self) - - def build_baseinttype(self, ffi, finishlist): - if self.baseinttype is not None: - return self.baseinttype.get_cached_btype(ffi, finishlist) - # - if self.enumvalues: - smallest_value = min(self.enumvalues) - largest_value = max(self.enumvalues) - else: - import warnings - try: - # XXX! The goal is to ensure that the warnings.warn() - # will not suppress the warning. We want to get it - # several times if we reach this point several times. - __warningregistry__.clear() - except NameError: - pass - warnings.warn("%r has no values explicitly defined; " - "guessing that it is equivalent to 'unsigned int'" - % self._get_c_name()) - smallest_value = largest_value = 0 - if smallest_value < 0: # needs a signed type - sign = 1 - candidate1 = PrimitiveType("int") - candidate2 = PrimitiveType("long") - else: - sign = 0 - candidate1 = PrimitiveType("unsigned int") - candidate2 = PrimitiveType("unsigned long") - btype1 = candidate1.get_cached_btype(ffi, finishlist) - btype2 = candidate2.get_cached_btype(ffi, finishlist) - size1 = ffi.sizeof(btype1) - size2 = ffi.sizeof(btype2) - if (smallest_value >= ((-1) << (8*size1-1)) and - largest_value < (1 << (8*size1-sign))): - return btype1 - if (smallest_value >= ((-1) << (8*size2-1)) and - largest_value < (1 << (8*size2-sign))): - return btype2 - raise CDefError("%s values don't all fit into either 'long' " - "or 'unsigned long'" % self._get_c_name()) - -def unknown_type(name, structname=None): - if structname is None: - structname = '$%s' % name - tp = StructType(structname, None, None, None) - tp.force_the_name(name) - tp.origin = "unknown_type" - return tp - -def unknown_ptr_type(name, structname=None): - if structname is None: - structname = '$$%s' % name - tp = StructType(structname, None, None, None) - return NamedPointerType(tp, name) - - -global_lock = allocate_lock() -_typecache_cffi_backend = weakref.WeakValueDictionary() - -def get_typecache(backend): - # returns _typecache_cffi_backend if backend is the _cffi_backend - # module, or type(backend).__typecache if backend is an instance of - # CTypesBackend (or some FakeBackend class during tests) - if isinstance(backend, types.ModuleType): - return _typecache_cffi_backend - with global_lock: - if not hasattr(type(backend), '__typecache'): - type(backend).__typecache = weakref.WeakValueDictionary() - return type(backend).__typecache - -def global_cache(srctype, ffi, funcname, *args, **kwds): - key = kwds.pop('key', (funcname, args)) - assert not kwds - try: - return ffi._typecache[key] - except KeyError: - pass - try: - res = getattr(ffi._backend, funcname)(*args) - except NotImplementedError as e: - raise NotImplementedError("%s: %r: %s" % (funcname, srctype, e)) - # note that setdefault() on WeakValueDictionary is not atomic - # and contains a rare bug (http://bugs.python.org/issue19542); - # we have to use a lock and do it ourselves - cache = ffi._typecache - with global_lock: - res1 = cache.get(key) - if res1 is None: - cache[key] = res - return res - else: - return res1 - -def pointer_cache(ffi, BType): - return global_cache('?', ffi, 'new_pointer_type', BType) - -def attach_exception_info(e, name): - if e.args and type(e.args[0]) is str: - e.args = ('%s: %s' % (name, e.args[0]),) + e.args[1:] diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/parse_c_type.h b/.venv-docs/lib/python3.12/site-packages/cffi/parse_c_type.h deleted file mode 100644 index 84e4ef85..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi/parse_c_type.h +++ /dev/null @@ -1,181 +0,0 @@ - -/* This part is from file 'cffi/parse_c_type.h'. It is copied at the - beginning of C sources generated by CFFI's ffi.set_source(). */ - -typedef void *_cffi_opcode_t; - -#define _CFFI_OP(opcode, arg) (_cffi_opcode_t)(opcode | (((uintptr_t)(arg)) << 8)) -#define _CFFI_GETOP(cffi_opcode) ((unsigned char)(uintptr_t)cffi_opcode) -#define _CFFI_GETARG(cffi_opcode) (((intptr_t)cffi_opcode) >> 8) - -#define _CFFI_OP_PRIMITIVE 1 -#define _CFFI_OP_POINTER 3 -#define _CFFI_OP_ARRAY 5 -#define _CFFI_OP_OPEN_ARRAY 7 -#define _CFFI_OP_STRUCT_UNION 9 -#define _CFFI_OP_ENUM 11 -#define _CFFI_OP_FUNCTION 13 -#define _CFFI_OP_FUNCTION_END 15 -#define _CFFI_OP_NOOP 17 -#define _CFFI_OP_BITFIELD 19 -#define _CFFI_OP_TYPENAME 21 -#define _CFFI_OP_CPYTHON_BLTN_V 23 // varargs -#define _CFFI_OP_CPYTHON_BLTN_N 25 // noargs -#define _CFFI_OP_CPYTHON_BLTN_O 27 // O (i.e. a single arg) -#define _CFFI_OP_CONSTANT 29 -#define _CFFI_OP_CONSTANT_INT 31 -#define _CFFI_OP_GLOBAL_VAR 33 -#define _CFFI_OP_DLOPEN_FUNC 35 -#define _CFFI_OP_DLOPEN_CONST 37 -#define _CFFI_OP_GLOBAL_VAR_F 39 -#define _CFFI_OP_EXTERN_PYTHON 41 - -#define _CFFI_PRIM_VOID 0 -#define _CFFI_PRIM_BOOL 1 -#define _CFFI_PRIM_CHAR 2 -#define _CFFI_PRIM_SCHAR 3 -#define _CFFI_PRIM_UCHAR 4 -#define _CFFI_PRIM_SHORT 5 -#define _CFFI_PRIM_USHORT 6 -#define _CFFI_PRIM_INT 7 -#define _CFFI_PRIM_UINT 8 -#define _CFFI_PRIM_LONG 9 -#define _CFFI_PRIM_ULONG 10 -#define _CFFI_PRIM_LONGLONG 11 -#define _CFFI_PRIM_ULONGLONG 12 -#define _CFFI_PRIM_FLOAT 13 -#define _CFFI_PRIM_DOUBLE 14 -#define _CFFI_PRIM_LONGDOUBLE 15 - -#define _CFFI_PRIM_WCHAR 16 -#define _CFFI_PRIM_INT8 17 -#define _CFFI_PRIM_UINT8 18 -#define _CFFI_PRIM_INT16 19 -#define _CFFI_PRIM_UINT16 20 -#define _CFFI_PRIM_INT32 21 -#define _CFFI_PRIM_UINT32 22 -#define _CFFI_PRIM_INT64 23 -#define _CFFI_PRIM_UINT64 24 -#define _CFFI_PRIM_INTPTR 25 -#define _CFFI_PRIM_UINTPTR 26 -#define _CFFI_PRIM_PTRDIFF 27 -#define _CFFI_PRIM_SIZE 28 -#define _CFFI_PRIM_SSIZE 29 -#define _CFFI_PRIM_INT_LEAST8 30 -#define _CFFI_PRIM_UINT_LEAST8 31 -#define _CFFI_PRIM_INT_LEAST16 32 -#define _CFFI_PRIM_UINT_LEAST16 33 -#define _CFFI_PRIM_INT_LEAST32 34 -#define _CFFI_PRIM_UINT_LEAST32 35 -#define _CFFI_PRIM_INT_LEAST64 36 -#define _CFFI_PRIM_UINT_LEAST64 37 -#define _CFFI_PRIM_INT_FAST8 38 -#define _CFFI_PRIM_UINT_FAST8 39 -#define _CFFI_PRIM_INT_FAST16 40 -#define _CFFI_PRIM_UINT_FAST16 41 -#define _CFFI_PRIM_INT_FAST32 42 -#define _CFFI_PRIM_UINT_FAST32 43 -#define _CFFI_PRIM_INT_FAST64 44 -#define _CFFI_PRIM_UINT_FAST64 45 -#define _CFFI_PRIM_INTMAX 46 -#define _CFFI_PRIM_UINTMAX 47 -#define _CFFI_PRIM_FLOATCOMPLEX 48 -#define _CFFI_PRIM_DOUBLECOMPLEX 49 -#define _CFFI_PRIM_CHAR16 50 -#define _CFFI_PRIM_CHAR32 51 - -#define _CFFI__NUM_PRIM 52 -#define _CFFI__UNKNOWN_PRIM (-1) -#define _CFFI__UNKNOWN_FLOAT_PRIM (-2) -#define _CFFI__UNKNOWN_LONG_DOUBLE (-3) - -#define _CFFI__IO_FILE_STRUCT (-1) - - -struct _cffi_global_s { - const char *name; - void *address; - _cffi_opcode_t type_op; - void *size_or_direct_fn; // OP_GLOBAL_VAR: size, or 0 if unknown - // OP_CPYTHON_BLTN_*: addr of direct function -}; - -struct _cffi_getconst_s { - unsigned long long value; - const struct _cffi_type_context_s *ctx; - int gindex; -}; - -struct _cffi_struct_union_s { - const char *name; - int type_index; // -> _cffi_types, on a OP_STRUCT_UNION - int flags; // _CFFI_F_* flags below - size_t size; - int alignment; - int first_field_index; // -> _cffi_fields array - int num_fields; -}; -#define _CFFI_F_UNION 0x01 // is a union, not a struct -#define _CFFI_F_CHECK_FIELDS 0x02 // complain if fields are not in the - // "standard layout" or if some are missing -#define _CFFI_F_PACKED 0x04 // for CHECK_FIELDS, assume a packed struct -#define _CFFI_F_EXTERNAL 0x08 // in some other ffi.include() -#define _CFFI_F_OPAQUE 0x10 // opaque - -struct _cffi_field_s { - const char *name; - size_t field_offset; - size_t field_size; - _cffi_opcode_t field_type_op; -}; - -struct _cffi_enum_s { - const char *name; - int type_index; // -> _cffi_types, on a OP_ENUM - int type_prim; // _CFFI_PRIM_xxx - const char *enumerators; // comma-delimited string -}; - -struct _cffi_typename_s { - const char *name; - int type_index; /* if opaque, points to a possibly artificial - OP_STRUCT which is itself opaque */ -}; - -struct _cffi_type_context_s { - _cffi_opcode_t *types; - const struct _cffi_global_s *globals; - const struct _cffi_field_s *fields; - const struct _cffi_struct_union_s *struct_unions; - const struct _cffi_enum_s *enums; - const struct _cffi_typename_s *typenames; - int num_globals; - int num_struct_unions; - int num_enums; - int num_typenames; - const char *const *includes; - int num_types; - int flags; /* future extension */ -}; - -struct _cffi_parse_info_s { - const struct _cffi_type_context_s *ctx; - _cffi_opcode_t *output; - unsigned int output_size; - size_t error_location; - const char *error_message; -}; - -struct _cffi_externpy_s { - const char *name; - size_t size_of_result; - void *reserved1, *reserved2; -}; - -#ifdef _CFFI_INTERNAL -static int parse_c_type(struct _cffi_parse_info_s *info, const char *input); -static int search_in_globals(const struct _cffi_type_context_s *ctx, - const char *search, size_t search_len); -static int search_in_struct_unions(const struct _cffi_type_context_s *ctx, - const char *search, size_t search_len); -#endif diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/pkgconfig.py b/.venv-docs/lib/python3.12/site-packages/cffi/pkgconfig.py deleted file mode 100644 index 5c93f15a..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi/pkgconfig.py +++ /dev/null @@ -1,121 +0,0 @@ -# pkg-config, https://www.freedesktop.org/wiki/Software/pkg-config/ integration for cffi -import sys, os, subprocess - -from .error import PkgConfigError - - -def merge_flags(cfg1, cfg2): - """Merge values from cffi config flags cfg2 to cf1 - - Example: - merge_flags({"libraries": ["one"]}, {"libraries": ["two"]}) - {"libraries": ["one", "two"]} - """ - for key, value in cfg2.items(): - if key not in cfg1: - cfg1[key] = value - else: - if not isinstance(cfg1[key], list): - raise TypeError("cfg1[%r] should be a list of strings" % (key,)) - if not isinstance(value, list): - raise TypeError("cfg2[%r] should be a list of strings" % (key,)) - cfg1[key].extend(value) - return cfg1 - - -def call(libname, flag, encoding=sys.getfilesystemencoding()): - """Calls pkg-config and returns the output if found - """ - a = ["pkg-config", "--print-errors"] - a.append(flag) - a.append(libname) - try: - pc = subprocess.Popen(a, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - except EnvironmentError as e: - raise PkgConfigError("cannot run pkg-config: %s" % (str(e).strip(),)) - - bout, berr = pc.communicate() - if pc.returncode != 0: - try: - berr = berr.decode(encoding) - except Exception: - pass - raise PkgConfigError(berr.strip()) - - if sys.version_info >= (3,) and not isinstance(bout, str): # Python 3.x - try: - bout = bout.decode(encoding) - except UnicodeDecodeError: - raise PkgConfigError("pkg-config %s %s returned bytes that cannot " - "be decoded with encoding %r:\n%r" % - (flag, libname, encoding, bout)) - - if os.altsep != '\\' and '\\' in bout: - raise PkgConfigError("pkg-config %s %s returned an unsupported " - "backslash-escaped output:\n%r" % - (flag, libname, bout)) - return bout - - -def flags_from_pkgconfig(libs): - r"""Return compiler line flags for FFI.set_source based on pkg-config output - - Usage - ... - ffibuilder.set_source("_foo", pkgconfig = ["libfoo", "libbar >= 1.8.3"]) - - If pkg-config is installed on build machine, then arguments include_dirs, - library_dirs, libraries, define_macros, extra_compile_args and - extra_link_args are extended with an output of pkg-config for libfoo and - libbar. - - Raises PkgConfigError in case the pkg-config call fails. - """ - - def get_include_dirs(string): - return [x[2:] for x in string.split() if x.startswith("-I")] - - def get_library_dirs(string): - return [x[2:] for x in string.split() if x.startswith("-L")] - - def get_libraries(string): - return [x[2:] for x in string.split() if x.startswith("-l")] - - # convert -Dfoo=bar to list of tuples [("foo", "bar")] expected by distutils - def get_macros(string): - def _macro(x): - x = x[2:] # drop "-D" - if '=' in x: - return tuple(x.split("=", 1)) # "-Dfoo=bar" => ("foo", "bar") - else: - return (x, None) # "-Dfoo" => ("foo", None) - return [_macro(x) for x in string.split() if x.startswith("-D")] - - def get_other_cflags(string): - return [x for x in string.split() if not x.startswith("-I") and - not x.startswith("-D")] - - def get_other_libs(string): - return [x for x in string.split() if not x.startswith("-L") and - not x.startswith("-l")] - - # return kwargs for given libname - def kwargs(libname): - fse = sys.getfilesystemencoding() - all_cflags = call(libname, "--cflags") - all_libs = call(libname, "--libs") - return { - "include_dirs": get_include_dirs(all_cflags), - "library_dirs": get_library_dirs(all_libs), - "libraries": get_libraries(all_libs), - "define_macros": get_macros(all_cflags), - "extra_compile_args": get_other_cflags(all_cflags), - "extra_link_args": get_other_libs(all_libs), - } - - # merge all arguments together - ret = {} - for libname in libs: - lib_flags = kwargs(libname) - merge_flags(ret, lib_flags) - return ret diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/recompiler.py b/.venv-docs/lib/python3.12/site-packages/cffi/recompiler.py deleted file mode 100644 index 7734a348..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi/recompiler.py +++ /dev/null @@ -1,1598 +0,0 @@ -import io, os, sys, sysconfig -from . import ffiplatform, model -from .error import VerificationError -from .cffi_opcode import * - -VERSION_BASE = 0x2601 -VERSION_EMBEDDED = 0x2701 -VERSION_CHAR16CHAR32 = 0x2801 - -USE_LIMITED_API = ((sys.platform != 'win32' or sys.version_info < (3, 0) or - sys.version_info >= (3, 5)) and - not sysconfig.get_config_var("Py_GIL_DISABLED")) # free-threaded doesn't yet support limited API - -class GlobalExpr: - def __init__(self, name, address, type_op, size=0, check_value=0): - self.name = name - self.address = address - self.type_op = type_op - self.size = size - self.check_value = check_value - - def as_c_expr(self): - return ' { "%s", (void *)%s, %s, (void *)%s },' % ( - self.name, self.address, self.type_op.as_c_expr(), self.size) - - def as_python_expr(self): - return "b'%s%s',%d" % (self.type_op.as_python_bytes(), self.name, - self.check_value) - -class FieldExpr: - def __init__(self, name, field_offset, field_size, fbitsize, field_type_op): - self.name = name - self.field_offset = field_offset - self.field_size = field_size - self.fbitsize = fbitsize - self.field_type_op = field_type_op - - def as_c_expr(self): - spaces = " " * len(self.name) - return (' { "%s", %s,\n' % (self.name, self.field_offset) + - ' %s %s,\n' % (spaces, self.field_size) + - ' %s %s },' % (spaces, self.field_type_op.as_c_expr())) - - def as_python_expr(self): - raise NotImplementedError - - def as_field_python_expr(self): - if self.field_type_op.op == OP_NOOP: - size_expr = '' - elif self.field_type_op.op == OP_BITFIELD: - size_expr = format_four_bytes(self.fbitsize) - else: - raise NotImplementedError - return "b'%s%s%s'" % (self.field_type_op.as_python_bytes(), - size_expr, - self.name) - -class StructUnionExpr: - def __init__(self, name, type_index, flags, size, alignment, comment, - first_field_index, c_fields): - self.name = name - self.type_index = type_index - self.flags = flags - self.size = size - self.alignment = alignment - self.comment = comment - self.first_field_index = first_field_index - self.c_fields = c_fields - - def as_c_expr(self): - return (' { "%s", %d, %s,' % (self.name, self.type_index, self.flags) - + '\n %s, %s, ' % (self.size, self.alignment) - + '%d, %d ' % (self.first_field_index, len(self.c_fields)) - + ('/* %s */ ' % self.comment if self.comment else '') - + '},') - - def as_python_expr(self): - flags = eval(self.flags, G_FLAGS) - fields_expr = [c_field.as_field_python_expr() - for c_field in self.c_fields] - return "(b'%s%s%s',%s)" % ( - format_four_bytes(self.type_index), - format_four_bytes(flags), - self.name, - ','.join(fields_expr)) - -class EnumExpr: - def __init__(self, name, type_index, size, signed, allenums): - self.name = name - self.type_index = type_index - self.size = size - self.signed = signed - self.allenums = allenums - - def as_c_expr(self): - return (' { "%s", %d, _cffi_prim_int(%s, %s),\n' - ' "%s" },' % (self.name, self.type_index, - self.size, self.signed, self.allenums)) - - def as_python_expr(self): - prim_index = { - (1, 0): PRIM_UINT8, (1, 1): PRIM_INT8, - (2, 0): PRIM_UINT16, (2, 1): PRIM_INT16, - (4, 0): PRIM_UINT32, (4, 1): PRIM_INT32, - (8, 0): PRIM_UINT64, (8, 1): PRIM_INT64, - }[self.size, self.signed] - return "b'%s%s%s\\x00%s'" % (format_four_bytes(self.type_index), - format_four_bytes(prim_index), - self.name, self.allenums) - -class TypenameExpr: - def __init__(self, name, type_index): - self.name = name - self.type_index = type_index - - def as_c_expr(self): - return ' { "%s", %d },' % (self.name, self.type_index) - - def as_python_expr(self): - return "b'%s%s'" % (format_four_bytes(self.type_index), self.name) - - -# ____________________________________________________________ - - -class Recompiler: - _num_externpy = 0 - - def __init__(self, ffi, module_name, target_is_python=False): - self.ffi = ffi - self.module_name = module_name - self.target_is_python = target_is_python - self._version = VERSION_BASE - - def needs_version(self, ver): - self._version = max(self._version, ver) - - def collect_type_table(self): - self._typesdict = {} - self._generate("collecttype") - # - all_decls = sorted(self._typesdict, key=str) - # - # prepare all FUNCTION bytecode sequences first - self.cffi_types = [] - for tp in all_decls: - if tp.is_raw_function: - assert self._typesdict[tp] is None - self._typesdict[tp] = len(self.cffi_types) - self.cffi_types.append(tp) # placeholder - for tp1 in tp.args: - assert isinstance(tp1, (model.VoidType, - model.BasePrimitiveType, - model.PointerType, - model.StructOrUnionOrEnum, - model.FunctionPtrType)) - if self._typesdict[tp1] is None: - self._typesdict[tp1] = len(self.cffi_types) - self.cffi_types.append(tp1) # placeholder - self.cffi_types.append('END') # placeholder - # - # prepare all OTHER bytecode sequences - for tp in all_decls: - if not tp.is_raw_function and self._typesdict[tp] is None: - self._typesdict[tp] = len(self.cffi_types) - self.cffi_types.append(tp) # placeholder - if tp.is_array_type and tp.length is not None: - self.cffi_types.append('LEN') # placeholder - assert None not in self._typesdict.values() - # - # collect all structs and unions and enums - self._struct_unions = {} - self._enums = {} - for tp in all_decls: - if isinstance(tp, model.StructOrUnion): - self._struct_unions[tp] = None - elif isinstance(tp, model.EnumType): - self._enums[tp] = None - for i, tp in enumerate(sorted(self._struct_unions, - key=lambda tp: tp.name)): - self._struct_unions[tp] = i - for i, tp in enumerate(sorted(self._enums, - key=lambda tp: tp.name)): - self._enums[tp] = i - # - # emit all bytecode sequences now - for tp in all_decls: - method = getattr(self, '_emit_bytecode_' + tp.__class__.__name__) - method(tp, self._typesdict[tp]) - # - # consistency check - for op in self.cffi_types: - assert isinstance(op, CffiOp) - self.cffi_types = tuple(self.cffi_types) # don't change any more - - def _enum_fields(self, tp): - # When producing C, expand all anonymous struct/union fields. - # That's necessary to have C code checking the offsets of the - # individual fields contained in them. When producing Python, - # don't do it and instead write it like it is, with the - # corresponding fields having an empty name. Empty names are - # recognized at runtime when we import the generated Python - # file. - expand_anonymous_struct_union = not self.target_is_python - return tp.enumfields(expand_anonymous_struct_union) - - def _do_collect_type(self, tp): - if not isinstance(tp, model.BaseTypeByIdentity): - if isinstance(tp, tuple): - for x in tp: - self._do_collect_type(x) - return - if tp not in self._typesdict: - self._typesdict[tp] = None - if isinstance(tp, model.FunctionPtrType): - self._do_collect_type(tp.as_raw_function()) - elif isinstance(tp, model.StructOrUnion): - if tp.fldtypes is not None and ( - tp not in self.ffi._parser._included_declarations): - for name1, tp1, _, _ in self._enum_fields(tp): - self._do_collect_type(self._field_type(tp, name1, tp1)) - else: - for _, x in tp._get_items(): - self._do_collect_type(x) - - def _generate(self, step_name): - lst = self.ffi._parser._declarations.items() - for name, (tp, quals) in sorted(lst): - kind, realname = name.split(' ', 1) - try: - method = getattr(self, '_generate_cpy_%s_%s' % (kind, - step_name)) - except AttributeError: - raise VerificationError( - "not implemented in recompile(): %r" % name) - try: - self._current_quals = quals - method(tp, realname) - except Exception as e: - model.attach_exception_info(e, name) - raise - - # ---------- - - ALL_STEPS = ["global", "field", "struct_union", "enum", "typename"] - - def collect_step_tables(self): - # collect the declarations for '_cffi_globals', '_cffi_typenames', etc. - self._lsts = {} - for step_name in self.ALL_STEPS: - self._lsts[step_name] = [] - self._seen_struct_unions = set() - self._generate("ctx") - self._add_missing_struct_unions() - # - for step_name in self.ALL_STEPS: - lst = self._lsts[step_name] - if step_name != "field": - lst.sort(key=lambda entry: entry.name) - self._lsts[step_name] = tuple(lst) # don't change any more - # - # check for a possible internal inconsistency: _cffi_struct_unions - # should have been generated with exactly self._struct_unions - lst = self._lsts["struct_union"] - for tp, i in self._struct_unions.items(): - assert i < len(lst) - assert lst[i].name == tp.name - assert len(lst) == len(self._struct_unions) - # same with enums - lst = self._lsts["enum"] - for tp, i in self._enums.items(): - assert i < len(lst) - assert lst[i].name == tp.name - assert len(lst) == len(self._enums) - - # ---------- - - def _prnt(self, what=''): - self._f.write(what + '\n') - - def write_source_to_f(self, f, preamble): - if self.target_is_python: - assert preamble is None - self.write_py_source_to_f(f) - else: - assert preamble is not None - self.write_c_source_to_f(f, preamble) - - def _rel_readlines(self, filename): - g = open(os.path.join(os.path.dirname(__file__), filename), 'r') - lines = g.readlines() - g.close() - return lines - - def write_c_source_to_f(self, f, preamble): - self._f = f - prnt = self._prnt - if self.ffi._embedding is not None: - prnt('#define _CFFI_USE_EMBEDDING') - if not USE_LIMITED_API: - prnt('#define _CFFI_NO_LIMITED_API') - # - # first the '#include' (actually done by inlining the file's content) - lines = self._rel_readlines('_cffi_include.h') - i = lines.index('#include "parse_c_type.h"\n') - lines[i:i+1] = self._rel_readlines('parse_c_type.h') - prnt(''.join(lines)) - # - # if we have ffi._embedding != None, we give it here as a macro - # and include an extra file - base_module_name = self.module_name.split('.')[-1] - if self.ffi._embedding is not None: - prnt('#define _CFFI_MODULE_NAME "%s"' % (self.module_name,)) - prnt('static const char _CFFI_PYTHON_STARTUP_CODE[] = {') - self._print_string_literal_in_array(self.ffi._embedding) - prnt('0 };') - prnt('#ifdef PYPY_VERSION') - prnt('# define _CFFI_PYTHON_STARTUP_FUNC _cffi_pypyinit_%s' % ( - base_module_name,)) - prnt('#elif PY_MAJOR_VERSION >= 3') - prnt('# define _CFFI_PYTHON_STARTUP_FUNC PyInit_%s' % ( - base_module_name,)) - prnt('#else') - prnt('# define _CFFI_PYTHON_STARTUP_FUNC init%s' % ( - base_module_name,)) - prnt('#endif') - lines = self._rel_readlines('_embedding.h') - i = lines.index('#include "_cffi_errors.h"\n') - lines[i:i+1] = self._rel_readlines('_cffi_errors.h') - prnt(''.join(lines)) - self.needs_version(VERSION_EMBEDDED) - # - # then paste the C source given by the user, verbatim. - prnt('/************************************************************/') - prnt() - prnt(preamble) - prnt() - prnt('/************************************************************/') - prnt() - # - # the declaration of '_cffi_types' - prnt('static void *_cffi_types[] = {') - typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()]) - for i, op in enumerate(self.cffi_types): - comment = '' - if i in typeindex2type: - comment = ' // ' + typeindex2type[i]._get_c_name() - prnt('/* %2d */ %s,%s' % (i, op.as_c_expr(), comment)) - if not self.cffi_types: - prnt(' 0') - prnt('};') - prnt() - # - # call generate_cpy_xxx_decl(), for every xxx found from - # ffi._parser._declarations. This generates all the functions. - self._seen_constants = set() - self._generate("decl") - # - # the declaration of '_cffi_globals' and '_cffi_typenames' - nums = {} - for step_name in self.ALL_STEPS: - lst = self._lsts[step_name] - nums[step_name] = len(lst) - if nums[step_name] > 0: - prnt('static const struct _cffi_%s_s _cffi_%ss[] = {' % ( - step_name, step_name)) - for entry in lst: - prnt(entry.as_c_expr()) - prnt('};') - prnt() - # - # the declaration of '_cffi_includes' - if self.ffi._included_ffis: - prnt('static const char * const _cffi_includes[] = {') - for ffi_to_include in self.ffi._included_ffis: - try: - included_module_name, included_source = ( - ffi_to_include._assigned_source[:2]) - except AttributeError: - raise VerificationError( - "ffi object %r includes %r, but the latter has not " - "been prepared with set_source()" % ( - self.ffi, ffi_to_include,)) - if included_source is None: - raise VerificationError( - "not implemented yet: ffi.include() of a Python-based " - "ffi inside a C-based ffi") - prnt(' "%s",' % (included_module_name,)) - prnt(' NULL') - prnt('};') - prnt() - # - # the declaration of '_cffi_type_context' - prnt('static const struct _cffi_type_context_s _cffi_type_context = {') - prnt(' _cffi_types,') - for step_name in self.ALL_STEPS: - if nums[step_name] > 0: - prnt(' _cffi_%ss,' % step_name) - else: - prnt(' NULL, /* no %ss */' % step_name) - for step_name in self.ALL_STEPS: - if step_name != "field": - prnt(' %d, /* num_%ss */' % (nums[step_name], step_name)) - if self.ffi._included_ffis: - prnt(' _cffi_includes,') - else: - prnt(' NULL, /* no includes */') - prnt(' %d, /* num_types */' % (len(self.cffi_types),)) - flags = 0 - if self._num_externpy > 0 or self.ffi._embedding is not None: - flags |= 1 # set to mean that we use extern "Python" - prnt(' %d, /* flags */' % flags) - prnt('};') - prnt() - # - # the init function - prnt('#ifdef __GNUC__') - prnt('# pragma GCC visibility push(default) /* for -fvisibility= */') - prnt('#endif') - prnt() - prnt('#ifdef PYPY_VERSION') - prnt('PyMODINIT_FUNC') - prnt('_cffi_pypyinit_%s(const void *p[])' % (base_module_name,)) - prnt('{') - if flags & 1: - prnt(' if (((intptr_t)p[0]) >= 0x0A03) {') - prnt(' _cffi_call_python_org = ' - '(void(*)(struct _cffi_externpy_s *, char *))p[1];') - prnt(' }') - prnt(' p[0] = (const void *)0x%x;' % self._version) - prnt(' p[1] = &_cffi_type_context;') - prnt('#if PY_MAJOR_VERSION >= 3') - prnt(' return NULL;') - prnt('#endif') - prnt('}') - # on Windows, distutils insists on putting init_cffi_xyz in - # 'export_symbols', so instead of fighting it, just give up and - # give it one - prnt('# ifdef _MSC_VER') - prnt(' PyMODINIT_FUNC') - prnt('# if PY_MAJOR_VERSION >= 3') - prnt(' PyInit_%s(void) { return NULL; }' % (base_module_name,)) - prnt('# else') - prnt(' init%s(void) { }' % (base_module_name,)) - prnt('# endif') - prnt('# endif') - prnt('#elif PY_MAJOR_VERSION >= 3') - prnt('PyMODINIT_FUNC') - prnt('PyInit_%s(void)' % (base_module_name,)) - prnt('{') - prnt(' return _cffi_init("%s", 0x%x, &_cffi_type_context);' % ( - self.module_name, self._version)) - prnt('}') - prnt('#else') - prnt('PyMODINIT_FUNC') - prnt('init%s(void)' % (base_module_name,)) - prnt('{') - prnt(' _cffi_init("%s", 0x%x, &_cffi_type_context);' % ( - self.module_name, self._version)) - prnt('}') - prnt('#endif') - prnt() - prnt('#ifdef __GNUC__') - prnt('# pragma GCC visibility pop') - prnt('#endif') - self._version = None - - def _to_py(self, x): - if isinstance(x, str): - return "b'%s'" % (x,) - if isinstance(x, (list, tuple)): - rep = [self._to_py(item) for item in x] - if len(rep) == 1: - rep.append('') - return "(%s)" % (','.join(rep),) - return x.as_python_expr() # Py2: unicode unexpected; Py3: bytes unexp. - - def write_py_source_to_f(self, f): - self._f = f - prnt = self._prnt - # - # header - prnt("# auto-generated file") - prnt("import _cffi_backend") - # - # the 'import' of the included ffis - num_includes = len(self.ffi._included_ffis or ()) - for i in range(num_includes): - ffi_to_include = self.ffi._included_ffis[i] - try: - included_module_name, included_source = ( - ffi_to_include._assigned_source[:2]) - except AttributeError: - raise VerificationError( - "ffi object %r includes %r, but the latter has not " - "been prepared with set_source()" % ( - self.ffi, ffi_to_include,)) - if included_source is not None: - raise VerificationError( - "not implemented yet: ffi.include() of a C-based " - "ffi inside a Python-based ffi") - prnt('from %s import ffi as _ffi%d' % (included_module_name, i)) - prnt() - prnt("ffi = _cffi_backend.FFI('%s'," % (self.module_name,)) - prnt(" _version = 0x%x," % (self._version,)) - self._version = None - # - # the '_types' keyword argument - self.cffi_types = tuple(self.cffi_types) # don't change any more - types_lst = [op.as_python_bytes() for op in self.cffi_types] - prnt(' _types = %s,' % (self._to_py(''.join(types_lst)),)) - typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()]) - # - # the keyword arguments from ALL_STEPS - for step_name in self.ALL_STEPS: - lst = self._lsts[step_name] - if len(lst) > 0 and step_name != "field": - prnt(' _%ss = %s,' % (step_name, self._to_py(lst))) - # - # the '_includes' keyword argument - if num_includes > 0: - prnt(' _includes = (%s,),' % ( - ', '.join(['_ffi%d' % i for i in range(num_includes)]),)) - # - # the footer - prnt(')') - - # ---------- - - def _gettypenum(self, type): - # a KeyError here is a bug. please report it! :-) - return self._typesdict[type] - - def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode): - extraarg = '' - if isinstance(tp, model.BasePrimitiveType) and not tp.is_complex_type(): - if tp.is_integer_type() and tp.name != '_Bool': - converter = '_cffi_to_c_int' - extraarg = ', %s' % tp.name - elif isinstance(tp, model.UnknownFloatType): - # don't check with is_float_type(): it may be a 'long - # double' here, and _cffi_to_c_double would loose precision - converter = '(%s)_cffi_to_c_double' % (tp.get_c_name(''),) - else: - cname = tp.get_c_name('') - converter = '(%s)_cffi_to_c_%s' % (cname, - tp.name.replace(' ', '_')) - if cname in ('char16_t', 'char32_t'): - self.needs_version(VERSION_CHAR16CHAR32) - errvalue = '-1' - # - elif isinstance(tp, model.PointerType): - self._convert_funcarg_to_c_ptr_or_array(tp, fromvar, - tovar, errcode) - return - # - elif (isinstance(tp, model.StructOrUnionOrEnum) or - isinstance(tp, model.BasePrimitiveType)): - # a struct (not a struct pointer) as a function argument; - # or, a complex (the same code works) - self._prnt(' if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)' - % (tovar, self._gettypenum(tp), fromvar)) - self._prnt(' %s;' % errcode) - return - # - elif isinstance(tp, model.FunctionPtrType): - converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('') - extraarg = ', _cffi_type(%d)' % self._gettypenum(tp) - errvalue = 'NULL' - # - else: - raise NotImplementedError(tp) - # - self._prnt(' %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg)) - self._prnt(' if (%s == (%s)%s && PyErr_Occurred())' % ( - tovar, tp.get_c_name(''), errvalue)) - self._prnt(' %s;' % errcode) - - def _extra_local_variables(self, tp, localvars, freelines): - if isinstance(tp, model.PointerType): - localvars.add('Py_ssize_t datasize') - localvars.add('struct _cffi_freeme_s *large_args_free = NULL') - freelines.add('if (large_args_free != NULL)' - ' _cffi_free_array_arguments(large_args_free);') - - def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode): - self._prnt(' datasize = _cffi_prepare_pointer_call_argument(') - self._prnt(' _cffi_type(%d), %s, (char **)&%s);' % ( - self._gettypenum(tp), fromvar, tovar)) - self._prnt(' if (datasize != 0) {') - self._prnt(' %s = ((size_t)datasize) <= 640 ? ' - '(%s)alloca((size_t)datasize) : NULL;' % ( - tovar, tp.get_c_name(''))) - self._prnt(' if (_cffi_convert_array_argument(_cffi_type(%d), %s, ' - '(char **)&%s,' % (self._gettypenum(tp), fromvar, tovar)) - self._prnt(' datasize, &large_args_free) < 0)') - self._prnt(' %s;' % errcode) - self._prnt(' }') - - def _convert_expr_from_c(self, tp, var, context): - if isinstance(tp, model.BasePrimitiveType): - if tp.is_integer_type() and tp.name != '_Bool': - return '_cffi_from_c_int(%s, %s)' % (var, tp.name) - elif isinstance(tp, model.UnknownFloatType): - return '_cffi_from_c_double(%s)' % (var,) - elif tp.name != 'long double' and not tp.is_complex_type(): - cname = tp.name.replace(' ', '_') - if cname in ('char16_t', 'char32_t'): - self.needs_version(VERSION_CHAR16CHAR32) - return '_cffi_from_c_%s(%s)' % (cname, var) - else: - return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - elif isinstance(tp, (model.PointerType, model.FunctionPtrType)): - return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - elif isinstance(tp, model.ArrayType): - return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( - var, self._gettypenum(model.PointerType(tp.item))) - elif isinstance(tp, model.StructOrUnion): - if tp.fldnames is None: - raise TypeError("'%s' is used as %s, but is opaque" % ( - tp._get_c_name(), context)) - return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - elif isinstance(tp, model.EnumType): - return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - else: - raise NotImplementedError(tp) - - # ---------- - # typedefs - - def _typedef_type(self, tp, name): - return self._global_type(tp, "(*(%s *)0)" % (name,)) - - def _generate_cpy_typedef_collecttype(self, tp, name): - self._do_collect_type(self._typedef_type(tp, name)) - - def _generate_cpy_typedef_decl(self, tp, name): - pass - - def _typedef_ctx(self, tp, name): - type_index = self._typesdict[tp] - self._lsts["typename"].append(TypenameExpr(name, type_index)) - - def _generate_cpy_typedef_ctx(self, tp, name): - tp = self._typedef_type(tp, name) - self._typedef_ctx(tp, name) - if getattr(tp, "origin", None) == "unknown_type": - self._struct_ctx(tp, tp.name, approxname=None) - elif isinstance(tp, model.NamedPointerType): - self._struct_ctx(tp.totype, tp.totype.name, approxname=tp.name, - named_ptr=tp) - - # ---------- - # function declarations - - def _generate_cpy_function_collecttype(self, tp, name): - self._do_collect_type(tp.as_raw_function()) - if tp.ellipsis and not self.target_is_python: - self._do_collect_type(tp) - - def _generate_cpy_function_decl(self, tp, name): - assert not self.target_is_python - assert isinstance(tp, model.FunctionPtrType) - if tp.ellipsis: - # cannot support vararg functions better than this: check for its - # exact type (including the fixed arguments), and build it as a - # constant function pointer (no CPython wrapper) - self._generate_cpy_constant_decl(tp, name) - return - prnt = self._prnt - numargs = len(tp.args) - if numargs == 0: - argname = 'noarg' - elif numargs == 1: - argname = 'arg0' - else: - argname = 'args' - # - # ------------------------------ - # the 'd' version of the function, only for addressof(lib, 'func') - arguments = [] - call_arguments = [] - context = 'argument of %s' % name - for i, type in enumerate(tp.args): - arguments.append(type.get_c_name(' x%d' % i, context)) - call_arguments.append('x%d' % i) - repr_arguments = ', '.join(arguments) - repr_arguments = repr_arguments or 'void' - if tp.abi: - abi = tp.abi + ' ' - else: - abi = '' - name_and_arguments = '%s_cffi_d_%s(%s)' % (abi, name, repr_arguments) - prnt('static %s' % (tp.result.get_c_name(name_and_arguments),)) - prnt('{') - call_arguments = ', '.join(call_arguments) - result_code = 'return ' - if isinstance(tp.result, model.VoidType): - result_code = '' - prnt(' %s%s(%s);' % (result_code, name, call_arguments)) - prnt('}') - # - prnt('#ifndef PYPY_VERSION') # ------------------------------ - # - prnt('static PyObject *') - prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname)) - prnt('{') - # - context = 'argument of %s' % name - for i, type in enumerate(tp.args): - arg = type.get_c_name(' x%d' % i, context) - prnt(' %s;' % arg) - # - localvars = set() - freelines = set() - for type in tp.args: - self._extra_local_variables(type, localvars, freelines) - for decl in sorted(localvars): - prnt(' %s;' % (decl,)) - # - if not isinstance(tp.result, model.VoidType): - result_code = 'result = ' - context = 'result of %s' % name - result_decl = ' %s;' % tp.result.get_c_name(' result', context) - prnt(result_decl) - prnt(' PyObject *pyresult;') - else: - result_decl = None - result_code = '' - # - if len(tp.args) > 1: - rng = range(len(tp.args)) - for i in rng: - prnt(' PyObject *arg%d;' % i) - prnt() - prnt(' if (!PyArg_UnpackTuple(args, "%s", %d, %d, %s))' % ( - name, len(rng), len(rng), - ', '.join(['&arg%d' % i for i in rng]))) - prnt(' return NULL;') - prnt() - # - for i, type in enumerate(tp.args): - self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i, - 'return NULL') - prnt() - # - prnt(' Py_BEGIN_ALLOW_THREADS') - prnt(' _cffi_restore_errno();') - call_arguments = ['x%d' % i for i in range(len(tp.args))] - call_arguments = ', '.join(call_arguments) - prnt(' { %s%s(%s); }' % (result_code, name, call_arguments)) - prnt(' _cffi_save_errno();') - prnt(' Py_END_ALLOW_THREADS') - prnt() - # - prnt(' (void)self; /* unused */') - if numargs == 0: - prnt(' (void)noarg; /* unused */') - if result_code: - prnt(' pyresult = %s;' % - self._convert_expr_from_c(tp.result, 'result', 'result type')) - for freeline in freelines: - prnt(' ' + freeline) - prnt(' return pyresult;') - else: - for freeline in freelines: - prnt(' ' + freeline) - prnt(' Py_INCREF(Py_None);') - prnt(' return Py_None;') - prnt('}') - # - prnt('#else') # ------------------------------ - # - # the PyPy version: need to replace struct/union arguments with - # pointers, and if the result is a struct/union, insert a first - # arg that is a pointer to the result. We also do that for - # complex args and return type. - def need_indirection(type): - return (isinstance(type, model.StructOrUnion) or - (isinstance(type, model.PrimitiveType) and - type.is_complex_type())) - difference = False - arguments = [] - call_arguments = [] - context = 'argument of %s' % name - for i, type in enumerate(tp.args): - indirection = '' - if need_indirection(type): - indirection = '*' - difference = True - arg = type.get_c_name(' %sx%d' % (indirection, i), context) - arguments.append(arg) - call_arguments.append('%sx%d' % (indirection, i)) - tp_result = tp.result - if need_indirection(tp_result): - context = 'result of %s' % name - arg = tp_result.get_c_name(' *result', context) - arguments.insert(0, arg) - tp_result = model.void_type - result_decl = None - result_code = '*result = ' - difference = True - if difference: - repr_arguments = ', '.join(arguments) - repr_arguments = repr_arguments or 'void' - name_and_arguments = '%s_cffi_f_%s(%s)' % (abi, name, - repr_arguments) - prnt('static %s' % (tp_result.get_c_name(name_and_arguments),)) - prnt('{') - if result_decl: - prnt(result_decl) - call_arguments = ', '.join(call_arguments) - prnt(' { %s%s(%s); }' % (result_code, name, call_arguments)) - if result_decl: - prnt(' return result;') - prnt('}') - else: - prnt('# define _cffi_f_%s _cffi_d_%s' % (name, name)) - # - prnt('#endif') # ------------------------------ - prnt() - - def _generate_cpy_function_ctx(self, tp, name): - if tp.ellipsis and not self.target_is_python: - self._generate_cpy_constant_ctx(tp, name) - return - type_index = self._typesdict[tp.as_raw_function()] - numargs = len(tp.args) - if self.target_is_python: - meth_kind = OP_DLOPEN_FUNC - elif numargs == 0: - meth_kind = OP_CPYTHON_BLTN_N # 'METH_NOARGS' - elif numargs == 1: - meth_kind = OP_CPYTHON_BLTN_O # 'METH_O' - else: - meth_kind = OP_CPYTHON_BLTN_V # 'METH_VARARGS' - self._lsts["global"].append( - GlobalExpr(name, '_cffi_f_%s' % name, - CffiOp(meth_kind, type_index), - size='_cffi_d_%s' % name)) - - # ---------- - # named structs or unions - - def _field_type(self, tp_struct, field_name, tp_field): - if isinstance(tp_field, model.ArrayType): - actual_length = tp_field.length - if actual_length == '...': - ptr_struct_name = tp_struct.get_c_name('*') - actual_length = '_cffi_array_len(((%s)0)->%s)' % ( - ptr_struct_name, field_name) - tp_item = self._field_type(tp_struct, '%s[0]' % field_name, - tp_field.item) - tp_field = model.ArrayType(tp_item, actual_length) - return tp_field - - def _struct_collecttype(self, tp): - self._do_collect_type(tp) - if self.target_is_python: - # also requires nested anon struct/unions in ABI mode, recursively - for fldtype in tp.anonymous_struct_fields(): - self._struct_collecttype(fldtype) - - def _struct_decl(self, tp, cname, approxname): - if tp.fldtypes is None: - return - prnt = self._prnt - checkfuncname = '_cffi_checkfld_%s' % (approxname,) - prnt('_CFFI_UNUSED_FN') - prnt('static void %s(%s *p)' % (checkfuncname, cname)) - prnt('{') - prnt(' /* only to generate compile-time warnings or errors */') - prnt(' (void)p;') - for fname, ftype, fbitsize, fqual in self._enum_fields(tp): - try: - if ftype.is_integer_type() or fbitsize >= 0: - # accept all integers, but complain on float or double - if fname != '': - prnt(" (void)((p->%s) | 0); /* check that '%s.%s' is " - "an integer */" % (fname, cname, fname)) - continue - # only accept exactly the type declared, except that '[]' - # is interpreted as a '*' and so will match any array length. - # (It would also match '*', but that's harder to detect...) - while (isinstance(ftype, model.ArrayType) - and (ftype.length is None or ftype.length == '...')): - ftype = ftype.item - fname = fname + '[0]' - prnt(' { %s = &p->%s; (void)tmp; }' % ( - ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual), - fname)) - except VerificationError as e: - prnt(' /* %s */' % str(e)) # cannot verify it, ignore - prnt('}') - prnt('struct _cffi_align_%s { char x; %s y; };' % (approxname, cname)) - prnt() - - def _struct_ctx(self, tp, cname, approxname, named_ptr=None): - type_index = self._typesdict[tp] - reason_for_not_expanding = None - flags = [] - if isinstance(tp, model.UnionType): - flags.append("_CFFI_F_UNION") - if tp.fldtypes is None: - flags.append("_CFFI_F_OPAQUE") - reason_for_not_expanding = "opaque" - if (tp not in self.ffi._parser._included_declarations and - (named_ptr is None or - named_ptr not in self.ffi._parser._included_declarations)): - if tp.fldtypes is None: - pass # opaque - elif tp.partial or any(tp.anonymous_struct_fields()): - pass # field layout obtained silently from the C compiler - else: - flags.append("_CFFI_F_CHECK_FIELDS") - if tp.packed: - if tp.packed > 1: - raise NotImplementedError( - "%r is declared with 'pack=%r'; only 0 or 1 are " - "supported in API mode (try to use \"...;\", which " - "does not require a 'pack' declaration)" % - (tp, tp.packed)) - flags.append("_CFFI_F_PACKED") - else: - flags.append("_CFFI_F_EXTERNAL") - reason_for_not_expanding = "external" - flags = '|'.join(flags) or '0' - c_fields = [] - if reason_for_not_expanding is None: - enumfields = list(self._enum_fields(tp)) - for fldname, fldtype, fbitsize, fqual in enumfields: - fldtype = self._field_type(tp, fldname, fldtype) - self._check_not_opaque(fldtype, - "field '%s.%s'" % (tp.name, fldname)) - # cname is None for _add_missing_struct_unions() only - op = OP_NOOP - if fbitsize >= 0: - op = OP_BITFIELD - size = '%d /* bits */' % fbitsize - elif cname is None or ( - isinstance(fldtype, model.ArrayType) and - fldtype.length is None): - size = '(size_t)-1' - else: - size = 'sizeof(((%s)0)->%s)' % ( - tp.get_c_name('*') if named_ptr is None - else named_ptr.name, - fldname) - if cname is None or fbitsize >= 0: - offset = '(size_t)-1' - elif named_ptr is not None: - offset = '(size_t)(((char *)&((%s)4096)->%s) - (char *)4096)' % ( - named_ptr.name, fldname) - else: - offset = 'offsetof(%s, %s)' % (tp.get_c_name(''), fldname) - c_fields.append( - FieldExpr(fldname, offset, size, fbitsize, - CffiOp(op, self._typesdict[fldtype]))) - first_field_index = len(self._lsts["field"]) - self._lsts["field"].extend(c_fields) - # - if cname is None: # unknown name, for _add_missing_struct_unions - size = '(size_t)-2' - align = -2 - comment = "unnamed" - else: - if named_ptr is not None: - size = 'sizeof(*(%s)0)' % (named_ptr.name,) - align = '-1 /* unknown alignment */' - else: - size = 'sizeof(%s)' % (cname,) - align = 'offsetof(struct _cffi_align_%s, y)' % (approxname,) - comment = None - else: - size = '(size_t)-1' - align = -1 - first_field_index = -1 - comment = reason_for_not_expanding - self._lsts["struct_union"].append( - StructUnionExpr(tp.name, type_index, flags, size, align, comment, - first_field_index, c_fields)) - self._seen_struct_unions.add(tp) - - def _check_not_opaque(self, tp, location): - while isinstance(tp, model.ArrayType): - tp = tp.item - if isinstance(tp, model.StructOrUnion) and tp.fldtypes is None: - raise TypeError( - "%s is of an opaque type (not declared in cdef())" % location) - - def _add_missing_struct_unions(self): - # not very nice, but some struct declarations might be missing - # because they don't have any known C name. Check that they are - # not partial (we can't complete or verify them!) and emit them - # anonymously. - lst = list(self._struct_unions.items()) - lst.sort(key=lambda tp_order: tp_order[1]) - for tp, order in lst: - if tp not in self._seen_struct_unions: - if tp.partial: - raise NotImplementedError("internal inconsistency: %r is " - "partial but was not seen at " - "this point" % (tp,)) - if tp.name.startswith('$') and tp.name[1:].isdigit(): - approxname = tp.name[1:] - elif tp.name == '_IO_FILE' and tp.forcename == 'FILE': - approxname = 'FILE' - self._typedef_ctx(tp, 'FILE') - else: - raise NotImplementedError("internal inconsistency: %r" % - (tp,)) - self._struct_ctx(tp, None, approxname) - - def _generate_cpy_struct_collecttype(self, tp, name): - self._struct_collecttype(tp) - _generate_cpy_union_collecttype = _generate_cpy_struct_collecttype - - def _struct_names(self, tp): - cname = tp.get_c_name('') - if ' ' in cname: - return cname, cname.replace(' ', '_') - else: - return cname, '_' + cname - - def _generate_cpy_struct_decl(self, tp, name): - self._struct_decl(tp, *self._struct_names(tp)) - _generate_cpy_union_decl = _generate_cpy_struct_decl - - def _generate_cpy_struct_ctx(self, tp, name): - self._struct_ctx(tp, *self._struct_names(tp)) - _generate_cpy_union_ctx = _generate_cpy_struct_ctx - - # ---------- - # 'anonymous' declarations. These are produced for anonymous structs - # or unions; the 'name' is obtained by a typedef. - - def _generate_cpy_anonymous_collecttype(self, tp, name): - if isinstance(tp, model.EnumType): - self._generate_cpy_enum_collecttype(tp, name) - else: - self._struct_collecttype(tp) - - def _generate_cpy_anonymous_decl(self, tp, name): - if isinstance(tp, model.EnumType): - self._generate_cpy_enum_decl(tp) - else: - self._struct_decl(tp, name, 'typedef_' + name) - - def _generate_cpy_anonymous_ctx(self, tp, name): - if isinstance(tp, model.EnumType): - self._enum_ctx(tp, name) - else: - self._struct_ctx(tp, name, 'typedef_' + name) - - # ---------- - # constants, declared with "static const ..." - - def _generate_cpy_const(self, is_int, name, tp=None, category='const', - check_value=None): - if (category, name) in self._seen_constants: - raise VerificationError( - "duplicate declaration of %s '%s'" % (category, name)) - self._seen_constants.add((category, name)) - # - prnt = self._prnt - funcname = '_cffi_%s_%s' % (category, name) - if is_int: - prnt('static int %s(unsigned long long *o)' % funcname) - prnt('{') - prnt(' int n = (%s) <= 0;' % (name,)) - prnt(' *o = (unsigned long long)((%s) | 0);' - ' /* check that %s is an integer */' % (name, name)) - if check_value is not None: - if check_value > 0: - check_value = '%dU' % (check_value,) - prnt(' if (!_cffi_check_int(*o, n, %s))' % (check_value,)) - prnt(' n |= 2;') - prnt(' return n;') - prnt('}') - else: - assert check_value is None - prnt('static void %s(char *o)' % funcname) - prnt('{') - prnt(' *(%s)o = %s;' % (tp.get_c_name('*'), name)) - prnt('}') - prnt() - - def _generate_cpy_constant_collecttype(self, tp, name): - is_int = tp.is_integer_type() - if not is_int or self.target_is_python: - self._do_collect_type(tp) - - def _generate_cpy_constant_decl(self, tp, name): - is_int = tp.is_integer_type() - self._generate_cpy_const(is_int, name, tp) - - def _generate_cpy_constant_ctx(self, tp, name): - if not self.target_is_python and tp.is_integer_type(): - type_op = CffiOp(OP_CONSTANT_INT, -1) - else: - if self.target_is_python: - const_kind = OP_DLOPEN_CONST - else: - const_kind = OP_CONSTANT - type_index = self._typesdict[tp] - type_op = CffiOp(const_kind, type_index) - self._lsts["global"].append( - GlobalExpr(name, '_cffi_const_%s' % name, type_op)) - - # ---------- - # enums - - def _generate_cpy_enum_collecttype(self, tp, name): - self._do_collect_type(tp) - - def _generate_cpy_enum_decl(self, tp, name=None): - for enumerator in tp.enumerators: - self._generate_cpy_const(True, enumerator) - - def _enum_ctx(self, tp, cname): - type_index = self._typesdict[tp] - type_op = CffiOp(OP_ENUM, -1) - if self.target_is_python: - tp.check_not_partial() - for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): - self._lsts["global"].append( - GlobalExpr(enumerator, '_cffi_const_%s' % enumerator, type_op, - check_value=enumvalue)) - # - if cname is not None and '$' not in cname and not self.target_is_python: - size = "sizeof(%s)" % cname - signed = "((%s)-1) <= 0" % cname - else: - basetp = tp.build_baseinttype(self.ffi, []) - size = self.ffi.sizeof(basetp) - signed = int(int(self.ffi.cast(basetp, -1)) < 0) - allenums = ",".join(tp.enumerators) - self._lsts["enum"].append( - EnumExpr(tp.name, type_index, size, signed, allenums)) - - def _generate_cpy_enum_ctx(self, tp, name): - self._enum_ctx(tp, tp._get_c_name()) - - # ---------- - # macros: for now only for integers - - def _generate_cpy_macro_collecttype(self, tp, name): - pass - - def _generate_cpy_macro_decl(self, tp, name): - if tp == '...': - check_value = None - else: - check_value = tp # an integer - self._generate_cpy_const(True, name, check_value=check_value) - - def _generate_cpy_macro_ctx(self, tp, name): - if tp == '...': - if self.target_is_python: - raise VerificationError( - "cannot use the syntax '...' in '#define %s ...' when " - "using the ABI mode" % (name,)) - check_value = None - else: - check_value = tp # an integer - type_op = CffiOp(OP_CONSTANT_INT, -1) - self._lsts["global"].append( - GlobalExpr(name, '_cffi_const_%s' % name, type_op, - check_value=check_value)) - - # ---------- - # global variables - - def _global_type(self, tp, global_name): - if isinstance(tp, model.ArrayType): - actual_length = tp.length - if actual_length == '...': - actual_length = '_cffi_array_len(%s)' % (global_name,) - tp_item = self._global_type(tp.item, '%s[0]' % global_name) - tp = model.ArrayType(tp_item, actual_length) - return tp - - def _generate_cpy_variable_collecttype(self, tp, name): - self._do_collect_type(self._global_type(tp, name)) - - def _generate_cpy_variable_decl(self, tp, name): - prnt = self._prnt - tp = self._global_type(tp, name) - if isinstance(tp, model.ArrayType) and tp.length is None: - tp = tp.item - ampersand = '' - else: - ampersand = '&' - # This code assumes that casts from "tp *" to "void *" is a - # no-op, i.e. a function that returns a "tp *" can be called - # as if it returned a "void *". This should be generally true - # on any modern machine. The only exception to that rule (on - # uncommon architectures, and as far as I can tell) might be - # if 'tp' were a function type, but that is not possible here. - # (If 'tp' is a function _pointer_ type, then casts from "fn_t - # **" to "void *" are again no-ops, as far as I can tell.) - decl = '*_cffi_var_%s(void)' % (name,) - prnt('static ' + tp.get_c_name(decl, quals=self._current_quals)) - prnt('{') - prnt(' return %s(%s);' % (ampersand, name)) - prnt('}') - prnt() - - def _generate_cpy_variable_ctx(self, tp, name): - tp = self._global_type(tp, name) - type_index = self._typesdict[tp] - if self.target_is_python: - op = OP_GLOBAL_VAR - else: - op = OP_GLOBAL_VAR_F - self._lsts["global"].append( - GlobalExpr(name, '_cffi_var_%s' % name, CffiOp(op, type_index))) - - # ---------- - # extern "Python" - - def _generate_cpy_extern_python_collecttype(self, tp, name): - assert isinstance(tp, model.FunctionPtrType) - self._do_collect_type(tp) - _generate_cpy_dllexport_python_collecttype = \ - _generate_cpy_extern_python_plus_c_collecttype = \ - _generate_cpy_extern_python_collecttype - - def _extern_python_decl(self, tp, name, tag_and_space): - prnt = self._prnt - if isinstance(tp.result, model.VoidType): - size_of_result = '0' - else: - context = 'result of %s' % name - size_of_result = '(int)sizeof(%s)' % ( - tp.result.get_c_name('', context),) - prnt('static struct _cffi_externpy_s _cffi_externpy__%s =' % name) - prnt(' { "%s.%s", %s, 0, 0 };' % ( - self.module_name, name, size_of_result)) - prnt() - # - arguments = [] - context = 'argument of %s' % name - for i, type in enumerate(tp.args): - arg = type.get_c_name(' a%d' % i, context) - arguments.append(arg) - # - repr_arguments = ', '.join(arguments) - repr_arguments = repr_arguments or 'void' - name_and_arguments = '%s(%s)' % (name, repr_arguments) - if tp.abi == "__stdcall": - name_and_arguments = '_cffi_stdcall ' + name_and_arguments - # - def may_need_128_bits(tp): - return (isinstance(tp, model.PrimitiveType) and - tp.name == 'long double') - # - size_of_a = max(len(tp.args)*8, 8) - if may_need_128_bits(tp.result): - size_of_a = max(size_of_a, 16) - if isinstance(tp.result, model.StructOrUnion): - size_of_a = 'sizeof(%s) > %d ? sizeof(%s) : %d' % ( - tp.result.get_c_name(''), size_of_a, - tp.result.get_c_name(''), size_of_a) - prnt('%s%s' % (tag_and_space, tp.result.get_c_name(name_and_arguments))) - prnt('{') - prnt(' char a[%s];' % size_of_a) - prnt(' char *p = a;') - for i, type in enumerate(tp.args): - arg = 'a%d' % i - if (isinstance(type, model.StructOrUnion) or - may_need_128_bits(type)): - arg = '&' + arg - type = model.PointerType(type) - prnt(' *(%s)(p + %d) = %s;' % (type.get_c_name('*'), i*8, arg)) - prnt(' _cffi_call_python(&_cffi_externpy__%s, p);' % name) - if not isinstance(tp.result, model.VoidType): - prnt(' return *(%s)p;' % (tp.result.get_c_name('*'),)) - prnt('}') - prnt() - self._num_externpy += 1 - - def _generate_cpy_extern_python_decl(self, tp, name): - self._extern_python_decl(tp, name, 'static ') - - def _generate_cpy_dllexport_python_decl(self, tp, name): - self._extern_python_decl(tp, name, 'CFFI_DLLEXPORT ') - - def _generate_cpy_extern_python_plus_c_decl(self, tp, name): - self._extern_python_decl(tp, name, '') - - def _generate_cpy_extern_python_ctx(self, tp, name): - if self.target_is_python: - raise VerificationError( - "cannot use 'extern \"Python\"' in the ABI mode") - if tp.ellipsis: - raise NotImplementedError("a vararg function is extern \"Python\"") - type_index = self._typesdict[tp] - type_op = CffiOp(OP_EXTERN_PYTHON, type_index) - self._lsts["global"].append( - GlobalExpr(name, '&_cffi_externpy__%s' % name, type_op, name)) - - _generate_cpy_dllexport_python_ctx = \ - _generate_cpy_extern_python_plus_c_ctx = \ - _generate_cpy_extern_python_ctx - - def _print_string_literal_in_array(self, s): - prnt = self._prnt - prnt('// # NB. this is not a string because of a size limit in MSVC') - if not isinstance(s, bytes): # unicode - s = s.encode('utf-8') # -> bytes - else: - s.decode('utf-8') # got bytes, check for valid utf-8 - try: - s.decode('ascii') - except UnicodeDecodeError: - s = b'# -*- encoding: utf8 -*-\n' + s - for line in s.splitlines(True): - comment = line - if type('//') is bytes: # python2 - line = map(ord, line) # make a list of integers - else: # python3 - # type(line) is bytes, which enumerates like a list of integers - comment = ascii(comment)[1:-1] - prnt(('// ' + comment).rstrip()) - printed_line = '' - for c in line: - if len(printed_line) >= 76: - prnt(printed_line) - printed_line = '' - printed_line += '%d,' % (c,) - prnt(printed_line) - - # ---------- - # emitting the opcodes for individual types - - def _emit_bytecode_VoidType(self, tp, index): - self.cffi_types[index] = CffiOp(OP_PRIMITIVE, PRIM_VOID) - - def _emit_bytecode_PrimitiveType(self, tp, index): - prim_index = PRIMITIVE_TO_INDEX[tp.name] - self.cffi_types[index] = CffiOp(OP_PRIMITIVE, prim_index) - - def _emit_bytecode_UnknownIntegerType(self, tp, index): - s = ('_cffi_prim_int(sizeof(%s), (\n' - ' ((%s)-1) | 0 /* check that %s is an integer type */\n' - ' ) <= 0)' % (tp.name, tp.name, tp.name)) - self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s) - - def _emit_bytecode_UnknownFloatType(self, tp, index): - s = ('_cffi_prim_float(sizeof(%s) *\n' - ' (((%s)1) / 2) * 2 /* integer => 0, float => 1 */\n' - ' )' % (tp.name, tp.name)) - self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s) - - def _emit_bytecode_RawFunctionType(self, tp, index): - self.cffi_types[index] = CffiOp(OP_FUNCTION, self._typesdict[tp.result]) - index += 1 - for tp1 in tp.args: - realindex = self._typesdict[tp1] - if index != realindex: - if isinstance(tp1, model.PrimitiveType): - self._emit_bytecode_PrimitiveType(tp1, index) - else: - self.cffi_types[index] = CffiOp(OP_NOOP, realindex) - index += 1 - flags = int(tp.ellipsis) - if tp.abi is not None: - if tp.abi == '__stdcall': - flags |= 2 - else: - raise NotImplementedError("abi=%r" % (tp.abi,)) - self.cffi_types[index] = CffiOp(OP_FUNCTION_END, flags) - - def _emit_bytecode_PointerType(self, tp, index): - self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[tp.totype]) - - _emit_bytecode_ConstPointerType = _emit_bytecode_PointerType - _emit_bytecode_NamedPointerType = _emit_bytecode_PointerType - - def _emit_bytecode_FunctionPtrType(self, tp, index): - raw = tp.as_raw_function() - self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[raw]) - - def _emit_bytecode_ArrayType(self, tp, index): - item_index = self._typesdict[tp.item] - if tp.length is None: - self.cffi_types[index] = CffiOp(OP_OPEN_ARRAY, item_index) - elif tp.length == '...': - raise VerificationError( - "type %s badly placed: the '...' array length can only be " - "used on global arrays or on fields of structures" % ( - str(tp).replace('/*...*/', '...'),)) - else: - assert self.cffi_types[index + 1] == 'LEN' - self.cffi_types[index] = CffiOp(OP_ARRAY, item_index) - self.cffi_types[index + 1] = CffiOp(None, str(tp.length)) - - def _emit_bytecode_StructType(self, tp, index): - struct_index = self._struct_unions[tp] - self.cffi_types[index] = CffiOp(OP_STRUCT_UNION, struct_index) - _emit_bytecode_UnionType = _emit_bytecode_StructType - - def _emit_bytecode_EnumType(self, tp, index): - enum_index = self._enums[tp] - self.cffi_types[index] = CffiOp(OP_ENUM, enum_index) - - -if sys.version_info >= (3,): - NativeIO = io.StringIO -else: - class NativeIO(io.BytesIO): - def write(self, s): - if isinstance(s, unicode): - s = s.encode('ascii') - super(NativeIO, self).write(s) - -def _is_file_like(maybefile): - # compare to xml.etree.ElementTree._get_writer - return hasattr(maybefile, 'write') - -def _make_c_or_py_source(ffi, module_name, preamble, target_file, verbose): - if verbose: - print("generating %s" % (target_file,)) - recompiler = Recompiler(ffi, module_name, - target_is_python=(preamble is None)) - recompiler.collect_type_table() - recompiler.collect_step_tables() - if _is_file_like(target_file): - recompiler.write_source_to_f(target_file, preamble) - return True - f = NativeIO() - recompiler.write_source_to_f(f, preamble) - output = f.getvalue() - try: - with open(target_file, 'r') as f1: - if f1.read(len(output) + 1) != output: - raise IOError - if verbose: - print("(already up-to-date)") - return False # already up-to-date - except IOError: - tmp_file = '%s.~%d' % (target_file, os.getpid()) - with open(tmp_file, 'w') as f1: - f1.write(output) - try: - os.rename(tmp_file, target_file) - except OSError: - os.unlink(target_file) - os.rename(tmp_file, target_file) - return True - -def make_c_source(ffi, module_name, preamble, target_c_file, verbose=False): - assert preamble is not None - return _make_c_or_py_source(ffi, module_name, preamble, target_c_file, - verbose) - -def make_py_source(ffi, module_name, target_py_file, verbose=False): - return _make_c_or_py_source(ffi, module_name, None, target_py_file, - verbose) - -def _modname_to_file(outputdir, modname, extension): - parts = modname.split('.') - try: - os.makedirs(os.path.join(outputdir, *parts[:-1])) - except OSError: - pass - parts[-1] += extension - return os.path.join(outputdir, *parts), parts - - -# Aaargh. Distutils is not tested at all for the purpose of compiling -# DLLs that are not extension modules. Here are some hacks to work -# around that, in the _patch_for_*() functions... - -def _patch_meth(patchlist, cls, name, new_meth): - old = getattr(cls, name) - patchlist.append((cls, name, old)) - setattr(cls, name, new_meth) - return old - -def _unpatch_meths(patchlist): - for cls, name, old_meth in reversed(patchlist): - setattr(cls, name, old_meth) - -def _patch_for_embedding(patchlist): - if sys.platform == 'win32': - # we must not remove the manifest when building for embedding! - # FUTURE: this module was removed in setuptools 74; this is likely dead code and should be removed, - # since the toolchain it supports (VS2005-2008) is also long dead. - from cffi._shimmed_dist_utils import MSVCCompiler - if MSVCCompiler is not None: - _patch_meth(patchlist, MSVCCompiler, '_remove_visual_c_ref', - lambda self, manifest_file: manifest_file) - - if sys.platform == 'darwin': - # we must not make a '-bundle', but a '-dynamiclib' instead - from cffi._shimmed_dist_utils import CCompiler - def my_link_shared_object(self, *args, **kwds): - if '-bundle' in self.linker_so: - self.linker_so = list(self.linker_so) - i = self.linker_so.index('-bundle') - self.linker_so[i] = '-dynamiclib' - return old_link_shared_object(self, *args, **kwds) - old_link_shared_object = _patch_meth(patchlist, CCompiler, - 'link_shared_object', - my_link_shared_object) - -def _patch_for_target(patchlist, target): - from cffi._shimmed_dist_utils import build_ext - # if 'target' is different from '*', we need to patch some internal - # method to just return this 'target' value, instead of having it - # built from module_name - if target.endswith('.*'): - target = target[:-2] - if sys.platform == 'win32': - target += '.dll' - elif sys.platform == 'darwin': - target += '.dylib' - else: - target += '.so' - _patch_meth(patchlist, build_ext, 'get_ext_filename', - lambda self, ext_name: target) - - -def recompile(ffi, module_name, preamble, tmpdir='.', call_c_compiler=True, - c_file=None, source_extension='.c', extradir=None, - compiler_verbose=1, target=None, debug=None, - uses_ffiplatform=True, **kwds): - if not isinstance(module_name, str): - module_name = module_name.encode('ascii') - if ffi._windows_unicode: - ffi._apply_windows_unicode(kwds) - if preamble is not None: - if call_c_compiler and _is_file_like(c_file): - raise TypeError("Writing to file-like objects is not supported " - "with call_c_compiler=True") - embedding = (ffi._embedding is not None) - if embedding: - ffi._apply_embedding_fix(kwds) - if c_file is None: - c_file, parts = _modname_to_file(tmpdir, module_name, - source_extension) - if extradir: - parts = [extradir] + parts - ext_c_file = os.path.join(*parts) - else: - ext_c_file = c_file - # - if target is None: - if embedding: - target = '%s.*' % module_name - else: - target = '*' - # - if uses_ffiplatform: - ext = ffiplatform.get_extension(ext_c_file, module_name, **kwds) - else: - ext = None - updated = make_c_source(ffi, module_name, preamble, c_file, - verbose=compiler_verbose) - if call_c_compiler: - patchlist = [] - cwd = os.getcwd() - try: - if embedding: - _patch_for_embedding(patchlist) - if target != '*': - _patch_for_target(patchlist, target) - if compiler_verbose: - if tmpdir == '.': - msg = 'the current directory is' - else: - msg = 'setting the current directory to' - print('%s %r' % (msg, os.path.abspath(tmpdir))) - os.chdir(tmpdir) - outputfilename = ffiplatform.compile('.', ext, - compiler_verbose, debug) - finally: - os.chdir(cwd) - _unpatch_meths(patchlist) - return outputfilename - else: - return ext, updated - else: - if c_file is None: - c_file, _ = _modname_to_file(tmpdir, module_name, '.py') - updated = make_py_source(ffi, module_name, c_file, - verbose=compiler_verbose) - if call_c_compiler: - return c_file - else: - return None, updated - diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/setuptools_ext.py b/.venv-docs/lib/python3.12/site-packages/cffi/setuptools_ext.py deleted file mode 100644 index 5cdd246f..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi/setuptools_ext.py +++ /dev/null @@ -1,229 +0,0 @@ -import os -import sys -import sysconfig - -try: - basestring -except NameError: - # Python 3.x - basestring = str - -def error(msg): - from cffi._shimmed_dist_utils import DistutilsSetupError - raise DistutilsSetupError(msg) - - -def execfile(filename, glob): - # We use execfile() (here rewritten for Python 3) instead of - # __import__() to load the build script. The problem with - # a normal import is that in some packages, the intermediate - # __init__.py files may already try to import the file that - # we are generating. - with open(filename) as f: - src = f.read() - src += '\n' # Python 2.6 compatibility - code = compile(src, filename, 'exec') - exec(code, glob, glob) - - -def add_cffi_module(dist, mod_spec): - from cffi.api import FFI - - if not isinstance(mod_spec, basestring): - error("argument to 'cffi_modules=...' must be a str or a list of str," - " not %r" % (type(mod_spec).__name__,)) - mod_spec = str(mod_spec) - try: - build_file_name, ffi_var_name = mod_spec.split(':') - except ValueError: - error("%r must be of the form 'path/build.py:ffi_variable'" % - (mod_spec,)) - if not os.path.exists(build_file_name): - ext = '' - rewritten = build_file_name.replace('.', '/') + '.py' - if os.path.exists(rewritten): - ext = ' (rewrite cffi_modules to [%r])' % ( - rewritten + ':' + ffi_var_name,) - error("%r does not name an existing file%s" % (build_file_name, ext)) - - mod_vars = {'__name__': '__cffi__', '__file__': build_file_name} - execfile(build_file_name, mod_vars) - - try: - ffi = mod_vars[ffi_var_name] - except KeyError: - error("%r: object %r not found in module" % (mod_spec, - ffi_var_name)) - if not isinstance(ffi, FFI): - ffi = ffi() # maybe it's a function instead of directly an ffi - if not isinstance(ffi, FFI): - error("%r is not an FFI instance (got %r)" % (mod_spec, - type(ffi).__name__)) - if not hasattr(ffi, '_assigned_source'): - error("%r: the set_source() method was not called" % (mod_spec,)) - module_name, source, source_extension, kwds = ffi._assigned_source - if ffi._windows_unicode: - kwds = kwds.copy() - ffi._apply_windows_unicode(kwds) - - if source is None: - _add_py_module(dist, ffi, module_name) - else: - _add_c_module(dist, ffi, module_name, source, source_extension, kwds) - -def _set_py_limited_api(Extension, kwds): - """ - Add py_limited_api to kwds if setuptools >= 26 is in use. - Do not alter the setting if it already exists. - Setuptools takes care of ignoring the flag on Python 2 and PyPy. - - CPython itself should ignore the flag in a debugging version - (by not listing .abi3.so in the extensions it supports), but - it doesn't so far, creating troubles. That's why we check - for "not hasattr(sys, 'gettotalrefcount')" (the 2.7 compatible equivalent - of 'd' not in sys.abiflags). (http://bugs.python.org/issue28401) - - On Windows, with CPython <= 3.4, it's better not to use py_limited_api - because virtualenv *still* doesn't copy PYTHON3.DLL on these versions. - Recently (2020) we started shipping only >= 3.5 wheels, though. So - we'll give it another try and set py_limited_api on Windows >= 3.5. - """ - from cffi._shimmed_dist_utils import log - from cffi import recompiler - - if ('py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount') - and recompiler.USE_LIMITED_API): - import setuptools - try: - setuptools_major_version = int(setuptools.__version__.partition('.')[0]) - if setuptools_major_version >= 26: - kwds['py_limited_api'] = True - except ValueError: # certain development versions of setuptools - # If we don't know the version number of setuptools, we - # try to set 'py_limited_api' anyway. At worst, we get a - # warning. - kwds['py_limited_api'] = True - - if sysconfig.get_config_var("Py_GIL_DISABLED"): - if kwds.get('py_limited_api'): - log.info("Ignoring py_limited_api=True for free-threaded build.") - - kwds['py_limited_api'] = False - - if kwds.get('py_limited_api') is False: - # avoid setting Py_LIMITED_API if py_limited_api=False - # which _cffi_include.h does unless _CFFI_NO_LIMITED_API is defined - kwds.setdefault("define_macros", []).append(("_CFFI_NO_LIMITED_API", None)) - return kwds - -def _add_c_module(dist, ffi, module_name, source, source_extension, kwds): - # We are a setuptools extension. Need this build_ext for py_limited_api. - from setuptools.command.build_ext import build_ext - from cffi._shimmed_dist_utils import Extension, log, mkpath - from cffi import recompiler - - allsources = ['$PLACEHOLDER'] - allsources.extend(kwds.pop('sources', [])) - kwds = _set_py_limited_api(Extension, kwds) - ext = Extension(name=module_name, sources=allsources, **kwds) - - def make_mod(tmpdir, pre_run=None): - c_file = os.path.join(tmpdir, module_name + source_extension) - log.info("generating cffi module %r" % c_file) - mkpath(tmpdir) - # a setuptools-only, API-only hook: called with the "ext" and "ffi" - # arguments just before we turn the ffi into C code. To use it, - # subclass the 'distutils.command.build_ext.build_ext' class and - # add a method 'def pre_run(self, ext, ffi)'. - if pre_run is not None: - pre_run(ext, ffi) - updated = recompiler.make_c_source(ffi, module_name, source, c_file) - if not updated: - log.info("already up-to-date") - return c_file - - if dist.ext_modules is None: - dist.ext_modules = [] - dist.ext_modules.append(ext) - - base_class = dist.cmdclass.get('build_ext', build_ext) - class build_ext_make_mod(base_class): - def run(self): - if ext.sources[0] == '$PLACEHOLDER': - pre_run = getattr(self, 'pre_run', None) - ext.sources[0] = make_mod(self.build_temp, pre_run) - base_class.run(self) - dist.cmdclass['build_ext'] = build_ext_make_mod - # NB. multiple runs here will create multiple 'build_ext_make_mod' - # classes. Even in this case the 'build_ext' command should be - # run once; but just in case, the logic above does nothing if - # called again. - - -def _add_py_module(dist, ffi, module_name): - from setuptools.command.build_py import build_py - from setuptools.command.build_ext import build_ext - from cffi._shimmed_dist_utils import log, mkpath - from cffi import recompiler - - def generate_mod(py_file): - log.info("generating cffi module %r" % py_file) - mkpath(os.path.dirname(py_file)) - updated = recompiler.make_py_source(ffi, module_name, py_file) - if not updated: - log.info("already up-to-date") - - base_class = dist.cmdclass.get('build_py', build_py) - class build_py_make_mod(base_class): - def run(self): - base_class.run(self) - module_path = module_name.split('.') - module_path[-1] += '.py' - generate_mod(os.path.join(self.build_lib, *module_path)) - def get_source_files(self): - # This is called from 'setup.py sdist' only. Exclude - # the generate .py module in this case. - saved_py_modules = self.py_modules - try: - if saved_py_modules: - self.py_modules = [m for m in saved_py_modules - if m != module_name] - return base_class.get_source_files(self) - finally: - self.py_modules = saved_py_modules - dist.cmdclass['build_py'] = build_py_make_mod - - # distutils and setuptools have no notion I could find of a - # generated python module. If we don't add module_name to - # dist.py_modules, then things mostly work but there are some - # combination of options (--root and --record) that will miss - # the module. So we add it here, which gives a few apparently - # harmless warnings about not finding the file outside the - # build directory. - # Then we need to hack more in get_source_files(); see above. - if dist.py_modules is None: - dist.py_modules = [] - dist.py_modules.append(module_name) - - # the following is only for "build_ext -i" - base_class_2 = dist.cmdclass.get('build_ext', build_ext) - class build_ext_make_mod(base_class_2): - def run(self): - base_class_2.run(self) - if self.inplace: - # from get_ext_fullpath() in distutils/command/build_ext.py - module_path = module_name.split('.') - package = '.'.join(module_path[:-1]) - build_py = self.get_finalized_command('build_py') - package_dir = build_py.get_package_dir(package) - file_name = module_path[-1] + '.py' - generate_mod(os.path.join(package_dir, file_name)) - dist.cmdclass['build_ext'] = build_ext_make_mod - -def cffi_modules(dist, attr, value): - assert attr == 'cffi_modules' - if isinstance(value, basestring): - value = [value] - - for cffi_module in value: - add_cffi_module(dist, cffi_module) diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/vengine_cpy.py b/.venv-docs/lib/python3.12/site-packages/cffi/vengine_cpy.py deleted file mode 100644 index 02e6a471..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi/vengine_cpy.py +++ /dev/null @@ -1,1087 +0,0 @@ -# -# DEPRECATED: implementation for ffi.verify() -# -import sys -from . import model -from .error import VerificationError -from . import _imp_emulation as imp - - -class VCPythonEngine(object): - _class_key = 'x' - _gen_python_module = True - - def __init__(self, verifier): - self.verifier = verifier - self.ffi = verifier.ffi - self._struct_pending_verification = {} - self._types_of_builtin_functions = {} - - def patch_extension_kwds(self, kwds): - pass - - def find_module(self, module_name, path, so_suffixes): - try: - f, filename, descr = imp.find_module(module_name, path) - except ImportError: - return None - if f is not None: - f.close() - # Note that after a setuptools installation, there are both .py - # and .so files with the same basename. The code here relies on - # imp.find_module() locating the .so in priority. - if descr[0] not in so_suffixes: - return None - return filename - - def collect_types(self): - self._typesdict = {} - self._generate("collecttype") - - def _prnt(self, what=''): - self._f.write(what + '\n') - - def _gettypenum(self, type): - # a KeyError here is a bug. please report it! :-) - return self._typesdict[type] - - def _do_collect_type(self, tp): - if ((not isinstance(tp, model.PrimitiveType) - or tp.name == 'long double') - and tp not in self._typesdict): - num = len(self._typesdict) - self._typesdict[tp] = num - - def write_source_to_f(self): - self.collect_types() - # - # The new module will have a _cffi_setup() function that receives - # objects from the ffi world, and that calls some setup code in - # the module. This setup code is split in several independent - # functions, e.g. one per constant. The functions are "chained" - # by ending in a tail call to each other. - # - # This is further split in two chained lists, depending on if we - # can do it at import-time or if we must wait for _cffi_setup() to - # provide us with the objects. This is needed because we - # need the values of the enum constants in order to build the - # that we may have to pass to _cffi_setup(). - # - # The following two 'chained_list_constants' items contains - # the head of these two chained lists, as a string that gives the - # call to do, if any. - self._chained_list_constants = ['((void)lib,0)', '((void)lib,0)'] - # - prnt = self._prnt - # first paste some standard set of lines that are mostly '#define' - prnt(cffimod_header) - prnt() - # then paste the C source given by the user, verbatim. - prnt(self.verifier.preamble) - prnt() - # - # call generate_cpy_xxx_decl(), for every xxx found from - # ffi._parser._declarations. This generates all the functions. - self._generate("decl") - # - # implement the function _cffi_setup_custom() as calling the - # head of the chained list. - self._generate_setup_custom() - prnt() - # - # produce the method table, including the entries for the - # generated Python->C function wrappers, which are done - # by generate_cpy_function_method(). - prnt('static PyMethodDef _cffi_methods[] = {') - self._generate("method") - prnt(' {"_cffi_setup", _cffi_setup, METH_VARARGS, NULL},') - prnt(' {NULL, NULL, 0, NULL} /* Sentinel */') - prnt('};') - prnt() - # - # standard init. - modname = self.verifier.get_module_name() - constants = self._chained_list_constants[False] - prnt('#if PY_MAJOR_VERSION >= 3') - prnt() - prnt('static struct PyModuleDef _cffi_module_def = {') - prnt(' PyModuleDef_HEAD_INIT,') - prnt(' "%s",' % modname) - prnt(' NULL,') - prnt(' -1,') - prnt(' _cffi_methods,') - prnt(' NULL, NULL, NULL, NULL') - prnt('};') - prnt() - prnt('PyMODINIT_FUNC') - prnt('PyInit_%s(void)' % modname) - prnt('{') - prnt(' PyObject *lib;') - prnt(' lib = PyModule_Create(&_cffi_module_def);') - prnt(' if (lib == NULL)') - prnt(' return NULL;') - prnt(' if (%s < 0 || _cffi_init() < 0) {' % (constants,)) - prnt(' Py_DECREF(lib);') - prnt(' return NULL;') - prnt(' }') - prnt('#if Py_GIL_DISABLED') - prnt(' PyUnstable_Module_SetGIL(lib, Py_MOD_GIL_NOT_USED);') - prnt('#endif') - prnt(' return lib;') - prnt('}') - prnt() - prnt('#else') - prnt() - prnt('PyMODINIT_FUNC') - prnt('init%s(void)' % modname) - prnt('{') - prnt(' PyObject *lib;') - prnt(' lib = Py_InitModule("%s", _cffi_methods);' % modname) - prnt(' if (lib == NULL)') - prnt(' return;') - prnt(' if (%s < 0 || _cffi_init() < 0)' % (constants,)) - prnt(' return;') - prnt(' return;') - prnt('}') - prnt() - prnt('#endif') - - def load_library(self, flags=None): - # XXX review all usages of 'self' here! - # import it as a new extension module - imp.acquire_lock() - try: - if hasattr(sys, "getdlopenflags"): - previous_flags = sys.getdlopenflags() - try: - if hasattr(sys, "setdlopenflags") and flags is not None: - sys.setdlopenflags(flags) - module = imp.load_dynamic(self.verifier.get_module_name(), - self.verifier.modulefilename) - except ImportError as e: - error = "importing %r: %s" % (self.verifier.modulefilename, e) - raise VerificationError(error) - finally: - if hasattr(sys, "setdlopenflags"): - sys.setdlopenflags(previous_flags) - finally: - imp.release_lock() - # - # call loading_cpy_struct() to get the struct layout inferred by - # the C compiler - self._load(module, 'loading') - # - # the C code will need the objects. Collect them in - # order in a list. - revmapping = dict([(value, key) - for (key, value) in self._typesdict.items()]) - lst = [revmapping[i] for i in range(len(revmapping))] - lst = list(map(self.ffi._get_cached_btype, lst)) - # - # build the FFILibrary class and instance and call _cffi_setup(). - # this will set up some fields like '_cffi_types', and only then - # it will invoke the chained list of functions that will really - # build (notably) the constant objects, as if they are - # pointers, and store them as attributes on the 'library' object. - class FFILibrary(object): - _cffi_python_module = module - _cffi_ffi = self.ffi - _cffi_dir = [] - def __dir__(self): - return FFILibrary._cffi_dir + list(self.__dict__) - library = FFILibrary() - if module._cffi_setup(lst, VerificationError, library): - import warnings - warnings.warn("reimporting %r might overwrite older definitions" - % (self.verifier.get_module_name())) - # - # finally, call the loaded_cpy_xxx() functions. This will perform - # the final adjustments, like copying the Python->C wrapper - # functions from the module to the 'library' object, and setting - # up the FFILibrary class with properties for the global C variables. - self._load(module, 'loaded', library=library) - module._cffi_original_ffi = self.ffi - module._cffi_types_of_builtin_funcs = self._types_of_builtin_functions - return library - - def _get_declarations(self): - lst = [(key, tp) for (key, (tp, qual)) in - self.ffi._parser._declarations.items()] - lst.sort() - return lst - - def _generate(self, step_name): - for name, tp in self._get_declarations(): - kind, realname = name.split(' ', 1) - try: - method = getattr(self, '_generate_cpy_%s_%s' % (kind, - step_name)) - except AttributeError: - raise VerificationError( - "not implemented in verify(): %r" % name) - try: - method(tp, realname) - except Exception as e: - model.attach_exception_info(e, name) - raise - - def _load(self, module, step_name, **kwds): - for name, tp in self._get_declarations(): - kind, realname = name.split(' ', 1) - method = getattr(self, '_%s_cpy_%s' % (step_name, kind)) - try: - method(tp, realname, module, **kwds) - except Exception as e: - model.attach_exception_info(e, name) - raise - - def _generate_nothing(self, tp, name): - pass - - def _loaded_noop(self, tp, name, module, **kwds): - pass - - # ---------- - - def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode): - extraarg = '' - if isinstance(tp, model.PrimitiveType): - if tp.is_integer_type() and tp.name != '_Bool': - converter = '_cffi_to_c_int' - extraarg = ', %s' % tp.name - elif tp.is_complex_type(): - raise VerificationError( - "not implemented in verify(): complex types") - else: - converter = '(%s)_cffi_to_c_%s' % (tp.get_c_name(''), - tp.name.replace(' ', '_')) - errvalue = '-1' - # - elif isinstance(tp, model.PointerType): - self._convert_funcarg_to_c_ptr_or_array(tp, fromvar, - tovar, errcode) - return - # - elif isinstance(tp, (model.StructOrUnion, model.EnumType)): - # a struct (not a struct pointer) as a function argument - self._prnt(' if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)' - % (tovar, self._gettypenum(tp), fromvar)) - self._prnt(' %s;' % errcode) - return - # - elif isinstance(tp, model.FunctionPtrType): - converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('') - extraarg = ', _cffi_type(%d)' % self._gettypenum(tp) - errvalue = 'NULL' - # - else: - raise NotImplementedError(tp) - # - self._prnt(' %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg)) - self._prnt(' if (%s == (%s)%s && PyErr_Occurred())' % ( - tovar, tp.get_c_name(''), errvalue)) - self._prnt(' %s;' % errcode) - - def _extra_local_variables(self, tp, localvars, freelines): - if isinstance(tp, model.PointerType): - localvars.add('Py_ssize_t datasize') - localvars.add('struct _cffi_freeme_s *large_args_free = NULL') - freelines.add('if (large_args_free != NULL)' - ' _cffi_free_array_arguments(large_args_free);') - - def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode): - self._prnt(' datasize = _cffi_prepare_pointer_call_argument(') - self._prnt(' _cffi_type(%d), %s, (char **)&%s);' % ( - self._gettypenum(tp), fromvar, tovar)) - self._prnt(' if (datasize != 0) {') - self._prnt(' %s = ((size_t)datasize) <= 640 ? ' - 'alloca((size_t)datasize) : NULL;' % (tovar,)) - self._prnt(' if (_cffi_convert_array_argument(_cffi_type(%d), %s, ' - '(char **)&%s,' % (self._gettypenum(tp), fromvar, tovar)) - self._prnt(' datasize, &large_args_free) < 0)') - self._prnt(' %s;' % errcode) - self._prnt(' }') - - def _convert_expr_from_c(self, tp, var, context): - if isinstance(tp, model.PrimitiveType): - if tp.is_integer_type() and tp.name != '_Bool': - return '_cffi_from_c_int(%s, %s)' % (var, tp.name) - elif tp.name != 'long double': - return '_cffi_from_c_%s(%s)' % (tp.name.replace(' ', '_'), var) - else: - return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - elif isinstance(tp, (model.PointerType, model.FunctionPtrType)): - return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - elif isinstance(tp, model.ArrayType): - return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( - var, self._gettypenum(model.PointerType(tp.item))) - elif isinstance(tp, model.StructOrUnion): - if tp.fldnames is None: - raise TypeError("'%s' is used as %s, but is opaque" % ( - tp._get_c_name(), context)) - return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - elif isinstance(tp, model.EnumType): - return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - else: - raise NotImplementedError(tp) - - # ---------- - # typedefs: generates no code so far - - _generate_cpy_typedef_collecttype = _generate_nothing - _generate_cpy_typedef_decl = _generate_nothing - _generate_cpy_typedef_method = _generate_nothing - _loading_cpy_typedef = _loaded_noop - _loaded_cpy_typedef = _loaded_noop - - # ---------- - # function declarations - - def _generate_cpy_function_collecttype(self, tp, name): - assert isinstance(tp, model.FunctionPtrType) - if tp.ellipsis: - self._do_collect_type(tp) - else: - # don't call _do_collect_type(tp) in this common case, - # otherwise test_autofilled_struct_as_argument fails - for type in tp.args: - self._do_collect_type(type) - self._do_collect_type(tp.result) - - def _generate_cpy_function_decl(self, tp, name): - assert isinstance(tp, model.FunctionPtrType) - if tp.ellipsis: - # cannot support vararg functions better than this: check for its - # exact type (including the fixed arguments), and build it as a - # constant function pointer (no CPython wrapper) - self._generate_cpy_const(False, name, tp) - return - prnt = self._prnt - numargs = len(tp.args) - if numargs == 0: - argname = 'noarg' - elif numargs == 1: - argname = 'arg0' - else: - argname = 'args' - prnt('static PyObject *') - prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname)) - prnt('{') - # - context = 'argument of %s' % name - for i, type in enumerate(tp.args): - prnt(' %s;' % type.get_c_name(' x%d' % i, context)) - # - localvars = set() - freelines = set() - for type in tp.args: - self._extra_local_variables(type, localvars, freelines) - for decl in sorted(localvars): - prnt(' %s;' % (decl,)) - # - if not isinstance(tp.result, model.VoidType): - result_code = 'result = ' - context = 'result of %s' % name - prnt(' %s;' % tp.result.get_c_name(' result', context)) - prnt(' PyObject *pyresult;') - else: - result_code = '' - # - if len(tp.args) > 1: - rng = range(len(tp.args)) - for i in rng: - prnt(' PyObject *arg%d;' % i) - prnt() - prnt(' if (!PyArg_ParseTuple(args, "%s:%s", %s))' % ( - 'O' * numargs, name, ', '.join(['&arg%d' % i for i in rng]))) - prnt(' return NULL;') - prnt() - # - for i, type in enumerate(tp.args): - self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i, - 'return NULL') - prnt() - # - prnt(' Py_BEGIN_ALLOW_THREADS') - prnt(' _cffi_restore_errno();') - prnt(' { %s%s(%s); }' % ( - result_code, name, - ', '.join(['x%d' % i for i in range(len(tp.args))]))) - prnt(' _cffi_save_errno();') - prnt(' Py_END_ALLOW_THREADS') - prnt() - # - prnt(' (void)self; /* unused */') - if numargs == 0: - prnt(' (void)noarg; /* unused */') - if result_code: - prnt(' pyresult = %s;' % - self._convert_expr_from_c(tp.result, 'result', 'result type')) - for freeline in freelines: - prnt(' ' + freeline) - prnt(' return pyresult;') - else: - for freeline in freelines: - prnt(' ' + freeline) - prnt(' Py_INCREF(Py_None);') - prnt(' return Py_None;') - prnt('}') - prnt() - - def _generate_cpy_function_method(self, tp, name): - if tp.ellipsis: - return - numargs = len(tp.args) - if numargs == 0: - meth = 'METH_NOARGS' - elif numargs == 1: - meth = 'METH_O' - else: - meth = 'METH_VARARGS' - self._prnt(' {"%s", _cffi_f_%s, %s, NULL},' % (name, name, meth)) - - _loading_cpy_function = _loaded_noop - - def _loaded_cpy_function(self, tp, name, module, library): - if tp.ellipsis: - return - func = getattr(module, name) - setattr(library, name, func) - self._types_of_builtin_functions[func] = tp - - # ---------- - # named structs - - _generate_cpy_struct_collecttype = _generate_nothing - def _generate_cpy_struct_decl(self, tp, name): - assert name == tp.name - self._generate_struct_or_union_decl(tp, 'struct', name) - def _generate_cpy_struct_method(self, tp, name): - self._generate_struct_or_union_method(tp, 'struct', name) - def _loading_cpy_struct(self, tp, name, module): - self._loading_struct_or_union(tp, 'struct', name, module) - def _loaded_cpy_struct(self, tp, name, module, **kwds): - self._loaded_struct_or_union(tp) - - _generate_cpy_union_collecttype = _generate_nothing - def _generate_cpy_union_decl(self, tp, name): - assert name == tp.name - self._generate_struct_or_union_decl(tp, 'union', name) - def _generate_cpy_union_method(self, tp, name): - self._generate_struct_or_union_method(tp, 'union', name) - def _loading_cpy_union(self, tp, name, module): - self._loading_struct_or_union(tp, 'union', name, module) - def _loaded_cpy_union(self, tp, name, module, **kwds): - self._loaded_struct_or_union(tp) - - def _generate_struct_or_union_decl(self, tp, prefix, name): - if tp.fldnames is None: - return # nothing to do with opaque structs - checkfuncname = '_cffi_check_%s_%s' % (prefix, name) - layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) - cname = ('%s %s' % (prefix, name)).strip() - # - prnt = self._prnt - prnt('static void %s(%s *p)' % (checkfuncname, cname)) - prnt('{') - prnt(' /* only to generate compile-time warnings or errors */') - prnt(' (void)p;') - for fname, ftype, fbitsize, fqual in tp.enumfields(): - if (isinstance(ftype, model.PrimitiveType) - and ftype.is_integer_type()) or fbitsize >= 0: - # accept all integers, but complain on float or double - prnt(' (void)((p->%s) << 1);' % fname) - else: - # only accept exactly the type declared. - try: - prnt(' { %s = &p->%s; (void)tmp; }' % ( - ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual), - fname)) - except VerificationError as e: - prnt(' /* %s */' % str(e)) # cannot verify it, ignore - prnt('}') - prnt('static PyObject *') - prnt('%s(PyObject *self, PyObject *noarg)' % (layoutfuncname,)) - prnt('{') - prnt(' struct _cffi_aligncheck { char x; %s y; };' % cname) - prnt(' static Py_ssize_t nums[] = {') - prnt(' sizeof(%s),' % cname) - prnt(' offsetof(struct _cffi_aligncheck, y),') - for fname, ftype, fbitsize, fqual in tp.enumfields(): - if fbitsize >= 0: - continue # xxx ignore fbitsize for now - prnt(' offsetof(%s, %s),' % (cname, fname)) - if isinstance(ftype, model.ArrayType) and ftype.length is None: - prnt(' 0, /* %s */' % ftype._get_c_name()) - else: - prnt(' sizeof(((%s *)0)->%s),' % (cname, fname)) - prnt(' -1') - prnt(' };') - prnt(' (void)self; /* unused */') - prnt(' (void)noarg; /* unused */') - prnt(' return _cffi_get_struct_layout(nums);') - prnt(' /* the next line is not executed, but compiled */') - prnt(' %s(0);' % (checkfuncname,)) - prnt('}') - prnt() - - def _generate_struct_or_union_method(self, tp, prefix, name): - if tp.fldnames is None: - return # nothing to do with opaque structs - layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) - self._prnt(' {"%s", %s, METH_NOARGS, NULL},' % (layoutfuncname, - layoutfuncname)) - - def _loading_struct_or_union(self, tp, prefix, name, module): - if tp.fldnames is None: - return # nothing to do with opaque structs - layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) - # - function = getattr(module, layoutfuncname) - layout = function() - if isinstance(tp, model.StructOrUnion) and tp.partial: - # use the function()'s sizes and offsets to guide the - # layout of the struct - totalsize = layout[0] - totalalignment = layout[1] - fieldofs = layout[2::2] - fieldsize = layout[3::2] - tp.force_flatten() - assert len(fieldofs) == len(fieldsize) == len(tp.fldnames) - tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment - else: - cname = ('%s %s' % (prefix, name)).strip() - self._struct_pending_verification[tp] = layout, cname - - def _loaded_struct_or_union(self, tp): - if tp.fldnames is None: - return # nothing to do with opaque structs - self.ffi._get_cached_btype(tp) # force 'fixedlayout' to be considered - - if tp in self._struct_pending_verification: - # check that the layout sizes and offsets match the real ones - def check(realvalue, expectedvalue, msg): - if realvalue != expectedvalue: - raise VerificationError( - "%s (we have %d, but C compiler says %d)" - % (msg, expectedvalue, realvalue)) - ffi = self.ffi - BStruct = ffi._get_cached_btype(tp) - layout, cname = self._struct_pending_verification.pop(tp) - check(layout[0], ffi.sizeof(BStruct), "wrong total size") - check(layout[1], ffi.alignof(BStruct), "wrong total alignment") - i = 2 - for fname, ftype, fbitsize, fqual in tp.enumfields(): - if fbitsize >= 0: - continue # xxx ignore fbitsize for now - check(layout[i], ffi.offsetof(BStruct, fname), - "wrong offset for field %r" % (fname,)) - if layout[i+1] != 0: - BField = ffi._get_cached_btype(ftype) - check(layout[i+1], ffi.sizeof(BField), - "wrong size for field %r" % (fname,)) - i += 2 - assert i == len(layout) - - # ---------- - # 'anonymous' declarations. These are produced for anonymous structs - # or unions; the 'name' is obtained by a typedef. - - _generate_cpy_anonymous_collecttype = _generate_nothing - - def _generate_cpy_anonymous_decl(self, tp, name): - if isinstance(tp, model.EnumType): - self._generate_cpy_enum_decl(tp, name, '') - else: - self._generate_struct_or_union_decl(tp, '', name) - - def _generate_cpy_anonymous_method(self, tp, name): - if not isinstance(tp, model.EnumType): - self._generate_struct_or_union_method(tp, '', name) - - def _loading_cpy_anonymous(self, tp, name, module): - if isinstance(tp, model.EnumType): - self._loading_cpy_enum(tp, name, module) - else: - self._loading_struct_or_union(tp, '', name, module) - - def _loaded_cpy_anonymous(self, tp, name, module, **kwds): - if isinstance(tp, model.EnumType): - self._loaded_cpy_enum(tp, name, module, **kwds) - else: - self._loaded_struct_or_union(tp) - - # ---------- - # constants, likely declared with '#define' - - def _generate_cpy_const(self, is_int, name, tp=None, category='const', - vartp=None, delayed=True, size_too=False, - check_value=None): - prnt = self._prnt - funcname = '_cffi_%s_%s' % (category, name) - prnt('static int %s(PyObject *lib)' % funcname) - prnt('{') - prnt(' PyObject *o;') - prnt(' int res;') - if not is_int: - prnt(' %s;' % (vartp or tp).get_c_name(' i', name)) - else: - assert category == 'const' - # - if check_value is not None: - self._check_int_constant_value(name, check_value) - # - if not is_int: - if category == 'var': - realexpr = '&' + name - else: - realexpr = name - prnt(' i = (%s);' % (realexpr,)) - prnt(' o = %s;' % (self._convert_expr_from_c(tp, 'i', - 'variable type'),)) - assert delayed - else: - prnt(' o = _cffi_from_c_int_const(%s);' % name) - prnt(' if (o == NULL)') - prnt(' return -1;') - if size_too: - prnt(' {') - prnt(' PyObject *o1 = o;') - prnt(' o = Py_BuildValue("On", o1, (Py_ssize_t)sizeof(%s));' - % (name,)) - prnt(' Py_DECREF(o1);') - prnt(' if (o == NULL)') - prnt(' return -1;') - prnt(' }') - prnt(' res = PyObject_SetAttrString(lib, "%s", o);' % name) - prnt(' Py_DECREF(o);') - prnt(' if (res < 0)') - prnt(' return -1;') - prnt(' return %s;' % self._chained_list_constants[delayed]) - self._chained_list_constants[delayed] = funcname + '(lib)' - prnt('}') - prnt() - - def _generate_cpy_constant_collecttype(self, tp, name): - is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() - if not is_int: - self._do_collect_type(tp) - - def _generate_cpy_constant_decl(self, tp, name): - is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() - self._generate_cpy_const(is_int, name, tp) - - _generate_cpy_constant_method = _generate_nothing - _loading_cpy_constant = _loaded_noop - _loaded_cpy_constant = _loaded_noop - - # ---------- - # enums - - def _check_int_constant_value(self, name, value, err_prefix=''): - prnt = self._prnt - if value <= 0: - prnt(' if ((%s) > 0 || (long)(%s) != %dL) {' % ( - name, name, value)) - else: - prnt(' if ((%s) <= 0 || (unsigned long)(%s) != %dUL) {' % ( - name, name, value)) - prnt(' char buf[64];') - prnt(' if ((%s) <= 0)' % name) - prnt(' snprintf(buf, 63, "%%ld", (long)(%s));' % name) - prnt(' else') - prnt(' snprintf(buf, 63, "%%lu", (unsigned long)(%s));' % - name) - prnt(' PyErr_Format(_cffi_VerificationError,') - prnt(' "%s%s has the real value %s, not %s",') - prnt(' "%s", "%s", buf, "%d");' % ( - err_prefix, name, value)) - prnt(' return -1;') - prnt(' }') - - def _enum_funcname(self, prefix, name): - # "$enum_$1" => "___D_enum____D_1" - name = name.replace('$', '___D_') - return '_cffi_e_%s_%s' % (prefix, name) - - def _generate_cpy_enum_decl(self, tp, name, prefix='enum'): - if tp.partial: - for enumerator in tp.enumerators: - self._generate_cpy_const(True, enumerator, delayed=False) - return - # - funcname = self._enum_funcname(prefix, name) - prnt = self._prnt - prnt('static int %s(PyObject *lib)' % funcname) - prnt('{') - for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): - self._check_int_constant_value(enumerator, enumvalue, - "enum %s: " % name) - prnt(' return %s;' % self._chained_list_constants[True]) - self._chained_list_constants[True] = funcname + '(lib)' - prnt('}') - prnt() - - _generate_cpy_enum_collecttype = _generate_nothing - _generate_cpy_enum_method = _generate_nothing - - def _loading_cpy_enum(self, tp, name, module): - if tp.partial: - enumvalues = [getattr(module, enumerator) - for enumerator in tp.enumerators] - tp.enumvalues = tuple(enumvalues) - tp.partial_resolved = True - - def _loaded_cpy_enum(self, tp, name, module, library): - for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): - setattr(library, enumerator, enumvalue) - - # ---------- - # macros: for now only for integers - - def _generate_cpy_macro_decl(self, tp, name): - if tp == '...': - check_value = None - else: - check_value = tp # an integer - self._generate_cpy_const(True, name, check_value=check_value) - - _generate_cpy_macro_collecttype = _generate_nothing - _generate_cpy_macro_method = _generate_nothing - _loading_cpy_macro = _loaded_noop - _loaded_cpy_macro = _loaded_noop - - # ---------- - # global variables - - def _generate_cpy_variable_collecttype(self, tp, name): - if isinstance(tp, model.ArrayType): - tp_ptr = model.PointerType(tp.item) - else: - tp_ptr = model.PointerType(tp) - self._do_collect_type(tp_ptr) - - def _generate_cpy_variable_decl(self, tp, name): - if isinstance(tp, model.ArrayType): - tp_ptr = model.PointerType(tp.item) - self._generate_cpy_const(False, name, tp, vartp=tp_ptr, - size_too = tp.length_is_unknown()) - else: - tp_ptr = model.PointerType(tp) - self._generate_cpy_const(False, name, tp_ptr, category='var') - - _generate_cpy_variable_method = _generate_nothing - _loading_cpy_variable = _loaded_noop - - def _loaded_cpy_variable(self, tp, name, module, library): - value = getattr(library, name) - if isinstance(tp, model.ArrayType): # int a[5] is "constant" in the - # sense that "a=..." is forbidden - if tp.length_is_unknown(): - assert isinstance(value, tuple) - (value, size) = value - BItemType = self.ffi._get_cached_btype(tp.item) - length, rest = divmod(size, self.ffi.sizeof(BItemType)) - if rest != 0: - raise VerificationError( - "bad size: %r does not seem to be an array of %s" % - (name, tp.item)) - tp = tp.resolve_length(length) - # 'value' is a which we have to replace with - # a if the N is actually known - if tp.length is not None: - BArray = self.ffi._get_cached_btype(tp) - value = self.ffi.cast(BArray, value) - setattr(library, name, value) - return - # remove ptr= from the library instance, and replace - # it by a property on the class, which reads/writes into ptr[0]. - ptr = value - delattr(library, name) - def getter(library): - return ptr[0] - def setter(library, value): - ptr[0] = value - setattr(type(library), name, property(getter, setter)) - type(library)._cffi_dir.append(name) - - # ---------- - - def _generate_setup_custom(self): - prnt = self._prnt - prnt('static int _cffi_setup_custom(PyObject *lib)') - prnt('{') - prnt(' return %s;' % self._chained_list_constants[True]) - prnt('}') - -cffimod_header = r''' -#include -#include - -/* this block of #ifs should be kept exactly identical between - c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py - and cffi/_cffi_include.h */ -#if defined(_MSC_VER) -# include /* for alloca() */ -# if _MSC_VER < 1600 /* MSVC < 2010 */ - typedef __int8 int8_t; - typedef __int16 int16_t; - typedef __int32 int32_t; - typedef __int64 int64_t; - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; - typedef unsigned __int64 uint64_t; - typedef __int8 int_least8_t; - typedef __int16 int_least16_t; - typedef __int32 int_least32_t; - typedef __int64 int_least64_t; - typedef unsigned __int8 uint_least8_t; - typedef unsigned __int16 uint_least16_t; - typedef unsigned __int32 uint_least32_t; - typedef unsigned __int64 uint_least64_t; - typedef __int8 int_fast8_t; - typedef __int16 int_fast16_t; - typedef __int32 int_fast32_t; - typedef __int64 int_fast64_t; - typedef unsigned __int8 uint_fast8_t; - typedef unsigned __int16 uint_fast16_t; - typedef unsigned __int32 uint_fast32_t; - typedef unsigned __int64 uint_fast64_t; - typedef __int64 intmax_t; - typedef unsigned __int64 uintmax_t; -# else -# include -# endif -# if _MSC_VER < 1800 /* MSVC < 2013 */ -# ifndef __cplusplus - typedef unsigned char _Bool; -# endif -# endif -# define _cffi_float_complex_t _Fcomplex /* include for it */ -# define _cffi_double_complex_t _Dcomplex /* include for it */ -#else -# include -# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux) -# include -# endif -# define _cffi_float_complex_t float _Complex -# define _cffi_double_complex_t double _Complex -#endif - -#if PY_MAJOR_VERSION < 3 -# undef PyCapsule_CheckExact -# undef PyCapsule_GetPointer -# define PyCapsule_CheckExact(capsule) (PyCObject_Check(capsule)) -# define PyCapsule_GetPointer(capsule, name) \ - (PyCObject_AsVoidPtr(capsule)) -#endif - -#if PY_MAJOR_VERSION >= 3 -# define PyInt_FromLong PyLong_FromLong -#endif - -#define _cffi_from_c_double PyFloat_FromDouble -#define _cffi_from_c_float PyFloat_FromDouble -#define _cffi_from_c_long PyInt_FromLong -#define _cffi_from_c_ulong PyLong_FromUnsignedLong -#define _cffi_from_c_longlong PyLong_FromLongLong -#define _cffi_from_c_ulonglong PyLong_FromUnsignedLongLong -#define _cffi_from_c__Bool PyBool_FromLong - -#define _cffi_to_c_double PyFloat_AsDouble -#define _cffi_to_c_float PyFloat_AsDouble - -#define _cffi_from_c_int_const(x) \ - (((x) > 0) ? \ - ((unsigned long long)(x) <= (unsigned long long)LONG_MAX) ? \ - PyInt_FromLong((long)(x)) : \ - PyLong_FromUnsignedLongLong((unsigned long long)(x)) : \ - ((long long)(x) >= (long long)LONG_MIN) ? \ - PyInt_FromLong((long)(x)) : \ - PyLong_FromLongLong((long long)(x))) - -#define _cffi_from_c_int(x, type) \ - (((type)-1) > 0 ? /* unsigned */ \ - (sizeof(type) < sizeof(long) ? \ - PyInt_FromLong((long)x) : \ - sizeof(type) == sizeof(long) ? \ - PyLong_FromUnsignedLong((unsigned long)x) : \ - PyLong_FromUnsignedLongLong((unsigned long long)x)) : \ - (sizeof(type) <= sizeof(long) ? \ - PyInt_FromLong((long)x) : \ - PyLong_FromLongLong((long long)x))) - -#define _cffi_to_c_int(o, type) \ - ((type)( \ - sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o) \ - : (type)_cffi_to_c_i8(o)) : \ - sizeof(type) == 2 ? (((type)-1) > 0 ? (type)_cffi_to_c_u16(o) \ - : (type)_cffi_to_c_i16(o)) : \ - sizeof(type) == 4 ? (((type)-1) > 0 ? (type)_cffi_to_c_u32(o) \ - : (type)_cffi_to_c_i32(o)) : \ - sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o) \ - : (type)_cffi_to_c_i64(o)) : \ - (Py_FatalError("unsupported size for type " #type), (type)0))) - -#define _cffi_to_c_i8 \ - ((int(*)(PyObject *))_cffi_exports[1]) -#define _cffi_to_c_u8 \ - ((int(*)(PyObject *))_cffi_exports[2]) -#define _cffi_to_c_i16 \ - ((int(*)(PyObject *))_cffi_exports[3]) -#define _cffi_to_c_u16 \ - ((int(*)(PyObject *))_cffi_exports[4]) -#define _cffi_to_c_i32 \ - ((int(*)(PyObject *))_cffi_exports[5]) -#define _cffi_to_c_u32 \ - ((unsigned int(*)(PyObject *))_cffi_exports[6]) -#define _cffi_to_c_i64 \ - ((long long(*)(PyObject *))_cffi_exports[7]) -#define _cffi_to_c_u64 \ - ((unsigned long long(*)(PyObject *))_cffi_exports[8]) -#define _cffi_to_c_char \ - ((int(*)(PyObject *))_cffi_exports[9]) -#define _cffi_from_c_pointer \ - ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[10]) -#define _cffi_to_c_pointer \ - ((char *(*)(PyObject *, CTypeDescrObject *))_cffi_exports[11]) -#define _cffi_get_struct_layout \ - ((PyObject *(*)(Py_ssize_t[]))_cffi_exports[12]) -#define _cffi_restore_errno \ - ((void(*)(void))_cffi_exports[13]) -#define _cffi_save_errno \ - ((void(*)(void))_cffi_exports[14]) -#define _cffi_from_c_char \ - ((PyObject *(*)(char))_cffi_exports[15]) -#define _cffi_from_c_deref \ - ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[16]) -#define _cffi_to_c \ - ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[17]) -#define _cffi_from_c_struct \ - ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[18]) -#define _cffi_to_c_wchar_t \ - ((wchar_t(*)(PyObject *))_cffi_exports[19]) -#define _cffi_from_c_wchar_t \ - ((PyObject *(*)(wchar_t))_cffi_exports[20]) -#define _cffi_to_c_long_double \ - ((long double(*)(PyObject *))_cffi_exports[21]) -#define _cffi_to_c__Bool \ - ((_Bool(*)(PyObject *))_cffi_exports[22]) -#define _cffi_prepare_pointer_call_argument \ - ((Py_ssize_t(*)(CTypeDescrObject *, PyObject *, char **))_cffi_exports[23]) -#define _cffi_convert_array_from_object \ - ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[24]) -#define _CFFI_NUM_EXPORTS 25 - -typedef struct _ctypedescr CTypeDescrObject; - -static void *_cffi_exports[_CFFI_NUM_EXPORTS]; -static PyObject *_cffi_types, *_cffi_VerificationError; - -static int _cffi_setup_custom(PyObject *lib); /* forward */ - -static PyObject *_cffi_setup(PyObject *self, PyObject *args) -{ - PyObject *library; - int was_alive = (_cffi_types != NULL); - (void)self; /* unused */ - if (!PyArg_ParseTuple(args, "OOO", &_cffi_types, &_cffi_VerificationError, - &library)) - return NULL; - Py_INCREF(_cffi_types); - Py_INCREF(_cffi_VerificationError); - if (_cffi_setup_custom(library) < 0) - return NULL; - return PyBool_FromLong(was_alive); -} - -union _cffi_union_alignment_u { - unsigned char m_char; - unsigned short m_short; - unsigned int m_int; - unsigned long m_long; - unsigned long long m_longlong; - float m_float; - double m_double; - long double m_longdouble; -}; - -struct _cffi_freeme_s { - struct _cffi_freeme_s *next; - union _cffi_union_alignment_u alignment; -}; - -#ifdef __GNUC__ - __attribute__((unused)) -#endif -static int _cffi_convert_array_argument(CTypeDescrObject *ctptr, PyObject *arg, - char **output_data, Py_ssize_t datasize, - struct _cffi_freeme_s **freeme) -{ - char *p; - if (datasize < 0) - return -1; - - p = *output_data; - if (p == NULL) { - struct _cffi_freeme_s *fp = (struct _cffi_freeme_s *)PyObject_Malloc( - offsetof(struct _cffi_freeme_s, alignment) + (size_t)datasize); - if (fp == NULL) - return -1; - fp->next = *freeme; - *freeme = fp; - p = *output_data = (char *)&fp->alignment; - } - memset((void *)p, 0, (size_t)datasize); - return _cffi_convert_array_from_object(p, ctptr, arg); -} - -#ifdef __GNUC__ - __attribute__((unused)) -#endif -static void _cffi_free_array_arguments(struct _cffi_freeme_s *freeme) -{ - do { - void *p = (void *)freeme; - freeme = freeme->next; - PyObject_Free(p); - } while (freeme != NULL); -} - -static int _cffi_init(void) -{ - PyObject *module, *c_api_object = NULL; - - module = PyImport_ImportModule("_cffi_backend"); - if (module == NULL) - goto failure; - - c_api_object = PyObject_GetAttrString(module, "_C_API"); - if (c_api_object == NULL) - goto failure; - if (!PyCapsule_CheckExact(c_api_object)) { - PyErr_SetNone(PyExc_ImportError); - goto failure; - } - memcpy(_cffi_exports, PyCapsule_GetPointer(c_api_object, "cffi"), - _CFFI_NUM_EXPORTS * sizeof(void *)); - - Py_DECREF(module); - Py_DECREF(c_api_object); - return 0; - - failure: - Py_XDECREF(module); - Py_XDECREF(c_api_object); - return -1; -} - -#define _cffi_type(num) ((CTypeDescrObject *)PyList_GET_ITEM(_cffi_types, num)) - -/**********/ -''' diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/vengine_gen.py b/.venv-docs/lib/python3.12/site-packages/cffi/vengine_gen.py deleted file mode 100644 index bffc8212..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi/vengine_gen.py +++ /dev/null @@ -1,679 +0,0 @@ -# -# DEPRECATED: implementation for ffi.verify() -# -import sys, os -import types - -from . import model -from .error import VerificationError - - -class VGenericEngine(object): - _class_key = 'g' - _gen_python_module = False - - def __init__(self, verifier): - self.verifier = verifier - self.ffi = verifier.ffi - self.export_symbols = [] - self._struct_pending_verification = {} - - def patch_extension_kwds(self, kwds): - # add 'export_symbols' to the dictionary. Note that we add the - # list before filling it. When we fill it, it will thus also show - # up in kwds['export_symbols']. - kwds.setdefault('export_symbols', self.export_symbols) - - def find_module(self, module_name, path, so_suffixes): - for so_suffix in so_suffixes: - basename = module_name + so_suffix - if path is None: - path = sys.path - for dirname in path: - filename = os.path.join(dirname, basename) - if os.path.isfile(filename): - return filename - - def collect_types(self): - pass # not needed in the generic engine - - def _prnt(self, what=''): - self._f.write(what + '\n') - - def write_source_to_f(self): - prnt = self._prnt - # first paste some standard set of lines that are mostly '#include' - prnt(cffimod_header) - # then paste the C source given by the user, verbatim. - prnt(self.verifier.preamble) - # - # call generate_gen_xxx_decl(), for every xxx found from - # ffi._parser._declarations. This generates all the functions. - self._generate('decl') - # - # on Windows, distutils insists on putting init_cffi_xyz in - # 'export_symbols', so instead of fighting it, just give up and - # give it one - if sys.platform == 'win32': - if sys.version_info >= (3,): - prefix = 'PyInit_' - else: - prefix = 'init' - modname = self.verifier.get_module_name() - prnt("void %s%s(void) { }\n" % (prefix, modname)) - - def load_library(self, flags=0): - # import it with the CFFI backend - backend = self.ffi._backend - # needs to make a path that contains '/', on Posix - filename = os.path.join(os.curdir, self.verifier.modulefilename) - module = backend.load_library(filename, flags) - # - # call loading_gen_struct() to get the struct layout inferred by - # the C compiler - self._load(module, 'loading') - - # build the FFILibrary class and instance, this is a module subclass - # because modules are expected to have usually-constant-attributes and - # in PyPy this means the JIT is able to treat attributes as constant, - # which we want. - class FFILibrary(types.ModuleType): - _cffi_generic_module = module - _cffi_ffi = self.ffi - _cffi_dir = [] - def __dir__(self): - return FFILibrary._cffi_dir - library = FFILibrary("") - # - # finally, call the loaded_gen_xxx() functions. This will set - # up the 'library' object. - self._load(module, 'loaded', library=library) - return library - - def _get_declarations(self): - lst = [(key, tp) for (key, (tp, qual)) in - self.ffi._parser._declarations.items()] - lst.sort() - return lst - - def _generate(self, step_name): - for name, tp in self._get_declarations(): - kind, realname = name.split(' ', 1) - try: - method = getattr(self, '_generate_gen_%s_%s' % (kind, - step_name)) - except AttributeError: - raise VerificationError( - "not implemented in verify(): %r" % name) - try: - method(tp, realname) - except Exception as e: - model.attach_exception_info(e, name) - raise - - def _load(self, module, step_name, **kwds): - for name, tp in self._get_declarations(): - kind, realname = name.split(' ', 1) - method = getattr(self, '_%s_gen_%s' % (step_name, kind)) - try: - method(tp, realname, module, **kwds) - except Exception as e: - model.attach_exception_info(e, name) - raise - - def _generate_nothing(self, tp, name): - pass - - def _loaded_noop(self, tp, name, module, **kwds): - pass - - # ---------- - # typedefs: generates no code so far - - _generate_gen_typedef_decl = _generate_nothing - _loading_gen_typedef = _loaded_noop - _loaded_gen_typedef = _loaded_noop - - # ---------- - # function declarations - - def _generate_gen_function_decl(self, tp, name): - assert isinstance(tp, model.FunctionPtrType) - if tp.ellipsis: - # cannot support vararg functions better than this: check for its - # exact type (including the fixed arguments), and build it as a - # constant function pointer (no _cffi_f_%s wrapper) - self._generate_gen_const(False, name, tp) - return - prnt = self._prnt - numargs = len(tp.args) - argnames = [] - for i, type in enumerate(tp.args): - indirection = '' - if isinstance(type, model.StructOrUnion): - indirection = '*' - argnames.append('%sx%d' % (indirection, i)) - context = 'argument of %s' % name - arglist = [type.get_c_name(' %s' % arg, context) - for type, arg in zip(tp.args, argnames)] - tpresult = tp.result - if isinstance(tpresult, model.StructOrUnion): - arglist.insert(0, tpresult.get_c_name(' *r', context)) - tpresult = model.void_type - arglist = ', '.join(arglist) or 'void' - wrappername = '_cffi_f_%s' % name - self.export_symbols.append(wrappername) - if tp.abi: - abi = tp.abi + ' ' - else: - abi = '' - funcdecl = ' %s%s(%s)' % (abi, wrappername, arglist) - context = 'result of %s' % name - prnt(tpresult.get_c_name(funcdecl, context)) - prnt('{') - # - if isinstance(tp.result, model.StructOrUnion): - result_code = '*r = ' - elif not isinstance(tp.result, model.VoidType): - result_code = 'return ' - else: - result_code = '' - prnt(' %s%s(%s);' % (result_code, name, ', '.join(argnames))) - prnt('}') - prnt() - - _loading_gen_function = _loaded_noop - - def _loaded_gen_function(self, tp, name, module, library): - assert isinstance(tp, model.FunctionPtrType) - if tp.ellipsis: - newfunction = self._load_constant(False, tp, name, module) - else: - indirections = [] - base_tp = tp - if (any(isinstance(typ, model.StructOrUnion) for typ in tp.args) - or isinstance(tp.result, model.StructOrUnion)): - indirect_args = [] - for i, typ in enumerate(tp.args): - if isinstance(typ, model.StructOrUnion): - typ = model.PointerType(typ) - indirections.append((i, typ)) - indirect_args.append(typ) - indirect_result = tp.result - if isinstance(indirect_result, model.StructOrUnion): - if indirect_result.fldtypes is None: - raise TypeError("'%s' is used as result type, " - "but is opaque" % ( - indirect_result._get_c_name(),)) - indirect_result = model.PointerType(indirect_result) - indirect_args.insert(0, indirect_result) - indirections.insert(0, ("result", indirect_result)) - indirect_result = model.void_type - tp = model.FunctionPtrType(tuple(indirect_args), - indirect_result, tp.ellipsis) - BFunc = self.ffi._get_cached_btype(tp) - wrappername = '_cffi_f_%s' % name - newfunction = module.load_function(BFunc, wrappername) - for i, typ in indirections: - newfunction = self._make_struct_wrapper(newfunction, i, typ, - base_tp) - setattr(library, name, newfunction) - type(library)._cffi_dir.append(name) - - def _make_struct_wrapper(self, oldfunc, i, tp, base_tp): - backend = self.ffi._backend - BType = self.ffi._get_cached_btype(tp) - if i == "result": - ffi = self.ffi - def newfunc(*args): - res = ffi.new(BType) - oldfunc(res, *args) - return res[0] - else: - def newfunc(*args): - args = args[:i] + (backend.newp(BType, args[i]),) + args[i+1:] - return oldfunc(*args) - newfunc._cffi_base_type = base_tp - return newfunc - - # ---------- - # named structs - - def _generate_gen_struct_decl(self, tp, name): - assert name == tp.name - self._generate_struct_or_union_decl(tp, 'struct', name) - - def _loading_gen_struct(self, tp, name, module): - self._loading_struct_or_union(tp, 'struct', name, module) - - def _loaded_gen_struct(self, tp, name, module, **kwds): - self._loaded_struct_or_union(tp) - - def _generate_gen_union_decl(self, tp, name): - assert name == tp.name - self._generate_struct_or_union_decl(tp, 'union', name) - - def _loading_gen_union(self, tp, name, module): - self._loading_struct_or_union(tp, 'union', name, module) - - def _loaded_gen_union(self, tp, name, module, **kwds): - self._loaded_struct_or_union(tp) - - def _generate_struct_or_union_decl(self, tp, prefix, name): - if tp.fldnames is None: - return # nothing to do with opaque structs - checkfuncname = '_cffi_check_%s_%s' % (prefix, name) - layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) - cname = ('%s %s' % (prefix, name)).strip() - # - prnt = self._prnt - prnt('static void %s(%s *p)' % (checkfuncname, cname)) - prnt('{') - prnt(' /* only to generate compile-time warnings or errors */') - prnt(' (void)p;') - for fname, ftype, fbitsize, fqual in tp.enumfields(): - if (isinstance(ftype, model.PrimitiveType) - and ftype.is_integer_type()) or fbitsize >= 0: - # accept all integers, but complain on float or double - prnt(' (void)((p->%s) << 1);' % fname) - else: - # only accept exactly the type declared. - try: - prnt(' { %s = &p->%s; (void)tmp; }' % ( - ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual), - fname)) - except VerificationError as e: - prnt(' /* %s */' % str(e)) # cannot verify it, ignore - prnt('}') - self.export_symbols.append(layoutfuncname) - prnt('intptr_t %s(intptr_t i)' % (layoutfuncname,)) - prnt('{') - prnt(' struct _cffi_aligncheck { char x; %s y; };' % cname) - prnt(' static intptr_t nums[] = {') - prnt(' sizeof(%s),' % cname) - prnt(' offsetof(struct _cffi_aligncheck, y),') - for fname, ftype, fbitsize, fqual in tp.enumfields(): - if fbitsize >= 0: - continue # xxx ignore fbitsize for now - prnt(' offsetof(%s, %s),' % (cname, fname)) - if isinstance(ftype, model.ArrayType) and ftype.length is None: - prnt(' 0, /* %s */' % ftype._get_c_name()) - else: - prnt(' sizeof(((%s *)0)->%s),' % (cname, fname)) - prnt(' -1') - prnt(' };') - prnt(' return nums[i];') - prnt(' /* the next line is not executed, but compiled */') - prnt(' %s(0);' % (checkfuncname,)) - prnt('}') - prnt() - - def _loading_struct_or_union(self, tp, prefix, name, module): - if tp.fldnames is None: - return # nothing to do with opaque structs - layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) - # - BFunc = self.ffi._typeof_locked("intptr_t(*)(intptr_t)")[0] - function = module.load_function(BFunc, layoutfuncname) - layout = [] - num = 0 - while True: - x = function(num) - if x < 0: break - layout.append(x) - num += 1 - if isinstance(tp, model.StructOrUnion) and tp.partial: - # use the function()'s sizes and offsets to guide the - # layout of the struct - totalsize = layout[0] - totalalignment = layout[1] - fieldofs = layout[2::2] - fieldsize = layout[3::2] - tp.force_flatten() - assert len(fieldofs) == len(fieldsize) == len(tp.fldnames) - tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment - else: - cname = ('%s %s' % (prefix, name)).strip() - self._struct_pending_verification[tp] = layout, cname - - def _loaded_struct_or_union(self, tp): - if tp.fldnames is None: - return # nothing to do with opaque structs - self.ffi._get_cached_btype(tp) # force 'fixedlayout' to be considered - - if tp in self._struct_pending_verification: - # check that the layout sizes and offsets match the real ones - def check(realvalue, expectedvalue, msg): - if realvalue != expectedvalue: - raise VerificationError( - "%s (we have %d, but C compiler says %d)" - % (msg, expectedvalue, realvalue)) - ffi = self.ffi - BStruct = ffi._get_cached_btype(tp) - layout, cname = self._struct_pending_verification.pop(tp) - check(layout[0], ffi.sizeof(BStruct), "wrong total size") - check(layout[1], ffi.alignof(BStruct), "wrong total alignment") - i = 2 - for fname, ftype, fbitsize, fqual in tp.enumfields(): - if fbitsize >= 0: - continue # xxx ignore fbitsize for now - check(layout[i], ffi.offsetof(BStruct, fname), - "wrong offset for field %r" % (fname,)) - if layout[i+1] != 0: - BField = ffi._get_cached_btype(ftype) - check(layout[i+1], ffi.sizeof(BField), - "wrong size for field %r" % (fname,)) - i += 2 - assert i == len(layout) - - # ---------- - # 'anonymous' declarations. These are produced for anonymous structs - # or unions; the 'name' is obtained by a typedef. - - def _generate_gen_anonymous_decl(self, tp, name): - if isinstance(tp, model.EnumType): - self._generate_gen_enum_decl(tp, name, '') - else: - self._generate_struct_or_union_decl(tp, '', name) - - def _loading_gen_anonymous(self, tp, name, module): - if isinstance(tp, model.EnumType): - self._loading_gen_enum(tp, name, module, '') - else: - self._loading_struct_or_union(tp, '', name, module) - - def _loaded_gen_anonymous(self, tp, name, module, **kwds): - if isinstance(tp, model.EnumType): - self._loaded_gen_enum(tp, name, module, **kwds) - else: - self._loaded_struct_or_union(tp) - - # ---------- - # constants, likely declared with '#define' - - def _generate_gen_const(self, is_int, name, tp=None, category='const', - check_value=None): - prnt = self._prnt - funcname = '_cffi_%s_%s' % (category, name) - self.export_symbols.append(funcname) - if check_value is not None: - assert is_int - assert category == 'const' - prnt('int %s(char *out_error)' % funcname) - prnt('{') - self._check_int_constant_value(name, check_value) - prnt(' return 0;') - prnt('}') - elif is_int: - assert category == 'const' - prnt('int %s(long long *out_value)' % funcname) - prnt('{') - prnt(' *out_value = (long long)(%s);' % (name,)) - prnt(' return (%s) <= 0;' % (name,)) - prnt('}') - else: - assert tp is not None - assert check_value is None - if category == 'var': - ampersand = '&' - else: - ampersand = '' - extra = '' - if category == 'const' and isinstance(tp, model.StructOrUnion): - extra = 'const *' - ampersand = '&' - prnt(tp.get_c_name(' %s%s(void)' % (extra, funcname), name)) - prnt('{') - prnt(' return (%s%s);' % (ampersand, name)) - prnt('}') - prnt() - - def _generate_gen_constant_decl(self, tp, name): - is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() - self._generate_gen_const(is_int, name, tp) - - _loading_gen_constant = _loaded_noop - - def _load_constant(self, is_int, tp, name, module, check_value=None): - funcname = '_cffi_const_%s' % name - if check_value is not None: - assert is_int - self._load_known_int_constant(module, funcname) - value = check_value - elif is_int: - BType = self.ffi._typeof_locked("long long*")[0] - BFunc = self.ffi._typeof_locked("int(*)(long long*)")[0] - function = module.load_function(BFunc, funcname) - p = self.ffi.new(BType) - negative = function(p) - value = int(p[0]) - if value < 0 and not negative: - BLongLong = self.ffi._typeof_locked("long long")[0] - value += (1 << (8*self.ffi.sizeof(BLongLong))) - else: - assert check_value is None - fntypeextra = '(*)(void)' - if isinstance(tp, model.StructOrUnion): - fntypeextra = '*' + fntypeextra - BFunc = self.ffi._typeof_locked(tp.get_c_name(fntypeextra, name))[0] - function = module.load_function(BFunc, funcname) - value = function() - if isinstance(tp, model.StructOrUnion): - value = value[0] - return value - - def _loaded_gen_constant(self, tp, name, module, library): - is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() - value = self._load_constant(is_int, tp, name, module) - setattr(library, name, value) - type(library)._cffi_dir.append(name) - - # ---------- - # enums - - def _check_int_constant_value(self, name, value): - prnt = self._prnt - if value <= 0: - prnt(' if ((%s) > 0 || (long)(%s) != %dL) {' % ( - name, name, value)) - else: - prnt(' if ((%s) <= 0 || (unsigned long)(%s) != %dUL) {' % ( - name, name, value)) - prnt(' char buf[64];') - prnt(' if ((%s) <= 0)' % name) - prnt(' sprintf(buf, "%%ld", (long)(%s));' % name) - prnt(' else') - prnt(' sprintf(buf, "%%lu", (unsigned long)(%s));' % - name) - prnt(' sprintf(out_error, "%s has the real value %s, not %s",') - prnt(' "%s", buf, "%d");' % (name[:100], value)) - prnt(' return -1;') - prnt(' }') - - def _load_known_int_constant(self, module, funcname): - BType = self.ffi._typeof_locked("char[]")[0] - BFunc = self.ffi._typeof_locked("int(*)(char*)")[0] - function = module.load_function(BFunc, funcname) - p = self.ffi.new(BType, 256) - if function(p) < 0: - error = self.ffi.string(p) - if sys.version_info >= (3,): - error = str(error, 'utf-8') - raise VerificationError(error) - - def _enum_funcname(self, prefix, name): - # "$enum_$1" => "___D_enum____D_1" - name = name.replace('$', '___D_') - return '_cffi_e_%s_%s' % (prefix, name) - - def _generate_gen_enum_decl(self, tp, name, prefix='enum'): - if tp.partial: - for enumerator in tp.enumerators: - self._generate_gen_const(True, enumerator) - return - # - funcname = self._enum_funcname(prefix, name) - self.export_symbols.append(funcname) - prnt = self._prnt - prnt('int %s(char *out_error)' % funcname) - prnt('{') - for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): - self._check_int_constant_value(enumerator, enumvalue) - prnt(' return 0;') - prnt('}') - prnt() - - def _loading_gen_enum(self, tp, name, module, prefix='enum'): - if tp.partial: - enumvalues = [self._load_constant(True, tp, enumerator, module) - for enumerator in tp.enumerators] - tp.enumvalues = tuple(enumvalues) - tp.partial_resolved = True - else: - funcname = self._enum_funcname(prefix, name) - self._load_known_int_constant(module, funcname) - - def _loaded_gen_enum(self, tp, name, module, library): - for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): - setattr(library, enumerator, enumvalue) - type(library)._cffi_dir.append(enumerator) - - # ---------- - # macros: for now only for integers - - def _generate_gen_macro_decl(self, tp, name): - if tp == '...': - check_value = None - else: - check_value = tp # an integer - self._generate_gen_const(True, name, check_value=check_value) - - _loading_gen_macro = _loaded_noop - - def _loaded_gen_macro(self, tp, name, module, library): - if tp == '...': - check_value = None - else: - check_value = tp # an integer - value = self._load_constant(True, tp, name, module, - check_value=check_value) - setattr(library, name, value) - type(library)._cffi_dir.append(name) - - # ---------- - # global variables - - def _generate_gen_variable_decl(self, tp, name): - if isinstance(tp, model.ArrayType): - if tp.length_is_unknown(): - prnt = self._prnt - funcname = '_cffi_sizeof_%s' % (name,) - self.export_symbols.append(funcname) - prnt("size_t %s(void)" % funcname) - prnt("{") - prnt(" return sizeof(%s);" % (name,)) - prnt("}") - tp_ptr = model.PointerType(tp.item) - self._generate_gen_const(False, name, tp_ptr) - else: - tp_ptr = model.PointerType(tp) - self._generate_gen_const(False, name, tp_ptr, category='var') - - _loading_gen_variable = _loaded_noop - - def _loaded_gen_variable(self, tp, name, module, library): - if isinstance(tp, model.ArrayType): # int a[5] is "constant" in the - # sense that "a=..." is forbidden - if tp.length_is_unknown(): - funcname = '_cffi_sizeof_%s' % (name,) - BFunc = self.ffi._typeof_locked('size_t(*)(void)')[0] - function = module.load_function(BFunc, funcname) - size = function() - BItemType = self.ffi._get_cached_btype(tp.item) - length, rest = divmod(size, self.ffi.sizeof(BItemType)) - if rest != 0: - raise VerificationError( - "bad size: %r does not seem to be an array of %s" % - (name, tp.item)) - tp = tp.resolve_length(length) - tp_ptr = model.PointerType(tp.item) - value = self._load_constant(False, tp_ptr, name, module) - # 'value' is a which we have to replace with - # a if the N is actually known - if tp.length is not None: - BArray = self.ffi._get_cached_btype(tp) - value = self.ffi.cast(BArray, value) - setattr(library, name, value) - type(library)._cffi_dir.append(name) - return - # remove ptr= from the library instance, and replace - # it by a property on the class, which reads/writes into ptr[0]. - funcname = '_cffi_var_%s' % name - BFunc = self.ffi._typeof_locked(tp.get_c_name('*(*)(void)', name))[0] - function = module.load_function(BFunc, funcname) - ptr = function() - def getter(library): - return ptr[0] - def setter(library, value): - ptr[0] = value - setattr(type(library), name, property(getter, setter)) - type(library)._cffi_dir.append(name) - -cffimod_header = r''' -#include -#include -#include -#include -#include /* XXX for ssize_t on some platforms */ - -/* this block of #ifs should be kept exactly identical between - c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py - and cffi/_cffi_include.h */ -#if defined(_MSC_VER) -# include /* for alloca() */ -# if _MSC_VER < 1600 /* MSVC < 2010 */ - typedef __int8 int8_t; - typedef __int16 int16_t; - typedef __int32 int32_t; - typedef __int64 int64_t; - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; - typedef unsigned __int64 uint64_t; - typedef __int8 int_least8_t; - typedef __int16 int_least16_t; - typedef __int32 int_least32_t; - typedef __int64 int_least64_t; - typedef unsigned __int8 uint_least8_t; - typedef unsigned __int16 uint_least16_t; - typedef unsigned __int32 uint_least32_t; - typedef unsigned __int64 uint_least64_t; - typedef __int8 int_fast8_t; - typedef __int16 int_fast16_t; - typedef __int32 int_fast32_t; - typedef __int64 int_fast64_t; - typedef unsigned __int8 uint_fast8_t; - typedef unsigned __int16 uint_fast16_t; - typedef unsigned __int32 uint_fast32_t; - typedef unsigned __int64 uint_fast64_t; - typedef __int64 intmax_t; - typedef unsigned __int64 uintmax_t; -# else -# include -# endif -# if _MSC_VER < 1800 /* MSVC < 2013 */ -# ifndef __cplusplus - typedef unsigned char _Bool; -# endif -# endif -# define _cffi_float_complex_t _Fcomplex /* include for it */ -# define _cffi_double_complex_t _Dcomplex /* include for it */ -#else -# include -# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux) -# include -# endif -# define _cffi_float_complex_t float _Complex -# define _cffi_double_complex_t double _Complex -#endif -''' diff --git a/.venv-docs/lib/python3.12/site-packages/cffi/verifier.py b/.venv-docs/lib/python3.12/site-packages/cffi/verifier.py deleted file mode 100644 index e392a2b7..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cffi/verifier.py +++ /dev/null @@ -1,306 +0,0 @@ -# -# DEPRECATED: implementation for ffi.verify() -# -import sys, os, binascii, shutil, io -from . import __version_verifier_modules__ -from . import ffiplatform -from .error import VerificationError - -if sys.version_info >= (3, 3): - import importlib.machinery - def _extension_suffixes(): - return importlib.machinery.EXTENSION_SUFFIXES[:] -else: - import imp - def _extension_suffixes(): - return [suffix for suffix, _, type in imp.get_suffixes() - if type == imp.C_EXTENSION] - - -if sys.version_info >= (3,): - NativeIO = io.StringIO -else: - class NativeIO(io.BytesIO): - def write(self, s): - if isinstance(s, unicode): - s = s.encode('ascii') - super(NativeIO, self).write(s) - - -class Verifier(object): - - def __init__(self, ffi, preamble, tmpdir=None, modulename=None, - ext_package=None, tag='', force_generic_engine=False, - source_extension='.c', flags=None, relative_to=None, **kwds): - if ffi._parser._uses_new_feature: - raise VerificationError( - "feature not supported with ffi.verify(), but only " - "with ffi.set_source(): %s" % (ffi._parser._uses_new_feature,)) - self.ffi = ffi - self.preamble = preamble - if not modulename: - flattened_kwds = ffiplatform.flatten(kwds) - vengine_class = _locate_engine_class(ffi, force_generic_engine) - self._vengine = vengine_class(self) - self._vengine.patch_extension_kwds(kwds) - self.flags = flags - self.kwds = self.make_relative_to(kwds, relative_to) - # - if modulename: - if tag: - raise TypeError("can't specify both 'modulename' and 'tag'") - else: - key = '\x00'.join(['%d.%d' % sys.version_info[:2], - __version_verifier_modules__, - preamble, flattened_kwds] + - ffi._cdefsources) - if sys.version_info >= (3,): - key = key.encode('utf-8') - k1 = hex(binascii.crc32(key[0::2]) & 0xffffffff) - k1 = k1.lstrip('0x').rstrip('L') - k2 = hex(binascii.crc32(key[1::2]) & 0xffffffff) - k2 = k2.lstrip('0').rstrip('L') - modulename = '_cffi_%s_%s%s%s' % (tag, self._vengine._class_key, - k1, k2) - suffix = _get_so_suffixes()[0] - self.tmpdir = tmpdir or _caller_dir_pycache() - self.sourcefilename = os.path.join(self.tmpdir, modulename + source_extension) - self.modulefilename = os.path.join(self.tmpdir, modulename + suffix) - self.ext_package = ext_package - self._has_source = False - self._has_module = False - - def write_source(self, file=None): - """Write the C source code. It is produced in 'self.sourcefilename', - which can be tweaked beforehand.""" - with self.ffi._lock: - if self._has_source and file is None: - raise VerificationError( - "source code already written") - self._write_source(file) - - def compile_module(self): - """Write the C source code (if not done already) and compile it. - This produces a dynamic link library in 'self.modulefilename'.""" - with self.ffi._lock: - if self._has_module: - raise VerificationError("module already compiled") - if not self._has_source: - self._write_source() - self._compile_module() - - def load_library(self): - """Get a C module from this Verifier instance. - Returns an instance of a FFILibrary class that behaves like the - objects returned by ffi.dlopen(), but that delegates all - operations to the C module. If necessary, the C code is written - and compiled first. - """ - with self.ffi._lock: - if not self._has_module: - self._locate_module() - if not self._has_module: - if not self._has_source: - self._write_source() - self._compile_module() - return self._load_library() - - def get_module_name(self): - basename = os.path.basename(self.modulefilename) - # kill both the .so extension and the other .'s, as introduced - # by Python 3: 'basename.cpython-33m.so' - basename = basename.split('.', 1)[0] - # and the _d added in Python 2 debug builds --- but try to be - # conservative and not kill a legitimate _d - if basename.endswith('_d') and hasattr(sys, 'gettotalrefcount'): - basename = basename[:-2] - return basename - - def get_extension(self): - if not self._has_source: - with self.ffi._lock: - if not self._has_source: - self._write_source() - sourcename = ffiplatform.maybe_relative_path(self.sourcefilename) - modname = self.get_module_name() - return ffiplatform.get_extension(sourcename, modname, **self.kwds) - - def generates_python_module(self): - return self._vengine._gen_python_module - - def make_relative_to(self, kwds, relative_to): - if relative_to and os.path.dirname(relative_to): - dirname = os.path.dirname(relative_to) - kwds = kwds.copy() - for key in ffiplatform.LIST_OF_FILE_NAMES: - if key in kwds: - lst = kwds[key] - if not isinstance(lst, (list, tuple)): - raise TypeError("keyword '%s' should be a list or tuple" - % (key,)) - lst = [os.path.join(dirname, fn) for fn in lst] - kwds[key] = lst - return kwds - - # ---------- - - def _locate_module(self): - if not os.path.isfile(self.modulefilename): - if self.ext_package: - try: - pkg = __import__(self.ext_package, None, None, ['__doc__']) - except ImportError: - return # cannot import the package itself, give up - # (e.g. it might be called differently before installation) - path = pkg.__path__ - else: - path = None - filename = self._vengine.find_module(self.get_module_name(), path, - _get_so_suffixes()) - if filename is None: - return - self.modulefilename = filename - self._vengine.collect_types() - self._has_module = True - - def _write_source_to(self, file): - self._vengine._f = file - try: - self._vengine.write_source_to_f() - finally: - del self._vengine._f - - def _write_source(self, file=None): - if file is not None: - self._write_source_to(file) - else: - # Write our source file to an in memory file. - f = NativeIO() - self._write_source_to(f) - source_data = f.getvalue() - - # Determine if this matches the current file - if os.path.exists(self.sourcefilename): - with open(self.sourcefilename, "r") as fp: - needs_written = not (fp.read() == source_data) - else: - needs_written = True - - # Actually write the file out if it doesn't match - if needs_written: - _ensure_dir(self.sourcefilename) - with open(self.sourcefilename, "w") as fp: - fp.write(source_data) - - # Set this flag - self._has_source = True - - def _compile_module(self): - # compile this C source - tmpdir = os.path.dirname(self.sourcefilename) - outputfilename = ffiplatform.compile(tmpdir, self.get_extension()) - try: - same = ffiplatform.samefile(outputfilename, self.modulefilename) - except OSError: - same = False - if not same: - _ensure_dir(self.modulefilename) - shutil.move(outputfilename, self.modulefilename) - self._has_module = True - - def _load_library(self): - assert self._has_module - if self.flags is not None: - return self._vengine.load_library(self.flags) - else: - return self._vengine.load_library() - -# ____________________________________________________________ - -_FORCE_GENERIC_ENGINE = False # for tests - -def _locate_engine_class(ffi, force_generic_engine): - if _FORCE_GENERIC_ENGINE: - force_generic_engine = True - if not force_generic_engine: - if '__pypy__' in sys.builtin_module_names: - force_generic_engine = True - else: - try: - import _cffi_backend - except ImportError: - _cffi_backend = '?' - if ffi._backend is not _cffi_backend: - force_generic_engine = True - if force_generic_engine: - from . import vengine_gen - return vengine_gen.VGenericEngine - else: - from . import vengine_cpy - return vengine_cpy.VCPythonEngine - -# ____________________________________________________________ - -_TMPDIR = None - -def _caller_dir_pycache(): - if _TMPDIR: - return _TMPDIR - result = os.environ.get('CFFI_TMPDIR') - if result: - return result - filename = sys._getframe(2).f_code.co_filename - return os.path.abspath(os.path.join(os.path.dirname(filename), - '__pycache__')) - -def set_tmpdir(dirname): - """Set the temporary directory to use instead of __pycache__.""" - global _TMPDIR - _TMPDIR = dirname - -def cleanup_tmpdir(tmpdir=None, keep_so=False): - """Clean up the temporary directory by removing all files in it - called `_cffi_*.{c,so}` as well as the `build` subdirectory.""" - tmpdir = tmpdir or _caller_dir_pycache() - try: - filelist = os.listdir(tmpdir) - except OSError: - return - if keep_so: - suffix = '.c' # only remove .c files - else: - suffix = _get_so_suffixes()[0].lower() - for fn in filelist: - if fn.lower().startswith('_cffi_') and ( - fn.lower().endswith(suffix) or fn.lower().endswith('.c')): - try: - os.unlink(os.path.join(tmpdir, fn)) - except OSError: - pass - clean_dir = [os.path.join(tmpdir, 'build')] - for dir in clean_dir: - try: - for fn in os.listdir(dir): - fn = os.path.join(dir, fn) - if os.path.isdir(fn): - clean_dir.append(fn) - else: - os.unlink(fn) - except OSError: - pass - -def _get_so_suffixes(): - suffixes = _extension_suffixes() - if not suffixes: - # bah, no C_EXTENSION available. Occurs on pypy without cpyext - if sys.platform == 'win32': - suffixes = [".pyd"] - else: - suffixes = [".so"] - - return suffixes - -def _ensure_dir(filename): - dirname = os.path.dirname(filename) - if dirname and not os.path.isdir(dirname): - os.makedirs(dirname) diff --git a/.venv-docs/lib/python3.12/site-packages/cssselect2-0.8.0.dist-info/INSTALLER b/.venv-docs/lib/python3.12/site-packages/cssselect2-0.8.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e3..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cssselect2-0.8.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv-docs/lib/python3.12/site-packages/cssselect2-0.8.0.dist-info/METADATA b/.venv-docs/lib/python3.12/site-packages/cssselect2-0.8.0.dist-info/METADATA deleted file mode 100644 index c00c2e1a..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cssselect2-0.8.0.dist-info/METADATA +++ /dev/null @@ -1,63 +0,0 @@ -Metadata-Version: 2.4 -Name: cssselect2 -Version: 0.8.0 -Summary: CSS selectors for Python ElementTree -Keywords: css,elementtree -Author-email: Simon Sapin -Maintainer-email: CourtBouillon -Requires-Python: >=3.9 -Description-Content-Type: text/x-rst -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: BSD License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3 :: Only -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Programming Language :: Python :: 3.13 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Topic :: Internet :: WWW/HTTP -License-File: LICENSE -Requires-Dist: tinycss2 -Requires-Dist: webencodings -Requires-Dist: sphinx ; extra == "doc" -Requires-Dist: furo ; extra == "doc" -Requires-Dist: pytest ; extra == "test" -Requires-Dist: ruff ; extra == "test" -Project-URL: Changelog, https://github.com/Kozea/cssselect2/releases -Project-URL: Code, https://github.com/Kozea/cssselect2/ -Project-URL: Documentation, https://doc.courtbouillon.org/cssselect2/ -Project-URL: Donation, https://opencollective.com/courtbouillon -Project-URL: Homepage, https://doc.courtbouillon.org/cssselect2/ -Project-URL: Issues, https://github.com/Kozea/cssselect2/issues -Provides-Extra: doc -Provides-Extra: test - -cssselect2 is a straightforward implementation of CSS4 Selectors for markup -documents (HTML, XML, etc.) that can be read by ElementTree-like parsers -(including cElementTree, lxml, html5lib, etc.) - -* Free software: BSD license -* For Python 3.9+, tested on CPython and PyPy -* Documentation: https://doc.courtbouillon.org/cssselect2 -* Changelog: https://github.com/Kozea/cssselect2/releases -* Code, issues, tests: https://github.com/Kozea/cssselect2 -* Code of conduct: https://www.courtbouillon.org/code-of-conduct.html -* Professional support: https://www.courtbouillon.org -* Donation: https://opencollective.com/courtbouillon - -cssselect2 has been created and developed by Kozea (https://kozea.fr/). -Professional support, maintenance and community management is provided by -CourtBouillon (https://www.courtbouillon.org/). - -Copyrights are retained by their contributors, no copyright assignment is -required to contribute to cssselect2. Unless explicitly stated otherwise, any -contribution intentionally submitted for inclusion is licensed under the BSD -3-clause license, without any additional terms or conditions. For full -authorship information, see the version control history. - diff --git a/.venv-docs/lib/python3.12/site-packages/cssselect2-0.8.0.dist-info/RECORD b/.venv-docs/lib/python3.12/site-packages/cssselect2-0.8.0.dist-info/RECORD deleted file mode 100644 index a83b9da9..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cssselect2-0.8.0.dist-info/RECORD +++ /dev/null @@ -1,13 +0,0 @@ -cssselect2-0.8.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -cssselect2-0.8.0.dist-info/METADATA,sha256=kSCZCQ5aCztgfgWZU6A_JEYNNKwMh2oK0atzdgwpNUs,2913 -cssselect2-0.8.0.dist-info/RECORD,, -cssselect2-0.8.0.dist-info/WHEEL,sha256=_2ozNFCLWc93bK4WKHCO-eDUENDlo-dgc9cU3qokYO4,82 -cssselect2-0.8.0.dist-info/licenses/LICENSE,sha256=b9lyKaHRsPaotB4Qn0E0JtvAh0seA3RtZswzKCYBwsI,1548 -cssselect2/__init__.py,sha256=NE8miBh2KOpXtqGKNn5exISqDuWFJ_z3VmnxpAiblDI,4289 -cssselect2/__pycache__/__init__.cpython-312.pyc,, -cssselect2/__pycache__/compiler.cpython-312.pyc,, -cssselect2/__pycache__/parser.cpython-312.pyc,, -cssselect2/__pycache__/tree.cpython-312.pyc,, -cssselect2/compiler.py,sha256=c5jvLm9VEo3XLi8aeeUozZn6XlKBaeXAmOr-P2YFOUs,18899 -cssselect2/parser.py,sha256=Kmh5XY03eF2Bs5x53X2pRXDPbtPyMU5RZ3zrGOHgtJQ,16285 -cssselect2/tree.py,sha256=7ewFwKfLGwCYjdCle6hY0gk8_vAQqw7DvU_lmpqt1eg,13634 diff --git a/.venv-docs/lib/python3.12/site-packages/cssselect2-0.8.0.dist-info/WHEEL b/.venv-docs/lib/python3.12/site-packages/cssselect2-0.8.0.dist-info/WHEEL deleted file mode 100644 index 23d2d7e9..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cssselect2-0.8.0.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: flit 3.11.0 -Root-Is-Purelib: true -Tag: py3-none-any diff --git a/.venv-docs/lib/python3.12/site-packages/cssselect2-0.8.0.dist-info/licenses/LICENSE b/.venv-docs/lib/python3.12/site-packages/cssselect2-0.8.0.dist-info/licenses/LICENSE deleted file mode 100644 index 520a431b..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cssselect2-0.8.0.dist-info/licenses/LICENSE +++ /dev/null @@ -1,29 +0,0 @@ -BSD 3-Clause License - -Copyright (c) 2012-2018, Simon Sapin and contributors (see AUTHORS). -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/.venv-docs/lib/python3.12/site-packages/cssselect2/__init__.py b/.venv-docs/lib/python3.12/site-packages/cssselect2/__init__.py deleted file mode 100644 index e354d0f8..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cssselect2/__init__.py +++ /dev/null @@ -1,113 +0,0 @@ -"""CSS4 selectors for Python. - -cssselect2 is a straightforward implementation of CSS4 Selectors for markup -documents (HTML, XML, etc.) that can be read by ElementTree-like parsers -(including cElementTree, lxml, html5lib, etc.) - -""" - -from webencodings import ascii_lower - -# Classes are imported here to expose them at the top level of the module -from .compiler import compile_selector_list # noqa -from .parser import SelectorError # noqa -from .tree import ElementWrapper # noqa - -VERSION = __version__ = '0.8.0' - - -class Matcher: - """A CSS selectors storage that can match against HTML elements.""" - def __init__(self): - self.id_selectors = {} - self.class_selectors = {} - self.lower_local_name_selectors = {} - self.namespace_selectors = {} - self.lang_attr_selectors = [] - self.other_selectors = [] - self.order = 0 - - def add_selector(self, selector, payload): - """Add a selector and its payload to the matcher. - - :param selector: - A :class:`compiler.CompiledSelector` object. - :param payload: - Some data associated to the selector, - such as :class:`declarations ` - parsed from the :attr:`tinycss2.ast.QualifiedRule.content` - of a style rule. - It can be any Python object, - and will be returned as-is by :meth:`match`. - - """ - self.order += 1 - - if selector.never_matches: - return - - entry = ( - selector.test, selector.specificity, self.order, selector.pseudo_element, - payload) - if selector.id is not None: - self.id_selectors.setdefault(selector.id, []).append(entry) - elif selector.class_name is not None: - self.class_selectors.setdefault(selector.class_name, []).append(entry) - elif selector.local_name is not None: - self.lower_local_name_selectors.setdefault( - selector.lower_local_name, []).append(entry) - elif selector.namespace is not None: - self.namespace_selectors.setdefault(selector.namespace, []).append(entry) - elif selector.requires_lang_attr: - self.lang_attr_selectors.append(entry) - else: - self.other_selectors.append(entry) - - def match(self, element): - """Match selectors against the given element. - - :param element: - An :class:`ElementWrapper`. - :returns: - A list of the payload objects associated to selectors that match - element, in order of lowest to highest - :attr:`compiler.CompiledSelector` specificity and in order of - addition with :meth:`add_selector` among selectors of equal - specificity. - - """ - relevant_selectors = [] - - if element.id is not None and element.id in self.id_selectors: - self.add_relevant_selectors( - element, self.id_selectors[element.id], relevant_selectors) - - for class_name in element.classes: - if class_name in self.class_selectors: - self.add_relevant_selectors( - element, self.class_selectors[class_name], relevant_selectors) - - lower_name = ascii_lower(element.local_name) - if lower_name in self.lower_local_name_selectors: - self.add_relevant_selectors( - element, self.lower_local_name_selectors[lower_name], - relevant_selectors) - if element.namespace_url in self.namespace_selectors: - self.add_relevant_selectors( - element, self.namespace_selectors[element.namespace_url], - relevant_selectors) - - if 'lang' in element.etree_element.attrib: - self.add_relevant_selectors( - element, self.lang_attr_selectors, relevant_selectors) - - self.add_relevant_selectors(element, self.other_selectors, relevant_selectors) - - relevant_selectors.sort() - return relevant_selectors - - @staticmethod - def add_relevant_selectors(element, selectors, relevant_selectors): - for test, specificity, order, pseudo, payload in selectors: - if test(element): - relevant_selectors.append((specificity, order, pseudo, payload)) diff --git a/.venv-docs/lib/python3.12/site-packages/cssselect2/__pycache__/__init__.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/cssselect2/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index c9113e31..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/cssselect2/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/cssselect2/__pycache__/compiler.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/cssselect2/__pycache__/compiler.cpython-312.pyc deleted file mode 100644 index 8665a5cd..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/cssselect2/__pycache__/compiler.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/cssselect2/__pycache__/parser.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/cssselect2/__pycache__/parser.cpython-312.pyc deleted file mode 100644 index f3513d70..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/cssselect2/__pycache__/parser.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/cssselect2/__pycache__/tree.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/cssselect2/__pycache__/tree.cpython-312.pyc deleted file mode 100644 index b45db29d..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/cssselect2/__pycache__/tree.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/cssselect2/compiler.py b/.venv-docs/lib/python3.12/site-packages/cssselect2/compiler.py deleted file mode 100644 index 23017662..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cssselect2/compiler.py +++ /dev/null @@ -1,426 +0,0 @@ -import re -from urllib.parse import urlparse - -from tinycss2.nth import parse_nth -from webencodings import ascii_lower - -from . import parser -from .parser import SelectorError - -# http://dev.w3.org/csswg/selectors/#whitespace -split_whitespace = re.compile('[^ \t\r\n\f]+').findall - - -def compile_selector_list(input, namespaces=None): - """Compile a (comma-separated) list of selectors. - - :param input: - A string, or an iterable of tinycss2 component values such as - the :attr:`tinycss2.ast.QualifiedRule.prelude` of a style rule. - :param namespaces: - A optional dictionary of all `namespace prefix declarations - `_ in scope for this selector. - Keys are namespace prefixes as strings, or ``None`` for the default - namespace. - Values are namespace URLs as strings. - If omitted, assume that no prefix is declared. - :returns: - A list of opaque :class:`compiler.CompiledSelector` objects. - - """ - return [CompiledSelector(selector) for selector in parser.parse(input, namespaces)] - - -class CompiledSelector: - """Abstract representation of a selector.""" - def __init__(self, parsed_selector): - source = _compile_node(parsed_selector.parsed_tree) - self.never_matches = source == '0' - eval_globals = { - 'split_whitespace': split_whitespace, - 'ascii_lower': ascii_lower, - 'urlparse': urlparse, - } - self.test = eval('lambda el: ' + source, eval_globals, {}) - self.specificity = parsed_selector.specificity - self.pseudo_element = parsed_selector.pseudo_element - self.id = None - self.class_name = None - self.local_name = None - self.lower_local_name = None - self.namespace = None - self.requires_lang_attr = False - - node = parsed_selector.parsed_tree - if isinstance(node, parser.CombinedSelector): - node = node.right - for simple_selector in node.simple_selectors: - if isinstance(simple_selector, parser.IDSelector): - self.id = simple_selector.ident - elif isinstance(simple_selector, parser.ClassSelector): - self.class_name = simple_selector.class_name - elif isinstance(simple_selector, parser.LocalNameSelector): - self.local_name = simple_selector.local_name - self.lower_local_name = simple_selector.lower_local_name - elif isinstance(simple_selector, parser.NamespaceSelector): - self.namespace = simple_selector.namespace - elif isinstance(simple_selector, parser.AttributeSelector): - if simple_selector.name == 'lang': - self.requires_lang_attr = True - - -def _compile_node(selector): - """Return a boolean expression, as a Python source string. - - When evaluated in a context where the `el` variable is an - :class:`cssselect2.tree.Element` object, tells whether the element is a - subject of `selector`. - - """ - # To avoid precedence-related bugs, any sub-expression that is passed - # around must be "atomic": add parentheses when the top-level would be - # an operator. Bare literals and function calls are fine. - - # 1 and 0 are used for True and False to avoid global lookups. - - if isinstance(selector, parser.CombinedSelector): - left_inside = _compile_node(selector.left) - if left_inside == '0': - return '0' # 0 and x == 0 - elif left_inside == '1': - # 1 and x == x, but the element matching 1 still needs to exist. - if selector.combinator in (' ', '>'): - left = 'el.parent is not None' - elif selector.combinator in ('~', '+'): - left = 'el.previous is not None' - else: - raise SelectorError('Unknown combinator', selector.combinator) - # Rebind the `el` name inside a generator-expressions (in a new scope) - # so that 'left_inside' applies to different elements. - elif selector.combinator == ' ': - left = f'any(({left_inside}) for el in el.ancestors)' - elif selector.combinator == '>': - left = ( - f'next(el is not None and ({left_inside}) ' - 'for el in [el.parent])') - elif selector.combinator == '+': - left = ( - f'next(el is not None and ({left_inside}) ' - 'for el in [el.previous])') - elif selector.combinator == '~': - left = f'any(({left_inside}) for el in el.previous_siblings)' - else: - raise SelectorError('Unknown combinator', selector.combinator) - - right = _compile_node(selector.right) - if right == '0': - return '0' # 0 and x == 0 - elif right == '1': - return left # 1 and x == x - else: - # Evaluate combinators right to left - return f'({right}) and ({left})' - - elif isinstance(selector, parser.CompoundSelector): - sub_expressions = [ - expr for expr in [ - _compile_node(selector) - for selector in selector.simple_selectors] - if expr != '1'] - if len(sub_expressions) == 1: - return sub_expressions[0] - elif '0' in sub_expressions: - return '0' - elif sub_expressions: - return ' and '.join(f'({el})' for el in sub_expressions) - else: - return '1' # all([]) == True - - elif isinstance(selector, parser.NegationSelector): - sub_expressions = [ - expr for expr in [ - _compile_node(selector.parsed_tree) - for selector in selector.selector_list] - if expr != '1'] - if not sub_expressions: - return '0' - return f'not ({" or ".join(f"({expr})" for expr in sub_expressions)})' - - elif isinstance(selector, parser.RelationalSelector): - sub_expressions = [] - for relative_selector in selector.selector_list: - expression = _compile_node(relative_selector.selector.parsed_tree) - if expression == '0': - continue - if relative_selector.combinator == ' ': - elements = 'list(el.iter_subtree())[1:]' - elif relative_selector.combinator == '>': - elements = 'el.iter_children()' - elif relative_selector.combinator == '+': - elements = 'list(el.iter_next_siblings())[:1]' - elif relative_selector.combinator == '~': - elements = 'el.iter_next_siblings()' - sub_expressions.append(f'(any({expression} for el in {elements}))') - return ' or '.join(sub_expressions) - - elif isinstance(selector, ( - parser.MatchesAnySelector, parser.SpecificityAdjustmentSelector)): - sub_expressions = [ - expr for expr in [ - _compile_node(selector.parsed_tree) - for selector in selector.selector_list] - if expr != '0'] - if not sub_expressions: - return '0' - return ' or '.join(f'({expr})' for expr in sub_expressions) - - elif isinstance(selector, parser.LocalNameSelector): - if selector.lower_local_name == selector.local_name: - return f'el.local_name == {selector.local_name!r}' - else: - return ( - f'el.local_name == ({selector.lower_local_name!r} ' - f'if el.in_html_document else {selector.local_name!r})') - - elif isinstance(selector, parser.NamespaceSelector): - return f'el.namespace_url == {selector.namespace!r}' - - elif isinstance(selector, parser.ClassSelector): - return f'{selector.class_name!r} in el.classes' - - elif isinstance(selector, parser.IDSelector): - return f'el.id == {selector.ident!r}' - - elif isinstance(selector, parser.AttributeSelector): - if selector.namespace is not None: - if selector.namespace: - if selector.name == selector.lower_name: - key = repr(f'{{{selector.namespace}}}{selector.name}') - else: - lower = f'{{{selector.namespace}}}{selector.lower_name}' - name = f'{{{selector.namespace}}}{selector.name}' - key = f'({lower!r} if el.in_html_document else {name!r})' - else: - if selector.name == selector.lower_name: - key = repr(selector.name) - else: - lower, name = selector.lower_name, selector.name - key = f'({lower!r} if el.in_html_document else {name!r})' - value = selector.value - attribute_value = f'el.etree_element.get({key}, "")' - if selector.case_sensitive is False: - value = value.lower() - attribute_value += '.lower()' - if selector.operator is None: - return f'{key} in el.etree_element.attrib' - elif selector.operator == '=': - return ( - f'{key} in el.etree_element.attrib and ' - f'{attribute_value} == {value!r}') - elif selector.operator == '~=': - return ( - '0' if len(value.split()) != 1 or value.strip() != value - else f'{value!r} in split_whitespace({attribute_value})') - elif selector.operator == '|=': - return ( - f'{key} in el.etree_element.attrib and ' - f'{attribute_value} == {value!r} or ' - f'{attribute_value}.startswith({(value + "-")!r})') - elif selector.operator == '^=': - if value: - return f'{attribute_value}.startswith({value!r})' - else: - return '0' - elif selector.operator == '$=': - return ( - f'{attribute_value}.endswith({value!r})' if value else '0') - elif selector.operator == '*=': - return f'{value!r} in {attribute_value}' if value else '0' - else: - raise SelectorError('Unknown attribute operator', selector.operator) - else: # In any namespace - raise NotImplementedError # TODO - - elif isinstance(selector, parser.PseudoClassSelector): - if selector.name in ('link', 'any-link', 'local-link'): - test = html_tag_eq('a', 'area', 'link') - test += ' and el.etree_element.get("href") is not None ' - if selector.name == 'local-link': - test += 'and not urlparse(el.etree_element.get("href")).scheme' - return test - elif selector.name == 'enabled': - input = html_tag_eq( - 'button', 'input', 'select', 'textarea', 'option') - group = html_tag_eq('optgroup', 'menuitem', 'fieldset') - a = html_tag_eq('a', 'area', 'link') - return ( - f'({input} and el.etree_element.get("disabled") is None' - ' and not el.in_disabled_fieldset) or' - f'({group} and el.etree_element.get("disabled") is None) or ' - f'({a} and el.etree_element.get("href") is not None)') - elif selector.name == 'disabled': - input = html_tag_eq( - 'button', 'input', 'select', 'textarea', 'option') - group = html_tag_eq('optgroup', 'menuitem', 'fieldset') - return ( - f'({input} and (el.etree_element.get("disabled") is not None' - ' or el.in_disabled_fieldset)) or' - f'({group} and el.etree_element.get("disabled") is not None)') - elif selector.name == 'checked': - input = html_tag_eq('input', 'menuitem') - option = html_tag_eq('option') - return ( - f'({input} and el.etree_element.get("checked") is not None and' - ' ascii_lower(el.etree_element.get("type", "")) ' - ' in ("checkbox", "radio")) or (' - f'{option} and el.etree_element.get("selected") is not None)') - elif selector.name in ( - 'visited', 'hover', 'active', 'focus', 'focus-within', - 'focus-visible', 'target', 'target-within', 'current', 'past', - 'future', 'playing', 'paused', 'seeking', 'buffering', - 'stalled', 'muted', 'volume-locked', 'user-valid', - 'user-invalid'): - # Not applicable in a static context: never match. - return '0' - elif selector.name in ('root', 'scope'): - return 'el.parent is None' - elif selector.name == 'first-child': - return 'el.index == 0' - elif selector.name == 'last-child': - return 'el.index + 1 == len(el.etree_siblings)' - elif selector.name == 'first-of-type': - return ( - 'all(s.tag != el.etree_element.tag' - ' for s in el.etree_siblings[:el.index])') - elif selector.name == 'last-of-type': - return ( - 'all(s.tag != el.etree_element.tag' - ' for s in el.etree_siblings[el.index + 1:])') - elif selector.name == 'only-child': - return 'len(el.etree_siblings) == 1' - elif selector.name == 'only-of-type': - return ( - 'all(s.tag != el.etree_element.tag or i == el.index' - ' for i, s in enumerate(el.etree_siblings))') - elif selector.name == 'empty': - return 'not (el.etree_children or el.etree_element.text)' - else: - raise SelectorError('Unknown pseudo-class', selector.name) - - elif isinstance(selector, parser.FunctionalPseudoClassSelector): - if selector.name == 'lang': - langs = [] - tokens = [ - token for token in selector.arguments - if token.type not in ('whitespace', 'comment')] - while tokens: - token = tokens.pop(0) - if token.type == 'ident': - langs.append(token.lower_value) - elif token.type == 'string': - langs.append(ascii_lower(token.value)) - else: - raise SelectorError('Invalid arguments for :lang()') - if tokens: - token = tokens.pop(0) - if token.type != 'ident' and token.value != ',': - raise SelectorError('Invalid arguments for :lang()') - return ' or '.join( - f'el.lang == {lang!r} or el.lang.startswith({(lang + "-")!r})' - for lang in langs) - else: - nth = [] - selector_list = [] - current_list = nth - for argument in selector.arguments: - if argument.type == 'ident' and argument.value == 'of': - if current_list is nth: - current_list = selector_list - continue - current_list.append(argument) - - if selector_list: - test = ' and '.join( - _compile_node(selector.parsed_tree) - for selector in parser.parse(selector_list)) - if selector.name == 'nth-child': - count = ( - f'sum(1 for el in el.previous_siblings if ({test}))') - elif selector.name == 'nth-last-child': - count = ( - 'sum(1 for el in' - ' tuple(el.iter_siblings())[el.index + 1:]' - f' if ({test}))') - elif selector.name == 'nth-of-type': - count = ( - 'sum(1 for s in (' - ' el for el in el.previous_siblings' - f' if ({test}))' - ' if s.etree_element.tag == el.etree_element.tag)') - elif selector.name == 'nth-last-of-type': - count = ( - 'sum(1 for s in (' - ' el for el in' - ' tuple(el.iter_siblings())[el.index + 1:]' - f' if ({test}))' - ' if s.etree_element.tag == el.etree_element.tag)') - else: - raise SelectorError('Unknown pseudo-class', selector.name) - count += f'if ({test}) else float("nan")' - else: - if current_list is selector_list: - raise SelectorError( - f'Invalid arguments for :{selector.name}()') - if selector.name == 'nth-child': - count = 'el.index' - elif selector.name == 'nth-last-child': - count = 'len(el.etree_siblings) - el.index - 1' - elif selector.name == 'nth-of-type': - count = ( - 'sum(1 for s in el.etree_siblings[:el.index]' - ' if s.tag == el.etree_element.tag)') - elif selector.name == 'nth-last-of-type': - count = ( - 'sum(1 for s in el.etree_siblings[el.index + 1:]' - ' if s.tag == el.etree_element.tag)') - else: - raise SelectorError('Unknown pseudo-class', selector.name) - - result = parse_nth(nth) - if result is None: - raise SelectorError( - f'Invalid arguments for :{selector.name}()') - a, b = result - # x is the number of siblings before/after the element - # Matches if a positive or zero integer n exists so that: - # x = a*n + b-1 - # x = a*n + B - B = b - 1 # noqa: N806 - if a == 0: - # x = B - return f'({count}) == {B}' - else: - # n = (x - B) / a - return ( - 'next(r == 0 and n >= 0' - f' for n, r in [divmod(({count}) - {B}, {a})])') - - else: - raise TypeError(type(selector), selector) - - -def html_tag_eq(*local_names): - """Generate expression testing equality with HTML local names.""" - if len(local_names) == 1: - tag = f'{{http://www.w3.org/1999/xhtml}}{local_names[0]}' - return ( - f'((el.local_name == {local_names[0]!r}) if el.in_html_document ' - f'else (el.etree_element.tag == {tag!r}))') - else: - names = ', '.join(repr(n) for n in local_names) - tags = ', '.join( - repr(f'{{http://www.w3.org/1999/xhtml}}{name}') - for name in local_names) - return ( - f'((el.local_name in ({names})) if el.in_html_document ' - f'else (el.etree_element.tag in ({tags})))') diff --git a/.venv-docs/lib/python3.12/site-packages/cssselect2/parser.py b/.venv-docs/lib/python3.12/site-packages/cssselect2/parser.py deleted file mode 100644 index 1056d6be..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cssselect2/parser.py +++ /dev/null @@ -1,522 +0,0 @@ -from tinycss2 import parse_component_value_list - -__all__ = ['parse'] - -SUPPORTED_PSEUDO_ELEMENTS = { - # As per CSS Pseudo-Elements Module Level 4 - 'first-line', 'first-letter', 'prefix', 'postfix', 'selection', - 'target-text', 'spelling-error', 'grammar-error', 'before', 'after', - 'marker', 'placeholder', 'file-selector-button', - # As per CSS Generated Content for Paged Media Module - 'footnote-call', 'footnote-marker', - # As per CSS Scoping Module Level 1 - 'content', 'shadow', -} - - -def parse(input, namespaces=None, forgiving=False, relative=False): - """Yield tinycss2 selectors found in given ``input``. - - :param input: - A string, or an iterable of tinycss2 component values. - - """ - if isinstance(input, str): - input = parse_component_value_list(input) - tokens = TokenStream(input) - namespaces = namespaces or {} - try: - yield parse_selector(tokens, namespaces, relative) - except SelectorError as exception: - if forgiving: - return - raise exception - while 1: - next = tokens.next() - if next is None: - return - elif next == ',': - try: - yield parse_selector(tokens, namespaces, relative) - except SelectorError as exception: - if not forgiving: - raise exception - else: - if not forgiving: - raise SelectorError(next, f'unexpected {next.type} token.') - - -def parse_selector(tokens, namespaces, relative=False): - tokens.skip_whitespace_and_comment() - if relative: - peek = tokens.peek() - if peek in ('>', '+', '~'): - initial_combinator = peek.value - tokens.next() - else: - initial_combinator = ' ' - tokens.skip_whitespace_and_comment() - result, pseudo_element = parse_compound_selector(tokens, namespaces) - while 1: - has_whitespace = tokens.skip_whitespace() - while tokens.skip_comment(): - has_whitespace = tokens.skip_whitespace() or has_whitespace - selector = Selector(result, pseudo_element) - if relative: - selector = RelativeSelector(initial_combinator, selector) - if pseudo_element is not None: - return selector - peek = tokens.peek() - if peek is None or peek == ',': - return selector - elif peek in ('>', '+', '~'): - combinator = peek.value - tokens.next() - elif has_whitespace: - combinator = ' ' - else: - return selector - compound, pseudo_element = parse_compound_selector(tokens, namespaces) - result = CombinedSelector(result, combinator, compound) - - -def parse_compound_selector(tokens, namespaces): - type_selectors = parse_type_selector(tokens, namespaces) - simple_selectors = type_selectors if type_selectors is not None else [] - while 1: - simple_selector, pseudo_element = parse_simple_selector( - tokens, namespaces) - if pseudo_element is not None or simple_selector is None: - break - simple_selectors.append(simple_selector) - - if simple_selectors or (type_selectors, pseudo_element) != (None, None): - return CompoundSelector(simple_selectors), pseudo_element - - peek = tokens.peek() - peek_type = peek.type if peek else 'EOF' - raise SelectorError(peek, f'expected a compound selector, got {peek_type}') - - -def parse_type_selector(tokens, namespaces): - tokens.skip_whitespace() - qualified_name = parse_qualified_name(tokens, namespaces) - if qualified_name is None: - return None - - simple_selectors = [] - namespace, local_name = qualified_name - if local_name is not None: - simple_selectors.append(LocalNameSelector(local_name)) - if namespace is not None: - simple_selectors.append(NamespaceSelector(namespace)) - return simple_selectors - - -def parse_simple_selector(tokens, namespaces): - peek = tokens.peek() - if peek is None: - return None, None - if peek.type == 'hash' and peek.is_identifier: - tokens.next() - return IDSelector(peek.value), None - elif peek == '.': - tokens.next() - next = tokens.next() - if next is None or next.type != 'ident': - raise SelectorError(next, f'Expected a class name, got {next}') - return ClassSelector(next.value), None - elif peek.type == '[] block': - tokens.next() - attr = parse_attribute_selector(TokenStream(peek.content), namespaces) - return attr, None - elif peek == ':': - tokens.next() - next = tokens.next() - if next == ':': - next = tokens.next() - if next is None or next.type != 'ident': - raise SelectorError(next, f'Expected a pseudo-element name, got {next}') - value = next.lower_value - if value not in SUPPORTED_PSEUDO_ELEMENTS: - raise SelectorError( - next, f'Expected a supported pseudo-element, got {value}') - return None, value - elif next is not None and next.type == 'ident': - name = next.lower_value - if name in ('before', 'after', 'first-line', 'first-letter'): - return None, name - else: - return PseudoClassSelector(name), None - elif next is not None and next.type == 'function': - name = next.lower_name - if name in ('is', 'where', 'not', 'has'): - return parse_logical_combination(next, namespaces, name), None - else: - return (FunctionalPseudoClassSelector(name, next.arguments), None) - else: - raise SelectorError(next, f'unexpected {next} token.') - else: - return None, None - - -def parse_logical_combination(matches_any_token, namespaces, name): - forgiving = True - relative = False - if name == 'is': - selector_class = MatchesAnySelector - elif name == 'where': - selector_class = SpecificityAdjustmentSelector - elif name == 'not': - forgiving = False - selector_class = NegationSelector - elif name == 'has': - relative = True - selector_class = RelationalSelector - - selectors = [ - selector for selector in - parse(matches_any_token.arguments, namespaces, forgiving, relative) - if selector.pseudo_element is None] - return selector_class(selectors) - - -def parse_attribute_selector(tokens, namespaces): - tokens.skip_whitespace() - qualified_name = parse_qualified_name(tokens, namespaces, is_attribute=True) - if qualified_name is None: - next = tokens.next() - raise SelectorError(next, f'expected attribute name, got {next}') - namespace, local_name = qualified_name - - tokens.skip_whitespace() - peek = tokens.peek() - if peek is None: - operator = None - value = None - elif peek in ('=', '~=', '|=', '^=', '$=', '*='): - operator = peek.value - tokens.next() - tokens.skip_whitespace() - next = tokens.next() - if next is None or next.type not in ('ident', 'string'): - next_type = 'None' if next is None else next.type - raise SelectorError(next, f'expected attribute value, got {next_type}') - value = next.value - else: - raise SelectorError(peek, f'expected attribute selector operator, got {peek}') - - tokens.skip_whitespace() - next = tokens.next() - case_sensitive = None - if next is not None: - if next.type == 'ident' and next.value.lower() == 'i': - case_sensitive = False - elif next.type == 'ident' and next.value.lower() == 's': - case_sensitive = True - else: - raise SelectorError(next, f'expected ], got {next.type}') - return AttributeSelector(namespace, local_name, operator, value, case_sensitive) - - -def parse_qualified_name(tokens, namespaces, is_attribute=False): - """Return ``(namespace, local)`` for given tokens. - - Can also return ``None`` for a wildcard. - - The empty string for ``namespace`` means "no namespace". - - """ - peek = tokens.peek() - if peek is None: - return None - if peek.type == 'ident': - first_ident = tokens.next() - peek = tokens.peek() - if peek != '|': - namespace = '' if is_attribute else namespaces.get(None, None) - return namespace, (first_ident.value, first_ident.lower_value) - tokens.next() - namespace = namespaces.get(first_ident.value) - if namespace is None: - raise SelectorError( - first_ident, f'undefined namespace prefix: {first_ident.value}') - elif peek == '*': - next = tokens.next() - peek = tokens.peek() - if peek != '|': - if is_attribute: - raise SelectorError(next, f'expected local name, got {next.type}') - return namespaces.get(None, None), None - tokens.next() - namespace = None - elif peek == '|': - tokens.next() - namespace = '' - else: - return None - - # If we get here, we just consumed '|' and set ``namespace`` - next = tokens.next() - if next.type == 'ident': - return namespace, (next.value, next.lower_value) - elif next == '*' and not is_attribute: - return namespace, None - else: - raise SelectorError(next, f'expected local name, got {next.type}') - - -class SelectorError(ValueError): - """A specialized ``ValueError`` for invalid selectors.""" - - -class TokenStream: - def __init__(self, tokens): - self.tokens = iter(tokens) - self.peeked = [] # In reversed order - - def next(self): - if self.peeked: - return self.peeked.pop() - else: - return next(self.tokens, None) - - def peek(self): - if not self.peeked: - self.peeked.append(next(self.tokens, None)) - return self.peeked[-1] - - def skip(self, skip_types): - found = False - while 1: - peek = self.peek() - if peek is None or peek.type not in skip_types: - break - self.next() - found = True - return found - - def skip_whitespace(self): - return self.skip(['whitespace']) - - def skip_comment(self): - return self.skip(['comment']) - - def skip_whitespace_and_comment(self): - return self.skip(['comment', 'whitespace']) - - -class Selector: - def __init__(self, tree, pseudo_element=None): - self.parsed_tree = tree - self.pseudo_element = pseudo_element - if pseudo_element is None: - #: Tuple of 3 integers: http://www.w3.org/TR/selectors/#specificity - self.specificity = tree.specificity - else: - a, b, c = tree.specificity - self.specificity = a, b, c + 1 - - def __repr__(self): - pseudo = f'::{self.pseudo_element}' if self.pseudo_element else '' - return f'{self.parsed_tree!r}{pseudo}' - - -class RelativeSelector: - def __init__(self, combinator, selector): - self.combinator = combinator - self.selector = selector - - @property - def specificity(self): - return self.selector.specificity - - @property - def pseudo_element(self): - return self.selector.pseudo_element - - def __repr__(self): - return ( - f'{self.selector!r}' if self.combinator == ' ' - else f'{self.combinator} {self.selector!r}') - - -class CombinedSelector: - def __init__(self, left, combinator, right): - #: Combined or compound selector - self.left = left - # One of `` `` (a single space), ``>``, ``+`` or ``~``. - self.combinator = combinator - #: compound selector - self.right = right - - @property - def specificity(self): - a1, b1, c1 = self.left.specificity - a2, b2, c2 = self.right.specificity - return a1 + a2, b1 + b2, c1 + c2 - - def __repr__(self): - return f'{self.left!r}{self.combinator}{self.right!r}' - - -class CompoundSelector: - def __init__(self, simple_selectors): - self.simple_selectors = simple_selectors - - @property - def specificity(self): - if self.simple_selectors: - # zip(*foo) turns [(a1, b1, c1), (a2, b2, c2), ...] - # into [(a1, a2, ...), (b1, b2, ...), (c1, c2, ...)] - return tuple(map(sum, zip( - *(sel.specificity for sel in self.simple_selectors)))) - else: - return 0, 0, 0 - - def __repr__(self): - return ''.join(map(repr, self.simple_selectors)) - - -class LocalNameSelector: - specificity = 0, 0, 1 - - def __init__(self, local_name): - self.local_name, self.lower_local_name = local_name - - def __repr__(self): - return self.local_name - - -class NamespaceSelector: - specificity = 0, 0, 0 - - def __init__(self, namespace): - #: The namespace URL as a string, - #: or the empty string for elements not in any namespace. - self.namespace = namespace - - def __repr__(self): - return '|' if self.namespace == '' else f'{{{self.namespace}}}|' - - -class IDSelector: - specificity = 1, 0, 0 - - def __init__(self, ident): - self.ident = ident - - def __repr__(self): - return f'#{self.ident}' - - -class ClassSelector: - specificity = 0, 1, 0 - - def __init__(self, class_name): - self.class_name = class_name - - def __repr__(self): - return f'.{self.class_name}' - - -class AttributeSelector: - specificity = 0, 1, 0 - - def __init__(self, namespace, name, operator, value, case_sensitive): - self.namespace = namespace - self.name, self.lower_name = name - #: A string like ``=`` or ``~=``, or None for ``[attr]`` selectors - self.operator = operator - #: A string, or None for ``[attr]`` selectors - self.value = value - #: ``True`` if case-sensitive, ``False`` if case-insensitive, ``None`` - #: if depends on the document language - self.case_sensitive = case_sensitive - - def __repr__(self): - namespace = '*|' if self.namespace is None else f'{{{self.namespace}}}' - case_sensitive = ( - '' if self.case_sensitive is None else - f' {"s" if self.case_sensitive else "i"}') - return ( - f'[{namespace}{self.name}{self.operator}{self.value!r}' - f'{case_sensitive}]') - - -class PseudoClassSelector: - specificity = 0, 1, 0 - - def __init__(self, name): - self.name = name - - def __repr__(self): - return ':' + self.name - - -class FunctionalPseudoClassSelector: - specificity = 0, 1, 0 - - def __init__(self, name, arguments): - self.name = name - self.arguments = arguments - - def __repr__(self): - return f':{self.name}{tuple(self.arguments)!r}' - - -class NegationSelector: - def __init__(self, selector_list): - self.selector_list = selector_list - - @property - def specificity(self): - if self.selector_list: - return max(selector.specificity for selector in self.selector_list) - else: - return (0, 0, 0) - - def __repr__(self): - return f':not({", ".join(repr(sel) for sel in self.selector_list)})' - - -class RelationalSelector: - def __init__(self, selector_list): - self.selector_list = selector_list - - @property - def specificity(self): - if self.selector_list: - return max(selector.specificity for selector in self.selector_list) - else: - return (0, 0, 0) - - def __repr__(self): - return f':has({", ".join(repr(sel) for sel in self.selector_list)})' - - -class MatchesAnySelector: - def __init__(self, selector_list): - self.selector_list = selector_list - - @property - def specificity(self): - if self.selector_list: - return max(selector.specificity for selector in self.selector_list) - else: - return (0, 0, 0) - - def __repr__(self): - return f':is({", ".join(repr(sel) for sel in self.selector_list)})' - - -class SpecificityAdjustmentSelector: - def __init__(self, selector_list): - self.selector_list = selector_list - - @property - def specificity(self): - return (0, 0, 0) - - def __repr__(self): - return f':where({", ".join(repr(sel) for sel in self.selector_list)})' diff --git a/.venv-docs/lib/python3.12/site-packages/cssselect2/tree.py b/.venv-docs/lib/python3.12/site-packages/cssselect2/tree.py deleted file mode 100644 index 5108049f..00000000 --- a/.venv-docs/lib/python3.12/site-packages/cssselect2/tree.py +++ /dev/null @@ -1,385 +0,0 @@ -from functools import cached_property -from warnings import warn - -from webencodings import ascii_lower - -from .compiler import compile_selector_list, split_whitespace - - -class ElementWrapper: - """Wrapper of :class:`xml.etree.ElementTree.Element` for Selector matching. - - This class should not be instanciated directly. :meth:`from_xml_root` or - :meth:`from_html_root` should be used for the root element of a document, - and other elements should be accessed (and wrappers generated) using - methods such as :meth:`iter_children` and :meth:`iter_subtree`. - - :class:`ElementWrapper` objects compare equal if their underlying - :class:`xml.etree.ElementTree.Element` do. - - """ - @classmethod - def from_xml_root(cls, root, content_language=None): - """Wrap for selector matching the root of an XML or XHTML document. - - :param root: - An ElementTree :class:`xml.etree.ElementTree.Element` - for the root element of a document. - If the given element is not the root, - selector matching will behave is if it were. - In other words, selectors will be not be `scoped`_ - to the subtree rooted at that element. - :returns: - A new :class:`ElementWrapper` - - .. _scoped: https://drafts.csswg.org/selectors-4/#scoping - - """ - return cls._from_root(root, content_language, in_html_document=False) - - @classmethod - def from_html_root(cls, root, content_language=None): - """Same as :meth:`from_xml_root` with case-insensitive attribute names. - - Useful for documents parsed with an HTML parser like html5lib, which - should be the case of documents with the ``text/html`` MIME type. - - """ - return cls._from_root(root, content_language, in_html_document=True) - - @classmethod - def _from_root(cls, root, content_language, in_html_document=True): - if hasattr(root, 'getroot'): - root = root.getroot() - return cls( - root, parent=None, index=0, previous=None, - in_html_document=in_html_document, content_language=content_language) - - def __init__(self, etree_element, parent, index, previous, - in_html_document, content_language=None): - #: The underlying ElementTree :class:`xml.etree.ElementTree.Element` - self.etree_element = etree_element - #: The parent :class:`ElementWrapper`, - #: or :obj:`None` for the root element. - self.parent = parent - #: The previous sibling :class:`ElementWrapper`, - #: or :obj:`None` for the root element. - self.previous = previous - if parent is not None: - #: The :attr:`parent`’s children - #: as a list of - #: ElementTree :class:`xml.etree.ElementTree.Element`\ s. - #: For the root (which has no parent) - self.etree_siblings = parent.etree_children - else: - self.etree_siblings = [etree_element] - #: The position within the :attr:`parent`’s children, counting from 0. - #: ``e.etree_siblings[e.index]`` is always ``e.etree_element``. - self.index = index - self.in_html_document = in_html_document - self.transport_content_language = content_language - - # Cache - self._ancestors = None - self._previous_siblings = None - - def __eq__(self, other): - return ( - type(self) is type(other) and - self.etree_element == other.etree_element) - - def __ne__(self, other): - return not (self == other) - - def __hash__(self): - return hash((type(self), self.etree_element)) - - def __iter__(self): - yield from self.iter_children() - - @property - def ancestors(self): - """Tuple of existing ancestors. - - Tuple of existing :class:`ElementWrapper` objects for this element’s - ancestors, in reversed tree order, from :attr:`parent` to the root. - - """ - if self._ancestors is None: - self._ancestors = ( - () if self.parent is None else (*self.parent.ancestors, self.parent)) - return self._ancestors - - @property - def previous_siblings(self): - """Tuple of previous siblings. - - Tuple of existing :class:`ElementWrapper` objects for this element’s - previous siblings, in reversed tree order. - - """ - if self._previous_siblings is None: - self._previous_siblings = ( - () if self.previous is None else - (*self.previous.previous_siblings, self.previous)) - return self._previous_siblings - - def iter_ancestors(self): - """Iterate over ancestors. - - Return an iterator of existing :class:`ElementWrapper` objects for this - element’s ancestors, in reversed tree order (from :attr:`parent` to the - root). - - The element itself is not included, this is an empty sequence for the - root element. - - This method is deprecated and will be removed in version 0.7.0. Use - :attr:`ancestors` instead. - - """ - warn( - 'This method is deprecated and will be removed in version 0.7.0. ' - 'Use the "ancestors" attribute instead.', - DeprecationWarning) - yield from self.ancestors - - def iter_previous_siblings(self): - """Iterate over previous siblings. - - Return an iterator of existing :class:`ElementWrapper` objects for this - element’s previous siblings, in reversed tree order. - - The element itself is not included, this is an empty sequence for a - first child or the root element. - - This method is deprecated and will be removed in version 0.7.0. Use - :attr:`previous_siblings` instead. - - """ - warn( - 'This method is deprecated and will be removed in version 0.7.0. ' - 'Use the "previous_siblings" attribute instead.', - DeprecationWarning) - yield from self.previous_siblings - - def iter_siblings(self): - """Iterate over siblings. - - Return an iterator of newly-created :class:`ElementWrapper` objects for - this element’s siblings, in tree order. - - """ - if self.parent is None: - yield self - else: - yield from self.parent.iter_children() - - def iter_next_siblings(self): - """Iterate over next siblings. - - Return an iterator of newly-created :class:`ElementWrapper` objects for - this element’s next siblings, in tree order. - - """ - found = False - for sibling in self.iter_siblings(): - if found: - yield sibling - if sibling == self: - found = True - - def iter_children(self): - """Iterate over children. - - Return an iterator of newly-created :class:`ElementWrapper` objects for - this element’s child elements, in tree order. - - """ - child = None - for i, etree_child in enumerate(self.etree_children): - child = type(self)( - etree_child, parent=self, index=i, previous=child, - in_html_document=self.in_html_document) - yield child - - def iter_subtree(self): - """Iterate over subtree. - - Return an iterator of newly-created :class:`ElementWrapper` objects for - the entire subtree rooted at this element, in tree order. - - Unlike in other methods, the element itself *is* included. - - This loops over an entire document: - - .. code-block:: python - - for element in ElementWrapper.from_root(root_etree).iter_subtree(): - ... - - """ - stack = [iter([self])] - while stack: - element = next(stack[-1], None) - if element is None: - stack.pop() - else: - yield element - stack.append(element.iter_children()) - - @staticmethod - def _compile(selectors): - return [ - compiled_selector.test - for selector in selectors - for compiled_selector in ( - [selector] if hasattr(selector, 'test') - else compile_selector_list(selector)) - if compiled_selector.pseudo_element is None and - not compiled_selector.never_matches] - - def matches(self, *selectors): - """Return wether this elememt matches any of the given selectors. - - :param selectors: - Each given selector is either a :class:`compiler.CompiledSelector`, - or an argument to :func:`compile_selector_list`. - - """ - return any(test(self) for test in self._compile(selectors)) - - def query_all(self, *selectors): - """Return elements, in tree order, that match any of given selectors. - - Selectors are `scoped`_ to the subtree rooted at this element. - - .. _scoped: https://drafts.csswg.org/selectors-4/#scoping - - :param selectors: - Each given selector is either a :class:`compiler.CompiledSelector`, - or an argument to :func:`compile_selector_list`. - :returns: - An iterator of newly-created :class:`ElementWrapper` objects. - - """ - tests = self._compile(selectors) - if len(tests) == 1: - return filter(tests[0], self.iter_subtree()) - elif selectors: - return ( - element for element in self.iter_subtree() - if any(test(element) for test in tests)) - else: - return iter(()) - - def query(self, *selectors): - """Return first element that matches any of given selectors. - - :param selectors: - Each given selector is either a :class:`compiler.CompiledSelector`, - or an argument to :func:`compile_selector_list`. - :returns: - A newly-created :class:`ElementWrapper` object, - or :obj:`None` if there is no match. - - """ - return next(self.query_all(*selectors), None) - - @cached_property - def etree_children(self): - """Children as a list of :class:`xml.etree.ElementTree.Element`. - - Other ElementTree nodes such as - :func:`comments ` and - :func:`processing instructions - ` - are not included. - - """ - return [ - element for element in self.etree_element - if isinstance(element.tag, str)] - - @cached_property - def local_name(self): - """The local name of this element, as a string.""" - namespace_url, local_name = _split_etree_tag(self.etree_element.tag) - self.__dict__[str('namespace_url')] = namespace_url - return local_name - - @cached_property - def namespace_url(self): - """The namespace URL of this element, as a string.""" - namespace_url, local_name = _split_etree_tag(self.etree_element.tag) - self.__dict__[str('local_name')] = local_name - return namespace_url - - @cached_property - def id(self): - """The ID of this element, as a string.""" - return self.etree_element.get('id') - - @cached_property - def classes(self): - """The classes of this element, as a :class:`set` of strings.""" - return set(split_whitespace(self.etree_element.get('class', ''))) - - @cached_property - def lang(self): - """The language of this element, as a string.""" - # http://whatwg.org/C#language - xml_lang = self.etree_element.get('{http://www.w3.org/XML/1998/namespace}lang') - if xml_lang is not None: - return ascii_lower(xml_lang) - is_html = ( - self.in_html_document or - self.namespace_url == 'http://www.w3.org/1999/xhtml') - if is_html: - lang = self.etree_element.get('lang') - if lang is not None: - return ascii_lower(lang) - if self.parent is not None: - return self.parent.lang - # Root elememnt - if is_html: - content_language = None - iterator = self.etree_element.iter('{http://www.w3.org/1999/xhtml}meta') - for meta in iterator: - http_equiv = meta.get('http-equiv', '') - if ascii_lower(http_equiv) == 'content-language': - content_language = _parse_content_language(meta.get('content')) - if content_language is not None: - return ascii_lower(content_language) - # Empty string means unknown - return _parse_content_language(self.transport_content_language) or '' - - @cached_property - def in_disabled_fieldset(self): - if self.parent is None: - return False - fieldset = '{http://www.w3.org/1999/xhtml}fieldset' - legend = '{http://www.w3.org/1999/xhtml}legend' - disabled_fieldset = ( - self.parent.etree_element.tag == fieldset and - self.parent.etree_element.get('disabled') is not None and ( - self.etree_element.tag != legend or any( - sibling.etree_element.tag == legend - for sibling in self.iter_previous_siblings()))) - return disabled_fieldset or self.parent.in_disabled_fieldset - - -def _split_etree_tag(tag): - position = tag.rfind('}') - if position == -1: - return '', tag - else: - assert tag[0] == '{' - return tag[1:position], tag[position+1:] - - -def _parse_content_language(value): - if value is not None and ',' not in value: - parts = split_whitespace(value) - if len(parts) == 1: - return parts[0] diff --git a/.venv-docs/lib/python3.12/site-packages/docx/__init__.py b/.venv-docs/lib/python3.12/site-packages/docx/__init__.py deleted file mode 100644 index fd06c84d..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/__init__.py +++ /dev/null @@ -1,65 +0,0 @@ -"""Initialize `docx` package. - -Export the `Document` constructor function and establish the mapping of part-type to -the part-classe that implements that type. -""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Type - -from docx.api import Document - -if TYPE_CHECKING: - from docx.opc.part import Part - -__version__ = "1.2.0" - - -__all__ = ["Document"] - - -# -- register custom Part classes with opc package reader -- - -from docx.opc.constants import CONTENT_TYPE as CT -from docx.opc.constants import RELATIONSHIP_TYPE as RT -from docx.opc.part import PartFactory -from docx.opc.parts.coreprops import CorePropertiesPart -from docx.parts.comments import CommentsPart -from docx.parts.document import DocumentPart -from docx.parts.hdrftr import FooterPart, HeaderPart -from docx.parts.image import ImagePart -from docx.parts.numbering import NumberingPart -from docx.parts.settings import SettingsPart -from docx.parts.styles import StylesPart - - -def part_class_selector(content_type: str, reltype: str) -> Type[Part] | None: - if reltype == RT.IMAGE: - return ImagePart - return None - - -PartFactory.part_class_selector = part_class_selector -PartFactory.part_type_for[CT.OPC_CORE_PROPERTIES] = CorePropertiesPart -PartFactory.part_type_for[CT.WML_COMMENTS] = CommentsPart -PartFactory.part_type_for[CT.WML_DOCUMENT_MAIN] = DocumentPart -PartFactory.part_type_for[CT.WML_FOOTER] = FooterPart -PartFactory.part_type_for[CT.WML_HEADER] = HeaderPart -PartFactory.part_type_for[CT.WML_NUMBERING] = NumberingPart -PartFactory.part_type_for[CT.WML_SETTINGS] = SettingsPart -PartFactory.part_type_for[CT.WML_STYLES] = StylesPart - -del ( - CT, - CorePropertiesPart, - CommentsPart, - DocumentPart, - FooterPart, - HeaderPart, - NumberingPart, - PartFactory, - SettingsPart, - StylesPart, - part_class_selector, -) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/__init__.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index a9b182ce..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/api.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/api.cpython-312.pyc deleted file mode 100644 index ede063d6..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/api.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/blkcntnr.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/blkcntnr.cpython-312.pyc deleted file mode 100644 index 7ee827c6..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/blkcntnr.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/comments.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/comments.cpython-312.pyc deleted file mode 100644 index 0ad369a1..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/comments.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/document.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/document.cpython-312.pyc deleted file mode 100644 index c0fdac27..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/document.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/exceptions.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/exceptions.cpython-312.pyc deleted file mode 100644 index bf436fdf..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/exceptions.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/package.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/package.cpython-312.pyc deleted file mode 100644 index 5cea9130..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/package.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/section.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/section.cpython-312.pyc deleted file mode 100644 index 1b11f91d..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/section.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/settings.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/settings.cpython-312.pyc deleted file mode 100644 index f6112642..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/settings.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/shape.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/shape.cpython-312.pyc deleted file mode 100644 index 9d0f7d7d..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/shape.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/shared.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/shared.cpython-312.pyc deleted file mode 100644 index 4c2022b8..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/shared.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/table.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/table.cpython-312.pyc deleted file mode 100644 index 3d899117..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/table.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/types.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/types.cpython-312.pyc deleted file mode 100644 index 93bc1547..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/__pycache__/types.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/api.py b/.venv-docs/lib/python3.12/site-packages/docx/api.py deleted file mode 100644 index aea87645..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/api.py +++ /dev/null @@ -1,37 +0,0 @@ -"""Directly exposed API functions and classes, :func:`Document` for now. - -Provides a syntactically more convenient API for interacting with the OpcPackage graph. -""" - -from __future__ import annotations - -import os -from typing import IO, TYPE_CHECKING, cast - -from docx.opc.constants import CONTENT_TYPE as CT -from docx.package import Package - -if TYPE_CHECKING: - from docx.document import Document as DocumentObject - from docx.parts.document import DocumentPart - - -def Document(docx: str | IO[bytes] | None = None) -> DocumentObject: - """Return a |Document| object loaded from `docx`, where `docx` can be either a path - to a ``.docx`` file (a string) or a file-like object. - - If `docx` is missing or ``None``, the built-in default document "template" is - loaded. - """ - docx = _default_docx_path() if docx is None else docx - document_part = cast("DocumentPart", Package.open(docx).main_document_part) - if document_part.content_type != CT.WML_DOCUMENT_MAIN: - tmpl = "file '%s' is not a Word file, content type is '%s'" - raise ValueError(tmpl % (docx, document_part.content_type)) - return document_part.document - - -def _default_docx_path(): - """Return the path to the built-in default .docx package.""" - _thisdir = os.path.split(__file__)[0] - return os.path.join(_thisdir, "templates", "default.docx") diff --git a/.venv-docs/lib/python3.12/site-packages/docx/blkcntnr.py b/.venv-docs/lib/python3.12/site-packages/docx/blkcntnr.py deleted file mode 100644 index 82c7ef72..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/blkcntnr.py +++ /dev/null @@ -1,101 +0,0 @@ -# pyright: reportImportCycles=false - -"""Block item container, used by body, cell, header, etc. - -Block level items are things like paragraph and table, although there are a few other -specialized ones like structured document tags. -""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Iterator - -from typing_extensions import TypeAlias - -from docx.oxml.table import CT_Tbl -from docx.oxml.text.paragraph import CT_P -from docx.shared import StoryChild -from docx.text.paragraph import Paragraph - -if TYPE_CHECKING: - import docx.types as t - from docx.oxml.comments import CT_Comment - from docx.oxml.document import CT_Body - from docx.oxml.section import CT_HdrFtr - from docx.oxml.table import CT_Tc - from docx.shared import Length - from docx.styles.style import ParagraphStyle - from docx.table import Table - -BlockItemElement: TypeAlias = "CT_Body | CT_Comment | CT_HdrFtr | CT_Tc" - - -class BlockItemContainer(StoryChild): - """Base class for proxy objects that can contain block items. - - These containers include _Body, _Cell, header, footer, footnote, endnote, comment, - and text box objects. Provides the shared functionality to add a block item like a - paragraph or table. - """ - - def __init__(self, element: BlockItemElement, parent: t.ProvidesStoryPart): - super(BlockItemContainer, self).__init__(parent) - self._element = element - - def add_paragraph(self, text: str = "", style: str | ParagraphStyle | None = None) -> Paragraph: - """Return paragraph newly added to the end of the content in this container. - - The paragraph has `text` in a single run if present, and is given paragraph - style `style`. - - If `style` is |None|, no paragraph style is applied, which has the same effect - as applying the 'Normal' style. - """ - paragraph = self._add_paragraph() - if text: - paragraph.add_run(text) - if style is not None: - paragraph.style = style - return paragraph - - def add_table(self, rows: int, cols: int, width: Length) -> Table: - """Return table of `width` having `rows` rows and `cols` columns. - - The table is appended appended at the end of the content in this container. - - `width` is evenly distributed between the table columns. - """ - from docx.table import Table - - tbl = CT_Tbl.new_tbl(rows, cols, width) - self._element._insert_tbl(tbl) # pyright: ignore[reportPrivateUsage] - return Table(tbl, self) - - def iter_inner_content(self) -> Iterator[Paragraph | Table]: - """Generate each `Paragraph` or `Table` in this container in document order.""" - from docx.table import Table - - for element in self._element.inner_content_elements: - yield (Paragraph(element, self) if isinstance(element, CT_P) else Table(element, self)) - - @property - def paragraphs(self): - """A list containing the paragraphs in this container, in document order. - - Read-only. - """ - return [Paragraph(p, self) for p in self._element.p_lst] - - @property - def tables(self): - """A list containing the tables in this container, in document order. - - Read-only. - """ - from docx.table import Table - - return [Table(tbl, self) for tbl in self._element.tbl_lst] - - def _add_paragraph(self): - """Return paragraph newly added to the end of the content in this container.""" - return Paragraph(self._element.add_p(), self) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/comments.py b/.venv-docs/lib/python3.12/site-packages/docx/comments.py deleted file mode 100644 index 8ea19522..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/comments.py +++ /dev/null @@ -1,163 +0,0 @@ -"""Collection providing access to comments added to this document.""" - -from __future__ import annotations - -import datetime as dt -from typing import TYPE_CHECKING, Iterator - -from docx.blkcntnr import BlockItemContainer - -if TYPE_CHECKING: - from docx.oxml.comments import CT_Comment, CT_Comments - from docx.parts.comments import CommentsPart - from docx.styles.style import ParagraphStyle - from docx.text.paragraph import Paragraph - - -class Comments: - """Collection containing the comments added to this document.""" - - def __init__(self, comments_elm: CT_Comments, comments_part: CommentsPart): - self._comments_elm = comments_elm - self._comments_part = comments_part - - def __iter__(self) -> Iterator[Comment]: - """Iterator over the comments in this collection.""" - return ( - Comment(comment_elm, self._comments_part) - for comment_elm in self._comments_elm.comment_lst - ) - - def __len__(self) -> int: - """The number of comments in this collection.""" - return len(self._comments_elm.comment_lst) - - def add_comment(self, text: str = "", author: str = "", initials: str | None = "") -> Comment: - """Add a new comment to the document and return it. - - The comment is added to the end of the comments collection and is assigned a unique - comment-id. - - If `text` is provided, it is added to the comment. This option provides for the common - case where a comment contains a modest passage of plain text. Multiple paragraphs can be - added using the `text` argument by separating their text with newlines (`"\\\\n"`). - Between newlines, text is interpreted as it is in `Document.add_paragraph(text=...)`. - - The default is to place a single empty paragraph in the comment, which is the same - behavior as the Word UI when you add a comment. New runs can be added to the first - paragraph in the empty comment with `comments.paragraphs[0].add_run()` to adding more - complex text with emphasis or images. Additional paragraphs can be added using - `.add_paragraph()`. - - `author` is a required attribute, set to the empty string by default. - - `initials` is an optional attribute, set to the empty string by default. Passing |None| - for the `initials` parameter causes that attribute to be omitted from the XML. - """ - comment_elm = self._comments_elm.add_comment() - comment_elm.author = author - comment_elm.initials = initials - comment_elm.date = dt.datetime.now(dt.timezone.utc) - comment = Comment(comment_elm, self._comments_part) - - if text == "": - return comment - - para_text_iter = iter(text.split("\n")) - - first_para_text = next(para_text_iter) - first_para = comment.paragraphs[0] - first_para.add_run(first_para_text) - - for s in para_text_iter: - comment.add_paragraph(text=s) - - return comment - - def get(self, comment_id: int) -> Comment | None: - """Return the comment identified by `comment_id`, or |None| if not found.""" - comment_elm = self._comments_elm.get_comment_by_id(comment_id) - return Comment(comment_elm, self._comments_part) if comment_elm is not None else None - - -class Comment(BlockItemContainer): - """Proxy for a single comment in the document. - - Provides methods to access comment metadata such as author, initials, and date. - - A comment is also a block-item container, similar to a table cell, so it can contain both - paragraphs and tables and its paragraphs can contain rich text, hyperlinks and images, - although the common case is that a comment contains a single paragraph of plain text like a - sentence or phrase. - - Note that certain content like tables may not be displayed in the Word comment sidebar due to - space limitations. Such "over-sized" content can still be viewed in the review pane. - """ - - def __init__(self, comment_elm: CT_Comment, comments_part: CommentsPart): - super().__init__(comment_elm, comments_part) - self._comment_elm = comment_elm - - def add_paragraph(self, text: str = "", style: str | ParagraphStyle | None = None) -> Paragraph: - """Return paragraph newly added to the end of the content in this container. - - The paragraph has `text` in a single run if present, and is given paragraph style `style`. - When `style` is |None| or ommitted, the "CommentText" paragraph style is applied, which is - the default style for comments. - """ - paragraph = super().add_paragraph(text, style) - - # -- have to assign style directly to element because `paragraph.style` raises when - # -- a style is not present in the styles part - if style is None: - paragraph._p.style = "CommentText" # pyright: ignore[reportPrivateUsage] - - return paragraph - - @property - def author(self) -> str: - """Read/write. The recorded author of this comment. - - This field is required but can be set to the empty string. - """ - return self._comment_elm.author - - @author.setter - def author(self, value: str): - self._comment_elm.author = value - - @property - def comment_id(self) -> int: - """The unique identifier of this comment.""" - return self._comment_elm.id - - @property - def initials(self) -> str | None: - """Read/write. The recorded initials of the comment author. - - This attribute is optional in the XML, returns |None| if not set. Assigning |None| removes - any existing initials from the XML. - """ - return self._comment_elm.initials - - @initials.setter - def initials(self, value: str | None): - self._comment_elm.initials = value - - @property - def text(self) -> str: - """The text content of this comment as a string. - - Only content in paragraphs is included and of course all emphasis and styling is stripped. - - Paragraph boundaries are indicated with a newline (`"\\\\n"`) - """ - return "\n".join(p.text for p in self.paragraphs) - - @property - def timestamp(self) -> dt.datetime | None: - """The date and time this comment was authored. - - This attribute is optional in the XML, returns |None| if not set. - """ - return self._comment_elm.date diff --git a/.venv-docs/lib/python3.12/site-packages/docx/dml/__init__.py b/.venv-docs/lib/python3.12/site-packages/docx/dml/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/.venv-docs/lib/python3.12/site-packages/docx/dml/__pycache__/__init__.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/dml/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 4308c7c0..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/dml/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/dml/__pycache__/color.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/dml/__pycache__/color.cpython-312.pyc deleted file mode 100644 index 53c7cf07..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/dml/__pycache__/color.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/dml/color.py b/.venv-docs/lib/python3.12/site-packages/docx/dml/color.py deleted file mode 100644 index a8322d21..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/dml/color.py +++ /dev/null @@ -1,112 +0,0 @@ -"""DrawingML objects related to color, ColorFormat being the most prominent.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, cast - -from typing_extensions import TypeAlias - -from docx.enum.dml import MSO_COLOR_TYPE -from docx.oxml.simpletypes import ST_HexColorAuto -from docx.shared import ElementProxy, RGBColor - -if TYPE_CHECKING: - from docx.enum.dml import MSO_THEME_COLOR - from docx.oxml.text.font import CT_Color - from docx.oxml.text.run import CT_R - -# -- other element types can be a parent of an `w:rPr` element, but for now only `w:r` is -- -RPrParent: TypeAlias = "CT_R" - - -class ColorFormat(ElementProxy): - """Provides access to color settings like RGB color, theme color, and luminance adjustments.""" - - def __init__(self, rPr_parent: RPrParent): - super(ColorFormat, self).__init__(rPr_parent) - self._element = rPr_parent - - @property - def rgb(self) -> RGBColor | None: - """An |RGBColor| value or |None| if no RGB color is specified. - - When :attr:`type` is `MSO_COLOR_TYPE.RGB`, the value of this property will always be an - |RGBColor| value. It may also be an |RGBColor| value if :attr:`type` is - `MSO_COLOR_TYPE.THEME`, as Word writes the current value of a theme color when one is - assigned. In that case, the RGB value should be interpreted as no more than a good guess - however, as the theme color takes precedence at rendering time. Its value is |None| - whenever :attr:`type` is either |None| or `MSO_COLOR_TYPE.AUTO`. - - Assigning an |RGBColor| value causes :attr:`type` to become `MSO_COLOR_TYPE.RGB` and any - theme color is removed. Assigning |None| causes any color to be removed such that the - effective color is inherited from the style hierarchy. - """ - color = self._color - if color is None: - return None - if color.val == ST_HexColorAuto.AUTO: - return None - return cast(RGBColor, color.val) - - @rgb.setter - def rgb(self, value: RGBColor | None): - if value is None and self._color is None: - return - rPr = self._element.get_or_add_rPr() - rPr._remove_color() # pyright: ignore[reportPrivateUsage] - if value is not None: - rPr.get_or_add_color().val = value - - @property - def theme_color(self) -> MSO_THEME_COLOR | None: - """Member of :ref:`MsoThemeColorIndex` or |None| if no theme color is specified. - - When :attr:`type` is `MSO_COLOR_TYPE.THEME`, the value of this property will always be a - member of :ref:`MsoThemeColorIndex`. When :attr:`type` has any other value, the value of - this property is |None|. - - Assigning a member of :ref:`MsoThemeColorIndex` causes :attr:`type` to become - `MSO_COLOR_TYPE.THEME`. Any existing RGB value is retained but ignored by Word. Assigning - |None| causes any color specification to be removed such that the effective color is - inherited from the style hierarchy. - """ - color = self._color - if color is None: - return None - return color.themeColor - - @theme_color.setter - def theme_color(self, value: MSO_THEME_COLOR | None): - if value is None: - if self._color is not None and self._element.rPr is not None: - self._element.rPr._remove_color() # pyright: ignore[reportPrivateUsage] - return - self._element.get_or_add_rPr().get_or_add_color().themeColor = value - - @property - def type(self) -> MSO_COLOR_TYPE | None: - """Read-only. - - A member of :ref:`MsoColorType`, one of RGB, THEME, or AUTO, corresponding to the way this - color is defined. Its value is |None| if no color is applied at this level, which causes - the effective color to be inherited from the style hierarchy. - """ - color = self._color - if color is None: - return None - if color.themeColor is not None: - return MSO_COLOR_TYPE.THEME - if color.val == ST_HexColorAuto.AUTO: - return MSO_COLOR_TYPE.AUTO - return MSO_COLOR_TYPE.RGB - - @property - def _color(self) -> CT_Color | None: - """Return `w:rPr/w:color` or |None| if not present. - - Helper to factor out repetitive element access. - """ - rPr = self._element.rPr - if rPr is None: - return None - return rPr.color diff --git a/.venv-docs/lib/python3.12/site-packages/docx/document.py b/.venv-docs/lib/python3.12/site-packages/docx/document.py deleted file mode 100644 index 73757b46..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/document.py +++ /dev/null @@ -1,265 +0,0 @@ -# pyright: reportImportCycles=false -# pyright: reportPrivateUsage=false - -"""|Document| and closely related objects.""" - -from __future__ import annotations - -from typing import IO, TYPE_CHECKING, Iterator, List, Sequence - -from docx.blkcntnr import BlockItemContainer -from docx.enum.section import WD_SECTION -from docx.enum.text import WD_BREAK -from docx.section import Section, Sections -from docx.shared import ElementProxy, Emu, Inches, Length -from docx.text.run import Run - -if TYPE_CHECKING: - import docx.types as t - from docx.comments import Comment, Comments - from docx.oxml.document import CT_Body, CT_Document - from docx.parts.document import DocumentPart - from docx.settings import Settings - from docx.styles.style import ParagraphStyle, _TableStyle - from docx.table import Table - from docx.text.paragraph import Paragraph - - -class Document(ElementProxy): - """WordprocessingML (WML) document. - - Not intended to be constructed directly. Use :func:`docx.Document` to open or create - a document. - """ - - def __init__(self, element: CT_Document, part: DocumentPart): - super(Document, self).__init__(element) - self._element = element - self._part = part - self.__body = None - - def add_comment( - self, - runs: Run | Sequence[Run], - text: str | None = "", - author: str = "", - initials: str | None = "", - ) -> Comment: - """Add a comment to the document, anchored to the specified runs. - - `runs` can be a single `Run` object or a non-empty sequence of `Run` objects. Only the - first and last run of a sequence are used, it's just more convenient to pass a whole - sequence when that's what you have handy, like `paragraph.runs` for example. When `runs` - contains a single `Run` object, that run serves as both the first and last run. - - A comment can be anchored only on an even run boundary, meaning the text the comment - "references" must be a non-zero integer number of consecutive runs. The runs need not be - _contiguous_ per se, like the first can be in one paragraph and the last in the next - paragraph, but all runs between the first and the last will be included in the reference. - - The comment reference range is delimited by placing a `w:commentRangeStart` element before - the first run and a `w:commentRangeEnd` element after the last run. This is why only the - first and last run are required and why a single run can serve as both first and last. - Word works out which text to highlight in the UI based on these range markers. - - `text` allows the contents of a simple comment to be provided in the call, providing for - the common case where a comment is a single phrase or sentence without special formatting - such as bold or italics. More complex comments can be added using the returned `Comment` - object in much the same way as a `Document` or (table) `Cell` object, using methods like - `.add_paragraph()`, .add_run()`, etc. - - The `author` and `initials` parameters allow that metadata to be set for the comment. - `author` is a required attribute on a comment and is the empty string by default. - `initials` is optional on a comment and may be omitted by passing |None|, but Word adds an - `initials` attribute by default and we follow that convention by using the empty string - when no `initials` argument is provided. - """ - # -- normalize `runs` to a sequence of runs -- - runs = [runs] if isinstance(runs, Run) else runs - first_run = runs[0] - last_run = runs[-1] - - # -- Note that comments can only appear in the document part -- - comment = self.comments.add_comment(text=text, author=author, initials=initials) - - # -- let the first run orchestrate placement of the comment range start and end -- - first_run.mark_comment_range(last_run, comment.comment_id) - - return comment - - def add_heading(self, text: str = "", level: int = 1): - """Return a heading paragraph newly added to the end of the document. - - The heading paragraph will contain `text` and have its paragraph style - determined by `level`. If `level` is 0, the style is set to `Title`. If `level` - is 1 (or omitted), `Heading 1` is used. Otherwise the style is set to `Heading - {level}`. Raises |ValueError| if `level` is outside the range 0-9. - """ - if not 0 <= level <= 9: - raise ValueError("level must be in range 0-9, got %d" % level) - style = "Title" if level == 0 else "Heading %d" % level - return self.add_paragraph(text, style) - - def add_page_break(self): - """Return newly |Paragraph| object containing only a page break.""" - paragraph = self.add_paragraph() - paragraph.add_run().add_break(WD_BREAK.PAGE) - return paragraph - - def add_paragraph(self, text: str = "", style: str | ParagraphStyle | None = None) -> Paragraph: - """Return paragraph newly added to the end of the document. - - The paragraph is populated with `text` and having paragraph style `style`. - - `text` can contain tab (``\\t``) characters, which are converted to the - appropriate XML form for a tab. `text` can also include newline (``\\n``) or - carriage return (``\\r``) characters, each of which is converted to a line - break. - """ - return self._body.add_paragraph(text, style) - - def add_picture( - self, - image_path_or_stream: str | IO[bytes], - width: int | Length | None = None, - height: int | Length | None = None, - ): - """Return new picture shape added in its own paragraph at end of the document. - - The picture contains the image at `image_path_or_stream`, scaled based on - `width` and `height`. If neither width nor height is specified, the picture - appears at its native size. If only one is specified, it is used to compute a - scaling factor that is then applied to the unspecified dimension, preserving the - aspect ratio of the image. The native size of the picture is calculated using - the dots-per-inch (dpi) value specified in the image file, defaulting to 72 dpi - if no value is specified, as is often the case. - """ - run = self.add_paragraph().add_run() - return run.add_picture(image_path_or_stream, width, height) - - def add_section(self, start_type: WD_SECTION = WD_SECTION.NEW_PAGE): - """Return a |Section| object newly added at the end of the document. - - The optional `start_type` argument must be a member of the :ref:`WdSectionStart` - enumeration, and defaults to ``WD_SECTION.NEW_PAGE`` if not provided. - """ - new_sectPr = self._element.body.add_section_break() - new_sectPr.start_type = start_type - return Section(new_sectPr, self._part) - - def add_table(self, rows: int, cols: int, style: str | _TableStyle | None = None): - """Add a table having row and column counts of `rows` and `cols` respectively. - - `style` may be a table style object or a table style name. If `style` is |None|, - the table inherits the default table style of the document. - """ - table = self._body.add_table(rows, cols, self._block_width) - table.style = style - return table - - @property - def comments(self) -> Comments: - """A |Comments| object providing access to comments added to the document.""" - return self._part.comments - - @property - def core_properties(self): - """A |CoreProperties| object providing Dublin Core properties of document.""" - return self._part.core_properties - - @property - def inline_shapes(self): - """The |InlineShapes| collection for this document. - - An inline shape is a graphical object, such as a picture, contained in a run of - text and behaving like a character glyph, being flowed like other text in a - paragraph. - """ - return self._part.inline_shapes - - def iter_inner_content(self) -> Iterator[Paragraph | Table]: - """Generate each `Paragraph` or `Table` in this document in document order.""" - return self._body.iter_inner_content() - - @property - def paragraphs(self) -> List[Paragraph]: - """The |Paragraph| instances in the document, in document order. - - Note that paragraphs within revision marks such as ```` or ```` do - not appear in this list. - """ - return self._body.paragraphs - - @property - def part(self) -> DocumentPart: - """The |DocumentPart| object of this document.""" - return self._part - - def save(self, path_or_stream: str | IO[bytes]): - """Save this document to `path_or_stream`. - - `path_or_stream` can be either a path to a filesystem location (a string) or a - file-like object. - """ - self._part.save(path_or_stream) - - @property - def sections(self) -> Sections: - """|Sections| object providing access to each section in this document.""" - return Sections(self._element, self._part) - - @property - def settings(self) -> Settings: - """A |Settings| object providing access to the document-level settings.""" - return self._part.settings - - @property - def styles(self): - """A |Styles| object providing access to the styles in this document.""" - return self._part.styles - - @property - def tables(self) -> List[Table]: - """All |Table| instances in the document, in document order. - - Note that only tables appearing at the top level of the document appear in this - list; a table nested inside a table cell does not appear. A table within - revision marks such as ```` or ```` will also not appear in the - list. - """ - return self._body.tables - - @property - def _block_width(self) -> Length: - """A |Length| object specifying the space between margins in last section.""" - section = self.sections[-1] - page_width = section.page_width or Inches(8.5) - left_margin = section.left_margin or Inches(1) - right_margin = section.right_margin or Inches(1) - return Emu(page_width - left_margin - right_margin) - - @property - def _body(self) -> _Body: - """The |_Body| instance containing the content for this document.""" - if self.__body is None: - self.__body = _Body(self._element.body, self) - return self.__body - - -class _Body(BlockItemContainer): - """Proxy for `` element in this document. - - It's primary role is a container for document content. - """ - - def __init__(self, body_elm: CT_Body, parent: t.ProvidesStoryPart): - super(_Body, self).__init__(body_elm, parent) - self._body = body_elm - - def clear_content(self) -> _Body: - """Return this |_Body| instance after clearing it of all content. - - Section properties for the main document story, if present, are preserved. - """ - self._body.clear_content() - return self diff --git a/.venv-docs/lib/python3.12/site-packages/docx/drawing/__init__.py b/.venv-docs/lib/python3.12/site-packages/docx/drawing/__init__.py deleted file mode 100644 index 00d1f51b..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/drawing/__init__.py +++ /dev/null @@ -1,59 +0,0 @@ -"""DrawingML-related objects are in this subpackage.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from docx.oxml.drawing import CT_Drawing -from docx.shared import Parented - -if TYPE_CHECKING: - import docx.types as t - from docx.image.image import Image - - -class Drawing(Parented): - """Container for a DrawingML object.""" - - def __init__(self, drawing: CT_Drawing, parent: t.ProvidesStoryPart): - super().__init__(parent) - self._parent = parent - self._drawing = self._element = drawing - - @property - def has_picture(self) -> bool: - """True when `drawing` contains an embedded picture. - - A drawing can contain a picture, but it can also contain a chart, SmartArt, or a - drawing canvas. Methods related to a picture, like `.image`, will raise when the drawing - does not contain a picture. Use this value to determine whether image methods will succeed. - - This value is `False` when a linked picture is present. This should be relatively rare and - the image would only be retrievable from the filesystem. - - Note this does not distinguish between inline and floating images. The presence of either - one will cause this value to be `True`. - """ - xpath_expr = ( - # -- an inline picture -- - "./wp:inline/a:graphic/a:graphicData/pic:pic" - # -- a floating picture -- - " | ./wp:anchor/a:graphic/a:graphicData/pic:pic" - ) - # -- xpath() will return a list, empty if there are no matches -- - return bool(self._drawing.xpath(xpath_expr)) - - @property - def image(self) -> Image: - """An `Image` proxy object for the image in this (picture) drawing. - - Raises `ValueError` when this drawing does contains something other than a picture. Use - `.has_picture` to qualify drawing objects before using this property. - """ - picture_rIds = self._drawing.xpath(".//pic:blipFill/a:blip/@r:embed") - if not picture_rIds: - raise ValueError("drawing does not contain a picture") - rId = picture_rIds[0] - doc_part = self.part - image_part = doc_part.related_parts[rId] - return image_part.image diff --git a/.venv-docs/lib/python3.12/site-packages/docx/drawing/__pycache__/__init__.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/drawing/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index aa376a7b..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/drawing/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/enum/__init__.py b/.venv-docs/lib/python3.12/site-packages/docx/enum/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/.venv-docs/lib/python3.12/site-packages/docx/enum/__pycache__/__init__.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/enum/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 770b0254..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/enum/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/enum/__pycache__/base.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/enum/__pycache__/base.cpython-312.pyc deleted file mode 100644 index 90c1d134..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/enum/__pycache__/base.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/enum/__pycache__/dml.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/enum/__pycache__/dml.cpython-312.pyc deleted file mode 100644 index f041e8ae..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/enum/__pycache__/dml.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/enum/__pycache__/section.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/enum/__pycache__/section.cpython-312.pyc deleted file mode 100644 index fe1bd4f1..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/enum/__pycache__/section.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/enum/__pycache__/shape.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/enum/__pycache__/shape.cpython-312.pyc deleted file mode 100644 index d15ee974..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/enum/__pycache__/shape.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/enum/__pycache__/style.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/enum/__pycache__/style.cpython-312.pyc deleted file mode 100644 index c092c624..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/enum/__pycache__/style.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/enum/__pycache__/table.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/enum/__pycache__/table.cpython-312.pyc deleted file mode 100644 index 8dff003e..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/enum/__pycache__/table.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/enum/__pycache__/text.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/enum/__pycache__/text.cpython-312.pyc deleted file mode 100644 index 66cd3781..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/enum/__pycache__/text.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/enum/base.py b/.venv-docs/lib/python3.12/site-packages/docx/enum/base.py deleted file mode 100644 index 66e98975..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/enum/base.py +++ /dev/null @@ -1,150 +0,0 @@ -"""Base classes and other objects used by enumerations.""" - -from __future__ import annotations - -import enum -import textwrap -from typing import TYPE_CHECKING, Any, Dict, Type, TypeVar - -if TYPE_CHECKING: - from typing_extensions import Self - -_T = TypeVar("_T", bound="BaseXmlEnum") - - -class BaseEnum(int, enum.Enum): - """Base class for Enums that do not map XML attr values. - - The enum's value will be an integer, corresponding to the integer assigned the - corresponding member in the MS API enum of the same name. - """ - - def __new__(cls, ms_api_value: int, docstr: str): - self = int.__new__(cls, ms_api_value) - self._value_ = ms_api_value - self.__doc__ = docstr.strip() - return self - - def __str__(self): - """The symbolic name and string value of this member, e.g. 'MIDDLE (3)'.""" - return f"{self.name} ({self.value})" - - -class BaseXmlEnum(int, enum.Enum): - """Base class for Enums that also map XML attr values. - - The enum's value will be an integer, corresponding to the integer assigned the - corresponding member in the MS API enum of the same name. - """ - - xml_value: str | None - - def __new__(cls, ms_api_value: int, xml_value: str | None, docstr: str): - self = int.__new__(cls, ms_api_value) - self._value_ = ms_api_value - self.xml_value = xml_value - self.__doc__ = docstr.strip() - return self - - def __str__(self): - """The symbolic name and string value of this member, e.g. 'MIDDLE (3)'.""" - return f"{self.name} ({self.value})" - - @classmethod - def from_xml(cls, xml_value: str | None) -> Self: - """Enumeration member corresponding to XML attribute value `xml_value`. - - Example:: - - >>> WD_PARAGRAPH_ALIGNMENT.from_xml("center") - WD_PARAGRAPH_ALIGNMENT.CENTER - - """ - member = next((member for member in cls if member.xml_value == xml_value), None) - if member is None: - raise ValueError(f"{cls.__name__} has no XML mapping for '{xml_value}'") - return member - - @classmethod - def to_xml(cls: Type[_T], value: int | _T | None) -> str | None: - """XML value of this enum member, generally an XML attribute value.""" - # -- presence of multi-arg `__new__()` method fools type-checker, but getting a - # -- member by its value using EnumCls(val) works as usual. - member = cls(value) - xml_value = member.xml_value - if not xml_value: - raise ValueError(f"{cls.__name__}.{member.name} has no XML representation") - return xml_value - - -class DocsPageFormatter: - """Generate an .rst doc page for an enumeration. - - Formats a RestructuredText documention page (string) for the enumeration class parts - passed to the constructor. An immutable one-shot service object. - """ - - def __init__(self, clsname: str, clsdict: Dict[str, Any]): - self._clsname = clsname - self._clsdict = clsdict - - @property - def page_str(self): - """The RestructuredText documentation page for the enumeration. - - This is the only API member for the class. - """ - tmpl = ".. _%s:\n\n%s\n\n%s\n\n----\n\n%s" - components = ( - self._ms_name, - self._page_title, - self._intro_text, - self._member_defs, - ) - return tmpl % components - - @property - def _intro_text(self): - """Docstring of the enumeration, formatted for documentation page.""" - try: - cls_docstring = self._clsdict["__doc__"] - except KeyError: - cls_docstring = "" - - if cls_docstring is None: - return "" - - return textwrap.dedent(cls_docstring).strip() - - def _member_def(self, member: BaseEnum | BaseXmlEnum): - """Return an individual member definition formatted as an RST glossary entry, - wrapped to fit within 78 columns.""" - assert member.__doc__ is not None - member_docstring = textwrap.dedent(member.__doc__).strip() - member_docstring = textwrap.fill( - member_docstring, - width=78, - initial_indent=" " * 4, - subsequent_indent=" " * 4, - ) - return "%s\n%s\n" % (member.name, member_docstring) - - @property - def _member_defs(self): - """A single string containing the aggregated member definitions section of the - documentation page.""" - members = self._clsdict["__members__"] - member_defs = [self._member_def(member) for member in members if member.name is not None] - return "\n".join(member_defs) - - @property - def _ms_name(self): - """The Microsoft API name for this enumeration.""" - return self._clsdict["__ms_name__"] - - @property - def _page_title(self): - """The title for the documentation page, formatted as code (surrounded in - double-backtics) and underlined with '=' characters.""" - title_underscore = "=" * (len(self._clsname) + 4) - return "``%s``\n%s" % (self._clsname, title_underscore) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/enum/dml.py b/.venv-docs/lib/python3.12/site-packages/docx/enum/dml.py deleted file mode 100644 index 27c63a28..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/enum/dml.py +++ /dev/null @@ -1,103 +0,0 @@ -"""Enumerations used by DrawingML objects.""" - -from .base import BaseEnum, BaseXmlEnum - - -class MSO_COLOR_TYPE(BaseEnum): - """Specifies the color specification scheme. - - Example:: - - from docx.enum.dml import MSO_COLOR_TYPE - - assert font.color.type == MSO_COLOR_TYPE.SCHEME - - MS API name: `MsoColorType` - - http://msdn.microsoft.com/en-us/library/office/ff864912(v=office.15).aspx - """ - - RGB = (1, "Color is specified by an |RGBColor| value.") - """Color is specified by an |RGBColor| value.""" - - THEME = (2, "Color is one of the preset theme colors.") - """Color is one of the preset theme colors.""" - - AUTO = (101, "Color is determined automatically by the application.") - """Color is determined automatically by the application.""" - - -class MSO_THEME_COLOR_INDEX(BaseXmlEnum): - """Indicates the Office theme color, one of those shown in the color gallery on the - formatting ribbon. - - Alias: ``MSO_THEME_COLOR`` - - Example:: - - from docx.enum.dml import MSO_THEME_COLOR - - font.color.theme_color = MSO_THEME_COLOR.ACCENT_1 - - MS API name: `MsoThemeColorIndex` - - http://msdn.microsoft.com/en-us/library/office/ff860782(v=office.15).aspx - """ - - NOT_THEME_COLOR = (0, "UNMAPPED", "Indicates the color is not a theme color.") - """Indicates the color is not a theme color.""" - - ACCENT_1 = (5, "accent1", "Specifies the Accent 1 theme color.") - """Specifies the Accent 1 theme color.""" - - ACCENT_2 = (6, "accent2", "Specifies the Accent 2 theme color.") - """Specifies the Accent 2 theme color.""" - - ACCENT_3 = (7, "accent3", "Specifies the Accent 3 theme color.") - """Specifies the Accent 3 theme color.""" - - ACCENT_4 = (8, "accent4", "Specifies the Accent 4 theme color.") - """Specifies the Accent 4 theme color.""" - - ACCENT_5 = (9, "accent5", "Specifies the Accent 5 theme color.") - """Specifies the Accent 5 theme color.""" - - ACCENT_6 = (10, "accent6", "Specifies the Accent 6 theme color.") - """Specifies the Accent 6 theme color.""" - - BACKGROUND_1 = (14, "background1", "Specifies the Background 1 theme color.") - """Specifies the Background 1 theme color.""" - - BACKGROUND_2 = (16, "background2", "Specifies the Background 2 theme color.") - """Specifies the Background 2 theme color.""" - - DARK_1 = (1, "dark1", "Specifies the Dark 1 theme color.") - """Specifies the Dark 1 theme color.""" - - DARK_2 = (3, "dark2", "Specifies the Dark 2 theme color.") - """Specifies the Dark 2 theme color.""" - - FOLLOWED_HYPERLINK = ( - 12, - "followedHyperlink", - "Specifies the theme color for a clicked hyperlink.", - ) - """Specifies the theme color for a clicked hyperlink.""" - - HYPERLINK = (11, "hyperlink", "Specifies the theme color for a hyperlink.") - """Specifies the theme color for a hyperlink.""" - - LIGHT_1 = (2, "light1", "Specifies the Light 1 theme color.") - """Specifies the Light 1 theme color.""" - - LIGHT_2 = (4, "light2", "Specifies the Light 2 theme color.") - """Specifies the Light 2 theme color.""" - - TEXT_1 = (13, "text1", "Specifies the Text 1 theme color.") - """Specifies the Text 1 theme color.""" - - TEXT_2 = (15, "text2", "Specifies the Text 2 theme color.") - """Specifies the Text 2 theme color.""" - - -MSO_THEME_COLOR = MSO_THEME_COLOR_INDEX diff --git a/.venv-docs/lib/python3.12/site-packages/docx/enum/section.py b/.venv-docs/lib/python3.12/site-packages/docx/enum/section.py deleted file mode 100644 index 982e1911..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/enum/section.py +++ /dev/null @@ -1,86 +0,0 @@ -"""Enumerations related to the main document in WordprocessingML files.""" - -from .base import BaseXmlEnum - - -class WD_HEADER_FOOTER_INDEX(BaseXmlEnum): - """Alias: **WD_HEADER_FOOTER** - - Specifies one of the three possible header/footer definitions for a section. - - For internal use only; not part of the python-docx API. - - MS API name: `WdHeaderFooterIndex` - URL: https://docs.microsoft.com/en-us/office/vba/api/word.wdheaderfooterindex - """ - - PRIMARY = (1, "default", "Header for odd pages or all if no even header.") - """Header for odd pages or all if no even header.""" - - FIRST_PAGE = (2, "first", "Header for first page of section.") - """Header for first page of section.""" - - EVEN_PAGE = (3, "even", "Header for even pages of recto/verso section.") - """Header for even pages of recto/verso section.""" - - -WD_HEADER_FOOTER = WD_HEADER_FOOTER_INDEX - - -class WD_ORIENTATION(BaseXmlEnum): - """Alias: **WD_ORIENT** - - Specifies the page layout orientation. - - Example:: - - from docx.enum.section import WD_ORIENT - - section = document.sections[-1] section.orientation = WD_ORIENT.LANDSCAPE - - MS API name: `WdOrientation` - MS API URL: http://msdn.microsoft.com/en-us/library/office/ff837902.aspx - """ - - PORTRAIT = (0, "portrait", "Portrait orientation.") - """Portrait orientation.""" - - LANDSCAPE = (1, "landscape", "Landscape orientation.") - """Landscape orientation.""" - - -WD_ORIENT = WD_ORIENTATION - - -class WD_SECTION_START(BaseXmlEnum): - """Alias: **WD_SECTION** - - Specifies the start type of a section break. - - Example:: - - from docx.enum.section import WD_SECTION - - section = document.sections[0] section.start_type = WD_SECTION.NEW_PAGE - - MS API name: `WdSectionStart` - MS API URL: http://msdn.microsoft.com/en-us/library/office/ff840975.aspx - """ - - CONTINUOUS = (0, "continuous", "Continuous section break.") - """Continuous section break.""" - - NEW_COLUMN = (1, "nextColumn", "New column section break.") - """New column section break.""" - - NEW_PAGE = (2, "nextPage", "New page section break.") - """New page section break.""" - - EVEN_PAGE = (3, "evenPage", "Even pages section break.") - """Even pages section break.""" - - ODD_PAGE = (4, "oddPage", "Section begins on next odd page.") - """Section begins on next odd page.""" - - -WD_SECTION = WD_SECTION_START diff --git a/.venv-docs/lib/python3.12/site-packages/docx/enum/shape.py b/.venv-docs/lib/python3.12/site-packages/docx/enum/shape.py deleted file mode 100644 index ed086c38..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/enum/shape.py +++ /dev/null @@ -1,19 +0,0 @@ -"""Enumerations related to DrawingML shapes in WordprocessingML files.""" - -import enum - - -class WD_INLINE_SHAPE_TYPE(enum.Enum): - """Corresponds to WdInlineShapeType enumeration. - - http://msdn.microsoft.com/en-us/library/office/ff192587.aspx. - """ - - CHART = 12 - LINKED_PICTURE = 4 - PICTURE = 3 - SMART_ART = 15 - NOT_IMPLEMENTED = -6 - - -WD_INLINE_SHAPE = WD_INLINE_SHAPE_TYPE diff --git a/.venv-docs/lib/python3.12/site-packages/docx/enum/style.py b/.venv-docs/lib/python3.12/site-packages/docx/enum/style.py deleted file mode 100644 index d2474611..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/enum/style.py +++ /dev/null @@ -1,452 +0,0 @@ -"""Enumerations related to styles.""" - -from .base import BaseEnum, BaseXmlEnum - - -class WD_BUILTIN_STYLE(BaseEnum): - """Alias: **WD_STYLE** - - Specifies a built-in Microsoft Word style. - - Example:: - - from docx import Document - from docx.enum.style import WD_STYLE - - document = Document() - styles = document.styles - style = styles[WD_STYLE.BODY_TEXT] - - - MS API name: `WdBuiltinStyle` - - http://msdn.microsoft.com/en-us/library/office/ff835210.aspx - """ - - BLOCK_QUOTATION = (-85, "Block Text.") - """Block Text.""" - - BODY_TEXT = (-67, "Body Text.") - """Body Text.""" - - BODY_TEXT_2 = (-81, "Body Text 2.") - """Body Text 2.""" - - BODY_TEXT_3 = (-82, "Body Text 3.") - """Body Text 3.""" - - BODY_TEXT_FIRST_INDENT = (-78, "Body Text First Indent.") - """Body Text First Indent.""" - - BODY_TEXT_FIRST_INDENT_2 = (-79, "Body Text First Indent 2.") - """Body Text First Indent 2.""" - - BODY_TEXT_INDENT = (-68, "Body Text Indent.") - """Body Text Indent.""" - - BODY_TEXT_INDENT_2 = (-83, "Body Text Indent 2.") - """Body Text Indent 2.""" - - BODY_TEXT_INDENT_3 = (-84, "Body Text Indent 3.") - """Body Text Indent 3.""" - - BOOK_TITLE = (-265, "Book Title.") - """Book Title.""" - - CAPTION = (-35, "Caption.") - """Caption.""" - - CLOSING = (-64, "Closing.") - """Closing.""" - - COMMENT_REFERENCE = (-40, "Comment Reference.") - """Comment Reference.""" - - COMMENT_TEXT = (-31, "Comment Text.") - """Comment Text.""" - - DATE = (-77, "Date.") - """Date.""" - - DEFAULT_PARAGRAPH_FONT = (-66, "Default Paragraph Font.") - """Default Paragraph Font.""" - - EMPHASIS = (-89, "Emphasis.") - """Emphasis.""" - - ENDNOTE_REFERENCE = (-43, "Endnote Reference.") - """Endnote Reference.""" - - ENDNOTE_TEXT = (-44, "Endnote Text.") - """Endnote Text.""" - - ENVELOPE_ADDRESS = (-37, "Envelope Address.") - """Envelope Address.""" - - ENVELOPE_RETURN = (-38, "Envelope Return.") - """Envelope Return.""" - - FOOTER = (-33, "Footer.") - """Footer.""" - - FOOTNOTE_REFERENCE = (-39, "Footnote Reference.") - """Footnote Reference.""" - - FOOTNOTE_TEXT = (-30, "Footnote Text.") - """Footnote Text.""" - - HEADER = (-32, "Header.") - """Header.""" - - HEADING_1 = (-2, "Heading 1.") - """Heading 1.""" - - HEADING_2 = (-3, "Heading 2.") - """Heading 2.""" - - HEADING_3 = (-4, "Heading 3.") - """Heading 3.""" - - HEADING_4 = (-5, "Heading 4.") - """Heading 4.""" - - HEADING_5 = (-6, "Heading 5.") - """Heading 5.""" - - HEADING_6 = (-7, "Heading 6.") - """Heading 6.""" - - HEADING_7 = (-8, "Heading 7.") - """Heading 7.""" - - HEADING_8 = (-9, "Heading 8.") - """Heading 8.""" - - HEADING_9 = (-10, "Heading 9.") - """Heading 9.""" - - HTML_ACRONYM = (-96, "HTML Acronym.") - """HTML Acronym.""" - - HTML_ADDRESS = (-97, "HTML Address.") - """HTML Address.""" - - HTML_CITE = (-98, "HTML Cite.") - """HTML Cite.""" - - HTML_CODE = (-99, "HTML Code.") - """HTML Code.""" - - HTML_DFN = (-100, "HTML Definition.") - """HTML Definition.""" - - HTML_KBD = (-101, "HTML Keyboard.") - """HTML Keyboard.""" - - HTML_NORMAL = (-95, "Normal (Web).") - """Normal (Web).""" - - HTML_PRE = (-102, "HTML Preformatted.") - """HTML Preformatted.""" - - HTML_SAMP = (-103, "HTML Sample.") - """HTML Sample.""" - - HTML_TT = (-104, "HTML Typewriter.") - """HTML Typewriter.""" - - HTML_VAR = (-105, "HTML Variable.") - """HTML Variable.""" - - HYPERLINK = (-86, "Hyperlink.") - """Hyperlink.""" - - HYPERLINK_FOLLOWED = (-87, "Followed Hyperlink.") - """Followed Hyperlink.""" - - INDEX_1 = (-11, "Index 1.") - """Index 1.""" - - INDEX_2 = (-12, "Index 2.") - """Index 2.""" - - INDEX_3 = (-13, "Index 3.") - """Index 3.""" - - INDEX_4 = (-14, "Index 4.") - """Index 4.""" - - INDEX_5 = (-15, "Index 5.") - """Index 5.""" - - INDEX_6 = (-16, "Index 6.") - """Index 6.""" - - INDEX_7 = (-17, "Index 7.") - """Index 7.""" - - INDEX_8 = (-18, "Index 8.") - """Index 8.""" - - INDEX_9 = (-19, "Index 9.") - """Index 9.""" - - INDEX_HEADING = (-34, "Index Heading") - """Index Heading""" - - INTENSE_EMPHASIS = (-262, "Intense Emphasis.") - """Intense Emphasis.""" - - INTENSE_QUOTE = (-182, "Intense Quote.") - """Intense Quote.""" - - INTENSE_REFERENCE = (-264, "Intense Reference.") - """Intense Reference.""" - - LINE_NUMBER = (-41, "Line Number.") - """Line Number.""" - - LIST = (-48, "List.") - """List.""" - - LIST_2 = (-51, "List 2.") - """List 2.""" - - LIST_3 = (-52, "List 3.") - """List 3.""" - - LIST_4 = (-53, "List 4.") - """List 4.""" - - LIST_5 = (-54, "List 5.") - """List 5.""" - - LIST_BULLET = (-49, "List Bullet.") - """List Bullet.""" - - LIST_BULLET_2 = (-55, "List Bullet 2.") - """List Bullet 2.""" - - LIST_BULLET_3 = (-56, "List Bullet 3.") - """List Bullet 3.""" - - LIST_BULLET_4 = (-57, "List Bullet 4.") - """List Bullet 4.""" - - LIST_BULLET_5 = (-58, "List Bullet 5.") - """List Bullet 5.""" - - LIST_CONTINUE = (-69, "List Continue.") - """List Continue.""" - - LIST_CONTINUE_2 = (-70, "List Continue 2.") - """List Continue 2.""" - - LIST_CONTINUE_3 = (-71, "List Continue 3.") - """List Continue 3.""" - - LIST_CONTINUE_4 = (-72, "List Continue 4.") - """List Continue 4.""" - - LIST_CONTINUE_5 = (-73, "List Continue 5.") - """List Continue 5.""" - - LIST_NUMBER = (-50, "List Number.") - """List Number.""" - - LIST_NUMBER_2 = (-59, "List Number 2.") - """List Number 2.""" - - LIST_NUMBER_3 = (-60, "List Number 3.") - """List Number 3.""" - - LIST_NUMBER_4 = (-61, "List Number 4.") - """List Number 4.""" - - LIST_NUMBER_5 = (-62, "List Number 5.") - """List Number 5.""" - - LIST_PARAGRAPH = (-180, "List Paragraph.") - """List Paragraph.""" - - MACRO_TEXT = (-46, "Macro Text.") - """Macro Text.""" - - MESSAGE_HEADER = (-74, "Message Header.") - """Message Header.""" - - NAV_PANE = (-90, "Document Map.") - """Document Map.""" - - NORMAL = (-1, "Normal.") - """Normal.""" - - NORMAL_INDENT = (-29, "Normal Indent.") - """Normal Indent.""" - - NORMAL_OBJECT = (-158, "Normal (applied to an object).") - """Normal (applied to an object).""" - - NORMAL_TABLE = (-106, "Normal (applied within a table).") - """Normal (applied within a table).""" - - NOTE_HEADING = (-80, "Note Heading.") - """Note Heading.""" - - PAGE_NUMBER = (-42, "Page Number.") - """Page Number.""" - - PLAIN_TEXT = (-91, "Plain Text.") - """Plain Text.""" - - QUOTE = (-181, "Quote.") - """Quote.""" - - SALUTATION = (-76, "Salutation.") - """Salutation.""" - - SIGNATURE = (-65, "Signature.") - """Signature.""" - - STRONG = (-88, "Strong.") - """Strong.""" - - SUBTITLE = (-75, "Subtitle.") - """Subtitle.""" - - SUBTLE_EMPHASIS = (-261, "Subtle Emphasis.") - """Subtle Emphasis.""" - - SUBTLE_REFERENCE = (-263, "Subtle Reference.") - """Subtle Reference.""" - - TABLE_COLORFUL_GRID = (-172, "Colorful Grid.") - """Colorful Grid.""" - - TABLE_COLORFUL_LIST = (-171, "Colorful List.") - """Colorful List.""" - - TABLE_COLORFUL_SHADING = (-170, "Colorful Shading.") - """Colorful Shading.""" - - TABLE_DARK_LIST = (-169, "Dark List.") - """Dark List.""" - - TABLE_LIGHT_GRID = (-161, "Light Grid.") - """Light Grid.""" - - TABLE_LIGHT_GRID_ACCENT_1 = (-175, "Light Grid Accent 1.") - """Light Grid Accent 1.""" - - TABLE_LIGHT_LIST = (-160, "Light List.") - """Light List.""" - - TABLE_LIGHT_LIST_ACCENT_1 = (-174, "Light List Accent 1.") - """Light List Accent 1.""" - - TABLE_LIGHT_SHADING = (-159, "Light Shading.") - """Light Shading.""" - - TABLE_LIGHT_SHADING_ACCENT_1 = (-173, "Light Shading Accent 1.") - """Light Shading Accent 1.""" - - TABLE_MEDIUM_GRID_1 = (-166, "Medium Grid 1.") - """Medium Grid 1.""" - - TABLE_MEDIUM_GRID_2 = (-167, "Medium Grid 2.") - """Medium Grid 2.""" - - TABLE_MEDIUM_GRID_3 = (-168, "Medium Grid 3.") - """Medium Grid 3.""" - - TABLE_MEDIUM_LIST_1 = (-164, "Medium List 1.") - """Medium List 1.""" - - TABLE_MEDIUM_LIST_1_ACCENT_1 = (-178, "Medium List 1 Accent 1.") - """Medium List 1 Accent 1.""" - - TABLE_MEDIUM_LIST_2 = (-165, "Medium List 2.") - """Medium List 2.""" - - TABLE_MEDIUM_SHADING_1 = (-162, "Medium Shading 1.") - """Medium Shading 1.""" - - TABLE_MEDIUM_SHADING_1_ACCENT_1 = (-176, "Medium Shading 1 Accent 1.") - """Medium Shading 1 Accent 1.""" - - TABLE_MEDIUM_SHADING_2 = (-163, "Medium Shading 2.") - """Medium Shading 2.""" - - TABLE_MEDIUM_SHADING_2_ACCENT_1 = (-177, "Medium Shading 2 Accent 1.") - """Medium Shading 2 Accent 1.""" - - TABLE_OF_AUTHORITIES = (-45, "Table of Authorities.") - """Table of Authorities.""" - - TABLE_OF_FIGURES = (-36, "Table of Figures.") - """Table of Figures.""" - - TITLE = (-63, "Title.") - """Title.""" - - TOAHEADING = (-47, "TOA Heading.") - """TOA Heading.""" - - TOC_1 = (-20, "TOC 1.") - """TOC 1.""" - - TOC_2 = (-21, "TOC 2.") - """TOC 2.""" - - TOC_3 = (-22, "TOC 3.") - """TOC 3.""" - - TOC_4 = (-23, "TOC 4.") - """TOC 4.""" - - TOC_5 = (-24, "TOC 5.") - """TOC 5.""" - - TOC_6 = (-25, "TOC 6.") - """TOC 6.""" - - TOC_7 = (-26, "TOC 7.") - """TOC 7.""" - - TOC_8 = (-27, "TOC 8.") - """TOC 8.""" - - TOC_9 = (-28, "TOC 9.") - """TOC 9.""" - - -WD_STYLE = WD_BUILTIN_STYLE - - -class WD_STYLE_TYPE(BaseXmlEnum): - """Specifies one of the four style types: paragraph, character, list, or table. - - Example:: - - from docx import Document - from docx.enum.style import WD_STYLE_TYPE - - styles = Document().styles - assert styles[0].type == WD_STYLE_TYPE.PARAGRAPH - - MS API name: `WdStyleType` - - http://msdn.microsoft.com/en-us/library/office/ff196870.aspx - """ - - CHARACTER = (2, "character", "Character style.") - """Character style.""" - - LIST = (4, "numbering", "List style.") - """List style.""" - - PARAGRAPH = (1, "paragraph", "Paragraph style.") - """Paragraph style.""" - - TABLE = (3, "table", "Table style.") - """Table style.""" diff --git a/.venv-docs/lib/python3.12/site-packages/docx/enum/table.py b/.venv-docs/lib/python3.12/site-packages/docx/enum/table.py deleted file mode 100644 index eb1eb9dc..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/enum/table.py +++ /dev/null @@ -1,136 +0,0 @@ -"""Enumerations related to tables in WordprocessingML files.""" - -from docx.enum.base import BaseEnum, BaseXmlEnum - - -class WD_CELL_VERTICAL_ALIGNMENT(BaseXmlEnum): - """Alias: **WD_ALIGN_VERTICAL** - - Specifies the vertical alignment of text in one or more cells of a table. - - Example:: - - from docx.enum.table import WD_ALIGN_VERTICAL - - table = document.add_table(3, 3) - table.cell(0, 0).vertical_alignment = WD_ALIGN_VERTICAL.BOTTOM - - MS API name: `WdCellVerticalAlignment` - - https://msdn.microsoft.com/en-us/library/office/ff193345.aspx - """ - - TOP = (0, "top", "Text is aligned to the top border of the cell.") - """Text is aligned to the top border of the cell.""" - - CENTER = (1, "center", "Text is aligned to the center of the cell.") - """Text is aligned to the center of the cell.""" - - BOTTOM = (3, "bottom", "Text is aligned to the bottom border of the cell.") - """Text is aligned to the bottom border of the cell.""" - - BOTH = ( - 101, - "both", - "This is an option in the OpenXml spec, but not in Word itself. It's not" - " clear what Word behavior this setting produces. If you find out please" - " let us know and we'll update this documentation. Otherwise, probably best" - " to avoid this option.", - ) - """This is an option in the OpenXml spec, but not in Word itself. - - It's not clear what Word behavior this setting produces. If you find out please let - us know and we'll update this documentation. Otherwise, probably best to avoid this - option. - """ - - -WD_ALIGN_VERTICAL = WD_CELL_VERTICAL_ALIGNMENT - - -class WD_ROW_HEIGHT_RULE(BaseXmlEnum): - """Alias: **WD_ROW_HEIGHT** - - Specifies the rule for determining the height of a table row - - Example:: - - from docx.enum.table import WD_ROW_HEIGHT_RULE - - table = document.add_table(3, 3) - table.rows[0].height_rule = WD_ROW_HEIGHT_RULE.EXACTLY - - MS API name: `WdRowHeightRule` - - https://msdn.microsoft.com/en-us/library/office/ff193620.aspx - """ - - AUTO = ( - 0, - "auto", - "The row height is adjusted to accommodate the tallest value in the row.", - ) - """The row height is adjusted to accommodate the tallest value in the row.""" - - AT_LEAST = (1, "atLeast", "The row height is at least a minimum specified value.") - """The row height is at least a minimum specified value.""" - - EXACTLY = (2, "exact", "The row height is an exact value.") - """The row height is an exact value.""" - - -WD_ROW_HEIGHT = WD_ROW_HEIGHT_RULE - - -class WD_TABLE_ALIGNMENT(BaseXmlEnum): - """Specifies table justification type. - - Example:: - - from docx.enum.table import WD_TABLE_ALIGNMENT - - table = document.add_table(3, 3) - table.alignment = WD_TABLE_ALIGNMENT.CENTER - - MS API name: `WdRowAlignment` - - http://office.microsoft.com/en-us/word-help/HV080607259.aspx - """ - - LEFT = (0, "left", "Left-aligned") - """Left-aligned""" - - CENTER = (1, "center", "Center-aligned.") - """Center-aligned.""" - - RIGHT = (2, "right", "Right-aligned.") - """Right-aligned.""" - - -class WD_TABLE_DIRECTION(BaseEnum): - """Specifies the direction in which an application orders cells in the specified - table or row. - - Example:: - - from docx.enum.table import WD_TABLE_DIRECTION - - table = document.add_table(3, 3) - table.direction = WD_TABLE_DIRECTION.RTL - - MS API name: `WdTableDirection` - - http://msdn.microsoft.com/en-us/library/ff835141.aspx - """ - - LTR = ( - 0, - "The table or row is arranged with the first column in the leftmost position.", - ) - """The table or row is arranged with the first column in the leftmost position.""" - - RTL = ( - 1, - "The table or row is arranged with the first column in the rightmost position.", - ) - """The table or row is arranged with the first column in the rightmost position.""" diff --git a/.venv-docs/lib/python3.12/site-packages/docx/enum/text.py b/.venv-docs/lib/python3.12/site-packages/docx/enum/text.py deleted file mode 100644 index 99e776fe..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/enum/text.py +++ /dev/null @@ -1,367 +0,0 @@ -"""Enumerations related to text in WordprocessingML files.""" - -from __future__ import annotations - -import enum - -from docx.enum.base import BaseXmlEnum - - -class WD_PARAGRAPH_ALIGNMENT(BaseXmlEnum): - """Alias: **WD_ALIGN_PARAGRAPH** - - Specifies paragraph justification type. - - Example:: - - from docx.enum.text import WD_ALIGN_PARAGRAPH - - paragraph = document.add_paragraph() - paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER - """ - - LEFT = (0, "left", "Left-aligned") - """Left-aligned""" - - CENTER = (1, "center", "Center-aligned.") - """Center-aligned.""" - - RIGHT = (2, "right", "Right-aligned.") - """Right-aligned.""" - - JUSTIFY = (3, "both", "Fully justified.") - """Fully justified.""" - - DISTRIBUTE = ( - 4, - "distribute", - "Paragraph characters are distributed to fill entire width of paragraph.", - ) - """Paragraph characters are distributed to fill entire width of paragraph.""" - - JUSTIFY_MED = ( - 5, - "mediumKashida", - "Justified with a medium character compression ratio.", - ) - """Justified with a medium character compression ratio.""" - - JUSTIFY_HI = ( - 7, - "highKashida", - "Justified with a high character compression ratio.", - ) - """Justified with a high character compression ratio.""" - - JUSTIFY_LOW = (8, "lowKashida", "Justified with a low character compression ratio.") - """Justified with a low character compression ratio.""" - - THAI_JUSTIFY = ( - 9, - "thaiDistribute", - "Justified according to Thai formatting layout.", - ) - """Justified according to Thai formatting layout.""" - - -WD_ALIGN_PARAGRAPH = WD_PARAGRAPH_ALIGNMENT - - -class WD_BREAK_TYPE(enum.Enum): - """Corresponds to WdBreakType enumeration. - - http://msdn.microsoft.com/en-us/library/office/ff195905.aspx. - """ - - COLUMN = 8 - LINE = 6 - LINE_CLEAR_LEFT = 9 - LINE_CLEAR_RIGHT = 10 - LINE_CLEAR_ALL = 11 # -- added for consistency, not in MS version -- - PAGE = 7 - SECTION_CONTINUOUS = 3 - SECTION_EVEN_PAGE = 4 - SECTION_NEXT_PAGE = 2 - SECTION_ODD_PAGE = 5 - TEXT_WRAPPING = 11 - - -WD_BREAK = WD_BREAK_TYPE - - -class WD_COLOR_INDEX(BaseXmlEnum): - """Specifies a standard preset color to apply. - - Used for font highlighting and perhaps other applications. - - * MS API name: `WdColorIndex` - * URL: https://msdn.microsoft.com/EN-US/library/office/ff195343.aspx - """ - - INHERITED = (-1, None, "Color is inherited from the style hierarchy.") - """Color is inherited from the style hierarchy.""" - - AUTO = (0, "default", "Automatic color. Default; usually black.") - """Automatic color. Default; usually black.""" - - BLACK = (1, "black", "Black color.") - """Black color.""" - - BLUE = (2, "blue", "Blue color") - """Blue color""" - - BRIGHT_GREEN = (4, "green", "Bright green color.") - """Bright green color.""" - - DARK_BLUE = (9, "darkBlue", "Dark blue color.") - """Dark blue color.""" - - DARK_RED = (13, "darkRed", "Dark red color.") - """Dark red color.""" - - DARK_YELLOW = (14, "darkYellow", "Dark yellow color.") - """Dark yellow color.""" - - GRAY_25 = (16, "lightGray", "25% shade of gray color.") - """25% shade of gray color.""" - - GRAY_50 = (15, "darkGray", "50% shade of gray color.") - """50% shade of gray color.""" - - GREEN = (11, "darkGreen", "Green color.") - """Green color.""" - - PINK = (5, "magenta", "Pink color.") - """Pink color.""" - - RED = (6, "red", "Red color.") - """Red color.""" - - TEAL = (10, "darkCyan", "Teal color.") - """Teal color.""" - - TURQUOISE = (3, "cyan", "Turquoise color.") - """Turquoise color.""" - - VIOLET = (12, "darkMagenta", "Violet color.") - """Violet color.""" - - WHITE = (8, "white", "White color.") - """White color.""" - - YELLOW = (7, "yellow", "Yellow color.") - """Yellow color.""" - - -WD_COLOR = WD_COLOR_INDEX - - -class WD_LINE_SPACING(BaseXmlEnum): - """Specifies a line spacing format to be applied to a paragraph. - - Example:: - - from docx.enum.text import WD_LINE_SPACING - - paragraph = document.add_paragraph() - paragraph.line_spacing_rule = WD_LINE_SPACING.EXACTLY - - - MS API name: `WdLineSpacing` - - URL: http://msdn.microsoft.com/en-us/library/office/ff844910.aspx - """ - - SINGLE = (0, "UNMAPPED", "Single spaced (default).") - """Single spaced (default).""" - - ONE_POINT_FIVE = (1, "UNMAPPED", "Space-and-a-half line spacing.") - """Space-and-a-half line spacing.""" - - DOUBLE = (2, "UNMAPPED", "Double spaced.") - """Double spaced.""" - - AT_LEAST = ( - 3, - "atLeast", - "Minimum line spacing is specified amount. Amount is specified separately.", - ) - """Minimum line spacing is specified amount. Amount is specified separately.""" - - EXACTLY = ( - 4, - "exact", - "Line spacing is exactly specified amount. Amount is specified separately.", - ) - """Line spacing is exactly specified amount. Amount is specified separately.""" - - MULTIPLE = ( - 5, - "auto", - "Line spacing is specified as multiple of line heights. Changing font size" - " will change line spacing proportionately.", - ) - """Line spacing is specified as multiple of line heights. Changing font size will - change the line spacing proportionately.""" - - -class WD_TAB_ALIGNMENT(BaseXmlEnum): - """Specifies the tab stop alignment to apply. - - MS API name: `WdTabAlignment` - - URL: https://msdn.microsoft.com/EN-US/library/office/ff195609.aspx - """ - - LEFT = (0, "left", "Left-aligned.") - """Left-aligned.""" - - CENTER = (1, "center", "Center-aligned.") - """Center-aligned.""" - - RIGHT = (2, "right", "Right-aligned.") - """Right-aligned.""" - - DECIMAL = (3, "decimal", "Decimal-aligned.") - """Decimal-aligned.""" - - BAR = (4, "bar", "Bar-aligned.") - """Bar-aligned.""" - - LIST = (6, "list", "List-aligned. (deprecated)") - """List-aligned. (deprecated)""" - - CLEAR = (101, "clear", "Clear an inherited tab stop.") - """Clear an inherited tab stop.""" - - END = (102, "end", "Right-aligned. (deprecated)") - """Right-aligned. (deprecated)""" - - NUM = (103, "num", "Left-aligned. (deprecated)") - """Left-aligned. (deprecated)""" - - START = (104, "start", "Left-aligned. (deprecated)") - """Left-aligned. (deprecated)""" - - -class WD_TAB_LEADER(BaseXmlEnum): - """Specifies the character to use as the leader with formatted tabs. - - MS API name: `WdTabLeader` - - URL: https://msdn.microsoft.com/en-us/library/office/ff845050.aspx - """ - - SPACES = (0, "none", "Spaces. Default.") - """Spaces. Default.""" - - DOTS = (1, "dot", "Dots.") - """Dots.""" - - DASHES = (2, "hyphen", "Dashes.") - """Dashes.""" - - LINES = (3, "underscore", "Double lines.") - """Double lines.""" - - HEAVY = (4, "heavy", "A heavy line.") - """A heavy line.""" - - MIDDLE_DOT = (5, "middleDot", "A vertically-centered dot.") - """A vertically-centered dot.""" - - -class WD_UNDERLINE(BaseXmlEnum): - """Specifies the style of underline applied to a run of characters. - - MS API name: `WdUnderline` - - URL: http://msdn.microsoft.com/en-us/library/office/ff822388.aspx - """ - - INHERITED = (-1, None, "Inherit underline setting from containing paragraph.") - """Inherit underline setting from containing paragraph.""" - - NONE = ( - 0, - "none", - "No underline.\n\nThis setting overrides any inherited underline value, so can" - " be used to remove underline from a run that inherits underlining from its" - " containing paragraph. Note this is not the same as assigning |None| to" - " Run.underline. |None| is a valid assignment value, but causes the run to" - " inherit its underline value. Assigning `WD_UNDERLINE.NONE` causes" - " underlining to be unconditionally turned off.", - ) - """No underline. - - This setting overrides any inherited underline value, so can be used to remove - underline from a run that inherits underlining from its containing paragraph. Note - this is not the same as assigning |None| to Run.underline. |None| is a valid - assignment value, but causes the run to inherit its underline value. Assigning - ``WD_UNDERLINE.NONE`` causes underlining to be unconditionally turned off. - """ - - SINGLE = ( - 1, - "single", - "A single line.\n\nNote that this setting is write-only in the sense that" - " |True| (rather than `WD_UNDERLINE.SINGLE`) is returned for a run having" - " this setting.", - ) - """A single line. - - Note that this setting is write-only in the sense that |True| - (rather than ``WD_UNDERLINE.SINGLE``) is returned for a run having this setting. - """ - - WORDS = (2, "words", "Underline individual words only.") - """Underline individual words only.""" - - DOUBLE = (3, "double", "A double line.") - """A double line.""" - - DOTTED = (4, "dotted", "Dots.") - """Dots.""" - - THICK = (6, "thick", "A single thick line.") - """A single thick line.""" - - DASH = (7, "dash", "Dashes.") - """Dashes.""" - - DOT_DASH = (9, "dotDash", "Alternating dots and dashes.") - """Alternating dots and dashes.""" - - DOT_DOT_DASH = (10, "dotDotDash", "An alternating dot-dot-dash pattern.") - """An alternating dot-dot-dash pattern.""" - - WAVY = (11, "wave", "A single wavy line.") - """A single wavy line.""" - - DOTTED_HEAVY = (20, "dottedHeavy", "Heavy dots.") - """Heavy dots.""" - - DASH_HEAVY = (23, "dashedHeavy", "Heavy dashes.") - """Heavy dashes.""" - - DOT_DASH_HEAVY = (25, "dashDotHeavy", "Alternating heavy dots and heavy dashes.") - """Alternating heavy dots and heavy dashes.""" - - DOT_DOT_DASH_HEAVY = ( - 26, - "dashDotDotHeavy", - "An alternating heavy dot-dot-dash pattern.", - ) - """An alternating heavy dot-dot-dash pattern.""" - - WAVY_HEAVY = (27, "wavyHeavy", "A heavy wavy line.") - """A heavy wavy line.""" - - DASH_LONG = (39, "dashLong", "Long dashes.") - """Long dashes.""" - - WAVY_DOUBLE = (43, "wavyDouble", "A double wavy line.") - """A double wavy line.""" - - DASH_LONG_HEAVY = (55, "dashLongHeavy", "Long heavy dashes.") - """Long heavy dashes.""" diff --git a/.venv-docs/lib/python3.12/site-packages/docx/exceptions.py b/.venv-docs/lib/python3.12/site-packages/docx/exceptions.py deleted file mode 100644 index e26f4c3b..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/exceptions.py +++ /dev/null @@ -1,18 +0,0 @@ -"""Exceptions used with python-docx. - -The base exception class is PythonDocxError. -""" - - -class PythonDocxError(Exception): - """Generic error class.""" - - -class InvalidSpanError(PythonDocxError): - """Raised when an invalid merge region is specified in a request to merge table - cells.""" - - -class InvalidXmlError(PythonDocxError): - """Raised when invalid XML is encountered, such as on attempt to access a missing - required child element.""" diff --git a/.venv-docs/lib/python3.12/site-packages/docx/image/__init__.py b/.venv-docs/lib/python3.12/site-packages/docx/image/__init__.py deleted file mode 100644 index 9d5e4b05..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/image/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -"""Provides objects that can characterize image streams. - -That characterization is as to content type and size, as a required step in including -them in a document. -""" - -from docx.image.bmp import Bmp -from docx.image.gif import Gif -from docx.image.jpeg import Exif, Jfif -from docx.image.png import Png -from docx.image.tiff import Tiff - -SIGNATURES = ( - # class, offset, signature_bytes - (Png, 0, b"\x89PNG\x0d\x0a\x1a\x0a"), - (Jfif, 6, b"JFIF"), - (Exif, 6, b"Exif"), - (Gif, 0, b"GIF87a"), - (Gif, 0, b"GIF89a"), - (Tiff, 0, b"MM\x00*"), # big-endian (Motorola) TIFF - (Tiff, 0, b"II*\x00"), # little-endian (Intel) TIFF - (Bmp, 0, b"BM"), -) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/__init__.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 5f78464a..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/bmp.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/bmp.cpython-312.pyc deleted file mode 100644 index 5b23fa82..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/bmp.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/constants.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/constants.cpython-312.pyc deleted file mode 100644 index 203061ca..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/constants.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/exceptions.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/exceptions.cpython-312.pyc deleted file mode 100644 index 5c012d7e..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/exceptions.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/gif.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/gif.cpython-312.pyc deleted file mode 100644 index fb320462..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/gif.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/helpers.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/helpers.cpython-312.pyc deleted file mode 100644 index fd6b3979..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/helpers.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/image.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/image.cpython-312.pyc deleted file mode 100644 index 67ec2479..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/image.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/jpeg.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/jpeg.cpython-312.pyc deleted file mode 100644 index 5853bd34..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/jpeg.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/png.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/png.cpython-312.pyc deleted file mode 100644 index a2110311..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/png.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/tiff.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/tiff.cpython-312.pyc deleted file mode 100644 index 27f319b7..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/image/__pycache__/tiff.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/image/bmp.py b/.venv-docs/lib/python3.12/site-packages/docx/image/bmp.py deleted file mode 100644 index 115b01d5..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/image/bmp.py +++ /dev/null @@ -1,43 +0,0 @@ -from .constants import MIME_TYPE -from .helpers import LITTLE_ENDIAN, StreamReader -from .image import BaseImageHeader - - -class Bmp(BaseImageHeader): - """Image header parser for BMP images.""" - - @classmethod - def from_stream(cls, stream): - """Return |Bmp| instance having header properties parsed from the BMP image in - `stream`.""" - stream_rdr = StreamReader(stream, LITTLE_ENDIAN) - - px_width = stream_rdr.read_long(0x12) - px_height = stream_rdr.read_long(0x16) - - horz_px_per_meter = stream_rdr.read_long(0x26) - vert_px_per_meter = stream_rdr.read_long(0x2A) - - horz_dpi = cls._dpi(horz_px_per_meter) - vert_dpi = cls._dpi(vert_px_per_meter) - - return cls(px_width, px_height, horz_dpi, vert_dpi) - - @property - def content_type(self): - """MIME content type for this image, unconditionally `image/bmp` for BMP - images.""" - return MIME_TYPE.BMP - - @property - def default_ext(self): - """Default filename extension, always 'bmp' for BMP images.""" - return "bmp" - - @staticmethod - def _dpi(px_per_meter): - """Return the integer pixels per inch from `px_per_meter`, defaulting to 96 if - `px_per_meter` is zero.""" - if px_per_meter == 0: - return 96 - return int(round(px_per_meter * 0.0254)) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/image/constants.py b/.venv-docs/lib/python3.12/site-packages/docx/image/constants.py deleted file mode 100644 index 03fae585..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/image/constants.py +++ /dev/null @@ -1,172 +0,0 @@ -"""Constants specific the the image sub-package.""" - - -class JPEG_MARKER_CODE: - """JPEG marker codes.""" - - TEM = b"\x01" - DHT = b"\xc4" - DAC = b"\xcc" - JPG = b"\xc8" - - SOF0 = b"\xc0" - SOF1 = b"\xc1" - SOF2 = b"\xc2" - SOF3 = b"\xc3" - SOF5 = b"\xc5" - SOF6 = b"\xc6" - SOF7 = b"\xc7" - SOF9 = b"\xc9" - SOFA = b"\xca" - SOFB = b"\xcb" - SOFD = b"\xcd" - SOFE = b"\xce" - SOFF = b"\xcf" - - RST0 = b"\xd0" - RST1 = b"\xd1" - RST2 = b"\xd2" - RST3 = b"\xd3" - RST4 = b"\xd4" - RST5 = b"\xd5" - RST6 = b"\xd6" - RST7 = b"\xd7" - - SOI = b"\xd8" - EOI = b"\xd9" - SOS = b"\xda" - DQT = b"\xdb" # Define Quantization Table(s) - DNL = b"\xdc" - DRI = b"\xdd" - DHP = b"\xde" - EXP = b"\xdf" - - APP0 = b"\xe0" - APP1 = b"\xe1" - APP2 = b"\xe2" - APP3 = b"\xe3" - APP4 = b"\xe4" - APP5 = b"\xe5" - APP6 = b"\xe6" - APP7 = b"\xe7" - APP8 = b"\xe8" - APP9 = b"\xe9" - APPA = b"\xea" - APPB = b"\xeb" - APPC = b"\xec" - APPD = b"\xed" - APPE = b"\xee" - APPF = b"\xef" - - STANDALONE_MARKERS = (TEM, SOI, EOI, RST0, RST1, RST2, RST3, RST4, RST5, RST6, RST7) - - SOF_MARKER_CODES = ( - SOF0, - SOF1, - SOF2, - SOF3, - SOF5, - SOF6, - SOF7, - SOF9, - SOFA, - SOFB, - SOFD, - SOFE, - SOFF, - ) - - marker_names = { - b"\x00": "UNKNOWN", - b"\xc0": "SOF0", - b"\xc2": "SOF2", - b"\xc4": "DHT", - b"\xda": "SOS", # start of scan - b"\xd8": "SOI", # start of image - b"\xd9": "EOI", # end of image - b"\xdb": "DQT", - b"\xe0": "APP0", - b"\xe1": "APP1", - b"\xe2": "APP2", - b"\xed": "APP13", - b"\xee": "APP14", - } - - @classmethod - def is_standalone(cls, marker_code): - return marker_code in cls.STANDALONE_MARKERS - - -class MIME_TYPE: - """Image content types.""" - - BMP = "image/bmp" - GIF = "image/gif" - JPEG = "image/jpeg" - PNG = "image/png" - TIFF = "image/tiff" - - -class PNG_CHUNK_TYPE: - """PNG chunk type names.""" - - IHDR = "IHDR" - pHYs = "pHYs" - IEND = "IEND" - - -class TIFF_FLD_TYPE: - """Tag codes for TIFF Image File Directory (IFD) entries.""" - - BYTE = 1 - ASCII = 2 - SHORT = 3 - LONG = 4 - RATIONAL = 5 - - field_type_names = { - 1: "BYTE", - 2: "ASCII char", - 3: "SHORT", - 4: "LONG", - 5: "RATIONAL", - } - - -TIFF_FLD = TIFF_FLD_TYPE - - -class TIFF_TAG: - """Tag codes for TIFF Image File Directory (IFD) entries.""" - - IMAGE_WIDTH = 0x0100 - IMAGE_LENGTH = 0x0101 - X_RESOLUTION = 0x011A - Y_RESOLUTION = 0x011B - RESOLUTION_UNIT = 0x0128 - - tag_names = { - 0x00FE: "NewSubfileType", - 0x0100: "ImageWidth", - 0x0101: "ImageLength", - 0x0102: "BitsPerSample", - 0x0103: "Compression", - 0x0106: "PhotometricInterpretation", - 0x010E: "ImageDescription", - 0x010F: "Make", - 0x0110: "Model", - 0x0111: "StripOffsets", - 0x0112: "Orientation", - 0x0115: "SamplesPerPixel", - 0x0117: "StripByteCounts", - 0x011A: "XResolution", - 0x011B: "YResolution", - 0x011C: "PlanarConfiguration", - 0x0128: "ResolutionUnit", - 0x0131: "Software", - 0x0132: "DateTime", - 0x0213: "YCbCrPositioning", - 0x8769: "ExifTag", - 0x8825: "GPS IFD", - 0xC4A5: "PrintImageMatching", - } diff --git a/.venv-docs/lib/python3.12/site-packages/docx/image/exceptions.py b/.venv-docs/lib/python3.12/site-packages/docx/image/exceptions.py deleted file mode 100644 index 2b35187d..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/image/exceptions.py +++ /dev/null @@ -1,13 +0,0 @@ -"""Exceptions specific the the image sub-package.""" - - -class InvalidImageStreamError(Exception): - """The recognized image stream appears to be corrupted.""" - - -class UnexpectedEndOfFileError(Exception): - """EOF was unexpectedly encountered while reading an image stream.""" - - -class UnrecognizedImageError(Exception): - """The provided image stream could not be recognized.""" diff --git a/.venv-docs/lib/python3.12/site-packages/docx/image/gif.py b/.venv-docs/lib/python3.12/site-packages/docx/image/gif.py deleted file mode 100644 index e1648726..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/image/gif.py +++ /dev/null @@ -1,38 +0,0 @@ -from struct import Struct - -from .constants import MIME_TYPE -from .image import BaseImageHeader - - -class Gif(BaseImageHeader): - """Image header parser for GIF images. - - Note that the GIF format does not support resolution (DPI) information. Both - horizontal and vertical DPI default to 72. - """ - - @classmethod - def from_stream(cls, stream): - """Return |Gif| instance having header properties parsed from GIF image in - `stream`.""" - px_width, px_height = cls._dimensions_from_stream(stream) - return cls(px_width, px_height, 72, 72) - - @property - def content_type(self): - """MIME content type for this image, unconditionally `image/gif` for GIF - images.""" - return MIME_TYPE.GIF - - @property - def default_ext(self): - """Default filename extension, always 'gif' for GIF images.""" - return "gif" - - @classmethod - def _dimensions_from_stream(cls, stream): - stream.seek(6) - bytes_ = stream.read(4) - struct = Struct("L" - return self._read_int(fmt, base, offset) - - def read_short(self, base, offset=0): - """Return the int value of the two bytes at the file position determined by - `base` and `offset`, similarly to ``read_long()`` above.""" - fmt = b"H" - return self._read_int(fmt, base, offset) - - def read_str(self, char_count, base, offset=0): - """Return a string containing the `char_count` bytes at the file position - determined by self._base_offset + `base` + `offset`.""" - - def str_struct(char_count): - format_ = "%ds" % char_count - return Struct(format_) - - struct = str_struct(char_count) - chars = self._unpack_item(struct, base, offset) - unicode_str = chars.decode("UTF-8") - return unicode_str - - def seek(self, base, offset=0): - location = self._base_offset + base + offset - self._stream.seek(location) - - def tell(self): - """Allow pass-through tell() call.""" - return self._stream.tell() - - def _read_bytes(self, byte_count, base, offset): - self.seek(base, offset) - bytes_ = self._stream.read(byte_count) - if len(bytes_) < byte_count: - raise UnexpectedEndOfFileError - return bytes_ - - def _read_int(self, fmt, base, offset): - struct = Struct(fmt) - return self._unpack_item(struct, base, offset) - - def _unpack_item(self, struct, base, offset): - bytes_ = self._read_bytes(struct.size, base, offset) - return struct.unpack(bytes_)[0] diff --git a/.venv-docs/lib/python3.12/site-packages/docx/image/image.py b/.venv-docs/lib/python3.12/site-packages/docx/image/image.py deleted file mode 100644 index e5e7f8a1..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/image/image.py +++ /dev/null @@ -1,234 +0,0 @@ -"""Provides objects that can characterize image streams. - -That characterization is as to content type and size, as a required step in including -them in a document. -""" - -from __future__ import annotations - -import hashlib -import io -import os -from typing import IO, Tuple - -from docx.image.exceptions import UnrecognizedImageError -from docx.shared import Emu, Inches, Length, lazyproperty - - -class Image: - """Graphical image stream such as JPEG, PNG, or GIF with properties and methods - required by ImagePart.""" - - def __init__(self, blob: bytes, filename: str, image_header: BaseImageHeader): - super(Image, self).__init__() - self._blob = blob - self._filename = filename - self._image_header = image_header - - @classmethod - def from_blob(cls, blob: bytes) -> Image: - """Return a new |Image| subclass instance parsed from the image binary contained - in `blob`.""" - stream = io.BytesIO(blob) - return cls._from_stream(stream, blob) - - @classmethod - def from_file(cls, image_descriptor: str | IO[bytes]): - """Return a new |Image| subclass instance loaded from the image file identified - by `image_descriptor`, a path or file-like object.""" - if isinstance(image_descriptor, str): - path = image_descriptor - with open(path, "rb") as f: - blob = f.read() - stream = io.BytesIO(blob) - filename = os.path.basename(path) - else: - stream = image_descriptor - stream.seek(0) - blob = stream.read() - filename = None - return cls._from_stream(stream, blob, filename) - - @property - def blob(self): - """The bytes of the image 'file'.""" - return self._blob - - @property - def content_type(self) -> str: - """MIME content type for this image, e.g. ``'image/jpeg'`` for a JPEG image.""" - return self._image_header.content_type - - @lazyproperty - def ext(self): - """The file extension for the image. - - If an actual one is available from a load filename it is used. Otherwise a - canonical extension is assigned based on the content type. Does not contain the - leading period, e.g. 'jpg', not '.jpg'. - """ - return os.path.splitext(self._filename)[1][1:] - - @property - def filename(self): - """Original image file name, if loaded from disk, or a generic filename if - loaded from an anonymous stream.""" - return self._filename - - @property - def px_width(self) -> int: - """The horizontal pixel dimension of the image.""" - return self._image_header.px_width - - @property - def px_height(self) -> int: - """The vertical pixel dimension of the image.""" - return self._image_header.px_height - - @property - def horz_dpi(self) -> int: - """Integer dots per inch for the width of this image. - - Defaults to 72 when not present in the file, as is often the case. - """ - return self._image_header.horz_dpi - - @property - def vert_dpi(self) -> int: - """Integer dots per inch for the height of this image. - - Defaults to 72 when not present in the file, as is often the case. - """ - return self._image_header.vert_dpi - - @property - def width(self) -> Inches: - """A |Length| value representing the native width of the image, calculated from - the values of `px_width` and `horz_dpi`.""" - return Inches(self.px_width / self.horz_dpi) - - @property - def height(self) -> Inches: - """A |Length| value representing the native height of the image, calculated from - the values of `px_height` and `vert_dpi`.""" - return Inches(self.px_height / self.vert_dpi) - - def scaled_dimensions( - self, width: int | Length | None = None, height: int | Length | None = None - ) -> Tuple[Length, Length]: - """(cx, cy) pair representing scaled dimensions of this image. - - The native dimensions of the image are scaled by applying the following rules to - the `width` and `height` arguments. - - * If both `width` and `height` are specified, the return value is (`width`, - `height`); no scaling is performed. - * If only one is specified, it is used to compute a scaling factor that is then - applied to the unspecified dimension, preserving the aspect ratio of the image. - * If both `width` and `height` are |None|, the native dimensions are returned. - - The native dimensions are calculated using the dots-per-inch (dpi) value - embedded in the image, defaulting to 72 dpi if no value is specified, as is - often the case. The returned values are both |Length| objects. - """ - if width is None and height is None: - return self.width, self.height - - if width is None: - assert height is not None - scaling_factor = float(height) / float(self.height) - width = round(self.width * scaling_factor) - - if height is None: - scaling_factor = float(width) / float(self.width) - height = round(self.height * scaling_factor) - - return Emu(width), Emu(height) - - @lazyproperty - def sha1(self): - """SHA1 hash digest of the image blob.""" - return hashlib.sha1(self._blob).hexdigest() - - @classmethod - def _from_stream( - cls, - stream: IO[bytes], - blob: bytes, - filename: str | None = None, - ) -> Image: - """Return an instance of the |Image| subclass corresponding to the format of the - image in `stream`.""" - image_header = _ImageHeaderFactory(stream) - if filename is None: - filename = "image.%s" % image_header.default_ext - return cls(blob, filename, image_header) - - -def _ImageHeaderFactory(stream: IO[bytes]): - """A |BaseImageHeader| subclass instance that can parse headers of image in `stream`.""" - from docx.image import SIGNATURES - - def read_32(stream: IO[bytes]): - stream.seek(0) - return stream.read(32) - - header = read_32(stream) - for cls, offset, signature_bytes in SIGNATURES: - end = offset + len(signature_bytes) - found_bytes = header[offset:end] - if found_bytes == signature_bytes: - return cls.from_stream(stream) - raise UnrecognizedImageError - - -class BaseImageHeader: - """Base class for image header subclasses like |Jpeg| and |Tiff|.""" - - def __init__(self, px_width: int, px_height: int, horz_dpi: int, vert_dpi: int): - self._px_width = px_width - self._px_height = px_height - self._horz_dpi = horz_dpi - self._vert_dpi = vert_dpi - - @property - def content_type(self) -> str: - """Abstract property definition, must be implemented by all subclasses.""" - msg = "content_type property must be implemented by all subclasses of BaseImageHeader" - raise NotImplementedError(msg) - - @property - def default_ext(self) -> str: - """Default filename extension for images of this type. - - An abstract property definition, must be implemented by all subclasses. - """ - raise NotImplementedError( - "default_ext property must be implemented by all subclasses of BaseImageHeader" - ) - - @property - def px_width(self): - """The horizontal pixel dimension of the image.""" - return self._px_width - - @property - def px_height(self): - """The vertical pixel dimension of the image.""" - return self._px_height - - @property - def horz_dpi(self): - """Integer dots per inch for the width of this image. - - Defaults to 72 when not present in the file, as is often the case. - """ - return self._horz_dpi - - @property - def vert_dpi(self): - """Integer dots per inch for the height of this image. - - Defaults to 72 when not present in the file, as is often the case. - """ - return self._vert_dpi diff --git a/.venv-docs/lib/python3.12/site-packages/docx/image/jpeg.py b/.venv-docs/lib/python3.12/site-packages/docx/image/jpeg.py deleted file mode 100644 index 74da5187..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/image/jpeg.py +++ /dev/null @@ -1,425 +0,0 @@ -"""Objects related to parsing headers of JPEG image streams. - -Includes both JFIF and Exif sub-formats. -""" - -import io - -from docx.image.constants import JPEG_MARKER_CODE, MIME_TYPE -from docx.image.helpers import BIG_ENDIAN, StreamReader -from docx.image.image import BaseImageHeader -from docx.image.tiff import Tiff - - -class Jpeg(BaseImageHeader): - """Base class for JFIF and EXIF subclasses.""" - - @property - def content_type(self): - """MIME content type for this image, unconditionally `image/jpeg` for JPEG - images.""" - return MIME_TYPE.JPEG - - @property - def default_ext(self): - """Default filename extension, always 'jpg' for JPG images.""" - return "jpg" - - -class Exif(Jpeg): - """Image header parser for Exif image format.""" - - @classmethod - def from_stream(cls, stream): - """Return |Exif| instance having header properties parsed from Exif image in - `stream`.""" - markers = _JfifMarkers.from_stream(stream) - # print('\n%s' % markers) - - px_width = markers.sof.px_width - px_height = markers.sof.px_height - horz_dpi = markers.app1.horz_dpi - vert_dpi = markers.app1.vert_dpi - - return cls(px_width, px_height, horz_dpi, vert_dpi) - - -class Jfif(Jpeg): - """Image header parser for JFIF image format.""" - - @classmethod - def from_stream(cls, stream): - """Return a |Jfif| instance having header properties parsed from image in - `stream`.""" - markers = _JfifMarkers.from_stream(stream) - - px_width = markers.sof.px_width - px_height = markers.sof.px_height - horz_dpi = markers.app0.horz_dpi - vert_dpi = markers.app0.vert_dpi - - return cls(px_width, px_height, horz_dpi, vert_dpi) - - -class _JfifMarkers: - """Sequence of markers in a JPEG file, perhaps truncated at first SOS marker for - performance reasons.""" - - def __init__(self, markers): - super(_JfifMarkers, self).__init__() - self._markers = list(markers) - - def __str__(self): # pragma: no cover - """Returns a tabular listing of the markers in this instance, which can be handy - for debugging and perhaps other uses.""" - header = " offset seglen mc name\n======= ====== == =====" - tmpl = "%7d %6d %02X %s" - rows = [] - for marker in self._markers: - rows.append( - tmpl - % ( - marker.offset, - marker.segment_length, - ord(marker.marker_code), - marker.name, - ) - ) - lines = [header] + rows - return "\n".join(lines) - - @classmethod - def from_stream(cls, stream): - """Return a |_JfifMarkers| instance containing a |_JfifMarker| subclass instance - for each marker in `stream`.""" - marker_parser = _MarkerParser.from_stream(stream) - markers = [] - for marker in marker_parser.iter_markers(): - markers.append(marker) - if marker.marker_code == JPEG_MARKER_CODE.SOS: - break - return cls(markers) - - @property - def app0(self): - """First APP0 marker in image markers.""" - for m in self._markers: - if m.marker_code == JPEG_MARKER_CODE.APP0: - return m - raise KeyError("no APP0 marker in image") - - @property - def app1(self): - """First APP1 marker in image markers.""" - for m in self._markers: - if m.marker_code == JPEG_MARKER_CODE.APP1: - return m - raise KeyError("no APP1 marker in image") - - @property - def sof(self): - """First start of frame (SOFn) marker in this sequence.""" - for m in self._markers: - if m.marker_code in JPEG_MARKER_CODE.SOF_MARKER_CODES: - return m - raise KeyError("no start of frame (SOFn) marker in image") - - -class _MarkerParser: - """Service class that knows how to parse a JFIF stream and iterate over its - markers.""" - - def __init__(self, stream_reader): - super(_MarkerParser, self).__init__() - self._stream = stream_reader - - @classmethod - def from_stream(cls, stream): - """Return a |_MarkerParser| instance to parse JFIF markers from `stream`.""" - stream_reader = StreamReader(stream, BIG_ENDIAN) - return cls(stream_reader) - - def iter_markers(self): - """Generate a (marker_code, segment_offset) 2-tuple for each marker in the JPEG - `stream`, in the order they occur in the stream.""" - marker_finder = _MarkerFinder.from_stream(self._stream) - start = 0 - marker_code = None - while marker_code != JPEG_MARKER_CODE.EOI: - marker_code, segment_offset = marker_finder.next(start) - marker = _MarkerFactory(marker_code, self._stream, segment_offset) - yield marker - start = segment_offset + marker.segment_length - - -class _MarkerFinder: - """Service class that knows how to find the next JFIF marker in a stream.""" - - def __init__(self, stream): - super(_MarkerFinder, self).__init__() - self._stream = stream - - @classmethod - def from_stream(cls, stream): - """Return a |_MarkerFinder| instance to find JFIF markers in `stream`.""" - return cls(stream) - - def next(self, start): - """Return a (marker_code, segment_offset) 2-tuple identifying and locating the - first marker in `stream` occuring after offset `start`. - - The returned `segment_offset` points to the position immediately following the - 2-byte marker code, the start of the marker segment, for those markers that have - a segment. - """ - position = start - while True: - # skip over any non-\xFF bytes - position = self._offset_of_next_ff_byte(start=position) - # skip over any \xFF padding bytes - position, byte_ = self._next_non_ff_byte(start=position + 1) - # 'FF 00' sequence is not a marker, start over if found - if byte_ == b"\x00": - continue - # this is a marker, gather return values and break out of scan - marker_code, segment_offset = byte_, position + 1 - break - return marker_code, segment_offset - - def _next_non_ff_byte(self, start): - """Return an offset, byte 2-tuple for the next byte in `stream` that is not - '\xff', starting with the byte at offset `start`. - - If the byte at offset `start` is not '\xff', `start` and the returned `offset` - will be the same. - """ - self._stream.seek(start) - byte_ = self._read_byte() - while byte_ == b"\xff": - byte_ = self._read_byte() - offset_of_non_ff_byte = self._stream.tell() - 1 - return offset_of_non_ff_byte, byte_ - - def _offset_of_next_ff_byte(self, start): - """Return the offset of the next '\xff' byte in `stream` starting with the byte - at offset `start`. - - Returns `start` if the byte at that offset is a hex 255; it does not necessarily - advance in the stream. - """ - self._stream.seek(start) - byte_ = self._read_byte() - while byte_ != b"\xff": - byte_ = self._read_byte() - offset_of_ff_byte = self._stream.tell() - 1 - return offset_of_ff_byte - - def _read_byte(self): - """Return the next byte read from stream. - - Raise Exception if stream is at end of file. - """ - byte_ = self._stream.read(1) - if not byte_: # pragma: no cover - raise Exception("unexpected end of file") - return byte_ - - -def _MarkerFactory(marker_code, stream, offset): - """Return |_Marker| or subclass instance appropriate for marker at `offset` in - `stream` having `marker_code`.""" - if marker_code == JPEG_MARKER_CODE.APP0: - marker_cls = _App0Marker - elif marker_code == JPEG_MARKER_CODE.APP1: - marker_cls = _App1Marker - elif marker_code in JPEG_MARKER_CODE.SOF_MARKER_CODES: - marker_cls = _SofMarker - else: - marker_cls = _Marker - return marker_cls.from_stream(stream, marker_code, offset) - - -class _Marker: - """Base class for JFIF marker classes. - - Represents a marker and its segment occuring in a JPEG byte stream. - """ - - def __init__(self, marker_code, offset, segment_length): - super(_Marker, self).__init__() - self._marker_code = marker_code - self._offset = offset - self._segment_length = segment_length - - @classmethod - def from_stream(cls, stream, marker_code, offset): - """Return a generic |_Marker| instance for the marker at `offset` in `stream` - having `marker_code`.""" - if JPEG_MARKER_CODE.is_standalone(marker_code): - segment_length = 0 - else: - segment_length = stream.read_short(offset) - return cls(marker_code, offset, segment_length) - - @property - def marker_code(self): - """The single-byte code that identifies the type of this marker, e.g. ``'\xe0'`` - for start of image (SOI).""" - return self._marker_code - - @property - def name(self): # pragma: no cover - return JPEG_MARKER_CODE.marker_names[self._marker_code] - - @property - def offset(self): # pragma: no cover - return self._offset - - @property - def segment_length(self): - """The length in bytes of this marker's segment.""" - return self._segment_length - - -class _App0Marker(_Marker): - """Represents a JFIF APP0 marker segment.""" - - def __init__(self, marker_code, offset, length, density_units, x_density, y_density): - super(_App0Marker, self).__init__(marker_code, offset, length) - self._density_units = density_units - self._x_density = x_density - self._y_density = y_density - - @property - def horz_dpi(self): - """Horizontal dots per inch specified in this marker, defaults to 72 if not - specified.""" - return self._dpi(self._x_density) - - @property - def vert_dpi(self): - """Vertical dots per inch specified in this marker, defaults to 72 if not - specified.""" - return self._dpi(self._y_density) - - def _dpi(self, density): - """Return dots per inch corresponding to `density` value.""" - if self._density_units == 1: - dpi = density - elif self._density_units == 2: - dpi = int(round(density * 2.54)) - else: - dpi = 72 - return dpi - - @classmethod - def from_stream(cls, stream, marker_code, offset): - """Return an |_App0Marker| instance for the APP0 marker at `offset` in - `stream`.""" - # field off type notes - # ------------------ --- ----- ------------------- - # segment length 0 short - # JFIF identifier 2 5 chr 'JFIF\x00' - # major JPEG version 7 byte typically 1 - # minor JPEG version 8 byte typically 1 or 2 - # density units 9 byte 1=inches, 2=cm - # horz dots per unit 10 short - # vert dots per unit 12 short - # ------------------ --- ----- ------------------- - segment_length = stream.read_short(offset) - density_units = stream.read_byte(offset, 9) - x_density = stream.read_short(offset, 10) - y_density = stream.read_short(offset, 12) - return cls(marker_code, offset, segment_length, density_units, x_density, y_density) - - -class _App1Marker(_Marker): - """Represents a JFIF APP1 (Exif) marker segment.""" - - def __init__(self, marker_code, offset, length, horz_dpi, vert_dpi): - super(_App1Marker, self).__init__(marker_code, offset, length) - self._horz_dpi = horz_dpi - self._vert_dpi = vert_dpi - - @classmethod - def from_stream(cls, stream, marker_code, offset): - """Extract the horizontal and vertical dots-per-inch value from the APP1 header - at `offset` in `stream`.""" - # field off len type notes - # -------------------- --- --- ----- ---------------------------- - # segment length 0 2 short - # Exif identifier 2 6 6 chr 'Exif\x00\x00' - # TIFF byte order 8 2 2 chr 'II'=little 'MM'=big endian - # meaning of universe 10 2 2 chr '*\x00' or '\x00*' depending - # IFD0 off fr/II or MM 10 16 long relative to ...? - # -------------------- --- --- ----- ---------------------------- - segment_length = stream.read_short(offset) - if cls._is_non_Exif_APP1_segment(stream, offset): - return cls(marker_code, offset, segment_length, 72, 72) - tiff = cls._tiff_from_exif_segment(stream, offset, segment_length) - return cls(marker_code, offset, segment_length, tiff.horz_dpi, tiff.vert_dpi) - - @property - def horz_dpi(self): - """Horizontal dots per inch specified in this marker, defaults to 72 if not - specified.""" - return self._horz_dpi - - @property - def vert_dpi(self): - """Vertical dots per inch specified in this marker, defaults to 72 if not - specified.""" - return self._vert_dpi - - @classmethod - def _is_non_Exif_APP1_segment(cls, stream, offset): - """Return True if the APP1 segment at `offset` in `stream` is NOT an Exif - segment, as determined by the ``'Exif\x00\x00'`` signature at offset 2 in the - segment.""" - stream.seek(offset + 2) - exif_signature = stream.read(6) - return exif_signature != b"Exif\x00\x00" - - @classmethod - def _tiff_from_exif_segment(cls, stream, offset, segment_length): - """Return a |Tiff| instance parsed from the Exif APP1 segment of - `segment_length` at `offset` in `stream`.""" - # wrap full segment in its own stream and feed to Tiff() - stream.seek(offset + 8) - segment_bytes = stream.read(segment_length - 8) - substream = io.BytesIO(segment_bytes) - return Tiff.from_stream(substream) - - -class _SofMarker(_Marker): - """Represents a JFIF start of frame (SOFx) marker segment.""" - - def __init__(self, marker_code, offset, segment_length, px_width, px_height): - super(_SofMarker, self).__init__(marker_code, offset, segment_length) - self._px_width = px_width - self._px_height = px_height - - @classmethod - def from_stream(cls, stream, marker_code, offset): - """Return an |_SofMarker| instance for the SOFn marker at `offset` in stream.""" - # field off type notes - # ------------------ --- ----- ---------------------------- - # segment length 0 short - # Data precision 2 byte - # Vertical lines 3 short px_height - # Horizontal lines 5 short px_width - # ------------------ --- ----- ---------------------------- - segment_length = stream.read_short(offset) - px_height = stream.read_short(offset, 3) - px_width = stream.read_short(offset, 5) - return cls(marker_code, offset, segment_length, px_width, px_height) - - @property - def px_height(self): - """Image height in pixels.""" - return self._px_height - - @property - def px_width(self): - """Image width in pixels.""" - return self._px_width diff --git a/.venv-docs/lib/python3.12/site-packages/docx/image/png.py b/.venv-docs/lib/python3.12/site-packages/docx/image/png.py deleted file mode 100644 index dd3cf819..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/image/png.py +++ /dev/null @@ -1,253 +0,0 @@ -from .constants import MIME_TYPE, PNG_CHUNK_TYPE -from .exceptions import InvalidImageStreamError -from .helpers import BIG_ENDIAN, StreamReader -from .image import BaseImageHeader - - -class Png(BaseImageHeader): - """Image header parser for PNG images.""" - - @property - def content_type(self): - """MIME content type for this image, unconditionally `image/png` for PNG - images.""" - return MIME_TYPE.PNG - - @property - def default_ext(self): - """Default filename extension, always 'png' for PNG images.""" - return "png" - - @classmethod - def from_stream(cls, stream): - """Return a |Png| instance having header properties parsed from image in - `stream`.""" - parser = _PngParser.parse(stream) - - px_width = parser.px_width - px_height = parser.px_height - horz_dpi = parser.horz_dpi - vert_dpi = parser.vert_dpi - - return cls(px_width, px_height, horz_dpi, vert_dpi) - - -class _PngParser: - """Parses a PNG image stream to extract the image properties found in its chunks.""" - - def __init__(self, chunks): - super(_PngParser, self).__init__() - self._chunks = chunks - - @classmethod - def parse(cls, stream): - """Return a |_PngParser| instance containing the header properties parsed from - the PNG image in `stream`.""" - chunks = _Chunks.from_stream(stream) - return cls(chunks) - - @property - def px_width(self): - """The number of pixels in each row of the image.""" - IHDR = self._chunks.IHDR - return IHDR.px_width - - @property - def px_height(self): - """The number of stacked rows of pixels in the image.""" - IHDR = self._chunks.IHDR - return IHDR.px_height - - @property - def horz_dpi(self): - """Integer dots per inch for the width of this image. - - Defaults to 72 when not present in the file, as is often the case. - """ - pHYs = self._chunks.pHYs - if pHYs is None: - return 72 - return self._dpi(pHYs.units_specifier, pHYs.horz_px_per_unit) - - @property - def vert_dpi(self): - """Integer dots per inch for the height of this image. - - Defaults to 72 when not present in the file, as is often the case. - """ - pHYs = self._chunks.pHYs - if pHYs is None: - return 72 - return self._dpi(pHYs.units_specifier, pHYs.vert_px_per_unit) - - @staticmethod - def _dpi(units_specifier, px_per_unit): - """Return dots per inch value calculated from `units_specifier` and - `px_per_unit`.""" - if units_specifier == 1 and px_per_unit: - return int(round(px_per_unit * 0.0254)) - return 72 - - -class _Chunks: - """Collection of the chunks parsed from a PNG image stream.""" - - def __init__(self, chunk_iterable): - super(_Chunks, self).__init__() - self._chunks = list(chunk_iterable) - - @classmethod - def from_stream(cls, stream): - """Return a |_Chunks| instance containing the PNG chunks in `stream`.""" - chunk_parser = _ChunkParser.from_stream(stream) - chunks = list(chunk_parser.iter_chunks()) - return cls(chunks) - - @property - def IHDR(self): - """IHDR chunk in PNG image.""" - match = lambda chunk: chunk.type_name == PNG_CHUNK_TYPE.IHDR # noqa - IHDR = self._find_first(match) - if IHDR is None: - raise InvalidImageStreamError("no IHDR chunk in PNG image") - return IHDR - - @property - def pHYs(self): - """PHYs chunk in PNG image, or |None| if not present.""" - match = lambda chunk: chunk.type_name == PNG_CHUNK_TYPE.pHYs # noqa - return self._find_first(match) - - def _find_first(self, match): - """Return first chunk in stream order returning True for function `match`.""" - for chunk in self._chunks: - if match(chunk): - return chunk - return None - - -class _ChunkParser: - """Extracts chunks from a PNG image stream.""" - - def __init__(self, stream_rdr): - super(_ChunkParser, self).__init__() - self._stream_rdr = stream_rdr - - @classmethod - def from_stream(cls, stream): - """Return a |_ChunkParser| instance that can extract the chunks from the PNG - image in `stream`.""" - stream_rdr = StreamReader(stream, BIG_ENDIAN) - return cls(stream_rdr) - - def iter_chunks(self): - """Generate a |_Chunk| subclass instance for each chunk in this parser's PNG - stream, in the order encountered in the stream.""" - for chunk_type, offset in self._iter_chunk_offsets(): - chunk = _ChunkFactory(chunk_type, self._stream_rdr, offset) - yield chunk - - def _iter_chunk_offsets(self): - """Generate a (chunk_type, chunk_offset) 2-tuple for each of the chunks in the - PNG image stream. - - Iteration stops after the IEND chunk is returned. - """ - chunk_offset = 8 - while True: - chunk_data_len = self._stream_rdr.read_long(chunk_offset) - chunk_type = self._stream_rdr.read_str(4, chunk_offset, 4) - data_offset = chunk_offset + 8 - yield chunk_type, data_offset - if chunk_type == "IEND": - break - # incr offset for chunk len long, chunk type, chunk data, and CRC - chunk_offset += 4 + 4 + chunk_data_len + 4 - - -def _ChunkFactory(chunk_type, stream_rdr, offset): - """Return a |_Chunk| subclass instance appropriate to `chunk_type` parsed from - `stream_rdr` at `offset`.""" - chunk_cls_map = { - PNG_CHUNK_TYPE.IHDR: _IHDRChunk, - PNG_CHUNK_TYPE.pHYs: _pHYsChunk, - } - chunk_cls = chunk_cls_map.get(chunk_type, _Chunk) - return chunk_cls.from_offset(chunk_type, stream_rdr, offset) - - -class _Chunk: - """Base class for specific chunk types. - - Also serves as the default chunk type. - """ - - def __init__(self, chunk_type): - super(_Chunk, self).__init__() - self._chunk_type = chunk_type - - @classmethod - def from_offset(cls, chunk_type, stream_rdr, offset): - """Return a default _Chunk instance that only knows its chunk type.""" - return cls(chunk_type) - - @property - def type_name(self): - """The chunk type name, e.g. 'IHDR', 'pHYs', etc.""" - return self._chunk_type - - -class _IHDRChunk(_Chunk): - """IHDR chunk, contains the image dimensions.""" - - def __init__(self, chunk_type, px_width, px_height): - super(_IHDRChunk, self).__init__(chunk_type) - self._px_width = px_width - self._px_height = px_height - - @classmethod - def from_offset(cls, chunk_type, stream_rdr, offset): - """Return an _IHDRChunk instance containing the image dimensions extracted from - the IHDR chunk in `stream` at `offset`.""" - px_width = stream_rdr.read_long(offset) - px_height = stream_rdr.read_long(offset, 4) - return cls(chunk_type, px_width, px_height) - - @property - def px_width(self): - return self._px_width - - @property - def px_height(self): - return self._px_height - - -class _pHYsChunk(_Chunk): - """PYHs chunk, contains the image dpi information.""" - - def __init__(self, chunk_type, horz_px_per_unit, vert_px_per_unit, units_specifier): - super(_pHYsChunk, self).__init__(chunk_type) - self._horz_px_per_unit = horz_px_per_unit - self._vert_px_per_unit = vert_px_per_unit - self._units_specifier = units_specifier - - @classmethod - def from_offset(cls, chunk_type, stream_rdr, offset): - """Return a _pHYsChunk instance containing the image resolution extracted from - the pHYs chunk in `stream` at `offset`.""" - horz_px_per_unit = stream_rdr.read_long(offset) - vert_px_per_unit = stream_rdr.read_long(offset, 4) - units_specifier = stream_rdr.read_byte(offset, 8) - return cls(chunk_type, horz_px_per_unit, vert_px_per_unit, units_specifier) - - @property - def horz_px_per_unit(self): - return self._horz_px_per_unit - - @property - def vert_px_per_unit(self): - return self._vert_px_per_unit - - @property - def units_specifier(self): - return self._units_specifier diff --git a/.venv-docs/lib/python3.12/site-packages/docx/image/tiff.py b/.venv-docs/lib/python3.12/site-packages/docx/image/tiff.py deleted file mode 100644 index 1194929a..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/image/tiff.py +++ /dev/null @@ -1,289 +0,0 @@ -from .constants import MIME_TYPE, TIFF_FLD, TIFF_TAG -from .helpers import BIG_ENDIAN, LITTLE_ENDIAN, StreamReader -from .image import BaseImageHeader - - -class Tiff(BaseImageHeader): - """Image header parser for TIFF images. - - Handles both big and little endian byte ordering. - """ - - @property - def content_type(self): - """Return the MIME type of this TIFF image, unconditionally the string - ``image/tiff``.""" - return MIME_TYPE.TIFF - - @property - def default_ext(self): - """Default filename extension, always 'tiff' for TIFF images.""" - return "tiff" - - @classmethod - def from_stream(cls, stream): - """Return a |Tiff| instance containing the properties of the TIFF image in - `stream`.""" - parser = _TiffParser.parse(stream) - - px_width = parser.px_width - px_height = parser.px_height - horz_dpi = parser.horz_dpi - vert_dpi = parser.vert_dpi - - return cls(px_width, px_height, horz_dpi, vert_dpi) - - -class _TiffParser: - """Parses a TIFF image stream to extract the image properties found in its main - image file directory (IFD)""" - - def __init__(self, ifd_entries): - super(_TiffParser, self).__init__() - self._ifd_entries = ifd_entries - - @classmethod - def parse(cls, stream): - """Return an instance of |_TiffParser| containing the properties parsed from the - TIFF image in `stream`.""" - stream_rdr = cls._make_stream_reader(stream) - ifd0_offset = stream_rdr.read_long(4) - ifd_entries = _IfdEntries.from_stream(stream_rdr, ifd0_offset) - return cls(ifd_entries) - - @property - def horz_dpi(self): - """The horizontal dots per inch value calculated from the XResolution and - ResolutionUnit tags of the IFD; defaults to 72 if those tags are not present.""" - return self._dpi(TIFF_TAG.X_RESOLUTION) - - @property - def vert_dpi(self): - """The vertical dots per inch value calculated from the XResolution and - ResolutionUnit tags of the IFD; defaults to 72 if those tags are not present.""" - return self._dpi(TIFF_TAG.Y_RESOLUTION) - - @property - def px_height(self): - """The number of stacked rows of pixels in the image, |None| if the IFD contains - no ``ImageLength`` tag, the expected case when the TIFF is embeded in an Exif - image.""" - return self._ifd_entries.get(TIFF_TAG.IMAGE_LENGTH) - - @property - def px_width(self): - """The number of pixels in each row in the image, |None| if the IFD contains no - ``ImageWidth`` tag, the expected case when the TIFF is embeded in an Exif - image.""" - return self._ifd_entries.get(TIFF_TAG.IMAGE_WIDTH) - - @classmethod - def _detect_endian(cls, stream): - """Return either BIG_ENDIAN or LITTLE_ENDIAN depending on the endian indicator - found in the TIFF `stream` header, either 'MM' or 'II'.""" - stream.seek(0) - endian_str = stream.read(2) - return BIG_ENDIAN if endian_str == b"MM" else LITTLE_ENDIAN - - def _dpi(self, resolution_tag): - """Return the dpi value calculated for `resolution_tag`, which can be either - TIFF_TAG.X_RESOLUTION or TIFF_TAG.Y_RESOLUTION. - - The calculation is based on the values of both that tag and the - TIFF_TAG.RESOLUTION_UNIT tag in this parser's |_IfdEntries| instance. - """ - ifd_entries = self._ifd_entries - - if resolution_tag not in ifd_entries: - return 72 - - # resolution unit defaults to inches (2) - resolution_unit = ifd_entries.get(TIFF_TAG.RESOLUTION_UNIT, 2) - - if resolution_unit == 1: # aspect ratio only - return 72 - # resolution_unit == 2 for inches, 3 for centimeters - units_per_inch = 1 if resolution_unit == 2 else 2.54 - dots_per_unit = ifd_entries[resolution_tag] - return int(round(dots_per_unit * units_per_inch)) - - @classmethod - def _make_stream_reader(cls, stream): - """Return a |StreamReader| instance with wrapping `stream` and having "endian- - ness" determined by the 'MM' or 'II' indicator in the TIFF stream header.""" - endian = cls._detect_endian(stream) - return StreamReader(stream, endian) - - -class _IfdEntries: - """Image File Directory for a TIFF image, having mapping (dict) semantics allowing - "tag" values to be retrieved by tag code.""" - - def __init__(self, entries): - super(_IfdEntries, self).__init__() - self._entries = entries - - def __contains__(self, key): - """Provides ``in`` operator, e.g. ``tag in ifd_entries``""" - return self._entries.__contains__(key) - - def __getitem__(self, key): - """Provides indexed access, e.g. ``tag_value = ifd_entries[tag_code]``""" - return self._entries.__getitem__(key) - - @classmethod - def from_stream(cls, stream, offset): - """Return a new |_IfdEntries| instance parsed from `stream` starting at - `offset`.""" - ifd_parser = _IfdParser(stream, offset) - entries = {e.tag: e.value for e in ifd_parser.iter_entries()} - return cls(entries) - - def get(self, tag_code, default=None): - """Return value of IFD entry having tag matching `tag_code`, or `default` if no - matching tag found.""" - return self._entries.get(tag_code, default) - - -class _IfdParser: - """Service object that knows how to extract directory entries from an Image File - Directory (IFD)""" - - def __init__(self, stream_rdr, offset): - super(_IfdParser, self).__init__() - self._stream_rdr = stream_rdr - self._offset = offset - - def iter_entries(self): - """Generate an |_IfdEntry| instance corresponding to each entry in the - directory.""" - for idx in range(self._entry_count): - dir_entry_offset = self._offset + 2 + (idx * 12) - ifd_entry = _IfdEntryFactory(self._stream_rdr, dir_entry_offset) - yield ifd_entry - - @property - def _entry_count(self): - """The count of directory entries, read from the top of the IFD header.""" - return self._stream_rdr.read_short(self._offset) - - -def _IfdEntryFactory(stream_rdr, offset): - """Return an |_IfdEntry| subclass instance containing the value of the directory - entry at `offset` in `stream_rdr`.""" - ifd_entry_classes = { - TIFF_FLD.ASCII: _AsciiIfdEntry, - TIFF_FLD.SHORT: _ShortIfdEntry, - TIFF_FLD.LONG: _LongIfdEntry, - TIFF_FLD.RATIONAL: _RationalIfdEntry, - } - field_type = stream_rdr.read_short(offset, 2) - EntryCls = ifd_entry_classes.get(field_type, _IfdEntry) - return EntryCls.from_stream(stream_rdr, offset) - - -class _IfdEntry: - """Base class for IFD entry classes. - - Subclasses are differentiated by value type, e.g. ASCII, long int, etc. - """ - - def __init__(self, tag_code, value): - super(_IfdEntry, self).__init__() - self._tag_code = tag_code - self._value = value - - @classmethod - def from_stream(cls, stream_rdr, offset): - """Return an |_IfdEntry| subclass instance containing the tag and value of the - tag parsed from `stream_rdr` at `offset`. - - Note this method is common to all subclasses. Override the ``_parse_value()`` - method to provide distinctive behavior based on field type. - """ - tag_code = stream_rdr.read_short(offset, 0) - value_count = stream_rdr.read_long(offset, 4) - value_offset = stream_rdr.read_long(offset, 8) - value = cls._parse_value(stream_rdr, offset, value_count, value_offset) - return cls(tag_code, value) - - @classmethod - def _parse_value(cls, stream_rdr, offset, value_count, value_offset): - """Return the value of this field parsed from `stream_rdr` at `offset`. - - Intended to be overridden by subclasses. - """ - return "UNIMPLEMENTED FIELD TYPE" # pragma: no cover - - @property - def tag(self): - """Short int code that identifies this IFD entry.""" - return self._tag_code - - @property - def value(self): - """Value of this tag, its type being dependent on the tag.""" - return self._value - - -class _AsciiIfdEntry(_IfdEntry): - """IFD entry having the form of a NULL-terminated ASCII string.""" - - @classmethod - def _parse_value(cls, stream_rdr, offset, value_count, value_offset): - """Return the ASCII string parsed from `stream_rdr` at `value_offset`. - - The length of the string, including a terminating '\x00' (NUL) character, is in - `value_count`. - """ - return stream_rdr.read_str(value_count - 1, value_offset) - - -class _ShortIfdEntry(_IfdEntry): - """IFD entry expressed as a short (2-byte) integer.""" - - @classmethod - def _parse_value(cls, stream_rdr, offset, value_count, value_offset): - """Return the short int value contained in the `value_offset` field of this - entry. - - Only supports single values at present. - """ - if value_count == 1: - return stream_rdr.read_short(offset, 8) - else: # pragma: no cover - return "Multi-value short integer NOT IMPLEMENTED" - - -class _LongIfdEntry(_IfdEntry): - """IFD entry expressed as a long (4-byte) integer.""" - - @classmethod - def _parse_value(cls, stream_rdr, offset, value_count, value_offset): - """Return the long int value contained in the `value_offset` field of this - entry. - - Only supports single values at present. - """ - if value_count == 1: - return stream_rdr.read_long(offset, 8) - else: # pragma: no cover - return "Multi-value long integer NOT IMPLEMENTED" - - -class _RationalIfdEntry(_IfdEntry): - """IFD entry expressed as a numerator, denominator pair.""" - - @classmethod - def _parse_value(cls, stream_rdr, offset, value_count, value_offset): - """Return the rational (numerator / denominator) value at `value_offset` in - `stream_rdr` as a floating-point number. - - Only supports single values at present. - """ - if value_count == 1: - numerator = stream_rdr.read_long(value_offset) - denominator = stream_rdr.read_long(value_offset, 4) - return numerator / denominator - else: # pragma: no cover - return "Multi-value Rational NOT IMPLEMENTED" diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/__init__.py b/.venv-docs/lib/python3.12/site-packages/docx/opc/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/__init__.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 1a6d358e..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/constants.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/constants.cpython-312.pyc deleted file mode 100644 index ad404022..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/constants.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/coreprops.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/coreprops.cpython-312.pyc deleted file mode 100644 index b7d49ce2..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/coreprops.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/exceptions.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/exceptions.cpython-312.pyc deleted file mode 100644 index ee735fcc..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/exceptions.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/oxml.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/oxml.cpython-312.pyc deleted file mode 100644 index a99eb09a..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/oxml.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/package.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/package.cpython-312.pyc deleted file mode 100644 index b137d507..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/package.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/packuri.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/packuri.cpython-312.pyc deleted file mode 100644 index f9be4b00..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/packuri.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/part.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/part.cpython-312.pyc deleted file mode 100644 index c5756e6a..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/part.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/phys_pkg.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/phys_pkg.cpython-312.pyc deleted file mode 100644 index a623abf0..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/phys_pkg.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/pkgreader.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/pkgreader.cpython-312.pyc deleted file mode 100644 index aed9a9b3..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/pkgreader.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/pkgwriter.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/pkgwriter.cpython-312.pyc deleted file mode 100644 index 5d0edb4e..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/pkgwriter.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/rel.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/rel.cpython-312.pyc deleted file mode 100644 index 957d1d7d..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/rel.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/shared.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/shared.cpython-312.pyc deleted file mode 100644 index 64718b56..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/shared.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/spec.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/spec.cpython-312.pyc deleted file mode 100644 index fb5274bd..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/opc/__pycache__/spec.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/constants.py b/.venv-docs/lib/python3.12/site-packages/docx/opc/constants.py deleted file mode 100644 index a3d0e081..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/opc/constants.py +++ /dev/null @@ -1,306 +0,0 @@ -"""Constant values related to the Open Packaging Convention. - -In particular it includes content types and relationship types. -""" - - -class CONTENT_TYPE: - """Content type URIs (like MIME-types) that specify a part's format.""" - - BMP = "image/bmp" - DML_CHART = "application/vnd.openxmlformats-officedocument.drawingml.chart+xml" - DML_CHARTSHAPES = "application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml" - DML_DIAGRAM_COLORS = "application/vnd.openxmlformats-officedocument.drawingml.diagramColors+xml" - DML_DIAGRAM_DATA = "application/vnd.openxmlformats-officedocument.drawingml.diagramData+xml" - DML_DIAGRAM_LAYOUT = "application/vnd.openxmlformats-officedocument.drawingml.diagramLayout+xml" - DML_DIAGRAM_STYLE = "application/vnd.openxmlformats-officedocument.drawingml.diagramStyle+xml" - GIF = "image/gif" - JPEG = "image/jpeg" - MS_PHOTO = "image/vnd.ms-photo" - OFC_CUSTOM_PROPERTIES = "application/vnd.openxmlformats-officedocument.custom-properties+xml" - OFC_CUSTOM_XML_PROPERTIES = ( - "application/vnd.openxmlformats-officedocument.customXmlProperties+xml" - ) - OFC_DRAWING = "application/vnd.openxmlformats-officedocument.drawing+xml" - OFC_EXTENDED_PROPERTIES = ( - "application/vnd.openxmlformats-officedocument.extended-properties+xml" - ) - OFC_OLE_OBJECT = "application/vnd.openxmlformats-officedocument.oleObject" - OFC_PACKAGE = "application/vnd.openxmlformats-officedocument.package" - OFC_THEME = "application/vnd.openxmlformats-officedocument.theme+xml" - OFC_THEME_OVERRIDE = "application/vnd.openxmlformats-officedocument.themeOverride+xml" - OFC_VML_DRAWING = "application/vnd.openxmlformats-officedocument.vmlDrawing" - OPC_CORE_PROPERTIES = "application/vnd.openxmlformats-package.core-properties+xml" - OPC_DIGITAL_SIGNATURE_CERTIFICATE = ( - "application/vnd.openxmlformats-package.digital-signature-certificate" - ) - OPC_DIGITAL_SIGNATURE_ORIGIN = "application/vnd.openxmlformats-package.digital-signature-origin" - OPC_DIGITAL_SIGNATURE_XMLSIGNATURE = ( - "application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml" - ) - OPC_RELATIONSHIPS = "application/vnd.openxmlformats-package.relationships+xml" - PML_COMMENTS = "application/vnd.openxmlformats-officedocument.presentationml.comments+xml" - PML_COMMENT_AUTHORS = ( - "application/vnd.openxmlformats-officedocument.presentationml.commentAuthors+xml" - ) - PML_HANDOUT_MASTER = ( - "application/vnd.openxmlformats-officedocument.presentationml.handoutMaster+xml" - ) - PML_NOTES_MASTER = ( - "application/vnd.openxmlformats-officedocument.presentationml.notesMaster+xml" - ) - PML_NOTES_SLIDE = "application/vnd.openxmlformats-officedocument.presentationml.notesSlide+xml" - PML_PRESENTATION_MAIN = ( - "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml" - ) - PML_PRES_PROPS = "application/vnd.openxmlformats-officedocument.presentationml.presProps+xml" - PML_PRINTER_SETTINGS = ( - "application/vnd.openxmlformats-officedocument.presentationml.printerSettings" - ) - PML_SLIDE = "application/vnd.openxmlformats-officedocument.presentationml.slide+xml" - PML_SLIDESHOW_MAIN = ( - "application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml" - ) - PML_SLIDE_LAYOUT = ( - "application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml" - ) - PML_SLIDE_MASTER = ( - "application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml" - ) - PML_SLIDE_UPDATE_INFO = ( - "application/vnd.openxmlformats-officedocument.presentationml.slideUpdateInfo+xml" - ) - PML_TABLE_STYLES = ( - "application/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml" - ) - PML_TAGS = "application/vnd.openxmlformats-officedocument.presentationml.tags+xml" - PML_TEMPLATE_MAIN = ( - "application/vnd.openxmlformats-officedocument.presentationml.template.main+xml" - ) - PML_VIEW_PROPS = "application/vnd.openxmlformats-officedocument.presentationml.viewProps+xml" - PNG = "image/png" - SML_CALC_CHAIN = "application/vnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml" - SML_CHARTSHEET = "application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml" - SML_COMMENTS = "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml" - SML_CONNECTIONS = "application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml" - SML_CUSTOM_PROPERTY = ( - "application/vnd.openxmlformats-officedocument.spreadsheetml.customProperty" - ) - SML_DIALOGSHEET = "application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml" - SML_EXTERNAL_LINK = ( - "application/vnd.openxmlformats-officedocument.spreadsheetml.externalLink+xml" - ) - SML_PIVOT_CACHE_DEFINITION = ( - "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml" - ) - SML_PIVOT_CACHE_RECORDS = ( - "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml" - ) - SML_PIVOT_TABLE = "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml" - SML_PRINTER_SETTINGS = ( - "application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings" - ) - SML_QUERY_TABLE = "application/vnd.openxmlformats-officedocument.spreadsheetml.queryTable+xml" - SML_REVISION_HEADERS = ( - "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionHeaders+xml" - ) - SML_REVISION_LOG = "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionLog+xml" - SML_SHARED_STRINGS = ( - "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml" - ) - SML_SHEET = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" - SML_SHEET_MAIN = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" - SML_SHEET_METADATA = ( - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheetMetadata+xml" - ) - SML_STYLES = "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml" - SML_TABLE = "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml" - SML_TABLE_SINGLE_CELLS = ( - "application/vnd.openxmlformats-officedocument.spreadsheetml.tableSingleCells+xml" - ) - SML_TEMPLATE_MAIN = ( - "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml" - ) - SML_USER_NAMES = "application/vnd.openxmlformats-officedocument.spreadsheetml.userNames+xml" - SML_VOLATILE_DEPENDENCIES = ( - "application/vnd.openxmlformats-officedocument.spreadsheetml.volatileDependencies+xml" - ) - SML_WORKSHEET = "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml" - TIFF = "image/tiff" - WML_COMMENTS = "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml" - WML_DOCUMENT = "application/vnd.openxmlformats-officedocument.wordprocessingml.document" - WML_DOCUMENT_GLOSSARY = ( - "application/vnd.openxmlformats-officedocument.wordprocessingml.document.glossary+xml" - ) - WML_DOCUMENT_MAIN = ( - "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml" - ) - WML_ENDNOTES = "application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml" - WML_FONT_TABLE = "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml" - WML_FOOTER = "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml" - WML_FOOTNOTES = "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml" - WML_HEADER = "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml" - WML_NUMBERING = "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml" - WML_PRINTER_SETTINGS = ( - "application/vnd.openxmlformats-officedocument.wordprocessingml.printerSettings" - ) - WML_SETTINGS = "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml" - WML_STYLES = "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml" - WML_WEB_SETTINGS = ( - "application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml" - ) - XML = "application/xml" - X_EMF = "image/x-emf" - X_FONTDATA = "application/x-fontdata" - X_FONT_TTF = "application/x-font-ttf" - X_WMF = "image/x-wmf" - - -class NAMESPACE: - """Constant values for OPC XML namespaces.""" - - DML_WORDPROCESSING_DRAWING = ( - "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" - ) - OFC_RELATIONSHIPS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships" - OPC_RELATIONSHIPS = "http://schemas.openxmlformats.org/package/2006/relationships" - OPC_CONTENT_TYPES = "http://schemas.openxmlformats.org/package/2006/content-types" - WML_MAIN = "http://schemas.openxmlformats.org/wordprocessingml/2006/main" - - -class RELATIONSHIP_TARGET_MODE: - """Open XML relationship target modes.""" - - EXTERNAL = "External" - INTERNAL = "Internal" - - -class RELATIONSHIP_TYPE: - AUDIO = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/audio" - A_F_CHUNK = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/aFChunk" - CALC_CHAIN = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/calcChain" - CERTIFICATE = ( - "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/certificate" - ) - CHART = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart" - CHARTSHEET = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartsheet" - CHART_USER_SHAPES = ( - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartUserShapes" - ) - COMMENTS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments" - COMMENT_AUTHORS = ( - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/commentAuthors" - ) - CONNECTIONS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/connections" - CONTROL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/control" - CORE_PROPERTIES = ( - "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" - ) - CUSTOM_PROPERTIES = ( - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties" - ) - CUSTOM_PROPERTY = ( - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/customProperty" - ) - CUSTOM_XML = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml" - CUSTOM_XML_PROPS = ( - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXmlProps" - ) - DIAGRAM_COLORS = ( - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/diagramColors" - ) - DIAGRAM_DATA = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/diagramData" - DIAGRAM_LAYOUT = ( - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/diagramLayout" - ) - DIAGRAM_QUICK_STYLE = ( - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/diagramQuickStyle" - ) - DIALOGSHEET = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/dialogsheet" - DRAWING = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing" - ENDNOTES = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/endnotes" - EXTENDED_PROPERTIES = ( - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" - ) - EXTERNAL_LINK = ( - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/externalLink" - ) - FONT = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/font" - FONT_TABLE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable" - FOOTER = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer" - FOOTNOTES = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes" - GLOSSARY_DOCUMENT = ( - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/glossaryDocument" - ) - HANDOUT_MASTER = ( - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/handoutMaster" - ) - HEADER = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header" - HYPERLINK = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" - IMAGE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" - NOTES_MASTER = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesMaster" - NOTES_SLIDE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide" - NUMBERING = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering" - OFFICE_DOCUMENT = ( - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" - ) - OLE_OBJECT = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/oleObject" - ORIGIN = "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/origin" - PACKAGE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/package" - PIVOT_CACHE_DEFINITION = ( - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition" - ) - PIVOT_CACHE_RECORDS = ( - "http://schemas.openxmlformats.org/officeDocument/2006/relationships" - "/spreadsheetml/pivotCacheRecords" - ) - PIVOT_TABLE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable" - PRES_PROPS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/presProps" - PRINTER_SETTINGS = ( - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/printerSettings" - ) - QUERY_TABLE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/queryTable" - REVISION_HEADERS = ( - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/revisionHeaders" - ) - REVISION_LOG = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/revisionLog" - SETTINGS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings" - SHARED_STRINGS = ( - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" - ) - SHEET_METADATA = ( - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sheetMetadata" - ) - SIGNATURE = ( - "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/signature" - ) - SLIDE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" - SLIDE_LAYOUT = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout" - SLIDE_MASTER = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster" - SLIDE_UPDATE_INFO = ( - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideUpdateInfo" - ) - STYLES = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" - TABLE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/table" - TABLE_SINGLE_CELLS = ( - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/tableSingleCells" - ) - TABLE_STYLES = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/tableStyles" - TAGS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/tags" - THEME = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" - THEME_OVERRIDE = ( - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/themeOverride" - ) - THUMBNAIL = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail" - USERNAMES = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/usernames" - VIDEO = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/video" - VIEW_PROPS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/viewProps" - VML_DRAWING = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing" - VOLATILE_DEPENDENCIES = ( - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/volatileDependencies" - ) - WEB_SETTINGS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/webSettings" - WORKSHEET_SOURCE = ( - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheetSource" - ) - XML_MAPS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/xmlMaps" diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/coreprops.py b/.venv-docs/lib/python3.12/site-packages/docx/opc/coreprops.py deleted file mode 100644 index 62f0c5ab..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/opc/coreprops.py +++ /dev/null @@ -1,142 +0,0 @@ -"""Provides CoreProperties, Dublin-Core attributes of the document. - -These are broadly-standardized attributes like author, last-modified, etc. -""" - -from __future__ import annotations - -import datetime as dt -from typing import TYPE_CHECKING - -from docx.oxml.coreprops import CT_CoreProperties - -if TYPE_CHECKING: - from docx.oxml.coreprops import CT_CoreProperties - - -class CoreProperties: - """Corresponds to part named ``/docProps/core.xml``, containing the core document - properties for this document package.""" - - def __init__(self, element: CT_CoreProperties): - self._element = element - - @property - def author(self): - return self._element.author_text - - @author.setter - def author(self, value: str): - self._element.author_text = value - - @property - def category(self): - return self._element.category_text - - @category.setter - def category(self, value: str): - self._element.category_text = value - - @property - def comments(self): - return self._element.comments_text - - @comments.setter - def comments(self, value: str): - self._element.comments_text = value - - @property - def content_status(self): - return self._element.contentStatus_text - - @content_status.setter - def content_status(self, value: str): - self._element.contentStatus_text = value - - @property - def created(self): - return self._element.created_datetime - - @created.setter - def created(self, value: dt.datetime): - self._element.created_datetime = value - - @property - def identifier(self): - return self._element.identifier_text - - @identifier.setter - def identifier(self, value: str): - self._element.identifier_text = value - - @property - def keywords(self): - return self._element.keywords_text - - @keywords.setter - def keywords(self, value: str): - self._element.keywords_text = value - - @property - def language(self): - return self._element.language_text - - @language.setter - def language(self, value: str): - self._element.language_text = value - - @property - def last_modified_by(self): - return self._element.lastModifiedBy_text - - @last_modified_by.setter - def last_modified_by(self, value: str): - self._element.lastModifiedBy_text = value - - @property - def last_printed(self): - return self._element.lastPrinted_datetime - - @last_printed.setter - def last_printed(self, value: dt.datetime): - self._element.lastPrinted_datetime = value - - @property - def modified(self): - return self._element.modified_datetime - - @modified.setter - def modified(self, value: dt.datetime): - self._element.modified_datetime = value - - @property - def revision(self): - return self._element.revision_number - - @revision.setter - def revision(self, value: int): - self._element.revision_number = value - - @property - def subject(self): - return self._element.subject_text - - @subject.setter - def subject(self, value: str): - self._element.subject_text = value - - @property - def title(self): - return self._element.title_text - - @title.setter - def title(self, value: str): - self._element.title_text = value - - @property - def version(self): - return self._element.version_text - - @version.setter - def version(self, value: str): - self._element.version_text = value diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/exceptions.py b/.venv-docs/lib/python3.12/site-packages/docx/opc/exceptions.py deleted file mode 100644 index c5583d30..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/opc/exceptions.py +++ /dev/null @@ -1,12 +0,0 @@ -"""Exceptions specific to python-opc. - -The base exception class is OpcError. -""" - - -class OpcError(Exception): - """Base error class for python-opc.""" - - -class PackageNotFoundError(OpcError): - """Raised when a package cannot be found at the specified path.""" diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/oxml.py b/.venv-docs/lib/python3.12/site-packages/docx/opc/oxml.py deleted file mode 100644 index 7d3c489d..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/opc/oxml.py +++ /dev/null @@ -1,247 +0,0 @@ -# pyright: reportPrivateUsage=false - -"""Temporary stand-in for main oxml module. - -This module came across with the PackageReader transplant. Probably much will get -replaced with objects from the pptx.oxml.core and then this module will either get -deleted or only hold the package related custom element classes. -""" - -from __future__ import annotations - -from typing import cast - -from lxml import etree - -from docx.opc.constants import NAMESPACE as NS -from docx.opc.constants import RELATIONSHIP_TARGET_MODE as RTM - -# configure XML parser -element_class_lookup = etree.ElementNamespaceClassLookup() -oxml_parser = etree.XMLParser(remove_blank_text=True, resolve_entities=False) -oxml_parser.set_element_class_lookup(element_class_lookup) - -nsmap = { - "ct": NS.OPC_CONTENT_TYPES, - "pr": NS.OPC_RELATIONSHIPS, - "r": NS.OFC_RELATIONSHIPS, -} - - -# =========================================================================== -# functions -# =========================================================================== - - -def parse_xml(text: str) -> etree._Element: - """`etree.fromstring()` replacement that uses oxml parser.""" - return etree.fromstring(text, oxml_parser) - - -def qn(tag: str) -> str: - """Stands for "qualified name", a utility function to turn a namespace prefixed tag - name into a Clark-notation qualified tag name for lxml. - - For - example, ``qn('p:cSld')`` returns ``'{http://schemas.../main}cSld'``. - """ - prefix, tagroot = tag.split(":") - uri = nsmap[prefix] - return "{%s}%s" % (uri, tagroot) - - -def serialize_part_xml(part_elm: etree._Element) -> bytes: - """Serialize `part_elm` etree element to XML suitable for storage as an XML part. - - That is to say, no insignificant whitespace added for readability, and an - appropriate XML declaration added with UTF-8 encoding specified. - """ - return etree.tostring(part_elm, encoding="UTF-8", standalone=True) - - -def serialize_for_reading(element: etree._Element) -> str: - """Serialize `element` to human-readable XML suitable for tests. - - No XML declaration. - """ - return etree.tostring(element, encoding="unicode", pretty_print=True) - - -# =========================================================================== -# Custom element classes -# =========================================================================== - - -class BaseOxmlElement(etree.ElementBase): - """Base class for all custom element classes, to add standardized behavior to all - classes in one place.""" - - @property - def xml(self) -> str: - """Return XML string for this element, suitable for testing purposes. - - Pretty printed for readability and without an XML declaration at the top. - """ - return serialize_for_reading(self) - - -class CT_Default(BaseOxmlElement): - """`` element that appears in `[Content_Types].xml` part. - - Used to specify a default content type to be applied to any part with the specified extension. - """ - - @property - def content_type(self): - """String held in the ``ContentType`` attribute of this ```` - element.""" - return self.get("ContentType") - - @property - def extension(self): - """String held in the ``Extension`` attribute of this ```` element.""" - return self.get("Extension") - - @staticmethod - def new(ext: str, content_type: str): - """Return a new ```` element with attributes set to parameter values.""" - xml = '' % nsmap["ct"] - default = parse_xml(xml) - default.set("Extension", ext) - default.set("ContentType", content_type) - return default - - -class CT_Override(BaseOxmlElement): - """```` element, specifying the content type to be applied for a part with - the specified partname.""" - - @property - def content_type(self): - """String held in the ``ContentType`` attribute of this ```` - element.""" - return self.get("ContentType") - - @staticmethod - def new(partname, content_type): - """Return a new ```` element with attributes set to parameter values.""" - xml = '' % nsmap["ct"] - override = parse_xml(xml) - override.set("PartName", partname) - override.set("ContentType", content_type) - return override - - @property - def partname(self): - """String held in the ``PartName`` attribute of this ```` element.""" - return self.get("PartName") - - -class CT_Relationship(BaseOxmlElement): - """`` element, representing a single relationship from source to target part.""" - - @staticmethod - def new(rId: str, reltype: str, target: str, target_mode: str = RTM.INTERNAL): - """Return a new ```` element.""" - xml = '' % nsmap["pr"] - relationship = parse_xml(xml) - relationship.set("Id", rId) - relationship.set("Type", reltype) - relationship.set("Target", target) - if target_mode == RTM.EXTERNAL: - relationship.set("TargetMode", RTM.EXTERNAL) - return relationship - - @property - def rId(self): - """String held in the ``Id`` attribute of this ```` element.""" - return self.get("Id") - - @property - def reltype(self): - """String held in the ``Type`` attribute of this ```` element.""" - return self.get("Type") - - @property - def target_ref(self): - """String held in the ``Target`` attribute of this ```` - element.""" - return self.get("Target") - - @property - def target_mode(self): - """String held in the ``TargetMode`` attribute of this ```` - element, either ``Internal`` or ``External``. - - Defaults to ``Internal``. - """ - return self.get("TargetMode", RTM.INTERNAL) - - -class CT_Relationships(BaseOxmlElement): - """```` element, the root element in a .rels file.""" - - def add_rel(self, rId: str, reltype: str, target: str, is_external: bool = False): - """Add a child ```` element with attributes set according to - parameter values.""" - target_mode = RTM.EXTERNAL if is_external else RTM.INTERNAL - relationship = CT_Relationship.new(rId, reltype, target, target_mode) - self.append(relationship) - - @staticmethod - def new() -> CT_Relationships: - """Return a new ```` element.""" - xml = '' % nsmap["pr"] - return cast(CT_Relationships, parse_xml(xml)) - - @property - def Relationship_lst(self): - """Return a list containing all the ```` child elements.""" - return self.findall(qn("pr:Relationship")) - - @property - def xml(self): - """Return XML string for this element, suitable for saving in a .rels stream, - not pretty printed and with an XML declaration at the top.""" - return serialize_part_xml(self) - - -class CT_Types(BaseOxmlElement): - """```` element, the container element for Default and Override elements in - [Content_Types].xml.""" - - def add_default(self, ext, content_type): - """Add a child ```` element with attributes set to parameter values.""" - default = CT_Default.new(ext, content_type) - self.append(default) - - def add_override(self, partname, content_type): - """Add a child ```` element with attributes set to parameter - values.""" - override = CT_Override.new(partname, content_type) - self.append(override) - - @property - def defaults(self): - return self.findall(qn("ct:Default")) - - @staticmethod - def new(): - """Return a new ```` element.""" - xml = '' % nsmap["ct"] - types = parse_xml(xml) - return types - - @property - def overrides(self): - return self.findall(qn("ct:Override")) - - -ct_namespace = element_class_lookup.get_namespace(nsmap["ct"]) -ct_namespace["Default"] = CT_Default -ct_namespace["Override"] = CT_Override -ct_namespace["Types"] = CT_Types - -pr_namespace = element_class_lookup.get_namespace(nsmap["pr"]) -pr_namespace["Relationship"] = CT_Relationship -pr_namespace["Relationships"] = CT_Relationships diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/package.py b/.venv-docs/lib/python3.12/site-packages/docx/opc/package.py deleted file mode 100644 index 3c1cdca2..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/opc/package.py +++ /dev/null @@ -1,219 +0,0 @@ -"""Objects that implement reading and writing OPC packages.""" - -from __future__ import annotations - -from typing import IO, TYPE_CHECKING, Iterator, cast - -from docx.opc.constants import RELATIONSHIP_TYPE as RT -from docx.opc.packuri import PACKAGE_URI, PackURI -from docx.opc.part import PartFactory -from docx.opc.parts.coreprops import CorePropertiesPart -from docx.opc.pkgreader import PackageReader -from docx.opc.pkgwriter import PackageWriter -from docx.opc.rel import Relationships -from docx.shared import lazyproperty - -if TYPE_CHECKING: - from typing_extensions import Self - - from docx.opc.coreprops import CoreProperties - from docx.opc.part import Part - from docx.opc.rel import _Relationship # pyright: ignore[reportPrivateUsage] - - -class OpcPackage: - """Main API class for |python-opc|. - - A new instance is constructed by calling the :meth:`open` class method with a path - to a package file or file-like object containing one. - """ - - def after_unmarshal(self): - """Entry point for any post-unmarshaling processing. - - May be overridden by subclasses without forwarding call to super. - """ - # don't place any code here, just catch call if not overridden by - # subclass - pass - - @property - def core_properties(self) -> CoreProperties: - """|CoreProperties| object providing read/write access to the Dublin Core - properties for this document.""" - return self._core_properties_part.core_properties - - def iter_rels(self) -> Iterator[_Relationship]: - """Generate exactly one reference to each relationship in the package by - performing a depth-first traversal of the rels graph.""" - - def walk_rels( - source: OpcPackage | Part, visited: list[Part] | None = None - ) -> Iterator[_Relationship]: - visited = [] if visited is None else visited - for rel in source.rels.values(): - yield rel - if rel.is_external: - continue - part = rel.target_part - if part in visited: - continue - visited.append(part) - new_source = part - for rel in walk_rels(new_source, visited): - yield rel - - for rel in walk_rels(self): - yield rel - - def iter_parts(self) -> Iterator[Part]: - """Generate exactly one reference to each of the parts in the package by - performing a depth-first traversal of the rels graph.""" - - def walk_parts(source, visited=[]): - for rel in source.rels.values(): - if rel.is_external: - continue - part = rel.target_part - if part in visited: - continue - visited.append(part) - yield part - new_source = part - for part in walk_parts(new_source, visited): - yield part - - for part in walk_parts(self): - yield part - - def load_rel(self, reltype: str, target: Part | str, rId: str, is_external: bool = False): - """Return newly added |_Relationship| instance of `reltype` between this part - and `target` with key `rId`. - - Target mode is set to ``RTM.EXTERNAL`` if `is_external` is |True|. Intended for - use during load from a serialized package, where the rId is well known. Other - methods exist for adding a new relationship to the package during processing. - """ - return self.rels.add_relationship(reltype, target, rId, is_external) - - @property - def main_document_part(self): - """Return a reference to the main document part for this package. - - Examples include a document part for a WordprocessingML package, a presentation - part for a PresentationML package, or a workbook part for a SpreadsheetML - package. - """ - return self.part_related_by(RT.OFFICE_DOCUMENT) - - def next_partname(self, template: str) -> PackURI: - """Return a |PackURI| instance representing partname matching `template`. - - The returned part-name has the next available numeric suffix to distinguish it - from other parts of its type. `template` is a printf (%)-style template string - containing a single replacement item, a '%d' to be used to insert the integer - portion of the partname. Example: "/word/header%d.xml" - """ - partnames = {part.partname for part in self.iter_parts()} - for n in range(1, len(partnames) + 2): - candidate_partname = template % n - if candidate_partname not in partnames: - return PackURI(candidate_partname) - - @classmethod - def open(cls, pkg_file: str | IO[bytes]) -> Self: - """Return an |OpcPackage| instance loaded with the contents of `pkg_file`.""" - pkg_reader = PackageReader.from_file(pkg_file) - package = cls() - Unmarshaller.unmarshal(pkg_reader, package, PartFactory) - return package - - def part_related_by(self, reltype: str) -> Part: - """Return part to which this package has a relationship of `reltype`. - - Raises |KeyError| if no such relationship is found and |ValueError| if more than - one such relationship is found. - """ - return self.rels.part_with_reltype(reltype) - - @property - def parts(self) -> list[Part]: - """Return a list containing a reference to each of the parts in this package.""" - return list(self.iter_parts()) - - def relate_to(self, part: Part, reltype: str): - """Return rId key of new or existing relationship to `part`. - - If a relationship of `reltype` to `part` already exists, its rId is returned. Otherwise a - new relationship is created and that rId is returned. - """ - rel = self.rels.get_or_add(reltype, part) - return rel.rId - - @lazyproperty - def rels(self): - """Return a reference to the |Relationships| instance holding the collection of - relationships for this package.""" - return Relationships(PACKAGE_URI.baseURI) - - def save(self, pkg_file: str | IO[bytes]): - """Save this package to `pkg_file`. - - `pkg_file` can be either a file-path or a file-like object. - """ - for part in self.parts: - part.before_marshal() - PackageWriter.write(pkg_file, self.rels, self.parts) - - @property - def _core_properties_part(self) -> CorePropertiesPart: - """|CorePropertiesPart| object related to this package. - - Creates a default core properties part if one is not present (not common). - """ - try: - return cast(CorePropertiesPart, self.part_related_by(RT.CORE_PROPERTIES)) - except KeyError: - core_properties_part = CorePropertiesPart.default(self) - self.relate_to(core_properties_part, RT.CORE_PROPERTIES) - return core_properties_part - - -class Unmarshaller: - """Hosts static methods for unmarshalling a package from a |PackageReader|.""" - - @staticmethod - def unmarshal(pkg_reader, package, part_factory): - """Construct graph of parts and realized relationships based on the contents of - `pkg_reader`, delegating construction of each part to `part_factory`. - - Package relationships are added to `pkg`. - """ - parts = Unmarshaller._unmarshal_parts(pkg_reader, package, part_factory) - Unmarshaller._unmarshal_relationships(pkg_reader, package, parts) - for part in parts.values(): - part.after_unmarshal() - package.after_unmarshal() - - @staticmethod - def _unmarshal_parts(pkg_reader, package, part_factory): - """Return a dictionary of |Part| instances unmarshalled from `pkg_reader`, keyed - by partname. - - Side-effect is that each part in `pkg_reader` is constructed using - `part_factory`. - """ - parts = {} - for partname, content_type, reltype, blob in pkg_reader.iter_sparts(): - parts[partname] = part_factory(partname, content_type, reltype, blob, package) - return parts - - @staticmethod - def _unmarshal_relationships(pkg_reader, package, parts): - """Add a relationship to the source object corresponding to each of the - relationships in `pkg_reader` with its target_part set to the actual target part - in `parts`.""" - for source_uri, srel in pkg_reader.iter_srels(): - source = package if source_uri == "/" else parts[source_uri] - target = srel.target_ref if srel.is_external else parts[srel.target_partname] - source.load_rel(srel.reltype, target, srel.rId, srel.is_external) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/packuri.py b/.venv-docs/lib/python3.12/site-packages/docx/opc/packuri.py deleted file mode 100644 index 89437b16..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/opc/packuri.py +++ /dev/null @@ -1,109 +0,0 @@ -"""Provides the PackURI value type. - -Also some useful known pack URI strings such as PACKAGE_URI. -""" - -from __future__ import annotations - -import posixpath -import re - - -class PackURI(str): - """Provides access to pack URI components such as the baseURI and the filename slice. - - Behaves as |str| otherwise. - """ - - _filename_re = re.compile("([a-zA-Z]+)([1-9][0-9]*)?") - - def __new__(cls, pack_uri_str: str): - if pack_uri_str[0] != "/": - tmpl = "PackURI must begin with slash, got '%s'" - raise ValueError(tmpl % pack_uri_str) - return str.__new__(cls, pack_uri_str) - - @staticmethod - def from_rel_ref(baseURI: str, relative_ref: str) -> PackURI: - """The absolute PackURI formed by translating `relative_ref` onto `baseURI`.""" - joined_uri = posixpath.join(baseURI, relative_ref) - abs_uri = posixpath.abspath(joined_uri) - return PackURI(abs_uri) - - @property - def baseURI(self) -> str: - """The base URI of this pack URI, the directory portion, roughly speaking. - - E.g. ``'/ppt/slides'`` for ``'/ppt/slides/slide1.xml'``. For the package pseudo- - partname '/', baseURI is '/'. - """ - return posixpath.split(self)[0] - - @property - def ext(self) -> str: - """The extension portion of this pack URI, e.g. ``'xml'`` for ``'/word/document.xml'``. - - Note the period is not included. - """ - # raw_ext is either empty string or starts with period, e.g. '.xml' - raw_ext = posixpath.splitext(self)[1] - return raw_ext[1:] if raw_ext.startswith(".") else raw_ext - - @property - def filename(self): - """The "filename" portion of this pack URI, e.g. ``'slide1.xml'`` for - ``'/ppt/slides/slide1.xml'``. - - For the package pseudo-partname '/', filename is ''. - """ - return posixpath.split(self)[1] - - @property - def idx(self): - """Return partname index as integer for tuple partname or None for singleton - partname, e.g. ``21`` for ``'/ppt/slides/slide21.xml'`` and |None| for - ``'/ppt/presentation.xml'``.""" - filename = self.filename - if not filename: - return None - name_part = posixpath.splitext(filename)[0] # filename w/ext removed - match = self._filename_re.match(name_part) - if match is None: - return None - if match.group(2): - return int(match.group(2)) - return None - - @property - def membername(self): - """The pack URI with the leading slash stripped off, the form used as the Zip - file membername for the package item. - - Returns '' for the package pseudo-partname '/'. - """ - return self[1:] - - def relative_ref(self, baseURI: str): - """Return string containing relative reference to package item from `baseURI`. - - E.g. PackURI('/ppt/slideLayouts/slideLayout1.xml') would return - '../slideLayouts/slideLayout1.xml' for baseURI '/ppt/slides'. - """ - # workaround for posixpath bug in 2.6, doesn't generate correct - # relative path when `start` (second) parameter is root ('/') - return self[1:] if baseURI == "/" else posixpath.relpath(self, baseURI) - - @property - def rels_uri(self): - """The pack URI of the .rels part corresponding to the current pack URI. - - Only produces sensible output if the pack URI is a partname or the package - pseudo-partname '/'. - """ - rels_filename = "%s.rels" % self.filename - rels_uri_str = posixpath.join(self.baseURI, "_rels", rels_filename) - return PackURI(rels_uri_str) - - -PACKAGE_URI = PackURI("/") -CONTENT_TYPES_URI = PackURI("/[Content_Types].xml") diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/part.py b/.venv-docs/lib/python3.12/site-packages/docx/opc/part.py deleted file mode 100644 index cbb4ab55..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/opc/part.py +++ /dev/null @@ -1,247 +0,0 @@ -# pyright: reportImportCycles=false - -"""Open Packaging Convention (OPC) objects related to package parts.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Callable, Type, cast - -from docx.opc.oxml import serialize_part_xml -from docx.opc.packuri import PackURI -from docx.opc.rel import Relationships -from docx.opc.shared import cls_method_fn -from docx.oxml.parser import parse_xml -from docx.shared import lazyproperty - -if TYPE_CHECKING: - from docx.oxml.xmlchemy import BaseOxmlElement - from docx.package import Package - - -class Part: - """Base class for package parts. - - Provides common properties and methods, but intended to be subclassed in client code - to implement specific part behaviors. - """ - - def __init__( - self, - partname: PackURI, - content_type: str, - blob: bytes | None = None, - package: Package | None = None, - ): - super(Part, self).__init__() - self._partname = partname - self._content_type = content_type - self._blob = blob - self._package = package - - def after_unmarshal(self): - """Entry point for post-unmarshaling processing, for example to parse the part - XML. - - May be overridden by subclasses without forwarding call to super. - """ - # don't place any code here, just catch call if not overridden by - # subclass - pass - - def before_marshal(self): - """Entry point for pre-serialization processing, for example to finalize part - naming if necessary. - - May be overridden by subclasses without forwarding call to super. - """ - # don't place any code here, just catch call if not overridden by - # subclass - pass - - @property - def blob(self) -> bytes: - """Contents of this package part as a sequence of bytes. - - May be text or binary. Intended to be overridden by subclasses. Default behavior - is to return load blob. - """ - return self._blob or b"" - - @property - def content_type(self): - """Content type of this part.""" - return self._content_type - - def drop_rel(self, rId: str): - """Remove the relationship identified by `rId` if its reference count is less - than 2. - - Relationships with a reference count of 0 are implicit relationships. - """ - if self._rel_ref_count(rId) < 2: - del self.rels[rId] - - @classmethod - def load(cls, partname: PackURI, content_type: str, blob: bytes, package: Package): - return cls(partname, content_type, blob, package) - - def load_rel(self, reltype: str, target: Part | str, rId: str, is_external: bool = False): - """Return newly added |_Relationship| instance of `reltype`. - - The new relationship relates the `target` part to this part with key `rId`. - - Target mode is set to ``RTM.EXTERNAL`` if `is_external` is |True|. Intended for - use during load from a serialized package, where the rId is well-known. Other - methods exist for adding a new relationship to a part when manipulating a part. - """ - return self.rels.add_relationship(reltype, target, rId, is_external) - - @property - def package(self): - """|OpcPackage| instance this part belongs to.""" - return self._package - - @property - def partname(self): - """|PackURI| instance holding partname of this part, e.g. - '/ppt/slides/slide1.xml'.""" - return self._partname - - @partname.setter - def partname(self, partname: str): - if not isinstance(partname, PackURI): - tmpl = "partname must be instance of PackURI, got '%s'" - raise TypeError(tmpl % type(partname).__name__) - self._partname = partname - - def part_related_by(self, reltype: str) -> Part: - """Return part to which this part has a relationship of `reltype`. - - Raises |KeyError| if no such relationship is found and |ValueError| if more than - one such relationship is found. Provides ability to resolve implicitly related - part, such as Slide -> SlideLayout. - """ - return self.rels.part_with_reltype(reltype) - - def relate_to(self, target: Part | str, reltype: str, is_external: bool = False) -> str: - """Return rId key of relationship of `reltype` to `target`. - - The returned `rId` is from an existing relationship if there is one, otherwise a - new relationship is created. - """ - if is_external: - return self.rels.get_or_add_ext_rel(reltype, cast(str, target)) - else: - rel = self.rels.get_or_add(reltype, cast(Part, target)) - return rel.rId - - @property - def related_parts(self): - """Dictionary mapping related parts by rId, so child objects can resolve - explicit relationships present in the part XML, e.g. sldIdLst to a specific - |Slide| instance.""" - return self.rels.related_parts - - @lazyproperty - def rels(self): - """|Relationships| instance holding the relationships for this part.""" - # -- prevent breakage in `python-docx-template` by retaining legacy `._rels` attribute -- - self._rels = Relationships(self._partname.baseURI) - return self._rels - - def target_ref(self, rId: str) -> str: - """Return URL contained in target ref of relationship identified by `rId`.""" - rel = self.rels[rId] - return rel.target_ref - - def _rel_ref_count(self, rId: str) -> int: - """Return the count of references in this part to the relationship identified by `rId`. - - Only an XML part can contain references, so this is 0 for `Part`. - """ - return 0 - - -class PartFactory: - """Provides a way for client code to specify a subclass of |Part| to be constructed - by |Unmarshaller| based on its content type and/or a custom callable. - - Setting ``PartFactory.part_class_selector`` to a callable object will cause that - object to be called with the parameters ``content_type, reltype``, once for each - part in the package. If the callable returns an object, it is used as the class for - that part. If it returns |None|, part class selection falls back to the content type - map defined in ``PartFactory.part_type_for``. If no class is returned from either of - these, the class contained in ``PartFactory.default_part_type`` is used to construct - the part, which is by default ``opc.package.Part``. - """ - - part_class_selector: Callable[[str, str], Type[Part] | None] | None - part_type_for: dict[str, Type[Part]] = {} - default_part_type = Part - - def __new__( - cls, - partname: PackURI, - content_type: str, - reltype: str, - blob: bytes, - package: Package, - ): - PartClass: Type[Part] | None = None - if cls.part_class_selector is not None: - part_class_selector = cls_method_fn(cls, "part_class_selector") - PartClass = part_class_selector(content_type, reltype) - if PartClass is None: - PartClass = cls._part_cls_for(content_type) - return PartClass.load(partname, content_type, blob, package) - - @classmethod - def _part_cls_for(cls, content_type: str): - """Return the custom part class registered for `content_type`, or the default - part class if no custom class is registered for `content_type`.""" - if content_type in cls.part_type_for: - return cls.part_type_for[content_type] - return cls.default_part_type - - -class XmlPart(Part): - """Base class for package parts containing an XML payload, which is most of them. - - Provides additional methods to the |Part| base class that take care of parsing and - reserializing the XML payload and managing relationships to other parts. - """ - - def __init__( - self, partname: PackURI, content_type: str, element: BaseOxmlElement, package: Package - ): - super(XmlPart, self).__init__(partname, content_type, package=package) - self._element = element - - @property - def blob(self): - return serialize_part_xml(self._element) - - @property - def element(self): - """The root XML element of this XML part.""" - return self._element - - @classmethod - def load(cls, partname: PackURI, content_type: str, blob: bytes, package: Package): - element = parse_xml(blob) - return cls(partname, content_type, element, package) - - @property - def part(self): - """Part of the parent protocol, "children" of the document will not know the - part that contains them so must ask their parent object. - - That chain of delegation ends here for child objects. - """ - return self - - def _rel_ref_count(self, rId: str) -> int: - """Return the count of references in this part's XML to the relationship - identified by `rId`.""" - rIds = cast("list[str]", self._element.xpath("//@r:id")) - return len([_rId for _rId in rIds if _rId == rId]) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/parts/__init__.py b/.venv-docs/lib/python3.12/site-packages/docx/opc/parts/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/parts/__pycache__/__init__.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/opc/parts/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 861363f0..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/opc/parts/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/parts/__pycache__/coreprops.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/opc/parts/__pycache__/coreprops.cpython-312.pyc deleted file mode 100644 index a2c1214a..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/opc/parts/__pycache__/coreprops.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/parts/coreprops.py b/.venv-docs/lib/python3.12/site-packages/docx/opc/parts/coreprops.py deleted file mode 100644 index fda01121..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/opc/parts/coreprops.py +++ /dev/null @@ -1,48 +0,0 @@ -"""Core properties part, corresponds to ``/docProps/core.xml`` part in package.""" - -from __future__ import annotations - -import datetime as dt -from typing import TYPE_CHECKING - -from docx.opc.constants import CONTENT_TYPE as CT -from docx.opc.coreprops import CoreProperties -from docx.opc.packuri import PackURI -from docx.opc.part import XmlPart -from docx.oxml.coreprops import CT_CoreProperties - -if TYPE_CHECKING: - from docx.opc.package import OpcPackage - - -class CorePropertiesPart(XmlPart): - """Corresponds to part named ``/docProps/core.xml``. - - The "core" is short for "Dublin Core" and contains document metadata relatively common across - documents of all types, not just DOCX. - """ - - @classmethod - def default(cls, package: OpcPackage): - """Return a new |CorePropertiesPart| object initialized with default values for - its base properties.""" - core_properties_part = cls._new(package) - core_properties = core_properties_part.core_properties - core_properties.title = "Word Document" - core_properties.last_modified_by = "python-docx" - core_properties.revision = 1 - core_properties.modified = dt.datetime.now(dt.timezone.utc) - return core_properties_part - - @property - def core_properties(self): - """A |CoreProperties| object providing read/write access to the core properties - contained in this core properties part.""" - return CoreProperties(self.element) - - @classmethod - def _new(cls, package: OpcPackage) -> CorePropertiesPart: - partname = PackURI("/docProps/core.xml") - content_type = CT.OPC_CORE_PROPERTIES - coreProperties = CT_CoreProperties.new() - return CorePropertiesPart(partname, content_type, coreProperties, package) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/phys_pkg.py b/.venv-docs/lib/python3.12/site-packages/docx/opc/phys_pkg.py deleted file mode 100644 index 5ec32237..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/opc/phys_pkg.py +++ /dev/null @@ -1,119 +0,0 @@ -"""Provides a general interface to a `physical` OPC package, such as a zip file.""" - -import os -from zipfile import ZIP_DEFLATED, ZipFile, is_zipfile - -from docx.opc.exceptions import PackageNotFoundError -from docx.opc.packuri import CONTENT_TYPES_URI - - -class PhysPkgReader: - """Factory for physical package reader objects.""" - - def __new__(cls, pkg_file): - # if `pkg_file` is a string, treat it as a path - if isinstance(pkg_file, str): - if os.path.isdir(pkg_file): - reader_cls = _DirPkgReader - elif is_zipfile(pkg_file): - reader_cls = _ZipPkgReader - else: - raise PackageNotFoundError("Package not found at '%s'" % pkg_file) - else: # assume it's a stream and pass it to Zip reader to sort out - reader_cls = _ZipPkgReader - - return super(PhysPkgReader, cls).__new__(reader_cls) - - -class PhysPkgWriter: - """Factory for physical package writer objects.""" - - def __new__(cls, pkg_file): - return super(PhysPkgWriter, cls).__new__(_ZipPkgWriter) - - -class _DirPkgReader(PhysPkgReader): - """Implements |PhysPkgReader| interface for an OPC package extracted into a - directory.""" - - def __init__(self, path): - """`path` is the path to a directory containing an expanded package.""" - super(_DirPkgReader, self).__init__() - self._path = os.path.abspath(path) - - def blob_for(self, pack_uri): - """Return contents of file corresponding to `pack_uri` in package directory.""" - path = os.path.join(self._path, pack_uri.membername) - with open(path, "rb") as f: - blob = f.read() - return blob - - def close(self): - """Provides interface consistency with |ZipFileSystem|, but does nothing, a - directory file system doesn't need closing.""" - pass - - @property - def content_types_xml(self): - """Return the `[Content_Types].xml` blob from the package.""" - return self.blob_for(CONTENT_TYPES_URI) - - def rels_xml_for(self, source_uri): - """Return rels item XML for source with `source_uri`, or None if the item has no - rels item.""" - try: - rels_xml = self.blob_for(source_uri.rels_uri) - except IOError: - rels_xml = None - return rels_xml - - -class _ZipPkgReader(PhysPkgReader): - """Implements |PhysPkgReader| interface for a zip file OPC package.""" - - def __init__(self, pkg_file): - super(_ZipPkgReader, self).__init__() - self._zipf = ZipFile(pkg_file, "r") - - def blob_for(self, pack_uri): - """Return blob corresponding to `pack_uri`. - - Raises |ValueError| if no matching member is present in zip archive. - """ - return self._zipf.read(pack_uri.membername) - - def close(self): - """Close the zip archive, releasing any resources it is using.""" - self._zipf.close() - - @property - def content_types_xml(self): - """Return the `[Content_Types].xml` blob from the zip package.""" - return self.blob_for(CONTENT_TYPES_URI) - - def rels_xml_for(self, source_uri): - """Return rels item XML for source with `source_uri` or None if no rels item is - present.""" - try: - rels_xml = self.blob_for(source_uri.rels_uri) - except KeyError: - rels_xml = None - return rels_xml - - -class _ZipPkgWriter(PhysPkgWriter): - """Implements |PhysPkgWriter| interface for a zip file OPC package.""" - - def __init__(self, pkg_file): - super(_ZipPkgWriter, self).__init__() - self._zipf = ZipFile(pkg_file, "w", compression=ZIP_DEFLATED) - - def close(self): - """Close the zip archive, flushing any pending physical writes and releasing any - resources it's using.""" - self._zipf.close() - - def write(self, pack_uri, blob): - """Write `blob` to this zip package with the membername corresponding to - `pack_uri`.""" - self._zipf.writestr(pack_uri.membername, blob) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/pkgreader.py b/.venv-docs/lib/python3.12/site-packages/docx/opc/pkgreader.py deleted file mode 100644 index 15207e51..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/opc/pkgreader.py +++ /dev/null @@ -1,254 +0,0 @@ -"""Low-level, read-only API to a serialized Open Packaging Convention (OPC) package.""" - -from docx.opc.constants import RELATIONSHIP_TARGET_MODE as RTM -from docx.opc.oxml import parse_xml -from docx.opc.packuri import PACKAGE_URI, PackURI -from docx.opc.phys_pkg import PhysPkgReader -from docx.opc.shared import CaseInsensitiveDict - - -class PackageReader: - """Provides access to the contents of a zip-format OPC package via its - :attr:`serialized_parts` and :attr:`pkg_srels` attributes.""" - - def __init__(self, content_types, pkg_srels, sparts): - super(PackageReader, self).__init__() - self._pkg_srels = pkg_srels - self._sparts = sparts - - @staticmethod - def from_file(pkg_file): - """Return a |PackageReader| instance loaded with contents of `pkg_file`.""" - phys_reader = PhysPkgReader(pkg_file) - content_types = _ContentTypeMap.from_xml(phys_reader.content_types_xml) - pkg_srels = PackageReader._srels_for(phys_reader, PACKAGE_URI) - sparts = PackageReader._load_serialized_parts(phys_reader, pkg_srels, content_types) - phys_reader.close() - return PackageReader(content_types, pkg_srels, sparts) - - def iter_sparts(self): - """Generate a 4-tuple `(partname, content_type, reltype, blob)` for each of the - serialized parts in the package.""" - for s in self._sparts: - yield (s.partname, s.content_type, s.reltype, s.blob) - - def iter_srels(self): - """Generate a 2-tuple `(source_uri, srel)` for each of the relationships in the - package.""" - for srel in self._pkg_srels: - yield (PACKAGE_URI, srel) - for spart in self._sparts: - for srel in spart.srels: - yield (spart.partname, srel) - - @staticmethod - def _load_serialized_parts(phys_reader, pkg_srels, content_types): - """Return a list of |_SerializedPart| instances corresponding to the parts in - `phys_reader` accessible by walking the relationship graph starting with - `pkg_srels`.""" - sparts = [] - part_walker = PackageReader._walk_phys_parts(phys_reader, pkg_srels) - for partname, blob, reltype, srels in part_walker: - content_type = content_types[partname] - spart = _SerializedPart(partname, content_type, reltype, blob, srels) - sparts.append(spart) - return tuple(sparts) - - @staticmethod - def _srels_for(phys_reader, source_uri): - """Return |_SerializedRelationships| instance populated with relationships for - source identified by `source_uri`.""" - rels_xml = phys_reader.rels_xml_for(source_uri) - return _SerializedRelationships.load_from_xml(source_uri.baseURI, rels_xml) - - @staticmethod - def _walk_phys_parts(phys_reader, srels, visited_partnames=None): - """Generate a 4-tuple `(partname, blob, reltype, srels)` for each of the parts - in `phys_reader` by walking the relationship graph rooted at srels.""" - if visited_partnames is None: - visited_partnames = [] - for srel in srels: - if srel.is_external: - continue - partname = srel.target_partname - if partname in visited_partnames: - continue - visited_partnames.append(partname) - reltype = srel.reltype - part_srels = PackageReader._srels_for(phys_reader, partname) - blob = phys_reader.blob_for(partname) - yield (partname, blob, reltype, part_srels) - next_walker = PackageReader._walk_phys_parts(phys_reader, part_srels, visited_partnames) - for partname, blob, reltype, srels in next_walker: - yield (partname, blob, reltype, srels) - - -class _ContentTypeMap: - """Value type providing dictionary semantics for looking up content type by part - name, e.g. ``content_type = cti['/ppt/presentation.xml']``.""" - - def __init__(self): - super(_ContentTypeMap, self).__init__() - self._overrides = CaseInsensitiveDict() - self._defaults = CaseInsensitiveDict() - - def __getitem__(self, partname): - """Return content type for part identified by `partname`.""" - if not isinstance(partname, PackURI): - tmpl = "_ContentTypeMap key must be , got %s" - raise KeyError(tmpl % type(partname)) - if partname in self._overrides: - return self._overrides[partname] - if partname.ext in self._defaults: - return self._defaults[partname.ext] - tmpl = "no content type for partname '%s' in [Content_Types].xml" - raise KeyError(tmpl % partname) - - @staticmethod - def from_xml(content_types_xml): - """Return a new |_ContentTypeMap| instance populated with the contents of - `content_types_xml`.""" - types_elm = parse_xml(content_types_xml) - ct_map = _ContentTypeMap() - for o in types_elm.overrides: - ct_map._add_override(o.partname, o.content_type) - for d in types_elm.defaults: - ct_map._add_default(d.extension, d.content_type) - return ct_map - - def _add_default(self, extension, content_type): - """Add the default mapping of `extension` to `content_type` to this content type - mapping.""" - self._defaults[extension] = content_type - - def _add_override(self, partname, content_type): - """Add the default mapping of `partname` to `content_type` to this content type - mapping.""" - self._overrides[partname] = content_type - - -class _SerializedPart: - """Value object for an OPC package part. - - Provides access to the partname, content type, blob, and serialized relationships - for the part. - """ - - def __init__(self, partname, content_type, reltype, blob, srels): - super(_SerializedPart, self).__init__() - self._partname = partname - self._content_type = content_type - self._reltype = reltype - self._blob = blob - self._srels = srels - - @property - def partname(self): - return self._partname - - @property - def content_type(self): - return self._content_type - - @property - def blob(self): - return self._blob - - @property - def reltype(self): - """The referring relationship type of this part.""" - return self._reltype - - @property - def srels(self): - return self._srels - - -class _SerializedRelationship: - """Value object representing a serialized relationship in an OPC package. - - Serialized, in this case, means any target part is referred to via its partname - rather than a direct link to an in-memory |Part| object. - """ - - def __init__(self, baseURI, rel_elm): - super(_SerializedRelationship, self).__init__() - self._baseURI = baseURI - self._rId = rel_elm.rId - self._reltype = rel_elm.reltype - self._target_mode = rel_elm.target_mode - self._target_ref = rel_elm.target_ref - - @property - def is_external(self): - """True if target_mode is ``RTM.EXTERNAL``""" - return self._target_mode == RTM.EXTERNAL - - @property - def reltype(self): - """Relationship type, like ``RT.OFFICE_DOCUMENT``""" - return self._reltype - - @property - def rId(self): - """Relationship id, like 'rId9', corresponds to the ``Id`` attribute on the - ``CT_Relationship`` element.""" - return self._rId - - @property - def target_mode(self): - """String in ``TargetMode`` attribute of ``CT_Relationship`` element, one of - ``RTM.INTERNAL`` or ``RTM.EXTERNAL``.""" - return self._target_mode - - @property - def target_ref(self): - """String in ``Target`` attribute of ``CT_Relationship`` element, a relative - part reference for internal target mode or an arbitrary URI, e.g. an HTTP URL, - for external target mode.""" - return self._target_ref - - @property - def target_partname(self): - """|PackURI| instance containing partname targeted by this relationship. - - Raises ``ValueError`` on reference if target_mode is ``'External'``. Use - :attr:`target_mode` to check before referencing. - """ - if self.is_external: - msg = ( - "target_partname attribute on Relationship is undefined w" - 'here TargetMode == "External"' - ) - raise ValueError(msg) - # lazy-load _target_partname attribute - if not hasattr(self, "_target_partname"): - self._target_partname = PackURI.from_rel_ref(self._baseURI, self.target_ref) - return self._target_partname - - -class _SerializedRelationships: - """Read-only sequence of |_SerializedRelationship| instances corresponding to the - relationships item XML passed to constructor.""" - - def __init__(self): - super(_SerializedRelationships, self).__init__() - self._srels = [] - - def __iter__(self): - """Support iteration, e.g. 'for x in srels:'.""" - return self._srels.__iter__() - - @staticmethod - def load_from_xml(baseURI, rels_item_xml): - """Return |_SerializedRelationships| instance loaded with the relationships - contained in `rels_item_xml`. - - Returns an empty collection if `rels_item_xml` is |None|. - """ - srels = _SerializedRelationships() - if rels_item_xml is not None: - rels_elm = parse_xml(rels_item_xml) - for rel_elm in rels_elm.Relationship_lst: - srels._srels.append(_SerializedRelationship(baseURI, rel_elm)) - return srels diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/pkgwriter.py b/.venv-docs/lib/python3.12/site-packages/docx/opc/pkgwriter.py deleted file mode 100644 index e6351697..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/opc/pkgwriter.py +++ /dev/null @@ -1,115 +0,0 @@ -"""Provides low-level, write-only API to serialized (OPC) package. - -OPC stands for Open Packaging Convention. This is e, essentially an implementation of -OpcPackage.save(). -""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Iterable - -from docx.opc.constants import CONTENT_TYPE as CT -from docx.opc.oxml import CT_Types, serialize_part_xml -from docx.opc.packuri import CONTENT_TYPES_URI, PACKAGE_URI -from docx.opc.phys_pkg import PhysPkgWriter -from docx.opc.shared import CaseInsensitiveDict -from docx.opc.spec import default_content_types - -if TYPE_CHECKING: - from docx.opc.part import Part - - -class PackageWriter: - """Writes a zip-format OPC package to `pkg_file`, where `pkg_file` can be either a - path to a zip file (a string) or a file-like object. - - Its single API method, :meth:`write`, is static, so this class is not intended to be - instantiated. - """ - - @staticmethod - def write(pkg_file, pkg_rels, parts): - """Write a physical package (.pptx file) to `pkg_file` containing `pkg_rels` and - `parts` and a content types stream based on the content types of the parts.""" - phys_writer = PhysPkgWriter(pkg_file) - PackageWriter._write_content_types_stream(phys_writer, parts) - PackageWriter._write_pkg_rels(phys_writer, pkg_rels) - PackageWriter._write_parts(phys_writer, parts) - phys_writer.close() - - @staticmethod - def _write_content_types_stream(phys_writer, parts): - """Write ``[Content_Types].xml`` part to the physical package with an - appropriate content type lookup target for each part in `parts`.""" - cti = _ContentTypesItem.from_parts(parts) - phys_writer.write(CONTENT_TYPES_URI, cti.blob) - - @staticmethod - def _write_parts(phys_writer: PhysPkgWriter, parts: Iterable[Part]): - """Write the blob of each part in `parts` to the package, along with a rels item - for its relationships if and only if it has any.""" - for part in parts: - phys_writer.write(part.partname, part.blob) - if len(part.rels): - phys_writer.write(part.partname.rels_uri, part.rels.xml) - - @staticmethod - def _write_pkg_rels(phys_writer, pkg_rels): - """Write the XML rels item for `pkg_rels` ('/_rels/.rels') to the package.""" - phys_writer.write(PACKAGE_URI.rels_uri, pkg_rels.xml) - - -class _ContentTypesItem: - """Service class that composes a content types item ([Content_Types].xml) based on a - list of parts. - - Not meant to be instantiated directly, its single interface method is xml_for(), - e.g. ``_ContentTypesItem.xml_for(parts)``. - """ - - def __init__(self): - self._defaults = CaseInsensitiveDict() - self._overrides = {} - - @property - def blob(self): - """Return XML form of this content types item, suitable for storage as - ``[Content_Types].xml`` in an OPC package.""" - return serialize_part_xml(self._element) - - @classmethod - def from_parts(cls, parts): - """Return content types XML mapping each part in `parts` to the appropriate - content type and suitable for storage as ``[Content_Types].xml`` in an OPC - package.""" - cti = cls() - cti._defaults["rels"] = CT.OPC_RELATIONSHIPS - cti._defaults["xml"] = CT.XML - for part in parts: - cti._add_content_type(part.partname, part.content_type) - return cti - - def _add_content_type(self, partname, content_type): - """Add a content type for the part with `partname` and `content_type`, using a - default or override as appropriate.""" - ext = partname.ext - if (ext.lower(), content_type) in default_content_types: - self._defaults[ext] = content_type - else: - self._overrides[partname] = content_type - - @property - def _element(self): - """Return XML form of this content types item, suitable for storage as - ``[Content_Types].xml`` in an OPC package. - - Although the sequence of elements is not strictly significant, as an aid to - testing and readability Default elements are sorted by extension and Override - elements are sorted by partname. - """ - _types_elm = CT_Types.new() - for ext in sorted(self._defaults.keys()): - _types_elm.add_default(ext, self._defaults[ext]) - for partname in sorted(self._overrides.keys()): - _types_elm.add_override(partname, self._overrides[partname]) - return _types_elm diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/rel.py b/.venv-docs/lib/python3.12/site-packages/docx/opc/rel.py deleted file mode 100644 index 153b308d..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/opc/rel.py +++ /dev/null @@ -1,153 +0,0 @@ -"""Relationship-related objects.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Dict, cast - -from docx.opc.oxml import CT_Relationships - -if TYPE_CHECKING: - from docx.opc.part import Part - - -class Relationships(Dict[str, "_Relationship"]): - """Collection object for |_Relationship| instances, having list semantics.""" - - def __init__(self, baseURI: str): - super(Relationships, self).__init__() - self._baseURI = baseURI - self._target_parts_by_rId: dict[str, Any] = {} - - def add_relationship( - self, reltype: str, target: Part | str, rId: str, is_external: bool = False - ) -> "_Relationship": - """Return a newly added |_Relationship| instance.""" - rel = _Relationship(rId, reltype, target, self._baseURI, is_external) - self[rId] = rel - if not is_external: - self._target_parts_by_rId[rId] = target - return rel - - def get_or_add(self, reltype: str, target_part: Part) -> _Relationship: - """Return relationship of `reltype` to `target_part`, newly added if not already - present in collection.""" - rel = self._get_matching(reltype, target_part) - if rel is None: - rId = self._next_rId - rel = self.add_relationship(reltype, target_part, rId) - return rel - - def get_or_add_ext_rel(self, reltype: str, target_ref: str) -> str: - """Return rId of external relationship of `reltype` to `target_ref`, newly added - if not already present in collection.""" - rel = self._get_matching(reltype, target_ref, is_external=True) - if rel is None: - rId = self._next_rId - rel = self.add_relationship(reltype, target_ref, rId, is_external=True) - return rel.rId - - def part_with_reltype(self, reltype: str) -> Part: - """Return target part of rel with matching `reltype`, raising |KeyError| if not - found and |ValueError| if more than one matching relationship is found.""" - rel = self._get_rel_of_type(reltype) - return rel.target_part - - @property - def related_parts(self): - """Dict mapping rIds to target parts for all the internal relationships in the - collection.""" - return self._target_parts_by_rId - - @property - def xml(self) -> str: - """Serialize this relationship collection into XML suitable for storage as a - .rels file in an OPC package.""" - rels_elm = CT_Relationships.new() - for rel in self.values(): - rels_elm.add_rel(rel.rId, rel.reltype, rel.target_ref, rel.is_external) - return rels_elm.xml - - def _get_matching( - self, reltype: str, target: Part | str, is_external: bool = False - ) -> _Relationship | None: - """Return relationship of matching `reltype`, `target`, and `is_external` from - collection, or None if not found.""" - - def matches(rel: _Relationship, reltype: str, target: Part | str, is_external: bool): - if rel.reltype != reltype: - return False - if rel.is_external != is_external: - return False - rel_target = rel.target_ref if rel.is_external else rel.target_part - return rel_target == target - - for rel in self.values(): - if matches(rel, reltype, target, is_external): - return rel - return None - - def _get_rel_of_type(self, reltype: str): - """Return single relationship of type `reltype` from the collection. - - Raises |KeyError| if no matching relationship is found. Raises |ValueError| if - more than one matching relationship is found. - """ - matching = [rel for rel in self.values() if rel.reltype == reltype] - if len(matching) == 0: - tmpl = "no relationship of type '%s' in collection" - raise KeyError(tmpl % reltype) - if len(matching) > 1: - tmpl = "multiple relationships of type '%s' in collection" - raise ValueError(tmpl % reltype) - return matching[0] - - @property - def _next_rId(self) -> str: # pyright: ignore[reportReturnType] - """Next available rId in collection, starting from 'rId1' and making use of any - gaps in numbering, e.g. 'rId2' for rIds ['rId1', 'rId3'].""" - for n in range(1, len(self) + 2): - rId_candidate = "rId%d" % n # like 'rId19' - if rId_candidate not in self: - return rId_candidate - - -class _Relationship: - """Value object for relationship to part.""" - - def __init__( - self, rId: str, reltype: str, target: Part | str, baseURI: str, external: bool = False - ): - super(_Relationship, self).__init__() - self._rId = rId - self._reltype = reltype - self._target = target - self._baseURI = baseURI - self._is_external = bool(external) - - @property - def is_external(self) -> bool: - return self._is_external - - @property - def reltype(self) -> str: - return self._reltype - - @property - def rId(self) -> str: - return self._rId - - @property - def target_part(self) -> Part: - if self._is_external: - raise ValueError( - "target_part property on _Relationship is undefined when target mode is External" - ) - return cast("Part", self._target) - - @property - def target_ref(self) -> str: - if self._is_external: - return cast(str, self._target) - else: - target = cast("Part", self._target) - return target.partname.relative_ref(self._baseURI) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/shared.py b/.venv-docs/lib/python3.12/site-packages/docx/opc/shared.py deleted file mode 100644 index 9d4c0a6d..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/opc/shared.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Objects shared by opc modules.""" - -from __future__ import annotations - -from typing import Any, Dict, TypeVar - -_T = TypeVar("_T") - - -class CaseInsensitiveDict(Dict[str, Any]): - """Mapping type that behaves like dict except that it matches without respect to the - case of the key. - - E.g. cid['A'] == cid['a']. Note this is not general-purpose, just complete enough to - satisfy opc package needs. It assumes str keys, and that it is created empty; keys - passed in constructor are not accounted for - """ - - def __contains__(self, key): - return super(CaseInsensitiveDict, self).__contains__(key.lower()) - - def __getitem__(self, key): - return super(CaseInsensitiveDict, self).__getitem__(key.lower()) - - def __setitem__(self, key, value): - return super(CaseInsensitiveDict, self).__setitem__(key.lower(), value) - - -def cls_method_fn(cls: type, method_name: str): - """Return method of `cls` having `method_name`.""" - return getattr(cls, method_name) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/opc/spec.py b/.venv-docs/lib/python3.12/site-packages/docx/opc/spec.py deleted file mode 100644 index 011a4825..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/opc/spec.py +++ /dev/null @@ -1,24 +0,0 @@ -"""Provides mappings that embody aspects of the Open XML spec ISO/IEC 29500.""" - -from docx.opc.constants import CONTENT_TYPE as CT - -default_content_types = ( - ("bin", CT.PML_PRINTER_SETTINGS), - ("bin", CT.SML_PRINTER_SETTINGS), - ("bin", CT.WML_PRINTER_SETTINGS), - ("bmp", CT.BMP), - ("emf", CT.X_EMF), - ("fntdata", CT.X_FONTDATA), - ("gif", CT.GIF), - ("jpe", CT.JPEG), - ("jpeg", CT.JPEG), - ("jpg", CT.JPEG), - ("png", CT.PNG), - ("rels", CT.OPC_RELATIONSHIPS), - ("tif", CT.TIFF), - ("tiff", CT.TIFF), - ("wdp", CT.MS_PHOTO), - ("wmf", CT.X_WMF), - ("xlsx", CT.SML_SHEET), - ("xml", CT.XML), -) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__init__.py b/.venv-docs/lib/python3.12/site-packages/docx/oxml/__init__.py deleted file mode 100644 index 37f608ce..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__init__.py +++ /dev/null @@ -1,251 +0,0 @@ -# ruff: noqa: E402, I001 - -"""Initializes oxml sub-package. - -This including registering custom element classes corresponding to Open XML elements. -""" - -from __future__ import annotations - -from docx.oxml.drawing import CT_Drawing -from docx.oxml.parser import OxmlElement, parse_xml, register_element_cls -from docx.oxml.shape import ( - CT_Anchor, - CT_Blip, - CT_BlipFillProperties, - CT_GraphicalObject, - CT_GraphicalObjectData, - CT_Inline, - CT_NonVisualDrawingProps, - CT_Picture, - CT_PictureNonVisual, - CT_Point2D, - CT_PositiveSize2D, - CT_ShapeProperties, - CT_Transform2D, -) -from docx.oxml.shared import CT_DecimalNumber, CT_OnOff, CT_String -from docx.oxml.text.hyperlink import CT_Hyperlink -from docx.oxml.text.pagebreak import CT_LastRenderedPageBreak -from docx.oxml.text.run import ( - CT_R, - CT_Br, - CT_Cr, - CT_NoBreakHyphen, - CT_PTab, - CT_Text, -) - -# -- `OxmlElement` and `parse_xml()` are not used in this module but several downstream -# -- "extension" packages expect to find them here and there's no compelling reason -# -- not to republish them here so those keep working. -__all__ = ["OxmlElement", "parse_xml"] - -# --------------------------------------------------------------------------- -# DrawingML-related elements - -register_element_cls("a:blip", CT_Blip) -register_element_cls("a:ext", CT_PositiveSize2D) -register_element_cls("a:graphic", CT_GraphicalObject) -register_element_cls("a:graphicData", CT_GraphicalObjectData) -register_element_cls("a:off", CT_Point2D) -register_element_cls("a:xfrm", CT_Transform2D) -register_element_cls("pic:blipFill", CT_BlipFillProperties) -register_element_cls("pic:cNvPr", CT_NonVisualDrawingProps) -register_element_cls("pic:nvPicPr", CT_PictureNonVisual) -register_element_cls("pic:pic", CT_Picture) -register_element_cls("pic:spPr", CT_ShapeProperties) -register_element_cls("w:drawing", CT_Drawing) -register_element_cls("wp:anchor", CT_Anchor) -register_element_cls("wp:docPr", CT_NonVisualDrawingProps) -register_element_cls("wp:extent", CT_PositiveSize2D) -register_element_cls("wp:inline", CT_Inline) - -# --------------------------------------------------------------------------- -# hyperlink-related elements - -register_element_cls("w:hyperlink", CT_Hyperlink) - -# --------------------------------------------------------------------------- -# text-related elements - -register_element_cls("w:br", CT_Br) -register_element_cls("w:cr", CT_Cr) -register_element_cls("w:lastRenderedPageBreak", CT_LastRenderedPageBreak) -register_element_cls("w:noBreakHyphen", CT_NoBreakHyphen) -register_element_cls("w:ptab", CT_PTab) -register_element_cls("w:r", CT_R) -register_element_cls("w:t", CT_Text) - -# --------------------------------------------------------------------------- -# header/footer-related mappings - -register_element_cls("w:evenAndOddHeaders", CT_OnOff) -register_element_cls("w:titlePg", CT_OnOff) - -# --------------------------------------------------------------------------- -# other custom element class mappings - -from .comments import CT_Comments, CT_Comment - -register_element_cls("w:comments", CT_Comments) -register_element_cls("w:comment", CT_Comment) - -from .coreprops import CT_CoreProperties - -register_element_cls("cp:coreProperties", CT_CoreProperties) - -from .document import CT_Body, CT_Document - -register_element_cls("w:body", CT_Body) -register_element_cls("w:document", CT_Document) - -from .numbering import CT_Num, CT_Numbering, CT_NumLvl, CT_NumPr - -register_element_cls("w:abstractNumId", CT_DecimalNumber) -register_element_cls("w:ilvl", CT_DecimalNumber) -register_element_cls("w:lvlOverride", CT_NumLvl) -register_element_cls("w:num", CT_Num) -register_element_cls("w:numId", CT_DecimalNumber) -register_element_cls("w:numPr", CT_NumPr) -register_element_cls("w:numbering", CT_Numbering) -register_element_cls("w:startOverride", CT_DecimalNumber) - -from .section import ( - CT_HdrFtr, - CT_HdrFtrRef, - CT_PageMar, - CT_PageSz, - CT_SectPr, - CT_SectType, -) - -register_element_cls("w:footerReference", CT_HdrFtrRef) -register_element_cls("w:ftr", CT_HdrFtr) -register_element_cls("w:hdr", CT_HdrFtr) -register_element_cls("w:headerReference", CT_HdrFtrRef) -register_element_cls("w:pgMar", CT_PageMar) -register_element_cls("w:pgSz", CT_PageSz) -register_element_cls("w:sectPr", CT_SectPr) -register_element_cls("w:type", CT_SectType) - -from .settings import CT_Settings - -register_element_cls("w:settings", CT_Settings) - -from .styles import CT_LatentStyles, CT_LsdException, CT_Style, CT_Styles - -register_element_cls("w:basedOn", CT_String) -register_element_cls("w:latentStyles", CT_LatentStyles) -register_element_cls("w:locked", CT_OnOff) -register_element_cls("w:lsdException", CT_LsdException) -register_element_cls("w:name", CT_String) -register_element_cls("w:next", CT_String) -register_element_cls("w:qFormat", CT_OnOff) -register_element_cls("w:semiHidden", CT_OnOff) -register_element_cls("w:style", CT_Style) -register_element_cls("w:styles", CT_Styles) -register_element_cls("w:uiPriority", CT_DecimalNumber) -register_element_cls("w:unhideWhenUsed", CT_OnOff) - -from .table import ( - CT_Height, - CT_Row, - CT_Tbl, - CT_TblGrid, - CT_TblGridCol, - CT_TblLayoutType, - CT_TblPr, - CT_TblPrEx, - CT_TblWidth, - CT_Tc, - CT_TcPr, - CT_TrPr, - CT_VMerge, - CT_VerticalJc, -) - -register_element_cls("w:bidiVisual", CT_OnOff) -register_element_cls("w:gridAfter", CT_DecimalNumber) -register_element_cls("w:gridBefore", CT_DecimalNumber) -register_element_cls("w:gridCol", CT_TblGridCol) -register_element_cls("w:gridSpan", CT_DecimalNumber) -register_element_cls("w:tbl", CT_Tbl) -register_element_cls("w:tblGrid", CT_TblGrid) -register_element_cls("w:tblLayout", CT_TblLayoutType) -register_element_cls("w:tblPr", CT_TblPr) -register_element_cls("w:tblPrEx", CT_TblPrEx) -register_element_cls("w:tblStyle", CT_String) -register_element_cls("w:tc", CT_Tc) -register_element_cls("w:tcPr", CT_TcPr) -register_element_cls("w:tcW", CT_TblWidth) -register_element_cls("w:tr", CT_Row) -register_element_cls("w:trHeight", CT_Height) -register_element_cls("w:trPr", CT_TrPr) -register_element_cls("w:vAlign", CT_VerticalJc) -register_element_cls("w:vMerge", CT_VMerge) - -from .text.font import ( - CT_Color, - CT_Fonts, - CT_Highlight, - CT_HpsMeasure, - CT_RPr, - CT_Underline, - CT_VerticalAlignRun, -) - -register_element_cls("w:b", CT_OnOff) -register_element_cls("w:bCs", CT_OnOff) -register_element_cls("w:caps", CT_OnOff) -register_element_cls("w:color", CT_Color) -register_element_cls("w:cs", CT_OnOff) -register_element_cls("w:dstrike", CT_OnOff) -register_element_cls("w:emboss", CT_OnOff) -register_element_cls("w:highlight", CT_Highlight) -register_element_cls("w:i", CT_OnOff) -register_element_cls("w:iCs", CT_OnOff) -register_element_cls("w:imprint", CT_OnOff) -register_element_cls("w:noProof", CT_OnOff) -register_element_cls("w:oMath", CT_OnOff) -register_element_cls("w:outline", CT_OnOff) -register_element_cls("w:rFonts", CT_Fonts) -register_element_cls("w:rPr", CT_RPr) -register_element_cls("w:rStyle", CT_String) -register_element_cls("w:rtl", CT_OnOff) -register_element_cls("w:shadow", CT_OnOff) -register_element_cls("w:smallCaps", CT_OnOff) -register_element_cls("w:snapToGrid", CT_OnOff) -register_element_cls("w:specVanish", CT_OnOff) -register_element_cls("w:strike", CT_OnOff) -register_element_cls("w:sz", CT_HpsMeasure) -register_element_cls("w:u", CT_Underline) -register_element_cls("w:vanish", CT_OnOff) -register_element_cls("w:vertAlign", CT_VerticalAlignRun) -register_element_cls("w:webHidden", CT_OnOff) - -from .text.paragraph import CT_P - -register_element_cls("w:p", CT_P) - -from .text.parfmt import ( - CT_Ind, - CT_Jc, - CT_PPr, - CT_Spacing, - CT_TabStop, - CT_TabStops, -) - -register_element_cls("w:ind", CT_Ind) -register_element_cls("w:jc", CT_Jc) -register_element_cls("w:keepLines", CT_OnOff) -register_element_cls("w:keepNext", CT_OnOff) -register_element_cls("w:outlineLvl", CT_DecimalNumber) -register_element_cls("w:pageBreakBefore", CT_OnOff) -register_element_cls("w:pPr", CT_PPr) -register_element_cls("w:pStyle", CT_String) -register_element_cls("w:spacing", CT_Spacing) -register_element_cls("w:tab", CT_TabStop) -register_element_cls("w:tabs", CT_TabStops) -register_element_cls("w:widowControl", CT_OnOff) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/__init__.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 8fb4e08b..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/comments.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/comments.cpython-312.pyc deleted file mode 100644 index e90dbc5f..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/comments.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/coreprops.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/coreprops.cpython-312.pyc deleted file mode 100644 index 5aba4e12..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/coreprops.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/document.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/document.cpython-312.pyc deleted file mode 100644 index 14319da3..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/document.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/drawing.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/drawing.cpython-312.pyc deleted file mode 100644 index ad4a601c..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/drawing.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/exceptions.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/exceptions.cpython-312.pyc deleted file mode 100644 index bddc19d9..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/exceptions.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/ns.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/ns.cpython-312.pyc deleted file mode 100644 index c871f97c..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/ns.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/numbering.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/numbering.cpython-312.pyc deleted file mode 100644 index 7c7d7b89..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/numbering.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/parser.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/parser.cpython-312.pyc deleted file mode 100644 index b82d1f0a..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/parser.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/section.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/section.cpython-312.pyc deleted file mode 100644 index 855ba77f..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/section.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/settings.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/settings.cpython-312.pyc deleted file mode 100644 index 25d417e6..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/settings.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/shape.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/shape.cpython-312.pyc deleted file mode 100644 index dc0e6f83..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/shape.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/shared.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/shared.cpython-312.pyc deleted file mode 100644 index 2c43c14c..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/shared.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/simpletypes.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/simpletypes.cpython-312.pyc deleted file mode 100644 index d92c4e97..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/simpletypes.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/styles.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/styles.cpython-312.pyc deleted file mode 100644 index 6b3d90e3..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/styles.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/table.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/table.cpython-312.pyc deleted file mode 100644 index d65bc02b..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/table.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/xmlchemy.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/xmlchemy.cpython-312.pyc deleted file mode 100644 index 9d1da020..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/oxml/__pycache__/xmlchemy.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/comments.py b/.venv-docs/lib/python3.12/site-packages/docx/oxml/comments.py deleted file mode 100644 index ad982175..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/oxml/comments.py +++ /dev/null @@ -1,124 +0,0 @@ -"""Custom element classes related to document comments.""" - -from __future__ import annotations - -import datetime as dt -from typing import TYPE_CHECKING, Callable, cast - -from docx.oxml.ns import nsdecls -from docx.oxml.parser import parse_xml -from docx.oxml.simpletypes import ST_DateTime, ST_DecimalNumber, ST_String -from docx.oxml.xmlchemy import BaseOxmlElement, OptionalAttribute, RequiredAttribute, ZeroOrMore - -if TYPE_CHECKING: - from docx.oxml.table import CT_Tbl - from docx.oxml.text.paragraph import CT_P - - -class CT_Comments(BaseOxmlElement): - """`w:comments` element, the root element for the comments part. - - Simply contains a collection of `w:comment` elements, each representing a single comment. Each - contained comment is identified by a unique `w:id` attribute, used to reference the comment - from the document text. The offset of the comment in this collection is arbitrary; it is - essentially a _set_ implemented as a list. - """ - - # -- type-declarations to fill in the gaps for metaclass-added methods -- - comment_lst: list[CT_Comment] - - comment = ZeroOrMore("w:comment") - - def add_comment(self) -> CT_Comment: - """Return newly added `w:comment` child of this `w:comments`. - - The returned `w:comment` element is the minimum valid value, having a `w:id` value unique - within the existing comments and the required `w:author` attribute present but set to the - empty string. It's content is limited to a single run containing the necessary annotation - reference but no text. Content is added by adding runs to this first paragraph and by - adding additional paragraphs as needed. - """ - next_id = self._next_available_comment_id() - comment = cast( - CT_Comment, - parse_xml( - f'' - f" " - f" " - f' ' - f" " - f" " - f" " - f' ' - f" " - f" " - f" " - f" " - f"" - ), - ) - self.append(comment) - return comment - - def get_comment_by_id(self, comment_id: int) -> CT_Comment | None: - """Return the `w:comment` element identified by `comment_id`, or |None| if not found.""" - comment_elms = self.xpath(f"(./w:comment[@w:id='{comment_id}'])[1]") - return comment_elms[0] if comment_elms else None - - def _next_available_comment_id(self) -> int: - """The next available comment id. - - According to the schema, this can be any positive integer, as big as you like, and the - default mechanism is to use `max() + 1`. However, if that yields a value larger than will - fit in a 32-bit signed integer, we take a more deliberate approach to use the first - ununsed integer starting from 0. - """ - used_ids = [int(x) for x in self.xpath("./w:comment/@w:id")] - - next_id = max(used_ids, default=-1) + 1 - - if next_id <= 2**31 - 1: - return next_id - - # -- fall-back to enumerating all used ids to find the first unused one -- - for expected, actual in enumerate(sorted(used_ids)): - if expected != actual: - return expected - - return len(used_ids) - - -class CT_Comment(BaseOxmlElement): - """`w:comment` element, representing a single comment. - - A comment is a so-called "story" and can contain paragraphs and tables much like a table-cell. - While probably most often used for a single sentence or phrase, a comment can contain rich - content, including multiple rich-text paragraphs, hyperlinks, images, and tables. - """ - - # -- attributes on `w:comment` -- - id: int = RequiredAttribute("w:id", ST_DecimalNumber) # pyright: ignore[reportAssignmentType] - author: str = RequiredAttribute("w:author", ST_String) # pyright: ignore[reportAssignmentType] - initials: str | None = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "w:initials", ST_String - ) - date: dt.datetime | None = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "w:date", ST_DateTime - ) - - # -- children -- - - p = ZeroOrMore("w:p", successors=()) - tbl = ZeroOrMore("w:tbl", successors=()) - - # -- type-declarations for methods added by metaclass -- - - add_p: Callable[[], CT_P] - p_lst: list[CT_P] - tbl_lst: list[CT_Tbl] - _insert_tbl: Callable[[CT_Tbl], CT_Tbl] - - @property - def inner_content_elements(self) -> list[CT_P | CT_Tbl]: - """Generate all `w:p` and `w:tbl` elements in this comment.""" - return self.xpath("./w:p | ./w:tbl") diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/coreprops.py b/.venv-docs/lib/python3.12/site-packages/docx/oxml/coreprops.py deleted file mode 100644 index fcff0c7b..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/oxml/coreprops.py +++ /dev/null @@ -1,298 +0,0 @@ -"""Custom element classes for core properties-related XML elements.""" - -from __future__ import annotations - -import datetime as dt -import re -from typing import TYPE_CHECKING, Any, Callable, cast - -from docx.oxml.ns import nsdecls, qn -from docx.oxml.parser import parse_xml -from docx.oxml.xmlchemy import BaseOxmlElement, ZeroOrOne - -if TYPE_CHECKING: - from lxml.etree import _Element as etree_Element # pyright: ignore[reportPrivateUsage] - - -class CT_CoreProperties(BaseOxmlElement): - """`` element, the root element of the Core Properties part. - - Stored as `/docProps/core.xml`. Implements many of the Dublin Core document metadata - elements. String elements resolve to an empty string ("") if the element is not - present in the XML. String elements are limited in length to 255 unicode characters. - """ - - get_or_add_revision: Callable[[], etree_Element] - - category = ZeroOrOne("cp:category", successors=()) - contentStatus = ZeroOrOne("cp:contentStatus", successors=()) - created = ZeroOrOne("dcterms:created", successors=()) - creator = ZeroOrOne("dc:creator", successors=()) - description = ZeroOrOne("dc:description", successors=()) - identifier = ZeroOrOne("dc:identifier", successors=()) - keywords = ZeroOrOne("cp:keywords", successors=()) - language = ZeroOrOne("dc:language", successors=()) - lastModifiedBy = ZeroOrOne("cp:lastModifiedBy", successors=()) - lastPrinted = ZeroOrOne("cp:lastPrinted", successors=()) - modified = ZeroOrOne("dcterms:modified", successors=()) - revision: etree_Element | None = ZeroOrOne( # pyright: ignore[reportAssignmentType] - "cp:revision", successors=() - ) - subject = ZeroOrOne("dc:subject", successors=()) - title = ZeroOrOne("dc:title", successors=()) - version = ZeroOrOne("cp:version", successors=()) - - _coreProperties_tmpl = "\n" % nsdecls("cp", "dc", "dcterms") - - @classmethod - def new(cls) -> CT_CoreProperties: - """Return a new `` element.""" - xml = cls._coreProperties_tmpl - coreProperties = cast(CT_CoreProperties, parse_xml(xml)) - return coreProperties - - @property - def author_text(self) -> str: - """The text in the `dc:creator` child element.""" - return self._text_of_element("creator") - - @author_text.setter - def author_text(self, value: str): - self._set_element_text("creator", value) - - @property - def category_text(self) -> str: - return self._text_of_element("category") - - @category_text.setter - def category_text(self, value: str): - self._set_element_text("category", value) - - @property - def comments_text(self) -> str: - return self._text_of_element("description") - - @comments_text.setter - def comments_text(self, value: str): - self._set_element_text("description", value) - - @property - def contentStatus_text(self) -> str: - return self._text_of_element("contentStatus") - - @contentStatus_text.setter - def contentStatus_text(self, value: str): - self._set_element_text("contentStatus", value) - - @property - def created_datetime(self) -> dt.datetime | None: - return self._datetime_of_element("created") - - @created_datetime.setter - def created_datetime(self, value: dt.datetime): - self._set_element_datetime("created", value) - - @property - def identifier_text(self) -> str: - return self._text_of_element("identifier") - - @identifier_text.setter - def identifier_text(self, value: str): - self._set_element_text("identifier", value) - - @property - def keywords_text(self) -> str: - return self._text_of_element("keywords") - - @keywords_text.setter - def keywords_text(self, value: str): - self._set_element_text("keywords", value) - - @property - def language_text(self) -> str: - return self._text_of_element("language") - - @language_text.setter - def language_text(self, value: str): - self._set_element_text("language", value) - - @property - def lastModifiedBy_text(self) -> str: - return self._text_of_element("lastModifiedBy") - - @lastModifiedBy_text.setter - def lastModifiedBy_text(self, value: str): - self._set_element_text("lastModifiedBy", value) - - @property - def lastPrinted_datetime(self) -> dt.datetime | None: - return self._datetime_of_element("lastPrinted") - - @lastPrinted_datetime.setter - def lastPrinted_datetime(self, value: dt.datetime): - self._set_element_datetime("lastPrinted", value) - - @property - def modified_datetime(self) -> dt.datetime | None: - return self._datetime_of_element("modified") - - @modified_datetime.setter - def modified_datetime(self, value: dt.datetime): - self._set_element_datetime("modified", value) - - @property - def revision_number(self) -> int: - """Integer value of revision property.""" - revision = self.revision - if revision is None: - return 0 - revision_str = str(revision.text) - try: - revision = int(revision_str) - except ValueError: - # non-integer revision strings also resolve to 0 - revision = 0 - # as do negative integers - if revision < 0: - revision = 0 - return revision - - @revision_number.setter - def revision_number(self, value: int): - """Set revision property to string value of integer `value`.""" - if not isinstance(value, int) or value < 1: # pyright: ignore[reportUnnecessaryIsInstance] - tmpl = "revision property requires positive int, got '%s'" - raise ValueError(tmpl % value) - revision = self.get_or_add_revision() - revision.text = str(value) - - @property - def subject_text(self) -> str: - return self._text_of_element("subject") - - @subject_text.setter - def subject_text(self, value: str): - self._set_element_text("subject", value) - - @property - def title_text(self) -> str: - return self._text_of_element("title") - - @title_text.setter - def title_text(self, value: str): - self._set_element_text("title", value) - - @property - def version_text(self) -> str: - return self._text_of_element("version") - - @version_text.setter - def version_text(self, value: str): - self._set_element_text("version", value) - - def _datetime_of_element(self, property_name: str) -> dt.datetime | None: - element = getattr(self, property_name) - if element is None: - return None - datetime_str = element.text - try: - return self._parse_W3CDTF_to_datetime(datetime_str) - except ValueError: - # invalid datetime strings are ignored - return None - - def _get_or_add(self, prop_name: str) -> BaseOxmlElement: - """Return element returned by "get_or_add_" method for `prop_name`.""" - get_or_add_method_name = "get_or_add_%s" % prop_name - get_or_add_method = getattr(self, get_or_add_method_name) - element = get_or_add_method() - return element - - @classmethod - def _offset_dt(cls, dt_: dt.datetime, offset_str: str) -> dt.datetime: - """A |datetime| instance offset from `dt_` by timezone offset in `offset_str`. - - `offset_str` is like `"-07:00"`. - """ - match = cls._offset_pattern.match(offset_str) - if match is None: - raise ValueError("'%s' is not a valid offset string" % offset_str) - sign, hours_str, minutes_str = match.groups() - sign_factor = -1 if sign == "+" else 1 - hours = int(hours_str) * sign_factor - minutes = int(minutes_str) * sign_factor - td = dt.timedelta(hours=hours, minutes=minutes) - return dt_ + td - - _offset_pattern = re.compile(r"([+-])(\d\d):(\d\d)") - - @classmethod - def _parse_W3CDTF_to_datetime(cls, w3cdtf_str: str) -> dt.datetime: - # valid W3CDTF date cases: - # yyyy e.g. "2003" - # yyyy-mm e.g. "2003-12" - # yyyy-mm-dd e.g. "2003-12-31" - # UTC timezone e.g. "2003-12-31T10:14:55Z" - # numeric timezone e.g. "2003-12-31T10:14:55-08:00" - templates = ( - "%Y-%m-%dT%H:%M:%S", - "%Y-%m-%d", - "%Y-%m", - "%Y", - ) - # strptime isn't smart enough to parse literal timezone offsets like - # "-07:30", so we have to do it ourselves - parseable_part = w3cdtf_str[:19] - offset_str = w3cdtf_str[19:] - dt_ = None - for tmpl in templates: - try: - dt_ = dt.datetime.strptime(parseable_part, tmpl) - except ValueError: - continue - if dt_ is None: - tmpl = "could not parse W3CDTF datetime string '%s'" - raise ValueError(tmpl % w3cdtf_str) - if len(offset_str) == 6: - dt_ = cls._offset_dt(dt_, offset_str) - return dt_.replace(tzinfo=dt.timezone.utc) - - def _set_element_datetime(self, prop_name: str, value: dt.datetime) -> None: - """Set date/time value of child element having `prop_name` to `value`.""" - if not isinstance(value, dt.datetime): # pyright: ignore[reportUnnecessaryIsInstance] - tmpl = "property requires object, got %s" - raise ValueError(tmpl % type(value)) - element = self._get_or_add(prop_name) - dt_str = value.strftime("%Y-%m-%dT%H:%M:%SZ") - element.text = dt_str - if prop_name in ("created", "modified"): - # These two require an explicit "xsi:type="dcterms:W3CDTF"" - # attribute. The first and last line are a hack required to add - # the xsi namespace to the root element rather than each child - # element in which it is referenced - self.set(qn("xsi:foo"), "bar") - element.set(qn("xsi:type"), "dcterms:W3CDTF") - del self.attrib[qn("xsi:foo")] - - def _set_element_text(self, prop_name: str, value: Any) -> None: - """Set string value of `name` property to `value`.""" - if not isinstance(value, str): - value = str(value) - - if len(value) > 255: - tmpl = "exceeded 255 char limit for property, got:\n\n'%s'" - raise ValueError(tmpl % value) - element = self._get_or_add(prop_name) - element.text = value - - def _text_of_element(self, property_name: str) -> str: - """The text in the element matching `property_name`. - - The empty string if the element is not present or contains no text. - """ - element = getattr(self, property_name) - if element is None: - return "" - if element.text is None: - return "" - return element.text diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/document.py b/.venv-docs/lib/python3.12/site-packages/docx/oxml/document.py deleted file mode 100644 index 36819ef7..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/oxml/document.py +++ /dev/null @@ -1,88 +0,0 @@ -"""Custom element classes that correspond to the document part, e.g. .""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Callable, List - -from docx.oxml.section import CT_SectPr -from docx.oxml.xmlchemy import BaseOxmlElement, ZeroOrMore, ZeroOrOne - -if TYPE_CHECKING: - from docx.oxml.table import CT_Tbl - from docx.oxml.text.paragraph import CT_P - - -class CT_Document(BaseOxmlElement): - """```` element, the root element of a document.xml file.""" - - body: CT_Body = ZeroOrOne("w:body") # pyright: ignore[reportAssignmentType] - - @property - def sectPr_lst(self) -> List[CT_SectPr]: - """All `w:sectPr` elements directly accessible from document element. - - Note this does not include a `sectPr` child in a paragraphs wrapped in - revision marks or other intervening layer, perhaps `w:sdt` or customXml - elements. - - `w:sectPr` elements appear in document order. The last one is always - `w:body/w:sectPr`, all preceding are `w:p/w:pPr/w:sectPr`. - """ - xpath = "./w:body/w:p/w:pPr/w:sectPr | ./w:body/w:sectPr" - return self.xpath(xpath) - - -class CT_Body(BaseOxmlElement): - """`w:body`, the container element for the main document story in `document.xml`.""" - - add_p: Callable[[], CT_P] - get_or_add_sectPr: Callable[[], CT_SectPr] - p_lst: List[CT_P] - tbl_lst: List[CT_Tbl] - - _insert_tbl: Callable[[CT_Tbl], CT_Tbl] - - p = ZeroOrMore("w:p", successors=("w:sectPr",)) - tbl = ZeroOrMore("w:tbl", successors=("w:sectPr",)) - sectPr: CT_SectPr | None = ZeroOrOne( # pyright: ignore[reportAssignmentType] - "w:sectPr", successors=() - ) - - def add_section_break(self) -> CT_SectPr: - """Return `w:sectPr` element for new section added at end of document. - - The last `w:sectPr` becomes the second-to-last, with the new `w:sectPr` being an - exact clone of the previous one, except that all header and footer references - are removed (and are therefore now "inherited" from the prior section). - - A copy of the previously-last `w:sectPr` will now appear in a new `w:p` at the - end of the document. The returned `w:sectPr` is the sentinel `w:sectPr` for the - document (and as implemented, `is` the prior sentinel `w:sectPr` with headers - and footers removed). - """ - # ---get the sectPr at file-end, which controls last section (sections[-1])--- - sentinel_sectPr = self.get_or_add_sectPr() - # ---add exact copy to new `w:p` element; that is now second-to last section--- - self.add_p().set_sectPr(sentinel_sectPr.clone()) - # ---remove any header or footer references from "new" last section--- - for hdrftr_ref in sentinel_sectPr.xpath("w:headerReference|w:footerReference"): - sentinel_sectPr.remove(hdrftr_ref) - # ---the sentinel `w:sectPr` now controls the new last section--- - return sentinel_sectPr - - def clear_content(self): - """Remove all content child elements from this element. - - Leave the element if it is present. - """ - for content_elm in self.xpath("./*[not(self::w:sectPr)]"): - self.remove(content_elm) - - @property - def inner_content_elements(self) -> List[CT_P | CT_Tbl]: - """Generate all `w:p` and `w:tbl` elements in this document-body. - - Elements appear in document order. Elements shaded by nesting in a `w:ins` or - other "wrapper" element will not be included. - """ - return self.xpath("./w:p | ./w:tbl") diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/drawing.py b/.venv-docs/lib/python3.12/site-packages/docx/oxml/drawing.py deleted file mode 100644 index 5b627f97..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/oxml/drawing.py +++ /dev/null @@ -1,11 +0,0 @@ -"""Custom element-classes for DrawingML-related elements like ``. - -For legacy reasons, many DrawingML-related elements are in `docx.oxml.shape`. Expect -those to move over here as we have reason to touch them. -""" - -from docx.oxml.xmlchemy import BaseOxmlElement - - -class CT_Drawing(BaseOxmlElement): - """`` element, containing a DrawingML object like a picture or chart.""" diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/exceptions.py b/.venv-docs/lib/python3.12/site-packages/docx/oxml/exceptions.py deleted file mode 100644 index 8919239a..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/oxml/exceptions.py +++ /dev/null @@ -1,10 +0,0 @@ -"""Exceptions for oxml sub-package.""" - - -class XmlchemyError(Exception): - """Generic error class.""" - - -class InvalidXmlError(XmlchemyError): - """Raised when invalid XML is encountered, such as on attempt to access a missing - required child element.""" diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/ns.py b/.venv-docs/lib/python3.12/site-packages/docx/oxml/ns.py deleted file mode 100644 index ce03940f..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/oxml/ns.py +++ /dev/null @@ -1,109 +0,0 @@ -"""Namespace-related objects.""" - -from __future__ import annotations - -from typing import Dict - -nsmap = { - "a": "http://schemas.openxmlformats.org/drawingml/2006/main", - "c": "http://schemas.openxmlformats.org/drawingml/2006/chart", - "cp": "http://schemas.openxmlformats.org/package/2006/metadata/core-properties", - "dc": "http://purl.org/dc/elements/1.1/", - "dcmitype": "http://purl.org/dc/dcmitype/", - "dcterms": "http://purl.org/dc/terms/", - "dgm": "http://schemas.openxmlformats.org/drawingml/2006/diagram", - "m": "http://schemas.openxmlformats.org/officeDocument/2006/math", - "pic": "http://schemas.openxmlformats.org/drawingml/2006/picture", - "r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships", - "sl": "http://schemas.openxmlformats.org/schemaLibrary/2006/main", - "w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main", - "w14": "http://schemas.microsoft.com/office/word/2010/wordml", - "wp": "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing", - "xml": "http://www.w3.org/XML/1998/namespace", - "xsi": "http://www.w3.org/2001/XMLSchema-instance", -} - -pfxmap = {value: key for key, value in nsmap.items()} - - -class NamespacePrefixedTag(str): - """Value object that knows the semantics of an XML tag having a namespace prefix.""" - - def __new__(cls, nstag: str): - return super(NamespacePrefixedTag, cls).__new__(cls, nstag) - - def __init__(self, nstag: str): - self._pfx, self._local_part = nstag.split(":") - self._ns_uri = nsmap[self._pfx] - - @property - def clark_name(self) -> str: - return "{%s}%s" % (self._ns_uri, self._local_part) - - @classmethod - def from_clark_name(cls, clark_name: str) -> NamespacePrefixedTag: - nsuri, local_name = clark_name[1:].split("}") - nstag = "%s:%s" % (pfxmap[nsuri], local_name) - return cls(nstag) - - @property - def local_part(self) -> str: - """The local part of this tag. - - E.g. "foobar" is returned for tag "f:foobar". - """ - return self._local_part - - @property - def nsmap(self) -> Dict[str, str]: - """Single-member dict mapping prefix of this tag to it's namespace name. - - Example: `{"f": "http://foo/bar"}`. This is handy for passing to xpath calls - and other uses. - """ - return {self._pfx: self._ns_uri} - - @property - def nspfx(self) -> str: - """The namespace-prefix for this tag. - - For example, "f" is returned for tag "f:foobar". - """ - return self._pfx - - @property - def nsuri(self) -> str: - """The namespace URI for this tag. - - For example, "http://foo/bar" would be returned for tag "f:foobar" if the "f" - prefix maps to "http://foo/bar" in nsmap. - """ - return self._ns_uri - - -def nsdecls(*prefixes: str) -> str: - """Namespace declaration including each namespace-prefix in `prefixes`. - - Handy for adding required namespace declarations to a tree root element. - """ - return " ".join(['xmlns:%s="%s"' % (pfx, nsmap[pfx]) for pfx in prefixes]) - - -def nspfxmap(*nspfxs: str) -> Dict[str, str]: - """Subset namespace-prefix mappings specified by *nspfxs*. - - Any number of namespace prefixes can be supplied, e.g. namespaces("a", "r", "p"). - """ - return {pfx: nsmap[pfx] for pfx in nspfxs} - - -def qn(tag: str) -> str: - """Stands for "qualified name". - - This utility function converts a familiar namespace-prefixed tag name like "w:p" - into a Clark-notation qualified tag name for lxml. For example, `qn("w:p")` returns - "{http://schemas.openxmlformats.org/wordprocessingml/2006/main}p". - """ - prefix, tagroot = tag.split(":") - uri = nsmap[prefix] - return "{%s}%s" % (uri, tagroot) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/numbering.py b/.venv-docs/lib/python3.12/site-packages/docx/oxml/numbering.py deleted file mode 100644 index 3512de65..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/oxml/numbering.py +++ /dev/null @@ -1,109 +0,0 @@ -"""Custom element classes related to the numbering part.""" - -from docx.oxml.parser import OxmlElement -from docx.oxml.shared import CT_DecimalNumber -from docx.oxml.simpletypes import ST_DecimalNumber -from docx.oxml.xmlchemy import ( - BaseOxmlElement, - OneAndOnlyOne, - RequiredAttribute, - ZeroOrMore, - ZeroOrOne, -) - - -class CT_Num(BaseOxmlElement): - """```` element, which represents a concrete list definition instance, having - a required child that references an abstract numbering definition - that defines most of the formatting details.""" - - abstractNumId = OneAndOnlyOne("w:abstractNumId") - lvlOverride = ZeroOrMore("w:lvlOverride") - numId = RequiredAttribute("w:numId", ST_DecimalNumber) - - def add_lvlOverride(self, ilvl): - """Return a newly added CT_NumLvl () element having its ``ilvl`` - attribute set to `ilvl`.""" - return self._add_lvlOverride(ilvl=ilvl) - - @classmethod - def new(cls, num_id, abstractNum_id): - """Return a new ```` element having numId of `num_id` and having a - ```` child with val attribute set to `abstractNum_id`.""" - num = OxmlElement("w:num") - num.numId = num_id - abstractNumId = CT_DecimalNumber.new("w:abstractNumId", abstractNum_id) - num.append(abstractNumId) - return num - - -class CT_NumLvl(BaseOxmlElement): - """```` element, which identifies a level in a list definition to - override with settings it contains.""" - - startOverride = ZeroOrOne("w:startOverride", successors=("w:lvl",)) - ilvl = RequiredAttribute("w:ilvl", ST_DecimalNumber) - - def add_startOverride(self, val): - """Return a newly added CT_DecimalNumber element having tagname - ``w:startOverride`` and ``val`` attribute set to `val`.""" - return self._add_startOverride(val=val) - - -class CT_NumPr(BaseOxmlElement): - """A ```` element, a container for numbering properties applied to a - paragraph.""" - - ilvl = ZeroOrOne("w:ilvl", successors=("w:numId", "w:numberingChange", "w:ins")) - numId = ZeroOrOne("w:numId", successors=("w:numberingChange", "w:ins")) - - # @ilvl.setter - # def _set_ilvl(self, val): - # """ - # Get or add a child and set its ``w:val`` attribute to `val`. - # """ - # ilvl = self.get_or_add_ilvl() - # ilvl.val = val - - # @numId.setter - # def numId(self, val): - # """ - # Get or add a child and set its ``w:val`` attribute to - # `val`. - # """ - # numId = self.get_or_add_numId() - # numId.val = val - - -class CT_Numbering(BaseOxmlElement): - """```` element, the root element of a numbering part, i.e. - numbering.xml.""" - - num = ZeroOrMore("w:num", successors=("w:numIdMacAtCleanup",)) - - def add_num(self, abstractNum_id): - """Return a newly added CT_Num () element referencing the abstract - numbering definition identified by `abstractNum_id`.""" - next_num_id = self._next_numId - num = CT_Num.new(next_num_id, abstractNum_id) - return self._insert_num(num) - - def num_having_numId(self, numId): - """Return the ```` child element having ``numId`` attribute matching - `numId`.""" - xpath = './w:num[@w:numId="%d"]' % numId - try: - return self.xpath(xpath)[0] - except IndexError: - raise KeyError("no element with numId %d" % numId) - - @property - def _next_numId(self): - """The first ``numId`` unused by a ```` element, starting at 1 and - filling any gaps in numbering between existing ```` elements.""" - numId_strs = self.xpath("./w:num/@w:numId") - num_ids = [int(numId_str) for numId_str in numId_strs] - for num in range(1, len(num_ids) + 2): - if num not in num_ids: - break - return num diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/parser.py b/.venv-docs/lib/python3.12/site-packages/docx/oxml/parser.py deleted file mode 100644 index e16ba30b..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/oxml/parser.py +++ /dev/null @@ -1,62 +0,0 @@ -# pyright: reportImportCycles=false - -"""XML parser for python-docx.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Dict, Type, cast - -from lxml import etree - -from docx.oxml.ns import NamespacePrefixedTag, nsmap - -if TYPE_CHECKING: - from docx.oxml.xmlchemy import BaseOxmlElement - - -# -- configure XML parser -- -element_class_lookup = etree.ElementNamespaceClassLookup() -oxml_parser = etree.XMLParser(remove_blank_text=True, resolve_entities=False) -oxml_parser.set_element_class_lookup(element_class_lookup) - - -def parse_xml(xml: str | bytes) -> "BaseOxmlElement": - """Root lxml element obtained by parsing XML character string `xml`. - - The custom parser is used, so custom element classes are produced for elements in - `xml` that have them. - """ - return cast("BaseOxmlElement", etree.fromstring(xml, oxml_parser)) - - -def register_element_cls(tag: str, cls: Type["BaseOxmlElement"]): - """Register an lxml custom element-class to use for `tag`. - - A instance of `cls` to be constructed when the oxml parser encounters an element - with matching `tag`. `tag` is a string of the form `nspfx:tagroot`, e.g. - `'w:document'`. - """ - nspfx, tagroot = tag.split(":") - namespace = element_class_lookup.get_namespace(nsmap[nspfx]) - namespace[tagroot] = cls - - -def OxmlElement( - nsptag_str: str, - attrs: Dict[str, str] | None = None, - nsdecls: Dict[str, str] | None = None, -) -> BaseOxmlElement | etree._Element: # pyright: ignore[reportPrivateUsage] - """Return a 'loose' lxml element having the tag specified by `nsptag_str`. - - The tag in `nsptag_str` must contain the standard namespace prefix, e.g. `a:tbl`. - The resulting element is an instance of the custom element class for this tag name - if one is defined. A dictionary of attribute values may be provided as `attrs`; they - are set if present. All namespaces defined in the dict `nsdecls` are declared in the - element using the key as the prefix and the value as the namespace name. If - `nsdecls` is not provided, a single namespace declaration is added based on the - prefix on `nsptag_str`. - """ - nsptag = NamespacePrefixedTag(nsptag_str) - if nsdecls is None: - nsdecls = nsptag.nsmap - return oxml_parser.makeelement(nsptag.clark_name, attrib=attrs, nsmap=nsdecls) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/section.py b/.venv-docs/lib/python3.12/site-packages/docx/oxml/section.py deleted file mode 100644 index 71072e2d..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/oxml/section.py +++ /dev/null @@ -1,537 +0,0 @@ -"""Section-related custom element classes.""" - -from __future__ import annotations - -from copy import deepcopy -from typing import Callable, Iterator, List, Sequence, cast - -from lxml import etree -from typing_extensions import TypeAlias - -from docx.enum.section import WD_HEADER_FOOTER, WD_ORIENTATION, WD_SECTION_START -from docx.oxml.ns import nsmap -from docx.oxml.shared import CT_OnOff -from docx.oxml.simpletypes import ST_SignedTwipsMeasure, ST_TwipsMeasure, XsdString -from docx.oxml.table import CT_Tbl -from docx.oxml.text.paragraph import CT_P -from docx.oxml.xmlchemy import ( - BaseOxmlElement, - OptionalAttribute, - RequiredAttribute, - ZeroOrMore, - ZeroOrOne, -) -from docx.shared import Length, lazyproperty - -BlockElement: TypeAlias = "CT_P | CT_Tbl" - - -class CT_HdrFtr(BaseOxmlElement): - """`w:hdr` and `w:ftr`, the root element for header and footer part respectively.""" - - add_p: Callable[[], CT_P] - p_lst: List[CT_P] - tbl_lst: List[CT_Tbl] - - _insert_tbl: Callable[[CT_Tbl], CT_Tbl] - - p = ZeroOrMore("w:p", successors=()) - tbl = ZeroOrMore("w:tbl", successors=()) - - @property - def inner_content_elements(self) -> List[CT_P | CT_Tbl]: - """Generate all `w:p` and `w:tbl` elements in this header or footer. - - Elements appear in document order. Elements shaded by nesting in a `w:ins` or - other "wrapper" element will not be included. - """ - return self.xpath("./w:p | ./w:tbl") - - -class CT_HdrFtrRef(BaseOxmlElement): - """`w:headerReference` and `w:footerReference` elements.""" - - type_: WD_HEADER_FOOTER = RequiredAttribute( # pyright: ignore[reportAssignmentType] - "w:type", WD_HEADER_FOOTER - ) - rId: str = RequiredAttribute("r:id", XsdString) # pyright: ignore[reportAssignmentType] - - -class CT_PageMar(BaseOxmlElement): - """```` element, defining page margins.""" - - top: Length | None = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "w:top", ST_SignedTwipsMeasure - ) - right: Length | None = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "w:right", ST_TwipsMeasure - ) - bottom: Length | None = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "w:bottom", ST_SignedTwipsMeasure - ) - left: Length | None = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "w:left", ST_TwipsMeasure - ) - header: Length | None = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "w:header", ST_TwipsMeasure - ) - footer: Length | None = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "w:footer", ST_TwipsMeasure - ) - gutter: Length | None = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "w:gutter", ST_TwipsMeasure - ) - - -class CT_PageSz(BaseOxmlElement): - """```` element, defining page dimensions and orientation.""" - - w: Length | None = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "w:w", ST_TwipsMeasure - ) - h: Length | None = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "w:h", ST_TwipsMeasure - ) - orient: WD_ORIENTATION = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "w:orient", WD_ORIENTATION, default=WD_ORIENTATION.PORTRAIT - ) - - -class CT_SectPr(BaseOxmlElement): - """`w:sectPr` element, the container element for section properties.""" - - get_or_add_pgMar: Callable[[], CT_PageMar] - get_or_add_pgSz: Callable[[], CT_PageSz] - get_or_add_titlePg: Callable[[], CT_OnOff] - get_or_add_type: Callable[[], CT_SectType] - _add_footerReference: Callable[[], CT_HdrFtrRef] - _add_headerReference: Callable[[], CT_HdrFtrRef] - _remove_titlePg: Callable[[], None] - _remove_type: Callable[[], None] - - _tag_seq = ( - "w:footnotePr", - "w:endnotePr", - "w:type", - "w:pgSz", - "w:pgMar", - "w:paperSrc", - "w:pgBorders", - "w:lnNumType", - "w:pgNumType", - "w:cols", - "w:formProt", - "w:vAlign", - "w:noEndnote", - "w:titlePg", - "w:textDirection", - "w:bidi", - "w:rtlGutter", - "w:docGrid", - "w:printerSettings", - "w:sectPrChange", - ) - headerReference = ZeroOrMore("w:headerReference", successors=_tag_seq) - footerReference = ZeroOrMore("w:footerReference", successors=_tag_seq) - type: CT_SectType | None = ZeroOrOne( # pyright: ignore[reportAssignmentType] - "w:type", successors=_tag_seq[3:] - ) - pgSz: CT_PageSz | None = ZeroOrOne( # pyright: ignore[reportAssignmentType] - "w:pgSz", successors=_tag_seq[4:] - ) - pgMar: CT_PageMar | None = ZeroOrOne( # pyright: ignore[reportAssignmentType] - "w:pgMar", successors=_tag_seq[5:] - ) - titlePg: CT_OnOff | None = ZeroOrOne( # pyright: ignore[reportAssignmentType] - "w:titlePg", successors=_tag_seq[14:] - ) - del _tag_seq - - def add_footerReference(self, type_: WD_HEADER_FOOTER, rId: str) -> CT_HdrFtrRef: - """Return newly added CT_HdrFtrRef element of `type_` with `rId`. - - The element tag is `w:footerReference`. - """ - footerReference = self._add_footerReference() - footerReference.type_ = type_ - footerReference.rId = rId - return footerReference - - def add_headerReference(self, type_: WD_HEADER_FOOTER, rId: str) -> CT_HdrFtrRef: - """Return newly added CT_HdrFtrRef element of `type_` with `rId`. - - The element tag is `w:headerReference`. - """ - headerReference = self._add_headerReference() - headerReference.type_ = type_ - headerReference.rId = rId - return headerReference - - @property - def bottom_margin(self) -> Length | None: - """Value of the `w:bottom` attr of `` child element, as |Length|. - - |None| when either the element or the attribute is not present. - """ - pgMar = self.pgMar - if pgMar is None: - return None - return pgMar.bottom - - @bottom_margin.setter - def bottom_margin(self, value: int | Length | None): - pgMar = self.get_or_add_pgMar() - pgMar.bottom = value if value is None or isinstance(value, Length) else Length(value) - - def clone(self) -> CT_SectPr: - """Return an exact duplicate of this ```` element tree suitable for - use in adding a section break. - - All rsid* attributes are removed from the root ```` element. - """ - cloned_sectPr = deepcopy(self) - cloned_sectPr.attrib.clear() - return cloned_sectPr - - @property - def footer(self) -> Length | None: - """Distance from bottom edge of page to bottom edge of the footer. - - This is the value of the `w:footer` attribute in the `w:pgMar` child element, - as a |Length| object, or |None| if either the element or the attribute is not - present. - """ - pgMar = self.pgMar - if pgMar is None: - return None - return pgMar.footer - - @footer.setter - def footer(self, value: int | Length | None): - pgMar = self.get_or_add_pgMar() - pgMar.footer = value if value is None or isinstance(value, Length) else Length(value) - - def get_footerReference(self, type_: WD_HEADER_FOOTER) -> CT_HdrFtrRef | None: - """Return footerReference element of `type_` or None if not present.""" - path = "./w:footerReference[@w:type='%s']" % WD_HEADER_FOOTER.to_xml(type_) - footerReferences = self.xpath(path) - if not footerReferences: - return None - return footerReferences[0] - - def get_headerReference(self, type_: WD_HEADER_FOOTER) -> CT_HdrFtrRef | None: - """Return headerReference element of `type_` or None if not present.""" - matching_headerReferences = self.xpath( - "./w:headerReference[@w:type='%s']" % WD_HEADER_FOOTER.to_xml(type_) - ) - if len(matching_headerReferences) == 0: - return None - return matching_headerReferences[0] - - @property - def gutter(self) -> Length | None: - """The value of the ``w:gutter`` attribute in the ```` child element, - as a |Length| object, or |None| if either the element or the attribute is not - present.""" - pgMar = self.pgMar - if pgMar is None: - return None - return pgMar.gutter - - @gutter.setter - def gutter(self, value: int | Length | None): - pgMar = self.get_or_add_pgMar() - pgMar.gutter = value if value is None or isinstance(value, Length) else Length(value) - - @property - def header(self) -> Length | None: - """Distance from top edge of page to top edge of header. - - This value comes from the `w:header` attribute on the `w:pgMar` child element. - |None| if either the element or the attribute is not present. - """ - pgMar = self.pgMar - if pgMar is None: - return None - return pgMar.header - - @header.setter - def header(self, value: int | Length | None): - pgMar = self.get_or_add_pgMar() - pgMar.header = value if value is None or isinstance(value, Length) else Length(value) - - def iter_inner_content(self) -> Iterator[CT_P | CT_Tbl]: - """Generate all `w:p` and `w:tbl` elements in this section. - - Elements appear in document order. Elements shaded by nesting in a `w:ins` or - other "wrapper" element will not be included. - """ - return _SectBlockElementIterator.iter_sect_block_elements(self) - - @property - def left_margin(self) -> Length | None: - """The value of the ``w:left`` attribute in the ```` child element, as - a |Length| object, or |None| if either the element or the attribute is not - present.""" - pgMar = self.pgMar - if pgMar is None: - return None - return pgMar.left - - @left_margin.setter - def left_margin(self, value: int | Length | None): - pgMar = self.get_or_add_pgMar() - pgMar.left = value if value is None or isinstance(value, Length) else Length(value) - - @property - def orientation(self) -> WD_ORIENTATION: - """`WD_ORIENTATION` member indicating page-orientation for this section. - - This is the value of the `orient` attribute on the `w:pgSz` child, or - `WD_ORIENTATION.PORTRAIT` if not present. - """ - pgSz = self.pgSz - if pgSz is None: - return WD_ORIENTATION.PORTRAIT - return pgSz.orient - - @orientation.setter - def orientation(self, value: WD_ORIENTATION | None): - pgSz = self.get_or_add_pgSz() - pgSz.orient = value if value else WD_ORIENTATION.PORTRAIT - - @property - def page_height(self) -> Length | None: - """Value in EMU of the `h` attribute of the `w:pgSz` child element. - - |None| if not present. - """ - pgSz = self.pgSz - if pgSz is None: - return None - return pgSz.h - - @page_height.setter - def page_height(self, value: Length | None): - pgSz = self.get_or_add_pgSz() - pgSz.h = value - - @property - def page_width(self) -> Length | None: - """Value in EMU of the ``w`` attribute of the ```` child element. - - |None| if not present. - """ - pgSz = self.pgSz - if pgSz is None: - return None - return pgSz.w - - @page_width.setter - def page_width(self, value: Length | None): - pgSz = self.get_or_add_pgSz() - pgSz.w = value - - @property - def preceding_sectPr(self) -> CT_SectPr | None: - """SectPr immediately preceding this one or None if this is the first.""" - # -- [1] predicate returns list of zero or one value -- - preceding_sectPrs = self.xpath("./preceding::w:sectPr[1]") - return preceding_sectPrs[0] if len(preceding_sectPrs) > 0 else None - - def remove_footerReference(self, type_: WD_HEADER_FOOTER) -> str: - """Return rId of w:footerReference child of `type_` after removing it.""" - footerReference = self.get_footerReference(type_) - if footerReference is None: - # -- should never happen, but to satisfy type-check and just in case -- - raise ValueError("CT_SectPr has no footer reference") - rId = footerReference.rId - self.remove(footerReference) - return rId - - def remove_headerReference(self, type_: WD_HEADER_FOOTER): - """Return rId of w:headerReference child of `type_` after removing it.""" - headerReference = self.get_headerReference(type_) - if headerReference is None: - # -- should never happen, but to satisfy type-check and just in case -- - raise ValueError("CT_SectPr has no header reference") - rId = headerReference.rId - self.remove(headerReference) - return rId - - @property - def right_margin(self) -> Length | None: - """The value of the ``w:right`` attribute in the ```` child element, as - a |Length| object, or |None| if either the element or the attribute is not - present.""" - pgMar = self.pgMar - if pgMar is None: - return None - return pgMar.right - - @right_margin.setter - def right_margin(self, value: Length | None): - pgMar = self.get_or_add_pgMar() - pgMar.right = value - - @property - def start_type(self) -> WD_SECTION_START: - """The member of the ``WD_SECTION_START`` enumeration corresponding to the value - of the ``val`` attribute of the ```` child element, or - ``WD_SECTION_START.NEW_PAGE`` if not present.""" - type = self.type - if type is None or type.val is None: - return WD_SECTION_START.NEW_PAGE - return type.val - - @start_type.setter - def start_type(self, value: WD_SECTION_START | None): - if value is None or value is WD_SECTION_START.NEW_PAGE: - self._remove_type() - return - type = self.get_or_add_type() - type.val = value - - @property - def titlePg_val(self) -> bool: - """Value of `w:titlePg/@val` or |False| if `./w:titlePg` is not present.""" - titlePg = self.titlePg - if titlePg is None: - return False - return titlePg.val - - @titlePg_val.setter - def titlePg_val(self, value: bool | None): - if value in [None, False]: - self._remove_titlePg() - else: - self.get_or_add_titlePg().val = True - - @property - def top_margin(self) -> Length | None: - """The value of the ``w:top`` attribute in the ```` child element, as a - |Length| object, or |None| if either the element or the attribute is not - present.""" - pgMar = self.pgMar - if pgMar is None: - return None - return pgMar.top - - @top_margin.setter - def top_margin(self, value: Length | None): - pgMar = self.get_or_add_pgMar() - pgMar.top = value - - -class CT_SectType(BaseOxmlElement): - """```` element, defining the section start type.""" - - val: WD_SECTION_START | None = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "w:val", WD_SECTION_START - ) - - -# == HELPERS ========================================================================= - - -class _SectBlockElementIterator: - """Generates the block-item XML elements in a section. - - A block-item element is a `CT_P` (paragraph) or a `CT_Tbl` (table). - """ - - _compiled_blocks_xpath: etree.XPath | None = None - _compiled_count_xpath: etree.XPath | None = None - - def __init__(self, sectPr: CT_SectPr): - self._sectPr = sectPr - - @classmethod - def iter_sect_block_elements(cls, sectPr: CT_SectPr) -> Iterator[BlockElement]: - """Generate each CT_P or CT_Tbl element within extents governed by `sectPr`.""" - return cls(sectPr)._iter_sect_block_elements() - - def _iter_sect_block_elements(self) -> Iterator[BlockElement]: - """Generate each CT_P or CT_Tbl element in section.""" - # -- General strategy is to get all block ( and ) elements from - # -- start of doc to and including this section, then compute the count of those - # -- elements that came from prior sections and skip that many to leave only the - # -- ones in this section. It's possible to express this "between here and - # -- there" (end of prior section and end of this one) concept in XPath, but it - # -- would be harder to follow because there are special cases (e.g. no prior - # -- section) and the boundary expressions are fairly hairy. I also believe it - # -- would be computationally more expensive than doing it this straighforward - # -- albeit (theoretically) slightly wasteful way. - - sectPr, sectPrs = self._sectPr, self._sectPrs - sectPr_idx = sectPrs.index(sectPr) - - # -- count block items belonging to prior sections -- - n_blks_to_skip = ( - 0 - if sectPr_idx == 0 - else self._count_of_blocks_in_and_above_section(sectPrs[sectPr_idx - 1]) - ) - - # -- and skip those in set of all blks from doc start to end of this section -- - for element in self._blocks_in_and_above_section(sectPr)[n_blks_to_skip:]: - yield element - - def _blocks_in_and_above_section(self, sectPr: CT_SectPr) -> Sequence[BlockElement]: - """All ps and tbls in section defined by `sectPr` and all prior sections.""" - if self._compiled_blocks_xpath is None: - self._compiled_blocks_xpath = etree.XPath( - self._blocks_in_and_above_section_xpath, - namespaces=nsmap, - regexp=False, - ) - xpath = self._compiled_blocks_xpath - # -- XPath callable results are Any (basically), so need a cast. -- - return cast(Sequence[BlockElement], xpath(sectPr)) - - @lazyproperty - def _blocks_in_and_above_section_xpath(self) -> str: - """XPath expr for ps and tbls in context of a sectPr and all prior sectPrs.""" - # -- "p_sect" is a section with sectPr located at w:p/w:pPr/w:sectPr. - # -- "body_sect" is a section with sectPr located at w:body/w:sectPr. The last - # -- section in the document is a "body_sect". All others are of the "p_sect" - # -- variety. "term" means "terminal", like the last p or tbl in the section. - # -- "pred" means "predecessor", like a preceding p or tbl in the section. - - # -- the terminal block in a p-based sect is the p the sectPr appears in -- - p_sect_term_block = "./parent::w:pPr/parent::w:p" - # -- the terminus of a body-based sect is the sectPr itself (not a block) -- - body_sect_term = "self::w:sectPr[parent::w:body]" - # -- all the ps and tbls preceding (but not including) the context node -- - pred_ps_and_tbls = "preceding-sibling::*[self::w:p | self::w:tbl]" - - # -- p_sect_term_block and body_sect_term(inus) are mutually exclusive. So the - # -- result is either the union of nodes found by the first two selectors or the - # -- nodes found by the last selector, never both. - return ( - # -- include the p containing a sectPr -- - f"{p_sect_term_block}" - # -- along with all the blocks that precede it -- - f" | {p_sect_term_block}/{pred_ps_and_tbls}" - # -- or all the preceding blocks if sectPr is body-based (last sectPr) -- - f" | {body_sect_term}/{pred_ps_and_tbls}" - ) - - def _count_of_blocks_in_and_above_section(self, sectPr: CT_SectPr) -> int: - """All ps and tbls in section defined by `sectPr` and all prior sections.""" - if self._compiled_count_xpath is None: - self._compiled_count_xpath = etree.XPath( - f"count({self._blocks_in_and_above_section_xpath})", - namespaces=nsmap, - regexp=False, - ) - xpath = self._compiled_count_xpath - # -- numeric XPath results are always float, so need an int() conversion -- - return int(cast(float, xpath(sectPr))) - - @lazyproperty - def _sectPrs(self) -> Sequence[CT_SectPr]: - """All w:sectPr elements in document, in document-order.""" - return self._sectPr.xpath( - "/w:document/w:body/w:p/w:pPr/w:sectPr | /w:document/w:body/w:sectPr", - ) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/settings.py b/.venv-docs/lib/python3.12/site-packages/docx/oxml/settings.py deleted file mode 100644 index d5bb41a6..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/oxml/settings.py +++ /dev/null @@ -1,138 +0,0 @@ -"""Custom element classes related to document settings.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Callable - -from docx.oxml.xmlchemy import BaseOxmlElement, ZeroOrOne - -if TYPE_CHECKING: - from docx.oxml.shared import CT_OnOff - - -class CT_Settings(BaseOxmlElement): - """`w:settings` element, root element for the settings part.""" - - get_or_add_evenAndOddHeaders: Callable[[], CT_OnOff] - _remove_evenAndOddHeaders: Callable[[], None] - - _tag_seq = ( - "w:writeProtection", - "w:view", - "w:zoom", - "w:removePersonalInformation", - "w:removeDateAndTime", - "w:doNotDisplayPageBoundaries", - "w:displayBackgroundShape", - "w:printPostScriptOverText", - "w:printFractionalCharacterWidth", - "w:printFormsData", - "w:embedTrueTypeFonts", - "w:embedSystemFonts", - "w:saveSubsetFonts", - "w:saveFormsData", - "w:mirrorMargins", - "w:alignBordersAndEdges", - "w:bordersDoNotSurroundHeader", - "w:bordersDoNotSurroundFooter", - "w:gutterAtTop", - "w:hideSpellingErrors", - "w:hideGrammaticalErrors", - "w:activeWritingStyle", - "w:proofState", - "w:formsDesign", - "w:attachedTemplate", - "w:linkStyles", - "w:stylePaneFormatFilter", - "w:stylePaneSortMethod", - "w:documentType", - "w:mailMerge", - "w:revisionView", - "w:trackRevisions", - "w:doNotTrackMoves", - "w:doNotTrackFormatting", - "w:documentProtection", - "w:autoFormatOverride", - "w:styleLockTheme", - "w:styleLockQFSet", - "w:defaultTabStop", - "w:autoHyphenation", - "w:consecutiveHyphenLimit", - "w:hyphenationZone", - "w:doNotHyphenateCaps", - "w:showEnvelope", - "w:summaryLength", - "w:clickAndTypeStyle", - "w:defaultTableStyle", - "w:evenAndOddHeaders", - "w:bookFoldRevPrinting", - "w:bookFoldPrinting", - "w:bookFoldPrintingSheets", - "w:drawingGridHorizontalSpacing", - "w:drawingGridVerticalSpacing", - "w:displayHorizontalDrawingGridEvery", - "w:displayVerticalDrawingGridEvery", - "w:doNotUseMarginsForDrawingGridOrigin", - "w:drawingGridHorizontalOrigin", - "w:drawingGridVerticalOrigin", - "w:doNotShadeFormData", - "w:noPunctuationKerning", - "w:characterSpacingControl", - "w:printTwoOnOne", - "w:strictFirstAndLastChars", - "w:noLineBreaksAfter", - "w:noLineBreaksBefore", - "w:savePreviewPicture", - "w:doNotValidateAgainstSchema", - "w:saveInvalidXml", - "w:ignoreMixedContent", - "w:alwaysShowPlaceholderText", - "w:doNotDemarcateInvalidXml", - "w:saveXmlDataOnly", - "w:useXSLTWhenSaving", - "w:saveThroughXslt", - "w:showXMLTags", - "w:alwaysMergeEmptyNamespace", - "w:updateFields", - "w:hdrShapeDefaults", - "w:footnotePr", - "w:endnotePr", - "w:compat", - "w:docVars", - "w:rsids", - "m:mathPr", - "w:attachedSchema", - "w:themeFontLang", - "w:clrSchemeMapping", - "w:doNotIncludeSubdocsInStats", - "w:doNotAutoCompressPictures", - "w:forceUpgrade", - "w:captions", - "w:readModeInkLockDown", - "w:smartTagType", - "sl:schemaLibrary", - "w:shapeDefaults", - "w:doNotEmbedSmartTags", - "w:decimalSymbol", - "w:listSeparator", - ) - evenAndOddHeaders: CT_OnOff | None = ZeroOrOne( # pyright: ignore[reportAssignmentType] - "w:evenAndOddHeaders", successors=_tag_seq[48:] - ) - del _tag_seq - - @property - def evenAndOddHeaders_val(self) -> bool: - """Value of `w:evenAndOddHeaders/@w:val` or |None| if not present.""" - evenAndOddHeaders = self.evenAndOddHeaders - if evenAndOddHeaders is None: - return False - return evenAndOddHeaders.val - - @evenAndOddHeaders_val.setter - def evenAndOddHeaders_val(self, value: bool | None): - if value is None or value is False: - self._remove_evenAndOddHeaders() - return - - self.get_or_add_evenAndOddHeaders().val = value diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/shape.py b/.venv-docs/lib/python3.12/site-packages/docx/oxml/shape.py deleted file mode 100644 index c6df8e7b..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/oxml/shape.py +++ /dev/null @@ -1,299 +0,0 @@ -"""Custom element classes for shape-related elements like ``.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, cast - -from docx.oxml.ns import nsdecls -from docx.oxml.parser import parse_xml -from docx.oxml.simpletypes import ( - ST_Coordinate, - ST_DrawingElementId, - ST_PositiveCoordinate, - ST_RelationshipId, - XsdString, - XsdToken, -) -from docx.oxml.xmlchemy import ( - BaseOxmlElement, - OneAndOnlyOne, - OptionalAttribute, - RequiredAttribute, - ZeroOrOne, -) - -if TYPE_CHECKING: - from docx.shared import Length - - -class CT_Anchor(BaseOxmlElement): - """`` element, container for a "floating" shape.""" - - -class CT_Blip(BaseOxmlElement): - """```` element, specifies image source and adjustments such as alpha and - tint.""" - - embed: str | None = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "r:embed", ST_RelationshipId - ) - link: str | None = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "r:link", ST_RelationshipId - ) - - -class CT_BlipFillProperties(BaseOxmlElement): - """```` element, specifies picture properties.""" - - blip: CT_Blip = ZeroOrOne( # pyright: ignore[reportAssignmentType] - "a:blip", successors=("a:srcRect", "a:tile", "a:stretch") - ) - - -class CT_GraphicalObject(BaseOxmlElement): - """```` element, container for a DrawingML object.""" - - graphicData: CT_GraphicalObjectData = OneAndOnlyOne( # pyright: ignore[reportAssignmentType] - "a:graphicData" - ) - - -class CT_GraphicalObjectData(BaseOxmlElement): - """```` element, container for the XML of a DrawingML object.""" - - pic: CT_Picture = ZeroOrOne("pic:pic") # pyright: ignore[reportAssignmentType] - uri: str = RequiredAttribute("uri", XsdToken) # pyright: ignore[reportAssignmentType] - - -class CT_Inline(BaseOxmlElement): - """`` element, container for an inline shape.""" - - extent: CT_PositiveSize2D = OneAndOnlyOne("wp:extent") # pyright: ignore[reportAssignmentType] - docPr: CT_NonVisualDrawingProps = OneAndOnlyOne( # pyright: ignore[reportAssignmentType] - "wp:docPr" - ) - graphic: CT_GraphicalObject = OneAndOnlyOne( # pyright: ignore[reportAssignmentType] - "a:graphic" - ) - - @classmethod - def new(cls, cx: Length, cy: Length, shape_id: int, pic: CT_Picture) -> CT_Inline: - """Return a new ```` element populated with the values passed as - parameters.""" - inline = cast(CT_Inline, parse_xml(cls._inline_xml())) - inline.extent.cx = cx - inline.extent.cy = cy - inline.docPr.id = shape_id - inline.docPr.name = "Picture %d" % shape_id - inline.graphic.graphicData.uri = "http://schemas.openxmlformats.org/drawingml/2006/picture" - inline.graphic.graphicData._insert_pic(pic) - return inline - - @classmethod - def new_pic_inline( - cls, shape_id: int, rId: str, filename: str, cx: Length, cy: Length - ) -> CT_Inline: - """Create `wp:inline` element containing a `pic:pic` element. - - The contents of the `pic:pic` element is taken from the argument values. - """ - pic_id = 0 # Word doesn't seem to use this, but does not omit it - pic = CT_Picture.new(pic_id, filename, rId, cx, cy) - inline = cls.new(cx, cy, shape_id, pic) - return inline - - @classmethod - def _inline_xml(cls): - return ( - "\n" - ' \n' - ' \n' - " \n" - ' \n' - " \n" - " \n" - ' \n' - " \n" - "" % nsdecls("wp", "a", "pic", "r") - ) - - -class CT_NonVisualDrawingProps(BaseOxmlElement): - """Used for ```` element, and perhaps others. - - Specifies the id and name of a DrawingML drawing. - """ - - id = RequiredAttribute("id", ST_DrawingElementId) - name = RequiredAttribute("name", XsdString) - - -class CT_NonVisualPictureProperties(BaseOxmlElement): - """```` element, specifies picture locking and resize behaviors.""" - - -class CT_Picture(BaseOxmlElement): - """```` element, a DrawingML picture.""" - - nvPicPr: CT_PictureNonVisual = OneAndOnlyOne( # pyright: ignore[reportAssignmentType] - "pic:nvPicPr" - ) - blipFill: CT_BlipFillProperties = OneAndOnlyOne( # pyright: ignore[reportAssignmentType] - "pic:blipFill" - ) - spPr: CT_ShapeProperties = OneAndOnlyOne("pic:spPr") # pyright: ignore[reportAssignmentType] - - @classmethod - def new(cls, pic_id: int, filename: str, rId: str, cx: Length, cy: Length) -> CT_Picture: - """A new minimum viable `` (picture) element.""" - pic = parse_xml(cls._pic_xml()) - pic.nvPicPr.cNvPr.id = pic_id - pic.nvPicPr.cNvPr.name = filename - pic.blipFill.blip.embed = rId - pic.spPr.cx = cx - pic.spPr.cy = cy - return pic - - @classmethod - def _pic_xml(cls): - return ( - "\n" - " \n" - ' \n' - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - ' \n' - ' \n' - " \n" - ' \n' - " \n" - "" % nsdecls("pic", "a", "r") - ) - - -class CT_PictureNonVisual(BaseOxmlElement): - """```` element, non-visual picture properties.""" - - cNvPr = OneAndOnlyOne("pic:cNvPr") - - -class CT_Point2D(BaseOxmlElement): - """Used for ```` element, and perhaps others. - - Specifies an x, y coordinate (point). - """ - - x = RequiredAttribute("x", ST_Coordinate) - y = RequiredAttribute("y", ST_Coordinate) - - -class CT_PositiveSize2D(BaseOxmlElement): - """Used for ```` element, and perhaps others later. - - Specifies the size of a DrawingML drawing. - """ - - cx: Length = RequiredAttribute( # pyright: ignore[reportAssignmentType] - "cx", ST_PositiveCoordinate - ) - cy: Length = RequiredAttribute( # pyright: ignore[reportAssignmentType] - "cy", ST_PositiveCoordinate - ) - - -class CT_PresetGeometry2D(BaseOxmlElement): - """```` element, specifies an preset autoshape geometry, such as - ``rect``.""" - - -class CT_RelativeRect(BaseOxmlElement): - """```` element, specifying picture should fill containing rectangle - shape.""" - - -class CT_ShapeProperties(BaseOxmlElement): - """```` element, specifies size and shape of picture container.""" - - xfrm = ZeroOrOne( - "a:xfrm", - successors=( - "a:custGeom", - "a:prstGeom", - "a:ln", - "a:effectLst", - "a:effectDag", - "a:scene3d", - "a:sp3d", - "a:extLst", - ), - ) - - @property - def cx(self): - """Shape width as an instance of Emu, or None if not present.""" - xfrm = self.xfrm - if xfrm is None: - return None - return xfrm.cx - - @cx.setter - def cx(self, value): - xfrm = self.get_or_add_xfrm() - xfrm.cx = value - - @property - def cy(self): - """Shape height as an instance of Emu, or None if not present.""" - xfrm = self.xfrm - if xfrm is None: - return None - return xfrm.cy - - @cy.setter - def cy(self, value): - xfrm = self.get_or_add_xfrm() - xfrm.cy = value - - -class CT_StretchInfoProperties(BaseOxmlElement): - """```` element, specifies how picture should fill its containing - shape.""" - - -class CT_Transform2D(BaseOxmlElement): - """```` element, specifies size and shape of picture container.""" - - off = ZeroOrOne("a:off", successors=("a:ext",)) - ext = ZeroOrOne("a:ext", successors=()) - - @property - def cx(self): - ext = self.ext - if ext is None: - return None - return ext.cx - - @cx.setter - def cx(self, value): - ext = self.get_or_add_ext() - ext.cx = value - - @property - def cy(self): - ext = self.ext - if ext is None: - return None - return ext.cy - - @cy.setter - def cy(self, value): - ext = self.get_or_add_ext() - ext.cy = value diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/shared.py b/.venv-docs/lib/python3.12/site-packages/docx/oxml/shared.py deleted file mode 100644 index 8cfcd2be..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/oxml/shared.py +++ /dev/null @@ -1,52 +0,0 @@ -"""Objects shared by modules in the docx.oxml subpackage.""" - -from __future__ import annotations - -from typing import cast - -from docx.oxml.ns import qn -from docx.oxml.parser import OxmlElement -from docx.oxml.simpletypes import ST_DecimalNumber, ST_OnOff, ST_String -from docx.oxml.xmlchemy import BaseOxmlElement, OptionalAttribute, RequiredAttribute - - -class CT_DecimalNumber(BaseOxmlElement): - """Used for ````, ````, ```` and several others, - containing a text representation of a decimal number (e.g. 42) in its ``val`` - attribute.""" - - val: int = RequiredAttribute("w:val", ST_DecimalNumber) # pyright: ignore[reportAssignmentType] - - @classmethod - def new(cls, nsptagname: str, val: int): - """Return a new ``CT_DecimalNumber`` element having tagname `nsptagname` and - ``val`` attribute set to `val`.""" - return OxmlElement(nsptagname, attrs={qn("w:val"): str(val)}) - - -class CT_OnOff(BaseOxmlElement): - """Used for `w:b`, `w:i` elements and others. - - Contains a bool-ish string in its `val` attribute, xsd:boolean plus "on" and - "off". Defaults to `True`, so `` for example means "bold is turned on". - """ - - val: bool = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "w:val", ST_OnOff, default=True - ) - - -class CT_String(BaseOxmlElement): - """Used for `w:pStyle` and `w:tblStyle` elements and others. - - In those cases, it containing a style name in its `val` attribute. - """ - - val: str = RequiredAttribute("w:val", ST_String) # pyright: ignore[reportAssignmentType] - - @classmethod - def new(cls, nsptagname: str, val: str): - """A new `CT_String`` element with tagname `nsptagname` and `val` attribute set to `val`.""" - elm = cast(CT_String, OxmlElement(nsptagname)) - elm.val = val - return elm diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/simpletypes.py b/.venv-docs/lib/python3.12/site-packages/docx/oxml/simpletypes.py deleted file mode 100644 index a0fc87d3..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/oxml/simpletypes.py +++ /dev/null @@ -1,434 +0,0 @@ -# pyright: reportImportCycles=false - -"""Simple-type classes, corresponding to ST_* schema items. - -These provide validation and format translation for values stored in XML element -attributes. Naming generally corresponds to the simple type in the associated XML -schema. -""" - -from __future__ import annotations - -import datetime as dt -from typing import TYPE_CHECKING, Any, Tuple - -from docx.exceptions import InvalidXmlError -from docx.shared import Emu, Pt, RGBColor, Twips - -if TYPE_CHECKING: - from docx.shared import Length - - -class BaseSimpleType: - """Base class for simple-types.""" - - @classmethod - def from_xml(cls, xml_value: str) -> Any: - return cls.convert_from_xml(xml_value) - - @classmethod - def to_xml(cls, value: Any) -> str: - cls.validate(value) - str_value = cls.convert_to_xml(value) - return str_value - - @classmethod - def convert_from_xml(cls, str_value: str) -> Any: - return int(str_value) - - @classmethod - def convert_to_xml(cls, value: Any) -> str: ... - - @classmethod - def validate(cls, value: Any) -> None: ... - - @classmethod - def validate_int(cls, value: object): - if not isinstance(value, int): - raise TypeError("value must be , got %s" % type(value)) - - @classmethod - def validate_int_in_range(cls, value: int, min_inclusive: int, max_inclusive: int) -> None: - cls.validate_int(value) - if value < min_inclusive or value > max_inclusive: - raise ValueError( - "value must be in range %d to %d inclusive, got %d" - % (min_inclusive, max_inclusive, value) - ) - - @classmethod - def validate_string(cls, value: Any) -> str: - if not isinstance(value, str): - raise TypeError("value must be a string, got %s" % type(value)) - return value - - -class BaseIntType(BaseSimpleType): - @classmethod - def convert_from_xml(cls, str_value: str) -> int: - return int(str_value) - - @classmethod - def convert_to_xml(cls, value: int) -> str: - return str(value) - - @classmethod - def validate(cls, value: Any) -> None: - cls.validate_int(value) - - -class BaseStringType(BaseSimpleType): - @classmethod - def convert_from_xml(cls, str_value: str) -> str: - return str_value - - @classmethod - def convert_to_xml(cls, value: str) -> str: - return value - - @classmethod - def validate(cls, value: str): - cls.validate_string(value) - - -class BaseStringEnumerationType(BaseStringType): - _members: Tuple[str, ...] - - @classmethod - def validate(cls, value: Any) -> None: - cls.validate_string(value) - if value not in cls._members: - raise ValueError("must be one of %s, got '%s'" % (cls._members, value)) - - -class XsdAnyUri(BaseStringType): - """There's a regex in the spec this is supposed to meet... - - but current assessment is that spending cycles on validating wouldn't be worth it - for the number of programming errors it would catch. - """ - - -class XsdBoolean(BaseSimpleType): - @classmethod - def convert_from_xml(cls, str_value: str) -> bool: - if str_value not in ("1", "0", "true", "false"): - raise InvalidXmlError( - "value must be one of '1', '0', 'true' or 'false', got '%s'" % str_value - ) - return str_value in ("1", "true") - - @classmethod - def convert_to_xml(cls, value: bool) -> str: - return {True: "1", False: "0"}[value] - - @classmethod - def validate(cls, value: Any) -> None: - if value not in (True, False): - raise TypeError( - "only True or False (and possibly None) may be assigned, got '%s'" % value - ) - - -class XsdId(BaseStringType): - """String that must begin with a letter or underscore and cannot contain any colons. - - Not fully validated because not used in external API. - """ - - pass - - -class XsdInt(BaseIntType): - @classmethod - def validate(cls, value: Any) -> None: - cls.validate_int_in_range(value, -2147483648, 2147483647) - - -class XsdLong(BaseIntType): - @classmethod - def validate(cls, value: Any) -> None: - cls.validate_int_in_range(value, -9223372036854775808, 9223372036854775807) - - -class XsdString(BaseStringType): - pass - - -class XsdStringEnumeration(BaseStringEnumerationType): - """Set of enumerated xsd:string values.""" - - -class XsdToken(BaseStringType): - """Xsd:string with whitespace collapsing, e.g. multiple spaces reduced to one, - leading and trailing space stripped.""" - - pass - - -class XsdUnsignedInt(BaseIntType): - @classmethod - def validate(cls, value: Any) -> None: - cls.validate_int_in_range(value, 0, 4294967295) - - -class XsdUnsignedLong(BaseIntType): - @classmethod - def validate(cls, value: Any) -> None: - cls.validate_int_in_range(value, 0, 18446744073709551615) - - -class ST_BrClear(XsdString): - @classmethod - def validate(cls, value: str) -> None: - cls.validate_string(value) - valid_values = ("none", "left", "right", "all") - if value not in valid_values: - raise ValueError("must be one of %s, got '%s'" % (valid_values, value)) - - -class ST_BrType(XsdString): - @classmethod - def validate(cls, value: Any) -> None: - cls.validate_string(value) - valid_values = ("page", "column", "textWrapping") - if value not in valid_values: - raise ValueError("must be one of %s, got '%s'" % (valid_values, value)) - - -class ST_Coordinate(BaseIntType): - @classmethod - def convert_from_xml(cls, str_value: str) -> Length: - if "i" in str_value or "m" in str_value or "p" in str_value: - return ST_UniversalMeasure.convert_from_xml(str_value) - return Emu(int(str_value)) - - @classmethod - def validate(cls, value: Any) -> None: - ST_CoordinateUnqualified.validate(value) - - -class ST_CoordinateUnqualified(XsdLong): - @classmethod - def validate(cls, value: Any) -> None: - cls.validate_int_in_range(value, -27273042329600, 27273042316900) - - -class ST_DateTime(BaseSimpleType): - @classmethod - def convert_from_xml(cls, str_value: str) -> dt.datetime: - """Convert an xsd:dateTime string to a datetime object.""" - - def parse_xsd_datetime(dt_str: str) -> dt.datetime: - # -- handle trailing 'Z' (Zulu/UTC), common in Word files -- - if dt_str.endswith("Z"): - try: - # -- optional fractional seconds case -- - return dt.datetime.strptime(dt_str, "%Y-%m-%dT%H:%M:%S.%fZ").replace( - tzinfo=dt.timezone.utc - ) - except ValueError: - return dt.datetime.strptime(dt_str, "%Y-%m-%dT%H:%M:%SZ").replace( - tzinfo=dt.timezone.utc - ) - - # -- handles explicit offsets like +00:00, -05:00, or naive datetimes -- - try: - return dt.datetime.fromisoformat(dt_str) - except ValueError: - # -- fall-back to parsing as naive datetime (with or without fractional seconds) -- - try: - return dt.datetime.strptime(dt_str, "%Y-%m-%dT%H:%M:%S.%f") - except ValueError: - return dt.datetime.strptime(dt_str, "%Y-%m-%dT%H:%M:%S") - - try: - # -- parse anything reasonable, but never raise, just use default epoch time -- - return parse_xsd_datetime(str_value) - except Exception: - return dt.datetime(1970, 1, 1, tzinfo=dt.timezone.utc) - - @classmethod - def convert_to_xml(cls, value: dt.datetime) -> str: - # -- convert naive datetime to timezon-aware assuming local timezone -- - if value.tzinfo is None: - value = value.astimezone() - - # -- convert to UTC if not already -- - value = value.astimezone(dt.timezone.utc) - - # -- format with 'Z' suffix for UTC -- - return value.strftime("%Y-%m-%dT%H:%M:%SZ") - - @classmethod - def validate(cls, value: Any) -> None: - if not isinstance(value, dt.datetime): - raise TypeError("only a datetime.datetime object may be assigned, got '%s'" % value) - - -class ST_DecimalNumber(XsdInt): - pass - - -class ST_DrawingElementId(XsdUnsignedInt): - pass - - -class ST_HexColor(BaseStringType): - @classmethod - def convert_from_xml( # pyright: ignore[reportIncompatibleMethodOverride] - cls, str_value: str - ) -> RGBColor | str: - if str_value == "auto": - return ST_HexColorAuto.AUTO - return RGBColor.from_string(str_value) - - @classmethod - def convert_to_xml( # pyright: ignore[reportIncompatibleMethodOverride] - cls, value: RGBColor - ) -> str: - """Keep alpha hex numerals all uppercase just for consistency.""" - # expecting 3-tuple of ints in range 0-255 - return "%02X%02X%02X" % value - - @classmethod - def validate(cls, value: Any) -> None: - # must be an RGBColor object --- - if not isinstance(value, RGBColor): - raise ValueError( - "rgb color value must be RGBColor object, got %s %s" % (type(value), value) - ) - - -class ST_HexColorAuto(XsdStringEnumeration): - """Value for `w:color/[@val="auto"] attribute setting.""" - - AUTO = "auto" - - _members = (AUTO,) - - -class ST_HpsMeasure(XsdUnsignedLong): - """Half-point measure, e.g. 24.0 represents 12.0 points.""" - - @classmethod - def convert_from_xml(cls, str_value: str) -> Length: - if "m" in str_value or "n" in str_value or "p" in str_value: - return ST_UniversalMeasure.convert_from_xml(str_value) - return Pt(int(str_value) / 2.0) - - @classmethod - def convert_to_xml(cls, value: int | Length) -> str: - emu = Emu(value) - half_points = int(emu.pt * 2) - return str(half_points) - - -class ST_Merge(XsdStringEnumeration): - """Valid values for attribute.""" - - CONTINUE = "continue" - RESTART = "restart" - - _members = (CONTINUE, RESTART) - - -class ST_OnOff(XsdBoolean): - @classmethod - def convert_from_xml(cls, str_value: str) -> bool: - if str_value not in ("1", "0", "true", "false", "on", "off"): - raise InvalidXmlError( - "value must be one of '1', '0', 'true', 'false', 'on', or 'o" - "ff', got '%s'" % str_value - ) - return str_value in ("1", "true", "on") - - -class ST_PositiveCoordinate(XsdLong): - @classmethod - def convert_from_xml(cls, str_value: str) -> Length: - return Emu(int(str_value)) - - @classmethod - def validate(cls, value: Any) -> None: - cls.validate_int_in_range(value, 0, 27273042316900) - - -class ST_RelationshipId(XsdString): - pass - - -class ST_SignedTwipsMeasure(XsdInt): - @classmethod - def convert_from_xml(cls, str_value: str) -> Length: - if "i" in str_value or "m" in str_value or "p" in str_value: - return ST_UniversalMeasure.convert_from_xml(str_value) - return Twips(int(round(float(str_value)))) - - @classmethod - def convert_to_xml(cls, value: int | Length) -> str: - emu = Emu(value) - twips = emu.twips - return str(twips) - - -class ST_String(XsdString): - pass - - -class ST_TblLayoutType(XsdString): - @classmethod - def validate(cls, value: Any) -> None: - cls.validate_string(value) - valid_values = ("fixed", "autofit") - if value not in valid_values: - raise ValueError("must be one of %s, got '%s'" % (valid_values, value)) - - -class ST_TblWidth(XsdString): - @classmethod - def validate(cls, value: Any) -> None: - cls.validate_string(value) - valid_values = ("auto", "dxa", "nil", "pct") - if value not in valid_values: - raise ValueError("must be one of %s, got '%s'" % (valid_values, value)) - - -class ST_TwipsMeasure(XsdUnsignedLong): - @classmethod - def convert_from_xml(cls, str_value: str) -> Length: - if "i" in str_value or "m" in str_value or "p" in str_value: - return ST_UniversalMeasure.convert_from_xml(str_value) - return Twips(int(str_value)) - - @classmethod - def convert_to_xml(cls, value: int | Length) -> str: - emu = Emu(value) - twips = emu.twips - return str(twips) - - -class ST_UniversalMeasure(BaseSimpleType): - @classmethod - def convert_from_xml(cls, str_value: str) -> Emu: - float_part, units_part = str_value[:-2], str_value[-2:] - quantity = float(float_part) - multiplier = { - "mm": 36000, - "cm": 360000, - "in": 914400, - "pt": 12700, - "pc": 152400, - "pi": 152400, - }[units_part] - return Emu(int(round(quantity * multiplier))) - - -class ST_VerticalAlignRun(XsdStringEnumeration): - """Valid values for `w:vertAlign/@val`.""" - - BASELINE = "baseline" - SUPERSCRIPT = "superscript" - SUBSCRIPT = "subscript" - - _members = (BASELINE, SUPERSCRIPT, SUBSCRIPT) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/styles.py b/.venv-docs/lib/python3.12/site-packages/docx/oxml/styles.py deleted file mode 100644 index fb0e5d0d..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/oxml/styles.py +++ /dev/null @@ -1,320 +0,0 @@ -"""Custom element classes related to the styles part.""" - -from __future__ import annotations - -from docx.enum.style import WD_STYLE_TYPE -from docx.oxml.simpletypes import ST_DecimalNumber, ST_OnOff, ST_String -from docx.oxml.xmlchemy import ( - BaseOxmlElement, - OptionalAttribute, - RequiredAttribute, - ZeroOrMore, - ZeroOrOne, -) - - -def styleId_from_name(name): - """Return the style id corresponding to `name`, taking into account special-case - names such as 'Heading 1'.""" - return { - "caption": "Caption", - "heading 1": "Heading1", - "heading 2": "Heading2", - "heading 3": "Heading3", - "heading 4": "Heading4", - "heading 5": "Heading5", - "heading 6": "Heading6", - "heading 7": "Heading7", - "heading 8": "Heading8", - "heading 9": "Heading9", - }.get(name, name.replace(" ", "")) - - -class CT_LatentStyles(BaseOxmlElement): - """`w:latentStyles` element, defining behavior defaults for latent styles and - containing `w:lsdException` child elements that each override those defaults for a - named latent style.""" - - lsdException = ZeroOrMore("w:lsdException", successors=()) - - count = OptionalAttribute("w:count", ST_DecimalNumber) - defLockedState = OptionalAttribute("w:defLockedState", ST_OnOff) - defQFormat = OptionalAttribute("w:defQFormat", ST_OnOff) - defSemiHidden = OptionalAttribute("w:defSemiHidden", ST_OnOff) - defUIPriority = OptionalAttribute("w:defUIPriority", ST_DecimalNumber) - defUnhideWhenUsed = OptionalAttribute("w:defUnhideWhenUsed", ST_OnOff) - - def bool_prop(self, attr_name): - """Return the boolean value of the attribute having `attr_name`, or |False| if - not present.""" - value = getattr(self, attr_name) - if value is None: - return False - return value - - def get_by_name(self, name): - """Return the `w:lsdException` child having `name`, or |None| if not found.""" - found = self.xpath('w:lsdException[@w:name="%s"]' % name) - if not found: - return None - return found[0] - - def set_bool_prop(self, attr_name, value): - """Set the on/off attribute having `attr_name` to `value`.""" - setattr(self, attr_name, bool(value)) - - -class CT_LsdException(BaseOxmlElement): - """```` element, defining override visibility behaviors for a named - latent style.""" - - locked = OptionalAttribute("w:locked", ST_OnOff) - name = RequiredAttribute("w:name", ST_String) - qFormat = OptionalAttribute("w:qFormat", ST_OnOff) - semiHidden = OptionalAttribute("w:semiHidden", ST_OnOff) - uiPriority = OptionalAttribute("w:uiPriority", ST_DecimalNumber) - unhideWhenUsed = OptionalAttribute("w:unhideWhenUsed", ST_OnOff) - - def delete(self): - """Remove this `w:lsdException` element from the XML document.""" - self.getparent().remove(self) - - def on_off_prop(self, attr_name): - """Return the boolean value of the attribute having `attr_name`, or |None| if - not present.""" - return getattr(self, attr_name) - - def set_on_off_prop(self, attr_name, value): - """Set the on/off attribute having `attr_name` to `value`.""" - setattr(self, attr_name, value) - - -class CT_Style(BaseOxmlElement): - """A ```` element, representing a style definition.""" - - _tag_seq = ( - "w:name", - "w:aliases", - "w:basedOn", - "w:next", - "w:link", - "w:autoRedefine", - "w:hidden", - "w:uiPriority", - "w:semiHidden", - "w:unhideWhenUsed", - "w:qFormat", - "w:locked", - "w:personal", - "w:personalCompose", - "w:personalReply", - "w:rsid", - "w:pPr", - "w:rPr", - "w:tblPr", - "w:trPr", - "w:tcPr", - "w:tblStylePr", - ) - name = ZeroOrOne("w:name", successors=_tag_seq[1:]) - basedOn = ZeroOrOne("w:basedOn", successors=_tag_seq[3:]) - next = ZeroOrOne("w:next", successors=_tag_seq[4:]) - uiPriority = ZeroOrOne("w:uiPriority", successors=_tag_seq[8:]) - semiHidden = ZeroOrOne("w:semiHidden", successors=_tag_seq[9:]) - unhideWhenUsed = ZeroOrOne("w:unhideWhenUsed", successors=_tag_seq[10:]) - qFormat = ZeroOrOne("w:qFormat", successors=_tag_seq[11:]) - locked = ZeroOrOne("w:locked", successors=_tag_seq[12:]) - pPr = ZeroOrOne("w:pPr", successors=_tag_seq[17:]) - rPr = ZeroOrOne("w:rPr", successors=_tag_seq[18:]) - del _tag_seq - - type: WD_STYLE_TYPE | None = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "w:type", WD_STYLE_TYPE - ) - styleId: str | None = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "w:styleId", ST_String - ) - default = OptionalAttribute("w:default", ST_OnOff) - customStyle = OptionalAttribute("w:customStyle", ST_OnOff) - - @property - def basedOn_val(self): - """Value of `w:basedOn/@w:val` or |None| if not present.""" - basedOn = self.basedOn - if basedOn is None: - return None - return basedOn.val - - @basedOn_val.setter - def basedOn_val(self, value): - if value is None: - self._remove_basedOn() - else: - self.get_or_add_basedOn().val = value - - @property - def base_style(self): - """Sibling CT_Style element this style is based on or |None| if no base style or - base style not found.""" - basedOn = self.basedOn - if basedOn is None: - return None - styles = self.getparent() - base_style = styles.get_by_id(basedOn.val) - if base_style is None: - return None - return base_style - - def delete(self): - """Remove this `w:style` element from its parent `w:styles` element.""" - self.getparent().remove(self) - - @property - def locked_val(self): - """Value of `w:locked/@w:val` or |False| if not present.""" - locked = self.locked - if locked is None: - return False - return locked.val - - @locked_val.setter - def locked_val(self, value): - self._remove_locked() - if bool(value) is True: - locked = self._add_locked() - locked.val = value - - @property - def name_val(self): - """Value of ```` child or |None| if not present.""" - name = self.name - if name is None: - return None - return name.val - - @name_val.setter - def name_val(self, value): - self._remove_name() - if value is not None: - name = self._add_name() - name.val = value - - @property - def next_style(self): - """Sibling CT_Style element identified by the value of `w:name/@w:val` or |None| - if no value is present or no style with that style id is found.""" - next = self.next - if next is None: - return None - styles = self.getparent() - return styles.get_by_id(next.val) # None if not found - - @property - def qFormat_val(self): - """Value of `w:qFormat/@w:val` or |False| if not present.""" - qFormat = self.qFormat - if qFormat is None: - return False - return qFormat.val - - @qFormat_val.setter - def qFormat_val(self, value): - self._remove_qFormat() - if bool(value): - self._add_qFormat() - - @property - def semiHidden_val(self): - """Value of ```` child or |False| if not present.""" - semiHidden = self.semiHidden - if semiHidden is None: - return False - return semiHidden.val - - @semiHidden_val.setter - def semiHidden_val(self, value): - self._remove_semiHidden() - if bool(value) is True: - semiHidden = self._add_semiHidden() - semiHidden.val = value - - @property - def uiPriority_val(self): - """Value of ```` child or |None| if not present.""" - uiPriority = self.uiPriority - if uiPriority is None: - return None - return uiPriority.val - - @uiPriority_val.setter - def uiPriority_val(self, value): - self._remove_uiPriority() - if value is not None: - uiPriority = self._add_uiPriority() - uiPriority.val = value - - @property - def unhideWhenUsed_val(self): - """Value of `w:unhideWhenUsed/@w:val` or |False| if not present.""" - unhideWhenUsed = self.unhideWhenUsed - if unhideWhenUsed is None: - return False - return unhideWhenUsed.val - - @unhideWhenUsed_val.setter - def unhideWhenUsed_val(self, value): - self._remove_unhideWhenUsed() - if bool(value) is True: - unhideWhenUsed = self._add_unhideWhenUsed() - unhideWhenUsed.val = value - - -class CT_Styles(BaseOxmlElement): - """```` element, the root element of a styles part, i.e. styles.xml.""" - - _tag_seq = ("w:docDefaults", "w:latentStyles", "w:style") - latentStyles = ZeroOrOne("w:latentStyles", successors=_tag_seq[2:]) - style = ZeroOrMore("w:style", successors=()) - del _tag_seq - - def add_style_of_type(self, name, style_type, builtin): - """Return a newly added `w:style` element having `name` and `style_type`. - - `w:style/@customStyle` is set based on the value of `builtin`. - """ - style = self.add_style() - style.type = style_type - style.customStyle = None if builtin else True - style.styleId = styleId_from_name(name) - style.name_val = name - return style - - def default_for(self, style_type): - """Return `w:style[@w:type="*{style_type}*][-1]` or |None| if not found.""" - default_styles_for_type = [ - s for s in self._iter_styles() if s.type == style_type and s.default - ] - if not default_styles_for_type: - return None - # spec calls for last default in document order - return default_styles_for_type[-1] - - def get_by_id(self, styleId: str) -> CT_Style | None: - """`w:style` child where @styleId = `styleId`. - - |None| if not found. - """ - xpath = f'w:style[@w:styleId="{styleId}"]' - return next(iter(self.xpath(xpath)), None) - - def get_by_name(self, name: str) -> CT_Style | None: - """`w:style` child with `w:name` grandchild having value `name`. - - |None| if not found. - """ - xpath = 'w:style[w:name/@w:val="%s"]' % name - return next(iter(self.xpath(xpath)), None) - - def _iter_styles(self): - """Generate each of the `w:style` child elements in document order.""" - return (style for style in self.xpath("w:style")) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/table.py b/.venv-docs/lib/python3.12/site-packages/docx/oxml/table.py deleted file mode 100644 index 9457da20..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/oxml/table.py +++ /dev/null @@ -1,977 +0,0 @@ -"""Custom element classes for tables.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Callable, cast - -from docx.enum.table import WD_CELL_VERTICAL_ALIGNMENT, WD_ROW_HEIGHT_RULE, WD_TABLE_DIRECTION -from docx.exceptions import InvalidSpanError -from docx.oxml.ns import nsdecls, qn -from docx.oxml.parser import parse_xml -from docx.oxml.shared import CT_DecimalNumber -from docx.oxml.simpletypes import ( - ST_Merge, - ST_TblLayoutType, - ST_TblWidth, - ST_TwipsMeasure, - XsdInt, -) -from docx.oxml.text.paragraph import CT_P -from docx.oxml.xmlchemy import ( - BaseOxmlElement, - OneAndOnlyOne, - OneOrMore, - OptionalAttribute, - RequiredAttribute, - ZeroOrMore, - ZeroOrOne, -) -from docx.shared import Emu, Length, Twips - -if TYPE_CHECKING: - from docx.enum.table import WD_TABLE_ALIGNMENT - from docx.enum.text import WD_ALIGN_PARAGRAPH - from docx.oxml.shared import CT_OnOff, CT_String - from docx.oxml.text.parfmt import CT_Jc - - -class CT_Height(BaseOxmlElement): - """Used for `w:trHeight` to specify a row height and row height rule.""" - - val: Length | None = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "w:val", ST_TwipsMeasure - ) - hRule: WD_ROW_HEIGHT_RULE | None = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "w:hRule", WD_ROW_HEIGHT_RULE - ) - - -class CT_Row(BaseOxmlElement): - """```` element.""" - - add_tc: Callable[[], CT_Tc] - get_or_add_trPr: Callable[[], CT_TrPr] - _add_trPr: Callable[[], CT_TrPr] - - tc_lst: list[CT_Tc] - # -- custom inserter below -- - tblPrEx: CT_TblPrEx | None = ZeroOrOne("w:tblPrEx") # pyright: ignore[reportAssignmentType] - # -- custom inserter below -- - trPr: CT_TrPr | None = ZeroOrOne("w:trPr") # pyright: ignore[reportAssignmentType] - tc = ZeroOrMore("w:tc") - - @property - def grid_after(self) -> int: - """The number of unpopulated layout-grid cells at the end of this row.""" - trPr = self.trPr - if trPr is None: - return 0 - return trPr.grid_after - - @property - def grid_before(self) -> int: - """The number of unpopulated layout-grid cells at the start of this row.""" - trPr = self.trPr - if trPr is None: - return 0 - return trPr.grid_before - - def tc_at_grid_offset(self, grid_offset: int) -> CT_Tc: - """The `tc` element in this tr at exact `grid offset`. - - Raises ValueError when this `w:tr` contains no `w:tc` with exact starting `grid_offset`. - """ - # -- account for omitted cells at the start of the row -- - remaining_offset = grid_offset - self.grid_before - - for tc in self.tc_lst: - # -- We've gone past grid_offset without finding a tc, no sense searching further. -- - if remaining_offset < 0: - break - # -- We've arrived at grid_offset, this is the `w:tc` we're looking for. -- - if remaining_offset == 0: - return tc - # -- We're not there yet, skip forward the number of layout-grid cells this cell - # -- occupies. - remaining_offset -= tc.grid_span - - raise ValueError(f"no `tc` element at grid_offset={grid_offset}") - - @property - def tr_idx(self) -> int: - """Index of this `w:tr` element within its parent `w:tbl` element.""" - tbl = cast(CT_Tbl, self.getparent()) - return tbl.tr_lst.index(self) - - @property - def trHeight_hRule(self) -> WD_ROW_HEIGHT_RULE | None: - """The value of `./w:trPr/w:trHeight/@w:hRule`, or |None| if not present.""" - trPr = self.trPr - if trPr is None: - return None - return trPr.trHeight_hRule - - @trHeight_hRule.setter - def trHeight_hRule(self, value: WD_ROW_HEIGHT_RULE | None): - trPr = self.get_or_add_trPr() - trPr.trHeight_hRule = value - - @property - def trHeight_val(self): - """Return the value of `w:trPr/w:trHeight@w:val`, or |None| if not present.""" - trPr = self.trPr - if trPr is None: - return None - return trPr.trHeight_val - - @trHeight_val.setter - def trHeight_val(self, value: Length | None): - trPr = self.get_or_add_trPr() - trPr.trHeight_val = value - - def _insert_tblPrEx(self, tblPrEx: CT_TblPrEx): - self.insert(0, tblPrEx) - - def _insert_trPr(self, trPr: CT_TrPr): - tblPrEx = self.tblPrEx - if tblPrEx is not None: - tblPrEx.addnext(trPr) - else: - self.insert(0, trPr) - - def _new_tc(self): - return CT_Tc.new() - - -class CT_Tbl(BaseOxmlElement): - """```` element.""" - - add_tr: Callable[[], CT_Row] - tr_lst: list[CT_Row] - - tblPr: CT_TblPr = OneAndOnlyOne("w:tblPr") # pyright: ignore[reportAssignmentType] - tblGrid: CT_TblGrid = OneAndOnlyOne("w:tblGrid") # pyright: ignore[reportAssignmentType] - tr = ZeroOrMore("w:tr") - - @property - def bidiVisual_val(self) -> bool | None: - """Value of `./w:tblPr/w:bidiVisual/@w:val` or |None| if not present. - - Controls whether table cells are displayed right-to-left or left-to-right. - """ - bidiVisual = self.tblPr.bidiVisual - if bidiVisual is None: - return None - return bidiVisual.val - - @bidiVisual_val.setter - def bidiVisual_val(self, value: WD_TABLE_DIRECTION | None): - tblPr = self.tblPr - if value is None: - tblPr._remove_bidiVisual() # pyright: ignore[reportPrivateUsage] - else: - tblPr.get_or_add_bidiVisual().val = bool(value) - - @property - def col_count(self): - """The number of grid columns in this table.""" - return len(self.tblGrid.gridCol_lst) - - def iter_tcs(self): - """Generate each of the `w:tc` elements in this table, left to right and top to - bottom. - - Each cell in the first row is generated, followed by each cell in the second - row, etc. - """ - for tr in self.tr_lst: - for tc in tr.tc_lst: - yield tc - - @classmethod - def new_tbl(cls, rows: int, cols: int, width: Length) -> CT_Tbl: - """Return a new `w:tbl` element having `rows` rows and `cols` columns. - - `width` is distributed evenly between the columns. - """ - return cast(CT_Tbl, parse_xml(cls._tbl_xml(rows, cols, width))) - - @property - def tblStyle_val(self) -> str | None: - """`w:tblPr/w:tblStyle/@w:val` (a table style id) or |None| if not present.""" - tblStyle = self.tblPr.tblStyle - if tblStyle is None: - return None - return tblStyle.val - - @tblStyle_val.setter - def tblStyle_val(self, styleId: str | None) -> None: - """Set the value of `w:tblPr/w:tblStyle/@w:val` (a table style id) to `styleId`. - - If `styleId` is None, remove the `w:tblStyle` element. - """ - tblPr = self.tblPr - tblPr._remove_tblStyle() # pyright: ignore[reportPrivateUsage] - if styleId is None: - return - tblPr._add_tblStyle().val = styleId # pyright: ignore[reportPrivateUsage] - - @classmethod - def _tbl_xml(cls, rows: int, cols: int, width: Length) -> str: - col_width = Emu(width // cols) if cols > 0 else Emu(0) - return ( - f"\n" - f" \n" - f' \n' - f' \n' - f" \n" - f"{cls._tblGrid_xml(cols, col_width)}" - f"{cls._trs_xml(rows, cols, col_width)}" - f"\n" - ) - - @classmethod - def _tblGrid_xml(cls, col_count: int, col_width: Length) -> str: - xml = " \n" - for _ in range(col_count): - xml += ' \n' % col_width.twips - xml += " \n" - return xml - - @classmethod - def _trs_xml(cls, row_count: int, col_count: int, col_width: Length) -> str: - return f" \n{cls._tcs_xml(col_count, col_width)} \n" * row_count - - @classmethod - def _tcs_xml(cls, col_count: int, col_width: Length) -> str: - return ( - f" \n" - f" \n" - f' \n' - f" \n" - f" \n" - f" \n" - ) * col_count - - -class CT_TblGrid(BaseOxmlElement): - """`w:tblGrid` element. - - Child of `w:tbl`, holds `w:gridCol> elements that define column count, width, etc. - """ - - add_gridCol: Callable[[], CT_TblGridCol] - gridCol_lst: list[CT_TblGridCol] - - gridCol = ZeroOrMore("w:gridCol", successors=("w:tblGridChange",)) - - -class CT_TblGridCol(BaseOxmlElement): - """`w:gridCol` element, child of `w:tblGrid`, defines a table column.""" - - w: Length | None = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "w:w", ST_TwipsMeasure - ) - - @property - def gridCol_idx(self) -> int: - """Index of this `w:gridCol` element within its parent `w:tblGrid` element.""" - tblGrid = cast(CT_TblGrid, self.getparent()) - return tblGrid.gridCol_lst.index(self) - - -class CT_TblLayoutType(BaseOxmlElement): - """`w:tblLayout` element. - - Specifies whether column widths are fixed or can be automatically adjusted based on - content. - """ - - type: str | None = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "w:type", ST_TblLayoutType - ) - - -class CT_TblPr(BaseOxmlElement): - """```` element, child of ````, holds child elements that define - table properties such as style and borders.""" - - get_or_add_bidiVisual: Callable[[], CT_OnOff] - get_or_add_jc: Callable[[], CT_Jc] - get_or_add_tblLayout: Callable[[], CT_TblLayoutType] - _add_tblStyle: Callable[[], CT_String] - _remove_bidiVisual: Callable[[], None] - _remove_jc: Callable[[], None] - _remove_tblStyle: Callable[[], None] - - _tag_seq = ( - "w:tblStyle", - "w:tblpPr", - "w:tblOverlap", - "w:bidiVisual", - "w:tblStyleRowBandSize", - "w:tblStyleColBandSize", - "w:tblW", - "w:jc", - "w:tblCellSpacing", - "w:tblInd", - "w:tblBorders", - "w:shd", - "w:tblLayout", - "w:tblCellMar", - "w:tblLook", - "w:tblCaption", - "w:tblDescription", - "w:tblPrChange", - ) - tblStyle: CT_String | None = ZeroOrOne( # pyright: ignore[reportAssignmentType] - "w:tblStyle", successors=_tag_seq[1:] - ) - bidiVisual: CT_OnOff | None = ZeroOrOne( # pyright: ignore[reportAssignmentType] - "w:bidiVisual", successors=_tag_seq[4:] - ) - jc: CT_Jc | None = ZeroOrOne( # pyright: ignore[reportAssignmentType] - "w:jc", successors=_tag_seq[8:] - ) - tblLayout: CT_TblLayoutType | None = ZeroOrOne( # pyright: ignore[reportAssignmentType] - "w:tblLayout", successors=_tag_seq[13:] - ) - del _tag_seq - - @property - def alignment(self) -> WD_TABLE_ALIGNMENT | None: - """Horizontal alignment of table, |None| if `./w:jc` is not present.""" - jc = self.jc - if jc is None: - return None - return cast("WD_TABLE_ALIGNMENT | None", jc.val) - - @alignment.setter - def alignment(self, value: WD_TABLE_ALIGNMENT | None): - self._remove_jc() - if value is None: - return - jc = self.get_or_add_jc() - jc.val = cast("WD_ALIGN_PARAGRAPH", value) - - @property - def autofit(self) -> bool: - """|False| when there is a `w:tblLayout` child with `@w:type="fixed"`. - - Otherwise |True|. - """ - tblLayout = self.tblLayout - return True if tblLayout is None else tblLayout.type != "fixed" - - @autofit.setter - def autofit(self, value: bool): - tblLayout = self.get_or_add_tblLayout() - tblLayout.type = "autofit" if value else "fixed" - - @property - def style(self): - """Return the value of the ``val`` attribute of the ```` child or - |None| if not present.""" - tblStyle = self.tblStyle - if tblStyle is None: - return None - return tblStyle.val - - @style.setter - def style(self, value: str | None): - self._remove_tblStyle() - if value is None: - return - self._add_tblStyle().val = value - - -class CT_TblPrEx(BaseOxmlElement): - """`w:tblPrEx` element, exceptions to table-properties. - - Applied at a lower level, like a `w:tr` to modify the appearance. Possibly used when - two tables are merged. For more see: - http://officeopenxml.com/WPtablePropertyExceptions.php - """ - - -class CT_TblWidth(BaseOxmlElement): - """Used for `w:tblW` and `w:tcW` and others, specifies a table-related width.""" - - # the type for `w` attr is actually ST_MeasurementOrPercent, but using - # XsdInt for now because only dxa (twips) values are being used. It's not - # entirely clear what the semantics are for other values like -01.4mm - w: int = RequiredAttribute("w:w", XsdInt) # pyright: ignore[reportAssignmentType] - type = RequiredAttribute("w:type", ST_TblWidth) - - @property - def width(self) -> Length | None: - """EMU length indicated by the combined `w:w` and `w:type` attrs.""" - if self.type != "dxa": - return None - return Twips(self.w) - - @width.setter - def width(self, value: Length): - self.type = "dxa" - self.w = Emu(value).twips - - -class CT_Tc(BaseOxmlElement): - """`w:tc` table cell element.""" - - add_p: Callable[[], CT_P] - get_or_add_tcPr: Callable[[], CT_TcPr] - p_lst: list[CT_P] - tbl_lst: list[CT_Tbl] - _insert_tbl: Callable[[CT_Tbl], CT_Tbl] - _new_p: Callable[[], CT_P] - - # -- tcPr has many successors, `._insert_tcPr()` is overridden below -- - tcPr: CT_TcPr | None = ZeroOrOne("w:tcPr") # pyright: ignore[reportAssignmentType] - p = OneOrMore("w:p") - tbl = OneOrMore("w:tbl") - - @property - def bottom(self) -> int: - """The row index that marks the bottom extent of the vertical span of this cell. - - This is one greater than the index of the bottom-most row of the span, similar - to how a slice of the cell's rows would be specified. - """ - if self.vMerge is not None: - tc_below = self._tc_below - if tc_below is not None and tc_below.vMerge == ST_Merge.CONTINUE: - return tc_below.bottom - return self._tr_idx + 1 - - def clear_content(self): - """Remove all content elements, preserving `w:tcPr` element if present. - - Note that this leaves the `w:tc` element in an invalid state because it doesn't - contain at least one block-level element. It's up to the caller to add a - `w:p`child element as the last content element. - """ - # -- remove all cell inner-content except a `w:tcPr` when present. -- - for e in self.xpath("./*[not(self::w:tcPr)]"): - self.remove(e) - - @property - def grid_offset(self) -> int: - """Starting offset of `tc` in the layout-grid columns of its table. - - A cell in the leftmost grid-column has offset 0. - """ - grid_before = self._tr.grid_before - preceding_tc_grid_spans = sum( - tc.grid_span for tc in self.xpath("./preceding-sibling::w:tc") - ) - return grid_before + preceding_tc_grid_spans - - @property - def grid_span(self) -> int: - """The integer number of columns this cell spans. - - Determined by ./w:tcPr/w:gridSpan/@val, it defaults to 1. - """ - tcPr = self.tcPr - return 1 if tcPr is None else tcPr.grid_span - - @grid_span.setter - def grid_span(self, value: int): - tcPr = self.get_or_add_tcPr() - tcPr.grid_span = value - - @property - def inner_content_elements(self) -> list[CT_P | CT_Tbl]: - """Generate all `w:p` and `w:tbl` elements in this document-body. - - Elements appear in document order. Elements shaded by nesting in a `w:ins` or - other "wrapper" element will not be included. - """ - return self.xpath("./w:p | ./w:tbl") - - def iter_block_items(self): - """Generate a reference to each of the block-level content elements in this - cell, in the order they appear.""" - block_item_tags = (qn("w:p"), qn("w:tbl"), qn("w:sdt")) - for child in self: - if child.tag in block_item_tags: - yield child - - @property - def left(self) -> int: - """The grid column index at which this ```` element appears.""" - return self.grid_offset - - def merge(self, other_tc: CT_Tc) -> CT_Tc: - """Return top-left `w:tc` element of a new span. - - Span is formed by merging the rectangular region defined by using this tc - element and `other_tc` as diagonal corners. - """ - top, left, height, width = self._span_dimensions(other_tc) - top_tc = self._tbl.tr_lst[top].tc_at_grid_offset(left) - top_tc._grow_to(width, height) - return top_tc - - @classmethod - def new(cls) -> CT_Tc: - """A new `w:tc` element, containing an empty paragraph as the required EG_BlockLevelElt.""" - return cast(CT_Tc, parse_xml("" % nsdecls("w"))) - - @property - def right(self) -> int: - """The grid column index that marks the right-side extent of the horizontal span - of this cell. - - This is one greater than the index of the right-most column of the span, similar - to how a slice of the cell's columns would be specified. - """ - return self.grid_offset + self.grid_span - - @property - def top(self) -> int: - """The top-most row index in the vertical span of this cell.""" - if self.vMerge is None or self.vMerge == ST_Merge.RESTART: - return self._tr_idx - return self._tc_above.top - - @property - def vMerge(self) -> str | None: - """Value of ./w:tcPr/w:vMerge/@val, |None| if w:vMerge is not present.""" - tcPr = self.tcPr - if tcPr is None: - return None - return tcPr.vMerge_val - - @vMerge.setter - def vMerge(self, value: str | None): - tcPr = self.get_or_add_tcPr() - tcPr.vMerge_val = value - - @property - def width(self) -> Length | None: - """EMU length represented in `./w:tcPr/w:tcW` or |None| if not present.""" - tcPr = self.tcPr - if tcPr is None: - return None - return tcPr.width - - @width.setter - def width(self, value: Length): - tcPr = self.get_or_add_tcPr() - tcPr.width = value - - def _add_width_of(self, other_tc: CT_Tc): - """Add the width of `other_tc` to this cell. - - Does nothing if either this tc or `other_tc` does not have a specified width. - """ - if self.width and other_tc.width: - self.width = Length(self.width + other_tc.width) - - def _grow_to(self, width: int, height: int, top_tc: CT_Tc | None = None): - """Grow this cell to `width` grid columns and `height` rows. - - This is accomplished by expanding horizontal spans and creating continuation - cells to form vertical spans. - """ - - def vMerge_val(top_tc: CT_Tc): - return ( - ST_Merge.CONTINUE - if top_tc is not self - else None - if height == 1 - else ST_Merge.RESTART - ) - - top_tc = self if top_tc is None else top_tc - self._span_to_width(width, top_tc, vMerge_val(top_tc)) - if height > 1: - tc_below = self._tc_below - assert tc_below is not None - tc_below._grow_to(width, height - 1, top_tc) - - def _insert_tcPr(self, tcPr: CT_TcPr) -> CT_TcPr: - """Override default `._insert_tcPr()`.""" - # -- `tcPr`` has a large number of successors, but always comes first if it appears, - # -- so just using insert(0, ...) rather than spelling out successors. - self.insert(0, tcPr) - return tcPr - - @property - def _is_empty(self) -> bool: - """True if this cell contains only a single empty `w:p` element.""" - block_items = list(self.iter_block_items()) - if len(block_items) > 1: - return False - # -- cell must include at least one block item but can be a `w:tbl`, `w:sdt`, - # -- `w:customXml` or a `w:p` - only_item = block_items[0] - return isinstance(only_item, CT_P) and len(only_item.r_lst) == 0 - - def _move_content_to(self, other_tc: CT_Tc): - """Append the content of this cell to `other_tc`. - - Leaves this cell with a single empty ```` element. - """ - if other_tc is self: - return - if self._is_empty: - return - other_tc._remove_trailing_empty_p() - # -- appending moves each element from self to other_tc -- - for block_element in self.iter_block_items(): - other_tc.append(block_element) - # -- add back the required minimum single empty element -- - self.append(self._new_p()) - - def _new_tbl(self) -> None: - raise NotImplementedError( - "use CT_Tbl.new_tbl() to add a new table, specifying rows and columns" - ) - - @property - def _next_tc(self) -> CT_Tc | None: - """The `w:tc` element immediately following this one in this row, or |None| if - this is the last `w:tc` element in the row.""" - following_tcs = self.xpath("./following-sibling::w:tc") - return following_tcs[0] if following_tcs else None - - def _remove(self): - """Remove this `w:tc` element from the XML tree.""" - parent_element = self.getparent() - assert parent_element is not None - parent_element.remove(self) - - def _remove_trailing_empty_p(self): - """Remove last content element from this cell if it's an empty `w:p` element.""" - block_items = list(self.iter_block_items()) - last_content_elm = block_items[-1] - if not isinstance(last_content_elm, CT_P): - return - p = last_content_elm - if len(p.r_lst) > 0: - return - self.remove(p) - - def _span_dimensions(self, other_tc: CT_Tc) -> tuple[int, int, int, int]: - """Return a (top, left, height, width) 4-tuple specifying the extents of the - merged cell formed by using this tc and `other_tc` as opposite corner - extents.""" - - def raise_on_inverted_L(a: CT_Tc, b: CT_Tc): - if a.top == b.top and a.bottom != b.bottom: - raise InvalidSpanError("requested span not rectangular") - if a.left == b.left and a.right != b.right: - raise InvalidSpanError("requested span not rectangular") - - def raise_on_tee_shaped(a: CT_Tc, b: CT_Tc): - top_most, other = (a, b) if a.top < b.top else (b, a) - if top_most.top < other.top and top_most.bottom > other.bottom: - raise InvalidSpanError("requested span not rectangular") - - left_most, other = (a, b) if a.left < b.left else (b, a) - if left_most.left < other.left and left_most.right > other.right: - raise InvalidSpanError("requested span not rectangular") - - raise_on_inverted_L(self, other_tc) - raise_on_tee_shaped(self, other_tc) - - top = min(self.top, other_tc.top) - left = min(self.left, other_tc.left) - bottom = max(self.bottom, other_tc.bottom) - right = max(self.right, other_tc.right) - - return top, left, bottom - top, right - left - - def _span_to_width(self, grid_width: int, top_tc: CT_Tc, vMerge: str | None): - """Incorporate `w:tc` elements to the right until this cell spans `grid_width`. - - Incorporated `w:tc` elements are removed (replaced by gridSpan value). - - Raises |ValueError| if `grid_width` cannot be exactly achieved, such as when a - merged cell would drive the span width greater than `grid_width` or if not - enough grid columns are available to make this cell that wide. All content from - incorporated cells is appended to `top_tc`. The val attribute of the vMerge - element on the single remaining cell is set to `vMerge`. If `vMerge` is |None|, - the vMerge element is removed if present. - """ - self._move_content_to(top_tc) - while self.grid_span < grid_width: - self._swallow_next_tc(grid_width, top_tc) - self.vMerge = vMerge - - def _swallow_next_tc(self, grid_width: int, top_tc: CT_Tc): - """Extend the horizontal span of this `w:tc` element to incorporate the - following `w:tc` element in the row and then delete that following `w:tc` - element. - - Any content in the following `w:tc` element is appended to the content of - `top_tc`. The width of the following `w:tc` element is added to this one, if - present. Raises |InvalidSpanError| if the width of the resulting cell is greater - than `grid_width` or if there is no next `` element in the row. - """ - - def raise_on_invalid_swallow(next_tc: CT_Tc | None): - if next_tc is None: - raise InvalidSpanError("not enough grid columns") - if self.grid_span + next_tc.grid_span > grid_width: - raise InvalidSpanError("span is not rectangular") - - next_tc = self._next_tc - raise_on_invalid_swallow(next_tc) - assert next_tc is not None - next_tc._move_content_to(top_tc) - self._add_width_of(next_tc) - self.grid_span += next_tc.grid_span - next_tc._remove() - - @property - def _tbl(self) -> CT_Tbl: - """The tbl element this tc element appears in.""" - return cast(CT_Tbl, self.xpath("./ancestor::w:tbl[position()=1]")[0]) - - @property - def _tc_above(self) -> CT_Tc: - """The `w:tc` element immediately above this one in its grid column.""" - return self._tr_above.tc_at_grid_offset(self.grid_offset) - - @property - def _tc_below(self) -> CT_Tc | None: - """The tc element immediately below this one in its grid column.""" - tr_below = self._tr_below - if tr_below is None: - return None - return tr_below.tc_at_grid_offset(self.grid_offset) - - @property - def _tr(self) -> CT_Row: - """The tr element this tc element appears in.""" - return cast(CT_Row, self.xpath("./ancestor::w:tr[position()=1]")[0]) - - @property - def _tr_above(self) -> CT_Row: - """The tr element prior in sequence to the tr this cell appears in. - - Raises |ValueError| if called on a cell in the top-most row. - """ - tr_aboves = self.xpath("./ancestor::w:tr[position()=1]/preceding-sibling::w:tr[1]") - if not tr_aboves: - raise ValueError("no tr above topmost tr in w:tbl") - return tr_aboves[0] - - @property - def _tr_below(self) -> CT_Row | None: - """The tr element next in sequence after the tr this cell appears in, or |None| - if this cell appears in the last row.""" - tr_lst = self._tbl.tr_lst - tr_idx = tr_lst.index(self._tr) - try: - return tr_lst[tr_idx + 1] - except IndexError: - return None - - @property - def _tr_idx(self) -> int: - """The row index of the tr element this tc element appears in.""" - return self._tbl.tr_lst.index(self._tr) - - -class CT_TcPr(BaseOxmlElement): - """```` element, defining table cell properties.""" - - get_or_add_gridSpan: Callable[[], CT_DecimalNumber] - get_or_add_tcW: Callable[[], CT_TblWidth] - get_or_add_vAlign: Callable[[], CT_VerticalJc] - _add_vMerge: Callable[[], CT_VMerge] - _remove_gridSpan: Callable[[], None] - _remove_vAlign: Callable[[], None] - _remove_vMerge: Callable[[], None] - - _tag_seq = ( - "w:cnfStyle", - "w:tcW", - "w:gridSpan", - "w:hMerge", - "w:vMerge", - "w:tcBorders", - "w:shd", - "w:noWrap", - "w:tcMar", - "w:textDirection", - "w:tcFitText", - "w:vAlign", - "w:hideMark", - "w:headers", - "w:cellIns", - "w:cellDel", - "w:cellMerge", - "w:tcPrChange", - ) - tcW: CT_TblWidth | None = ZeroOrOne( # pyright: ignore[reportAssignmentType] - "w:tcW", successors=_tag_seq[2:] - ) - gridSpan: CT_DecimalNumber | None = ZeroOrOne( # pyright: ignore[reportAssignmentType] - "w:gridSpan", successors=_tag_seq[3:] - ) - vMerge: CT_VMerge | None = ZeroOrOne( # pyright: ignore[reportAssignmentType] - "w:vMerge", successors=_tag_seq[5:] - ) - vAlign: CT_VerticalJc | None = ZeroOrOne( # pyright: ignore[reportAssignmentType] - "w:vAlign", successors=_tag_seq[12:] - ) - del _tag_seq - - @property - def grid_span(self) -> int: - """The integer number of columns this cell spans. - - Determined by ./w:gridSpan/@val, it defaults to 1. - """ - gridSpan = self.gridSpan - return 1 if gridSpan is None else gridSpan.val - - @grid_span.setter - def grid_span(self, value: int): - self._remove_gridSpan() - if value > 1: - self.get_or_add_gridSpan().val = value - - @property - def vAlign_val(self): - """Value of `w:val` attribute on `w:vAlign` child. - - Value is |None| if `w:vAlign` child is not present. The `w:val` attribute on - `w:vAlign` is required. - """ - vAlign = self.vAlign - if vAlign is None: - return None - return vAlign.val - - @vAlign_val.setter - def vAlign_val(self, value: WD_CELL_VERTICAL_ALIGNMENT | None): - if value is None: - self._remove_vAlign() - return - self.get_or_add_vAlign().val = value - - @property - def vMerge_val(self): - """The value of the ./w:vMerge/@val attribute, or |None| if the w:vMerge element - is not present.""" - vMerge = self.vMerge - if vMerge is None: - return None - return vMerge.val - - @vMerge_val.setter - def vMerge_val(self, value: str | None): - self._remove_vMerge() - if value is not None: - self._add_vMerge().val = value - - @property - def width(self) -> Length | None: - """EMU length in `./w:tcW` or |None| if not present or its type is not 'dxa'.""" - tcW = self.tcW - if tcW is None: - return None - return tcW.width - - @width.setter - def width(self, value: Length): - tcW = self.get_or_add_tcW() - tcW.width = value - - -class CT_TrPr(BaseOxmlElement): - """```` element, defining table row properties.""" - - get_or_add_trHeight: Callable[[], CT_Height] - - _tag_seq = ( - "w:cnfStyle", - "w:divId", - "w:gridBefore", - "w:gridAfter", - "w:wBefore", - "w:wAfter", - "w:cantSplit", - "w:trHeight", - "w:tblHeader", - "w:tblCellSpacing", - "w:jc", - "w:hidden", - "w:ins", - "w:del", - "w:trPrChange", - ) - gridAfter: CT_DecimalNumber | None = ZeroOrOne( # pyright: ignore[reportAssignmentType] - "w:gridAfter", successors=_tag_seq[4:] - ) - gridBefore: CT_DecimalNumber | None = ZeroOrOne( # pyright: ignore[reportAssignmentType] - "w:gridBefore", successors=_tag_seq[3:] - ) - trHeight: CT_Height | None = ZeroOrOne( # pyright: ignore[reportAssignmentType] - "w:trHeight", successors=_tag_seq[8:] - ) - del _tag_seq - - @property - def grid_after(self) -> int: - """The number of unpopulated layout-grid cells at the end of this row.""" - gridAfter = self.gridAfter - return 0 if gridAfter is None else gridAfter.val - - @property - def grid_before(self) -> int: - """The number of unpopulated layout-grid cells at the start of this row.""" - gridBefore = self.gridBefore - return 0 if gridBefore is None else gridBefore.val - - @property - def trHeight_hRule(self) -> WD_ROW_HEIGHT_RULE | None: - """Return the value of `w:trHeight@w:hRule`, or |None| if not present.""" - trHeight = self.trHeight - return None if trHeight is None else trHeight.hRule - - @trHeight_hRule.setter - def trHeight_hRule(self, value: WD_ROW_HEIGHT_RULE | None): - if value is None and self.trHeight is None: - return - trHeight = self.get_or_add_trHeight() - trHeight.hRule = value - - @property - def trHeight_val(self): - """Return the value of `w:trHeight@w:val`, or |None| if not present.""" - trHeight = self.trHeight - return None if trHeight is None else trHeight.val - - @trHeight_val.setter - def trHeight_val(self, value: Length | None): - if value is None and self.trHeight is None: - return - trHeight = self.get_or_add_trHeight() - trHeight.val = value - - -class CT_VerticalJc(BaseOxmlElement): - """`w:vAlign` element, specifying vertical alignment of cell.""" - - val: WD_CELL_VERTICAL_ALIGNMENT = RequiredAttribute( # pyright: ignore[reportAssignmentType] - "w:val", WD_CELL_VERTICAL_ALIGNMENT - ) - - -class CT_VMerge(BaseOxmlElement): - """```` element, specifying vertical merging behavior of a cell.""" - - val: str | None = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "w:val", ST_Merge, default=ST_Merge.CONTINUE - ) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/__init__.py b/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/__pycache__/__init__.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index f3b1abc3..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/__pycache__/font.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/__pycache__/font.cpython-312.pyc deleted file mode 100644 index 0bb104da..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/__pycache__/font.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/__pycache__/hyperlink.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/__pycache__/hyperlink.cpython-312.pyc deleted file mode 100644 index 6c0edc8a..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/__pycache__/hyperlink.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/__pycache__/pagebreak.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/__pycache__/pagebreak.cpython-312.pyc deleted file mode 100644 index fb2a1eec..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/__pycache__/pagebreak.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/__pycache__/paragraph.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/__pycache__/paragraph.cpython-312.pyc deleted file mode 100644 index a66f404c..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/__pycache__/paragraph.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/__pycache__/parfmt.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/__pycache__/parfmt.cpython-312.pyc deleted file mode 100644 index 8b82084a..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/__pycache__/parfmt.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/__pycache__/run.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/__pycache__/run.cpython-312.pyc deleted file mode 100644 index f1d8b948..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/__pycache__/run.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/font.py b/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/font.py deleted file mode 100644 index 32eb567b..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/font.py +++ /dev/null @@ -1,331 +0,0 @@ -# pyright: reportAssignmentType=false - -"""Custom element classes related to run properties (font).""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Callable - -from docx.enum.dml import MSO_THEME_COLOR -from docx.enum.text import WD_COLOR_INDEX, WD_UNDERLINE -from docx.oxml.ns import nsdecls -from docx.oxml.parser import parse_xml -from docx.oxml.simpletypes import ( - ST_HexColor, - ST_HpsMeasure, - ST_String, - ST_VerticalAlignRun, -) -from docx.oxml.xmlchemy import ( - BaseOxmlElement, - OptionalAttribute, - RequiredAttribute, - ZeroOrOne, -) -from docx.shared import RGBColor - -if TYPE_CHECKING: - from docx.oxml.shared import CT_OnOff, CT_String - from docx.shared import Length - - -class CT_Color(BaseOxmlElement): - """`w:color` element, specifying the color of a font and perhaps other objects.""" - - val: RGBColor | str = RequiredAttribute("w:val", ST_HexColor) - themeColor: MSO_THEME_COLOR | None = OptionalAttribute("w:themeColor", MSO_THEME_COLOR) - - -class CT_Fonts(BaseOxmlElement): - """`` element. - - Specifies typeface name for the various language types. - """ - - ascii: str | None = OptionalAttribute("w:ascii", ST_String) - hAnsi: str | None = OptionalAttribute("w:hAnsi", ST_String) - - -class CT_Highlight(BaseOxmlElement): - """`w:highlight` element, specifying font highlighting/background color.""" - - val: WD_COLOR_INDEX = RequiredAttribute("w:val", WD_COLOR_INDEX) - - -class CT_HpsMeasure(BaseOxmlElement): - """Used for `` element and others, specifying font size in half-points.""" - - val: Length = RequiredAttribute("w:val", ST_HpsMeasure) - - -class CT_RPr(BaseOxmlElement): - """`` element, containing the properties for a run.""" - - get_or_add_color: Callable[[], CT_Color] - get_or_add_highlight: Callable[[], CT_Highlight] - get_or_add_rFonts: Callable[[], CT_Fonts] - get_or_add_sz: Callable[[], CT_HpsMeasure] - get_or_add_vertAlign: Callable[[], CT_VerticalAlignRun] - _add_rStyle: Callable[..., CT_String] - _add_u: Callable[[], CT_Underline] - _remove_color: Callable[[], None] - _remove_highlight: Callable[[], None] - _remove_rFonts: Callable[[], None] - _remove_rStyle: Callable[[], None] - _remove_sz: Callable[[], None] - _remove_u: Callable[[], None] - _remove_vertAlign: Callable[[], None] - - _tag_seq = ( - "w:rStyle", - "w:rFonts", - "w:b", - "w:bCs", - "w:i", - "w:iCs", - "w:caps", - "w:smallCaps", - "w:strike", - "w:dstrike", - "w:outline", - "w:shadow", - "w:emboss", - "w:imprint", - "w:noProof", - "w:snapToGrid", - "w:vanish", - "w:webHidden", - "w:color", - "w:spacing", - "w:w", - "w:kern", - "w:position", - "w:sz", - "w:szCs", - "w:highlight", - "w:u", - "w:effect", - "w:bdr", - "w:shd", - "w:fitText", - "w:vertAlign", - "w:rtl", - "w:cs", - "w:em", - "w:lang", - "w:eastAsianLayout", - "w:specVanish", - "w:oMath", - ) - rStyle: CT_String | None = ZeroOrOne("w:rStyle", successors=_tag_seq[1:]) - rFonts: CT_Fonts | None = ZeroOrOne("w:rFonts", successors=_tag_seq[2:]) - b: CT_OnOff | None = ZeroOrOne("w:b", successors=_tag_seq[3:]) - bCs = ZeroOrOne("w:bCs", successors=_tag_seq[4:]) - i = ZeroOrOne("w:i", successors=_tag_seq[5:]) - iCs = ZeroOrOne("w:iCs", successors=_tag_seq[6:]) - caps = ZeroOrOne("w:caps", successors=_tag_seq[7:]) - smallCaps = ZeroOrOne("w:smallCaps", successors=_tag_seq[8:]) - strike = ZeroOrOne("w:strike", successors=_tag_seq[9:]) - dstrike = ZeroOrOne("w:dstrike", successors=_tag_seq[10:]) - outline = ZeroOrOne("w:outline", successors=_tag_seq[11:]) - shadow = ZeroOrOne("w:shadow", successors=_tag_seq[12:]) - emboss = ZeroOrOne("w:emboss", successors=_tag_seq[13:]) - imprint = ZeroOrOne("w:imprint", successors=_tag_seq[14:]) - noProof = ZeroOrOne("w:noProof", successors=_tag_seq[15:]) - snapToGrid = ZeroOrOne("w:snapToGrid", successors=_tag_seq[16:]) - vanish = ZeroOrOne("w:vanish", successors=_tag_seq[17:]) - webHidden = ZeroOrOne("w:webHidden", successors=_tag_seq[18:]) - color: CT_Color | None = ZeroOrOne("w:color", successors=_tag_seq[19:]) - sz: CT_HpsMeasure | None = ZeroOrOne("w:sz", successors=_tag_seq[24:]) - highlight: CT_Highlight | None = ZeroOrOne("w:highlight", successors=_tag_seq[26:]) - u: CT_Underline | None = ZeroOrOne("w:u", successors=_tag_seq[27:]) - vertAlign: CT_VerticalAlignRun | None = ZeroOrOne("w:vertAlign", successors=_tag_seq[32:]) - rtl = ZeroOrOne("w:rtl", successors=_tag_seq[33:]) - cs = ZeroOrOne("w:cs", successors=_tag_seq[34:]) - specVanish = ZeroOrOne("w:specVanish", successors=_tag_seq[38:]) - oMath = ZeroOrOne("w:oMath", successors=_tag_seq[39:]) - del _tag_seq - - def _new_color(self): - """Override metaclass method to set `w:color/@val` to RGB black on create.""" - return parse_xml('' % nsdecls("w")) - - @property - def highlight_val(self) -> WD_COLOR_INDEX | None: - """Value of `./w:highlight/@val`. - - Specifies font's highlight color, or `None` if the text is not highlighted. - """ - highlight = self.highlight - if highlight is None: - return None - return highlight.val - - @highlight_val.setter - def highlight_val(self, value: WD_COLOR_INDEX | None) -> None: - if value is None: - self._remove_highlight() - return - highlight = self.get_or_add_highlight() - highlight.val = value - - @property - def rFonts_ascii(self) -> str | None: - """The value of `w:rFonts/@w:ascii` or |None| if not present. - - Represents the assigned typeface name. The rFonts element also specifies other - special-case typeface names; this method handles the case where just the common - name is required. - """ - rFonts = self.rFonts - if rFonts is None: - return None - return rFonts.ascii - - @rFonts_ascii.setter - def rFonts_ascii(self, value: str | None) -> None: - if value is None: - self._remove_rFonts() - return - rFonts = self.get_or_add_rFonts() - rFonts.ascii = value - - @property - def rFonts_hAnsi(self) -> str | None: - """The value of `w:rFonts/@w:hAnsi` or |None| if not present.""" - rFonts = self.rFonts - if rFonts is None: - return None - return rFonts.hAnsi - - @rFonts_hAnsi.setter - def rFonts_hAnsi(self, value: str | None): - if value is None and self.rFonts is None: - return - rFonts = self.get_or_add_rFonts() - rFonts.hAnsi = value - - @property - def style(self) -> str | None: - """String in `./w:rStyle/@val`, or None if `w:rStyle` is not present.""" - rStyle = self.rStyle - if rStyle is None: - return None - return rStyle.val - - @style.setter - def style(self, style: str | None) -> None: - """Set `./w:rStyle/@val` to `style`, adding the `w:rStyle` element if necessary. - - If `style` is |None|, remove `w:rStyle` element if present. - """ - if style is None: - self._remove_rStyle() - elif self.rStyle is None: - self._add_rStyle(val=style) - else: - self.rStyle.val = style - - @property - def subscript(self) -> bool | None: - """|True| if `./w:vertAlign/@w:val` is "subscript". - - |False| if `w:vertAlign/@w:val` contains any other value. |None| if - `w:vertAlign` is not present. - """ - vertAlign = self.vertAlign - if vertAlign is None: - return None - return vertAlign.val == ST_VerticalAlignRun.SUBSCRIPT - - @subscript.setter - def subscript(self, value: bool | None) -> None: - if value is None: - self._remove_vertAlign() - elif bool(value) is True: - self.get_or_add_vertAlign().val = ST_VerticalAlignRun.SUBSCRIPT - # -- assert bool(value) is False -- - elif self.vertAlign is not None and self.vertAlign.val == ST_VerticalAlignRun.SUBSCRIPT: - self._remove_vertAlign() - - @property - def superscript(self) -> bool | None: - """|True| if `w:vertAlign/@w:val` is 'superscript'. - - |False| if `w:vertAlign/@w:val` contains any other value. |None| if - `w:vertAlign` is not present. - """ - vertAlign = self.vertAlign - if vertAlign is None: - return None - return vertAlign.val == ST_VerticalAlignRun.SUPERSCRIPT - - @superscript.setter - def superscript(self, value: bool | None): - if value is None: - self._remove_vertAlign() - elif bool(value) is True: - self.get_or_add_vertAlign().val = ST_VerticalAlignRun.SUPERSCRIPT - # -- assert bool(value) is False -- - elif self.vertAlign is not None and self.vertAlign.val == ST_VerticalAlignRun.SUPERSCRIPT: - self._remove_vertAlign() - - @property - def sz_val(self) -> Length | None: - """The value of `w:sz/@w:val` or |None| if not present.""" - sz = self.sz - if sz is None: - return None - return sz.val - - @sz_val.setter - def sz_val(self, value: Length | None): - if value is None: - self._remove_sz() - return - sz = self.get_or_add_sz() - sz.val = value - - @property - def u_val(self) -> WD_UNDERLINE | None: - """Value of `w:u/@val`, or None if not present. - - Values `WD_UNDERLINE.SINGLE` and `WD_UNDERLINE.NONE` are mapped to `True` and - `False` respectively. - """ - u = self.u - if u is None: - return None - return u.val - - @u_val.setter - def u_val(self, value: WD_UNDERLINE | None): - self._remove_u() - if value is not None: - self._add_u().val = value - - def _get_bool_val(self, name: str) -> bool | None: - """Value of boolean child with `name`, e.g. "w:b", "w:i", and "w:smallCaps".""" - element = getattr(self, name) - if element is None: - return None - return element.val - - def _set_bool_val(self, name: str, value: bool | None): - if value is None: - getattr(self, "_remove_%s" % name)() - return - element = getattr(self, "get_or_add_%s" % name)() - element.val = value - - -class CT_Underline(BaseOxmlElement): - """`` element, specifying the underlining style for a run.""" - - val: WD_UNDERLINE | None = OptionalAttribute("w:val", WD_UNDERLINE) - - -class CT_VerticalAlignRun(BaseOxmlElement): - """`` element, specifying subscript or superscript.""" - - val: str = RequiredAttribute("w:val", ST_VerticalAlignRun) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/hyperlink.py b/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/hyperlink.py deleted file mode 100644 index 38a33ff1..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/hyperlink.py +++ /dev/null @@ -1,45 +0,0 @@ -"""Custom element classes related to hyperlinks (CT_Hyperlink).""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, List - -from docx.oxml.simpletypes import ST_OnOff, ST_String, XsdString -from docx.oxml.text.run import CT_R -from docx.oxml.xmlchemy import ( - BaseOxmlElement, - OptionalAttribute, - ZeroOrMore, -) - -if TYPE_CHECKING: - from docx.oxml.text.pagebreak import CT_LastRenderedPageBreak - - -class CT_Hyperlink(BaseOxmlElement): - """`` element, containing the text and address for a hyperlink.""" - - r_lst: List[CT_R] - - rId: str | None = OptionalAttribute("r:id", XsdString) # pyright: ignore[reportAssignmentType] - anchor: str | None = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "w:anchor", ST_String - ) - history: bool = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "w:history", ST_OnOff, default=True - ) - - r = ZeroOrMore("w:r") - - @property - def lastRenderedPageBreaks(self) -> List[CT_LastRenderedPageBreak]: - """All `w:lastRenderedPageBreak` descendants of this hyperlink.""" - return self.xpath("./w:r/w:lastRenderedPageBreak") - - @property - def text(self) -> str: # pyright: ignore[reportIncompatibleMethodOverride] - """The textual content of this hyperlink. - - `CT_Hyperlink` stores the hyperlink-text as one or more `w:r` children. - """ - return "".join(r.text for r in self.xpath("w:r")) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/pagebreak.py b/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/pagebreak.py deleted file mode 100644 index 45a6f51a..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/pagebreak.py +++ /dev/null @@ -1,278 +0,0 @@ -"""Custom element class for rendered page-break (CT_LastRenderedPageBreak).""" - -from __future__ import annotations - -import copy -from typing import TYPE_CHECKING - -from docx.oxml.xmlchemy import BaseOxmlElement -from docx.shared import lazyproperty - -if TYPE_CHECKING: - from docx.oxml.text.hyperlink import CT_Hyperlink - from docx.oxml.text.paragraph import CT_P - - -class CT_LastRenderedPageBreak(BaseOxmlElement): - """`` element, indicating page break inserted by renderer. - - A rendered page-break is one inserted by the renderer when it runs out of room on a - page. It is an empty element (no attrs or children) and is a child of CT_R, peer to - CT_Text. - - NOTE: this complex-type name does not exist in the schema, where - `w:lastRenderedPageBreak` maps to `CT_Empty`. This name was added to give it - distinguished behavior. CT_Empty is used for many elements. - """ - - @property - def following_fragment_p(self) -> CT_P: - """A "loose" `CT_P` containing only the paragraph content before this break. - - Raises `ValueError` if this `w:lastRenderedPageBreak` is not the first rendered - page-break in its paragraph. - - The returned `CT_P` is a "clone" (deepcopy) of the `w:p` ancestor of this - page-break with this `w:lastRenderedPageBreak` element and all content preceding - it removed. - - NOTE: this `w:p` can itself contain one or more `w:renderedPageBreak` elements - (when the paragraph contained more than one). While this is rare, the caller - should treat this paragraph the same as other paragraphs and split it if - necessary in a folloing step or recursion. - """ - if not self == self._first_lrpb_in_p(self._enclosing_p): - raise ValueError("only defined on first rendered page-break in paragraph") - - # -- splitting approach is different when break is inside a hyperlink -- - return ( - self._following_frag_in_hlink if self._is_in_hyperlink else self._following_frag_in_run - ) - - @property - def follows_all_content(self) -> bool: - """True when this page-break element is the last "content" in the paragraph. - - This is very uncommon case and may only occur in contrived or cases where the - XML is edited by hand, but it is not precluded by the spec. - """ - # -- a page-break inside a hyperlink never meets these criteria (for our - # -- purposes at least) because it is considered "atomic" and always associated - # -- with the page it starts on. - if self._is_in_hyperlink: - return False - - return bool( - # -- XPath will match zero-or-one w:lastRenderedPageBreak element -- - self._enclosing_p.xpath( - # -- in first run of paragraph -- - f"(./w:r)[last()]" - # -- all page-breaks -- - f"/w:lastRenderedPageBreak" - # -- that are not preceded by any content-bearing elements -- - f"[not(following-sibling::*[{self._run_inner_content_xpath}])]" - ) - ) - - @property - def precedes_all_content(self) -> bool: - """True when a `w:lastRenderedPageBreak` precedes all paragraph content. - - This is a common case; it occurs whenever the page breaks on an even paragraph - boundary. - """ - # -- a page-break inside a hyperlink never meets these criteria because there - # -- is always part of the hyperlink text before the page-break. - if self._is_in_hyperlink: - return False - - return bool( - # -- XPath will match zero-or-one w:lastRenderedPageBreak element -- - self._enclosing_p.xpath( - # -- in first run of paragraph -- - f"./w:r[1]" - # -- all page-breaks -- - f"/w:lastRenderedPageBreak" - # -- that are not preceded by any content-bearing elements -- - f"[not(preceding-sibling::*[{self._run_inner_content_xpath}])]" - ) - ) - - @property - def preceding_fragment_p(self) -> CT_P: - """A "loose" `CT_P` containing only the paragraph content before this break. - - Raises `ValueError` if this `w:lastRenderedPageBreak` is not the first rendered - paragraph in its paragraph. - - The returned `CT_P` is a "clone" (deepcopy) of the `w:p` ancestor of this - page-break with this `w:lastRenderedPageBreak` element and all its following - siblings removed. - """ - if not self == self._first_lrpb_in_p(self._enclosing_p): - raise ValueError("only defined on first rendered page-break in paragraph") - - # -- splitting approach is different when break is inside a hyperlink -- - return ( - self._preceding_frag_in_hlink if self._is_in_hyperlink else self._preceding_frag_in_run - ) - - def _enclosing_hyperlink(self, lrpb: CT_LastRenderedPageBreak) -> CT_Hyperlink: - """The `w:hyperlink` grandparent of this `w:lastRenderedPageBreak`. - - Raises `IndexError` when this page-break has a `w:p` grandparent, so only call - when `._is_in_hyperlink` is True. - """ - return lrpb.xpath("./parent::w:r/parent::w:hyperlink")[0] - - @property - def _enclosing_p(self) -> CT_P: - """The `w:p` element parent or grandparent of this `w:lastRenderedPageBreak`.""" - return self.xpath("./ancestor::w:p[1]")[0] - - def _first_lrpb_in_p(self, p: CT_P) -> CT_LastRenderedPageBreak: - """The first `w:lastRenderedPageBreak` element in `p`. - - Raises `ValueError` if there are no rendered page-breaks in `p`. - """ - lrpbs = p.xpath("./w:r/w:lastRenderedPageBreak | ./w:hyperlink/w:r/w:lastRenderedPageBreak") - if not lrpbs: - raise ValueError("no rendered page-breaks in paragraph element") - return lrpbs[0] - - @lazyproperty - def _following_frag_in_hlink(self) -> CT_P: - """Following CT_P fragment when break occurs within a hyperlink. - - Note this is a *partial-function* and raises when `lrpb` is not inside a - hyperlink. - """ - if not self._is_in_hyperlink: - raise ValueError("only defined on a rendered page-break in a hyperlink") - - # -- work on a clone `w:p` so our mutations don't persist -- - p = copy.deepcopy(self._enclosing_p) - - # -- get this `w:lastRenderedPageBreak` in the cloned `w:p` (not self) -- - lrpb = self._first_lrpb_in_p(p) - - # -- locate `w:hyperlink` in which this `w:lastRenderedPageBreak` is found -- - hyperlink = lrpb._enclosing_hyperlink(lrpb) - - # -- delete all w:p inner-content preceding the hyperlink -- - for e in hyperlink.xpath("./preceding-sibling::*[not(self::w:pPr)]"): - p.remove(e) - - # -- remove the whole hyperlink, it belongs to the preceding-fragment-p -- - hyperlink.getparent().remove(hyperlink) - - # -- that's it, return the remaining fragment of `w:p` clone -- - return p - - @lazyproperty - def _following_frag_in_run(self) -> CT_P: - """following CT_P fragment when break does not occur in a hyperlink. - - Note this is a *partial-function* and raises when `lrpb` is inside a hyperlink. - """ - if self._is_in_hyperlink: - raise ValueError("only defined on a rendered page-break not in a hyperlink") - - # -- work on a clone `w:p` so our mutations don't persist -- - p = copy.deepcopy(self._enclosing_p) - - # -- get this `w:lastRenderedPageBreak` in the cloned `w:p` (not self) -- - lrpb = self._first_lrpb_in_p(p) - - # -- locate `w:r` in which this `w:lastRenderedPageBreak` is found -- - enclosing_r = lrpb.xpath("./parent::w:r")[0] - - # -- delete all w:p inner-content preceding that run (but not w:pPr) -- - for e in enclosing_r.xpath("./preceding-sibling::*[not(self::w:pPr)]"): - p.remove(e) - - # -- then remove all run inner-content preceding this lrpb in its run (but not - # -- the `w:rPr`) and also remove the page-break itself - for e in lrpb.xpath("./preceding-sibling::*[not(self::w:rPr)]"): - enclosing_r.remove(e) - enclosing_r.remove(lrpb) - - return p - - @lazyproperty - def _is_in_hyperlink(self) -> bool: - """True when this page-break is embedded in a hyperlink run.""" - return bool(self.xpath("./parent::w:r/parent::w:hyperlink")) - - @lazyproperty - def _preceding_frag_in_hlink(self) -> CT_P: - """Preceding CT_P fragment when break occurs within a hyperlink. - - Note this is a *partial-function* and raises when `lrpb` is not inside a - hyperlink. - """ - if not self._is_in_hyperlink: - raise ValueError("only defined on a rendered page-break in a hyperlink") - - # -- work on a clone `w:p` so our mutations don't persist -- - p = copy.deepcopy(self._enclosing_p) - - # -- get this `w:lastRenderedPageBreak` in the cloned `w:p` (not self) -- - lrpb = self._first_lrpb_in_p(p) - - # -- locate `w:hyperlink` in which this `w:lastRenderedPageBreak` is found -- - hyperlink = lrpb._enclosing_hyperlink(lrpb) - - # -- delete all w:p inner-content following the hyperlink -- - for e in hyperlink.xpath("./following-sibling::*"): - p.remove(e) - - # -- remove this page-break from inside the hyperlink -- - lrpb.getparent().remove(lrpb) - - # -- that's it, the entire hyperlink goes into the preceding fragment so - # -- the hyperlink is not "split". - return p - - @lazyproperty - def _preceding_frag_in_run(self) -> CT_P: - """Preceding CT_P fragment when break does not occur in a hyperlink. - - Note this is a *partial-function* and raises when `lrpb` is inside a hyperlink. - """ - if self._is_in_hyperlink: - raise ValueError("only defined on a rendered page-break not in a hyperlink") - - # -- work on a clone `w:p` so our mutations don't persist -- - p = copy.deepcopy(self._enclosing_p) - - # -- get this `w:lastRenderedPageBreak` in the cloned `w:p` (not self) -- - lrpb = self._first_lrpb_in_p(p) - - # -- locate `w:r` in which this `w:lastRenderedPageBreak` is found -- - enclosing_r = lrpb.xpath("./parent::w:r")[0] - - # -- delete all `w:p` inner-content following that run -- - for e in enclosing_r.xpath("./following-sibling::*"): - p.remove(e) - - # -- then delete all `w:r` inner-content following this lrpb in its run and - # -- also remove the page-break itself - for e in lrpb.xpath("./following-sibling::*"): - enclosing_r.remove(e) - enclosing_r.remove(lrpb) - - return p - - @lazyproperty - def _run_inner_content_xpath(self) -> str: - """XPath fragment matching any run inner-content elements.""" - return ( - "self::w:br" - " | self::w:cr" - " | self::w:drawing" - " | self::w:noBreakHyphen" - " | self::w:ptab" - " | self::w:t" - " | self::w:tab" - ) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/paragraph.py b/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/paragraph.py deleted file mode 100644 index 63e96f31..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/paragraph.py +++ /dev/null @@ -1,106 +0,0 @@ -# pyright: reportPrivateUsage=false - -"""Custom element classes related to paragraphs (CT_P).""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Callable, List, cast - -from docx.oxml.parser import OxmlElement -from docx.oxml.xmlchemy import BaseOxmlElement, ZeroOrMore, ZeroOrOne - -if TYPE_CHECKING: - from docx.enum.text import WD_PARAGRAPH_ALIGNMENT - from docx.oxml.section import CT_SectPr - from docx.oxml.text.hyperlink import CT_Hyperlink - from docx.oxml.text.pagebreak import CT_LastRenderedPageBreak - from docx.oxml.text.parfmt import CT_PPr - from docx.oxml.text.run import CT_R - - -class CT_P(BaseOxmlElement): - """`` element, containing the properties and text for a paragraph.""" - - add_r: Callable[[], CT_R] - get_or_add_pPr: Callable[[], CT_PPr] - hyperlink_lst: List[CT_Hyperlink] - r_lst: List[CT_R] - - pPr: CT_PPr | None = ZeroOrOne("w:pPr") # pyright: ignore[reportAssignmentType] - hyperlink = ZeroOrMore("w:hyperlink") - r = ZeroOrMore("w:r") - - def add_p_before(self) -> CT_P: - """Return a new `` element inserted directly prior to this one.""" - new_p = cast(CT_P, OxmlElement("w:p")) - self.addprevious(new_p) - return new_p - - @property - def alignment(self) -> WD_PARAGRAPH_ALIGNMENT | None: - """The value of the `` grandchild element or |None| if not present.""" - pPr = self.pPr - if pPr is None: - return None - return pPr.jc_val - - @alignment.setter - def alignment(self, value: WD_PARAGRAPH_ALIGNMENT): - pPr = self.get_or_add_pPr() - pPr.jc_val = value - - def clear_content(self): - """Remove all child elements, except the `` element if present.""" - for child in self.xpath("./*[not(self::w:pPr)]"): - self.remove(child) - - @property - def inner_content_elements(self) -> List[CT_R | CT_Hyperlink]: - """Run and hyperlink children of the `w:p` element, in document order.""" - return self.xpath("./w:r | ./w:hyperlink") - - @property - def lastRenderedPageBreaks(self) -> List[CT_LastRenderedPageBreak]: - """All `w:lastRenderedPageBreak` descendants of this paragraph. - - Rendered page-breaks commonly occur in a run but can also occur in a run inside - a hyperlink. This returns both. - """ - return self.xpath( - "./w:r/w:lastRenderedPageBreak | ./w:hyperlink/w:r/w:lastRenderedPageBreak" - ) - - def set_sectPr(self, sectPr: CT_SectPr): - """Unconditionally replace or add `sectPr` as grandchild in correct sequence.""" - pPr = self.get_or_add_pPr() - pPr._remove_sectPr() - pPr._insert_sectPr(sectPr) - - @property - def style(self) -> str | None: - """String contained in `w:val` attribute of `./w:pPr/w:pStyle` grandchild. - - |None| if not present. - """ - pPr = self.pPr - if pPr is None: - return None - return pPr.style - - @style.setter - def style(self, style: str | None): - pPr = self.get_or_add_pPr() - pPr.style = style - - @property - def text(self): # pyright: ignore[reportIncompatibleMethodOverride] - """The textual content of this paragraph. - - Inner-content child elements like `w:r` and `w:hyperlink` are translated to - their text equivalent. - """ - return "".join(e.text for e in self.xpath("w:r | w:hyperlink")) - - def _insert_pPr(self, pPr: CT_PPr) -> CT_PPr: - self.insert(0, pPr) - return pPr diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/parfmt.py b/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/parfmt.py deleted file mode 100644 index 2133686b..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/parfmt.py +++ /dev/null @@ -1,392 +0,0 @@ -"""Custom element classes related to paragraph properties (CT_PPr).""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Callable - -from docx.enum.text import ( - WD_ALIGN_PARAGRAPH, - WD_LINE_SPACING, - WD_TAB_ALIGNMENT, - WD_TAB_LEADER, -) -from docx.oxml.shared import CT_DecimalNumber -from docx.oxml.simpletypes import ST_SignedTwipsMeasure, ST_TwipsMeasure -from docx.oxml.xmlchemy import ( - BaseOxmlElement, - OneOrMore, - OptionalAttribute, - RequiredAttribute, - ZeroOrOne, -) -from docx.shared import Length - -if TYPE_CHECKING: - from docx.oxml.section import CT_SectPr - from docx.oxml.shared import CT_String - - -class CT_Ind(BaseOxmlElement): - """```` element, specifying paragraph indentation.""" - - left: Length | None = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "w:left", ST_SignedTwipsMeasure - ) - right: Length | None = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "w:right", ST_SignedTwipsMeasure - ) - firstLine: Length | None = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "w:firstLine", ST_TwipsMeasure - ) - hanging: Length | None = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "w:hanging", ST_TwipsMeasure - ) - - -class CT_Jc(BaseOxmlElement): - """```` element, specifying paragraph justification.""" - - val: WD_ALIGN_PARAGRAPH = RequiredAttribute( # pyright: ignore[reportAssignmentType] - "w:val", WD_ALIGN_PARAGRAPH - ) - - -class CT_PPr(BaseOxmlElement): - """```` element, containing the properties for a paragraph.""" - - get_or_add_ind: Callable[[], CT_Ind] - get_or_add_pStyle: Callable[[], CT_String] - get_or_add_sectPr: Callable[[], CT_SectPr] - _insert_sectPr: Callable[[CT_SectPr], None] - _remove_pStyle: Callable[[], None] - _remove_sectPr: Callable[[], None] - - _tag_seq = ( - "w:pStyle", - "w:keepNext", - "w:keepLines", - "w:pageBreakBefore", - "w:framePr", - "w:widowControl", - "w:numPr", - "w:suppressLineNumbers", - "w:pBdr", - "w:shd", - "w:tabs", - "w:suppressAutoHyphens", - "w:kinsoku", - "w:wordWrap", - "w:overflowPunct", - "w:topLinePunct", - "w:autoSpaceDE", - "w:autoSpaceDN", - "w:bidi", - "w:adjustRightInd", - "w:snapToGrid", - "w:spacing", - "w:ind", - "w:contextualSpacing", - "w:mirrorIndents", - "w:suppressOverlap", - "w:jc", - "w:textDirection", - "w:textAlignment", - "w:textboxTightWrap", - "w:outlineLvl", - "w:divId", - "w:cnfStyle", - "w:rPr", - "w:sectPr", - "w:pPrChange", - ) - pStyle: CT_String | None = ZeroOrOne( # pyright: ignore[reportAssignmentType] - "w:pStyle", successors=_tag_seq[1:] - ) - keepNext = ZeroOrOne("w:keepNext", successors=_tag_seq[2:]) - keepLines = ZeroOrOne("w:keepLines", successors=_tag_seq[3:]) - pageBreakBefore = ZeroOrOne("w:pageBreakBefore", successors=_tag_seq[4:]) - widowControl = ZeroOrOne("w:widowControl", successors=_tag_seq[6:]) - numPr = ZeroOrOne("w:numPr", successors=_tag_seq[7:]) - tabs = ZeroOrOne("w:tabs", successors=_tag_seq[11:]) - spacing = ZeroOrOne("w:spacing", successors=_tag_seq[22:]) - ind: CT_Ind | None = ZeroOrOne( # pyright: ignore[reportAssignmentType] - "w:ind", successors=_tag_seq[23:] - ) - jc = ZeroOrOne("w:jc", successors=_tag_seq[27:]) - outlineLvl: CT_DecimalNumber = ZeroOrOne( # pyright: ignore[reportAssignmentType] - "w:outlineLvl", successors=_tag_seq[31:] - ) - sectPr = ZeroOrOne("w:sectPr", successors=_tag_seq[35:]) - del _tag_seq - - @property - def first_line_indent(self) -> Length | None: - """A |Length| value calculated from the values of `w:ind/@w:firstLine` and - `w:ind/@w:hanging`. - - Returns |None| if the `w:ind` child is not present. - """ - ind = self.ind - if ind is None: - return None - hanging = ind.hanging - if hanging is not None: - return Length(-hanging) - firstLine = ind.firstLine - if firstLine is None: - return None - return firstLine - - @first_line_indent.setter - def first_line_indent(self, value: Length | None): - if self.ind is None and value is None: - return - ind = self.get_or_add_ind() - ind.firstLine = ind.hanging = None - if value is None: - return - elif value < 0: - ind.hanging = -value - else: - ind.firstLine = value - - @property - def ind_left(self) -> Length | None: - """The value of `w:ind/@w:left` or |None| if not present.""" - ind = self.ind - if ind is None: - return None - return ind.left - - @ind_left.setter - def ind_left(self, value: Length | None): - if value is None and self.ind is None: - return - ind = self.get_or_add_ind() - ind.left = value - - @property - def ind_right(self) -> Length | None: - """The value of `w:ind/@w:right` or |None| if not present.""" - ind = self.ind - if ind is None: - return None - return ind.right - - @ind_right.setter - def ind_right(self, value: Length | None): - if value is None and self.ind is None: - return - ind = self.get_or_add_ind() - ind.right = value - - @property - def jc_val(self) -> WD_ALIGN_PARAGRAPH | None: - """Value of the `` child element or |None| if not present.""" - return self.jc.val if self.jc is not None else None - - @jc_val.setter - def jc_val(self, value): - if value is None: - self._remove_jc() - return - self.get_or_add_jc().val = value - - @property - def keepLines_val(self): - """The value of `keepLines/@val` or |None| if not present.""" - keepLines = self.keepLines - if keepLines is None: - return None - return keepLines.val - - @keepLines_val.setter - def keepLines_val(self, value): - if value is None: - self._remove_keepLines() - else: - self.get_or_add_keepLines().val = value - - @property - def keepNext_val(self): - """The value of `keepNext/@val` or |None| if not present.""" - keepNext = self.keepNext - if keepNext is None: - return None - return keepNext.val - - @keepNext_val.setter - def keepNext_val(self, value): - if value is None: - self._remove_keepNext() - else: - self.get_or_add_keepNext().val = value - - @property - def pageBreakBefore_val(self): - """The value of `pageBreakBefore/@val` or |None| if not present.""" - pageBreakBefore = self.pageBreakBefore - if pageBreakBefore is None: - return None - return pageBreakBefore.val - - @pageBreakBefore_val.setter - def pageBreakBefore_val(self, value): - if value is None: - self._remove_pageBreakBefore() - else: - self.get_or_add_pageBreakBefore().val = value - - @property - def spacing_after(self): - """The value of `w:spacing/@w:after` or |None| if not present.""" - spacing = self.spacing - if spacing is None: - return None - return spacing.after - - @spacing_after.setter - def spacing_after(self, value): - if value is None and self.spacing is None: - return - self.get_or_add_spacing().after = value - - @property - def spacing_before(self): - """The value of `w:spacing/@w:before` or |None| if not present.""" - spacing = self.spacing - if spacing is None: - return None - return spacing.before - - @spacing_before.setter - def spacing_before(self, value): - if value is None and self.spacing is None: - return - self.get_or_add_spacing().before = value - - @property - def spacing_line(self): - """The value of `w:spacing/@w:line` or |None| if not present.""" - spacing = self.spacing - if spacing is None: - return None - return spacing.line - - @spacing_line.setter - def spacing_line(self, value): - if value is None and self.spacing is None: - return - self.get_or_add_spacing().line = value - - @property - def spacing_lineRule(self): - """The value of `w:spacing/@w:lineRule` as a member of the :ref:`WdLineSpacing` - enumeration. - - Only the `MULTIPLE`, `EXACTLY`, and `AT_LEAST` members are used. It is the - responsibility of the client to calculate the use of `SINGLE`, `DOUBLE`, and - `MULTIPLE` based on the value of `w:spacing/@w:line` if that behavior is - desired. - """ - spacing = self.spacing - if spacing is None: - return None - lineRule = spacing.lineRule - if lineRule is None and spacing.line is not None: - return WD_LINE_SPACING.MULTIPLE - return lineRule - - @spacing_lineRule.setter - def spacing_lineRule(self, value): - if value is None and self.spacing is None: - return - self.get_or_add_spacing().lineRule = value - - @property - def style(self) -> str | None: - """String contained in `./w:pStyle/@val`, or None if child is not present.""" - pStyle = self.pStyle - if pStyle is None: - return None - return pStyle.val - - @style.setter - def style(self, style: str | None): - """Set `./w:pStyle/@val` `style`, adding a new element if necessary. - - If `style` is |None|, remove `./w:pStyle` when present. - """ - if style is None: - self._remove_pStyle() - return - pStyle = self.get_or_add_pStyle() - pStyle.val = style - - @property - def widowControl_val(self): - """The value of `widowControl/@val` or |None| if not present.""" - widowControl = self.widowControl - if widowControl is None: - return None - return widowControl.val - - @widowControl_val.setter - def widowControl_val(self, value): - if value is None: - self._remove_widowControl() - else: - self.get_or_add_widowControl().val = value - - -class CT_Spacing(BaseOxmlElement): - """```` element, specifying paragraph spacing attributes such as space - before and line spacing.""" - - after = OptionalAttribute("w:after", ST_TwipsMeasure) - before = OptionalAttribute("w:before", ST_TwipsMeasure) - line = OptionalAttribute("w:line", ST_SignedTwipsMeasure) - lineRule = OptionalAttribute("w:lineRule", WD_LINE_SPACING) - - -class CT_TabStop(BaseOxmlElement): - """`` element, representing an individual tab stop. - - Overloaded to use for a tab-character in a run, which also uses the w:tab tag but - only needs a __str__ method. - """ - - val: WD_TAB_ALIGNMENT = RequiredAttribute( # pyright: ignore[reportAssignmentType] - "w:val", WD_TAB_ALIGNMENT - ) - leader: WD_TAB_LEADER | None = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "w:leader", WD_TAB_LEADER, default=WD_TAB_LEADER.SPACES - ) - pos: Length = RequiredAttribute( # pyright: ignore[reportAssignmentType] - "w:pos", ST_SignedTwipsMeasure - ) - - def __str__(self) -> str: - """Text equivalent of a `w:tab` element appearing in a run. - - Allows text of run inner-content to be accessed consistently across all text - inner-content. - """ - return "\t" - - -class CT_TabStops(BaseOxmlElement): - """```` element, container for a sorted sequence of tab stops.""" - - tab = OneOrMore("w:tab", successors=()) - - def insert_tab_in_order(self, pos, align, leader): - """Insert a newly created `w:tab` child element in `pos` order.""" - new_tab = self._new_tab() - new_tab.pos, new_tab.val, new_tab.leader = pos, align, leader - for tab in self.tab_lst: - if new_tab.pos < tab.pos: - tab.addprevious(new_tab) - return new_tab - self.append(new_tab) - return new_tab diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/run.py b/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/run.py deleted file mode 100644 index 7496aa61..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/oxml/text/run.py +++ /dev/null @@ -1,307 +0,0 @@ -"""Custom element classes related to text runs (CT_R).""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Callable, Iterator, List, cast - -from docx.oxml.drawing import CT_Drawing -from docx.oxml.ns import qn -from docx.oxml.parser import OxmlElement -from docx.oxml.simpletypes import ST_BrClear, ST_BrType -from docx.oxml.text.font import CT_RPr -from docx.oxml.xmlchemy import BaseOxmlElement, OptionalAttribute, ZeroOrMore, ZeroOrOne -from docx.shared import TextAccumulator - -if TYPE_CHECKING: - from docx.oxml.shape import CT_Anchor, CT_Inline - from docx.oxml.text.pagebreak import CT_LastRenderedPageBreak - from docx.oxml.text.parfmt import CT_TabStop - -# ------------------------------------------------------------------------------------ -# Run-level elements - - -class CT_R(BaseOxmlElement): - """`` element, containing the properties and text for a run.""" - - add_br: Callable[[], CT_Br] - add_tab: Callable[[], CT_TabStop] - get_or_add_rPr: Callable[[], CT_RPr] - _add_drawing: Callable[[], CT_Drawing] - _add_t: Callable[..., CT_Text] - - rPr: CT_RPr | None = ZeroOrOne("w:rPr") # pyright: ignore[reportAssignmentType] - br = ZeroOrMore("w:br") - cr = ZeroOrMore("w:cr") - drawing = ZeroOrMore("w:drawing") - t = ZeroOrMore("w:t") - tab = ZeroOrMore("w:tab") - - def add_t(self, text: str) -> CT_Text: - """Return a newly added `` element containing `text`.""" - t = self._add_t(text=text) - if len(text.strip()) < len(text): - t.set(qn("xml:space"), "preserve") - return t - - def add_drawing(self, inline_or_anchor: CT_Inline | CT_Anchor) -> CT_Drawing: - """Return newly appended `CT_Drawing` (`w:drawing`) child element. - - The `w:drawing` element has `inline_or_anchor` as its child. - """ - drawing = self._add_drawing() - drawing.append(inline_or_anchor) - return drawing - - def clear_content(self) -> None: - """Remove all child elements except a `w:rPr` element if present.""" - # -- remove all run inner-content except a `w:rPr` when present. -- - for e in self.xpath("./*[not(self::w:rPr)]"): - self.remove(e) - - @property - def inner_content_items(self) -> List[str | CT_Drawing | CT_LastRenderedPageBreak]: - """Text of run, possibly punctuated by `w:lastRenderedPageBreak` elements.""" - from docx.oxml.text.pagebreak import CT_LastRenderedPageBreak - - accum = TextAccumulator() - - def iter_items() -> Iterator[str | CT_Drawing | CT_LastRenderedPageBreak]: - for e in self.xpath( - "w:br" - " | w:cr" - " | w:drawing" - " | w:lastRenderedPageBreak" - " | w:noBreakHyphen" - " | w:ptab" - " | w:t" - " | w:tab" - ): - if isinstance(e, (CT_Drawing, CT_LastRenderedPageBreak)): - yield from accum.pop() - yield e - else: - accum.push(str(e)) - - # -- don't forget the "tail" string -- - yield from accum.pop() - - return list(iter_items()) - - def insert_comment_range_end_and_reference_below(self, comment_id: int) -> None: - """Insert a `w:commentRangeEnd` and `w:commentReference` element after this run. - - The `w:commentRangeEnd` element is the immediate sibling of this `w:r` and is followed by - a `w:r` containing the `w:commentReference` element. - """ - self.addnext(self._new_comment_reference_run(comment_id)) - self.addnext(OxmlElement("w:commentRangeEnd", attrs={qn("w:id"): str(comment_id)})) - - def insert_comment_range_start_above(self, comment_id: int) -> None: - """Insert a `w:commentRangeStart` element with `comment_id` before this run.""" - self.addprevious(OxmlElement("w:commentRangeStart", attrs={qn("w:id"): str(comment_id)})) - - @property - def lastRenderedPageBreaks(self) -> List[CT_LastRenderedPageBreak]: - """All `w:lastRenderedPageBreaks` descendants of this run.""" - return self.xpath("./w:lastRenderedPageBreak") - - @property - def style(self) -> str | None: - """String contained in `w:val` attribute of `w:rStyle` grandchild. - - |None| if that element is not present. - """ - rPr = self.rPr - if rPr is None: - return None - return rPr.style - - @style.setter - def style(self, style: str | None): - """Set character style of this `w:r` element to `style`. - - If `style` is None, remove the style element. - """ - rPr = self.get_or_add_rPr() - rPr.style = style - - @property - def text(self) -> str: - """The textual content of this run. - - Inner-content child elements like `w:tab` are translated to their text - equivalent. - """ - return "".join( - str(e) for e in self.xpath("w:br | w:cr | w:noBreakHyphen | w:ptab | w:t | w:tab") - ) - - @text.setter - def text(self, text: str): # pyright: ignore[reportIncompatibleMethodOverride] - self.clear_content() - _RunContentAppender.append_to_run_from_text(self, text) - - def _insert_rPr(self, rPr: CT_RPr) -> CT_RPr: - self.insert(0, rPr) - return rPr - - def _new_comment_reference_run(self, comment_id: int) -> CT_R: - """Return a new `w:r` element with `w:commentReference` referencing `comment_id`. - - Should look like this: - - - - - - - """ - r = cast(CT_R, OxmlElement("w:r")) - rPr = r.get_or_add_rPr() - rPr.style = "CommentReference" - r.append(OxmlElement("w:commentReference", attrs={qn("w:id"): str(comment_id)})) - return r - - -# ------------------------------------------------------------------------------------ -# Run inner-content elements - - -class CT_Br(BaseOxmlElement): - """`` element, indicating a line, page, or column break in a run.""" - - type: str | None = OptionalAttribute( # pyright: ignore[reportAssignmentType] - "w:type", ST_BrType, default="textWrapping" - ) - clear: str | None = OptionalAttribute("w:clear", ST_BrClear) # pyright: ignore - - def __str__(self) -> str: - """Text equivalent of this element. Actual value depends on break type. - - A line break is translated as "\n". Column and page breaks produce the empty - string (""). - - This allows the text of run inner-content to be accessed in a consistent way - for all run inner-context text elements. - """ - return "\n" if self.type == "textWrapping" else "" - - -class CT_Cr(BaseOxmlElement): - """`` element, representing a carriage-return (0x0D) character within a run. - - In Word, this represents a "soft carriage-return" in the sense that it does not end - the paragraph the way pressing Enter (aka. Return) on the keyboard does. Here the - text equivalent is considered to be newline ("\n") since in plain-text that's the - closest Python equivalent. - - NOTE: this complex-type name does not exist in the schema, where `w:tab` maps to - `CT_Empty`. This name was added to give it distinguished behavior. CT_Empty is used - for many elements. - """ - - def __str__(self) -> str: - """Text equivalent of this element, a single newline ("\n").""" - return "\n" - - -class CT_NoBreakHyphen(BaseOxmlElement): - """`` element, a hyphen ineligible for a line-wrap position. - - This maps to a plain-text dash ("-"). - - NOTE: this complex-type name does not exist in the schema, where `w:noBreakHyphen` - maps to `CT_Empty`. This name was added to give it behavior distinguished from the - many other elements represented in the schema by CT_Empty. - """ - - def __str__(self) -> str: - """Text equivalent of this element, a single dash character ("-").""" - return "-" - - -class CT_PTab(BaseOxmlElement): - """`` element, representing an absolute-position tab character within a run. - - This character advances the rendering position to the specified position regardless - of any tab-stops, perhaps for layout of a table-of-contents (TOC) or similar. - """ - - def __str__(self) -> str: - """Text equivalent of this element, a single tab ("\t") character. - - This allows the text of run inner-content to be accessed in a consistent way - for all run inner-context text elements. - """ - return "\t" - - -# -- CT_Tab functionality is provided by CT_TabStop which also uses `w:tab` tag. That -# -- element class provides the __str__() method for this empty element, unconditionally -# -- returning "\t". - - -class CT_Text(BaseOxmlElement): - """`` element, containing a sequence of characters within a run.""" - - def __str__(self) -> str: - """Text contained in this element, the empty string if it has no content. - - This property allows this run inner-content element to be queried for its text - the same way as other run-content elements are. In particular, this never - returns None, as etree._Element does when there is no content. - """ - return self.text or "" - - -# ------------------------------------------------------------------------------------ -# Utility - - -class _RunContentAppender: - """Translates a Python string into run content elements appended in a `w:r` element. - - Contiguous sequences of regular characters are appended in a single `` element. - Each tab character ('\t') causes a `` element to be appended. Likewise a - newline or carriage return character ('\n', '\r') causes a `` element to be - appended. - """ - - def __init__(self, r: CT_R): - self._r = r - self._bfr: List[str] = [] - - @classmethod - def append_to_run_from_text(cls, r: CT_R, text: str): - """Append inner-content elements for `text` to `r` element.""" - appender = cls(r) - appender.add_text(text) - - def add_text(self, text: str): - """Append inner-content elements for `text` to the `w:r` element.""" - for char in text: - self.add_char(char) - self.flush() - - def add_char(self, char: str): - """Process next character of input through finite state maching (FSM). - - There are two possible states, buffer pending and not pending, but those are - hidden behind the `.flush()` method which must be called at the end of text to - ensure any pending `` element is written. - """ - if char == "\t": - self.flush() - self._r.add_tab() - elif char in "\r\n": - self.flush() - self._r.add_br() - else: - self._bfr.append(char) - - def flush(self): - text = "".join(self._bfr) - if text: - self._r.add_t(text) - self._bfr.clear() diff --git a/.venv-docs/lib/python3.12/site-packages/docx/oxml/xmlchemy.py b/.venv-docs/lib/python3.12/site-packages/docx/oxml/xmlchemy.py deleted file mode 100644 index e2c54b39..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/oxml/xmlchemy.py +++ /dev/null @@ -1,696 +0,0 @@ -# pyright: reportImportCycles=false - -"""Enabling declarative definition of lxml custom element classes.""" - -from __future__ import annotations - -import re -from typing import TYPE_CHECKING, Any, Callable, Sequence, Type, TypeVar - -from lxml import etree -from lxml.etree import ElementBase, _Element # pyright: ignore[reportPrivateUsage] - -from docx.oxml.exceptions import InvalidXmlError -from docx.oxml.ns import NamespacePrefixedTag, nsmap, qn -from docx.shared import lazyproperty - -if TYPE_CHECKING: - from docx.enum.base import BaseXmlEnum - from docx.oxml.simpletypes import BaseSimpleType - - -def serialize_for_reading(element: ElementBase): - """Serialize `element` to human-readable XML suitable for tests. - - No XML declaration. - """ - xml = etree.tostring(element, encoding="unicode", pretty_print=True) - return XmlString(xml) - - -class XmlString(str): - """Provides string comparison override suitable for serialized XML that is useful - for tests.""" - - # ' text' - # | | || | - # +----------+------------------------------------------++-----------+ - # front attrs | text - # close - - _xml_elm_line_patt = re.compile(r"( *)([^<]*)?$") - - def __eq__(self, other: object) -> bool: - if not isinstance(other, str): - return False - lines = self.splitlines() - lines_other = other.splitlines() - if len(lines) != len(lines_other): - return False - for line, line_other in zip(lines, lines_other): - if not self._eq_elm_strs(line, line_other): - return False - return True - - def __ne__(self, other: object) -> bool: - return not self.__eq__(other) - - def _attr_seq(self, attrs: str) -> list[str]: - """Return a sequence of attribute strings parsed from `attrs`. - - Each attribute string is stripped of whitespace on both ends. - """ - attrs = attrs.strip() - attr_lst = attrs.split() - return sorted(attr_lst) - - def _eq_elm_strs(self, line: str, line_2: str): - """Return True if the element in `line_2` is XML equivalent to the element in - `line`.""" - front, attrs, close, text = self._parse_line(line) - front_2, attrs_2, close_2, text_2 = self._parse_line(line_2) - if front != front_2: - return False - if self._attr_seq(attrs) != self._attr_seq(attrs_2): - return False - if close != close_2: - return False - return text == text_2 - - @classmethod - def _parse_line(cls, line: str) -> tuple[str, str, str, str]: - """(front, attrs, close, text) 4-tuple result of parsing XML element `line`.""" - match = cls._xml_elm_line_patt.match(line) - if match is None: - return "", "", "", "" - front, attrs, close, text = [match.group(n) for n in range(1, 5)] - return front, attrs, close, text - - -_T = TypeVar("_T") - - -class MetaOxmlElement(type): - """Metaclass for BaseOxmlElement.""" - - def __init__(cls, clsname: str, bases: tuple[type, ...], namespace: dict[str, Any]): - dispatchable = ( - OneAndOnlyOne, - OneOrMore, - OptionalAttribute, - RequiredAttribute, - ZeroOrMore, - ZeroOrOne, - ZeroOrOneChoice, - ) - for key, value in namespace.items(): - if isinstance(value, dispatchable): - value.populate_class_members(cls, key) - - -class BaseAttribute: - """Base class for OptionalAttribute and RequiredAttribute. - - Provides common methods. - """ - - def __init__(self, attr_name: str, simple_type: Type[BaseXmlEnum] | Type[BaseSimpleType]): - super(BaseAttribute, self).__init__() - self._attr_name = attr_name - self._simple_type = simple_type - - def populate_class_members(self, element_cls: MetaOxmlElement, prop_name: str) -> None: - """Add the appropriate methods to `element_cls`.""" - self._element_cls = element_cls - self._prop_name = prop_name - - self._add_attr_property() - - def _add_attr_property(self): - """Add a read/write `.{prop_name}` property to the element class. - - The property returns the interpreted value of this attribute on access and - changes the attribute value to its ST_* counterpart on assignment. - """ - property_ = property(self._getter, self._setter, None) - # -- assign unconditionally to overwrite element name definition -- - setattr(self._element_cls, self._prop_name, property_) - - @property - def _clark_name(self): - if ":" in self._attr_name: - return qn(self._attr_name) - return self._attr_name - - @property - def _getter(self) -> Callable[[BaseOxmlElement], Any | None]: ... - - @property - def _setter( - self, - ) -> Callable[[BaseOxmlElement, Any | None], None]: ... - - -class OptionalAttribute(BaseAttribute): - """Defines an optional attribute on a custom element class. - - An optional attribute returns a default value when not present for reading. When - assigned |None|, the attribute is removed, but still returns the default value when - one is specified. - """ - - def __init__( - self, - attr_name: str, - simple_type: Type[BaseXmlEnum] | Type[BaseSimpleType], - default: BaseXmlEnum | BaseSimpleType | str | bool | None = None, - ): - super(OptionalAttribute, self).__init__(attr_name, simple_type) - self._default = default - - @property - def _docstring(self): - """String to use as `__doc__` attribute of attribute property.""" - return ( - f"{self._simple_type.__name__} type-converted value of" - f" ``{self._attr_name}`` attribute, or |None| (or specified default" - f" value) if not present. Assigning the default value causes the" - f" attribute to be removed from the element." - ) - - @property - def _getter( - self, - ) -> Callable[[BaseOxmlElement], Any | None]: - """Function suitable for `__get__()` method on attribute property descriptor.""" - - def get_attr_value( - obj: BaseOxmlElement, - ) -> Any | None: - attr_str_value = obj.get(self._clark_name) - if attr_str_value is None: - return self._default - return self._simple_type.from_xml(attr_str_value) - - get_attr_value.__doc__ = self._docstring - return get_attr_value - - @property - def _setter(self) -> Callable[[BaseOxmlElement, Any], None]: - """Function suitable for `__set__()` method on attribute property descriptor.""" - - def set_attr_value(obj: BaseOxmlElement, value: Any | None): - if value is None or value == self._default: - if self._clark_name in obj.attrib: - del obj.attrib[self._clark_name] - return - str_value = self._simple_type.to_xml(value) - if str_value is None: - if self._clark_name in obj.attrib: - del obj.attrib[self._clark_name] - return - obj.set(self._clark_name, str_value) - - return set_attr_value - - -class RequiredAttribute(BaseAttribute): - """Defines a required attribute on a custom element class. - - A required attribute is assumed to be present for reading, so does not have a - default value; its actual value is always used. If missing on read, an - |InvalidXmlError| is raised. It also does not remove the attribute if |None| is - assigned. Assigning |None| raises |TypeError| or |ValueError|, depending on the - simple type of the attribute. - """ - - @property - def _docstring(self): - """Return the string to use as the ``__doc__`` attribute of the property for - this attribute.""" - return "%s type-converted value of ``%s`` attribute." % ( - self._simple_type.__name__, - self._attr_name, - ) - - @property - def _getter(self) -> Callable[[BaseOxmlElement], Any]: - """function object suitable for "get" side of attr property descriptor.""" - - def get_attr_value(obj: BaseOxmlElement) -> Any | None: - attr_str_value = obj.get(self._clark_name) - if attr_str_value is None: - raise InvalidXmlError( - "required '%s' attribute not present on element %s" % (self._attr_name, obj.tag) - ) - return self._simple_type.from_xml(attr_str_value) - - get_attr_value.__doc__ = self._docstring - return get_attr_value - - @property - def _setter(self) -> Callable[[BaseOxmlElement, Any], None]: - """function object suitable for "set" side of attribute property descriptor.""" - - def set_attr_value(obj: BaseOxmlElement, value: Any): - str_value = self._simple_type.to_xml(value) - if str_value is None: - raise ValueError(f"cannot assign {value} to this required attribute") - obj.set(self._clark_name, str_value) - - return set_attr_value - - -class _BaseChildElement: - """Base class for the child-element classes. - - The child-element sub-classes correspond to varying cardinalities, such as ZeroOrOne - and ZeroOrMore. - """ - - def __init__(self, nsptagname: str, successors: tuple[str, ...] = ()): - super(_BaseChildElement, self).__init__() - self._nsptagname = nsptagname - self._successors = successors - - def populate_class_members(self, element_cls: MetaOxmlElement, prop_name: str) -> None: - """Baseline behavior for adding the appropriate methods to `element_cls`.""" - self._element_cls = element_cls - self._prop_name = prop_name - - def _add_adder(self): - """Add an ``_add_x()`` method to the element class for this child element.""" - - def _add_child(obj: BaseOxmlElement, **attrs: Any): - new_method = getattr(obj, self._new_method_name) - child = new_method() - for key, value in attrs.items(): - setattr(child, key, value) - insert_method = getattr(obj, self._insert_method_name) - insert_method(child) - return child - - _add_child.__doc__ = ( - "Add a new ``<%s>`` child element unconditionally, inserted in t" - "he correct sequence." % self._nsptagname - ) - self._add_to_class(self._add_method_name, _add_child) - - def _add_creator(self): - """Add a ``_new_{prop_name}()`` method to the element class that creates a new, - empty element of the correct type, having no attributes.""" - creator = self._creator - creator.__doc__ = ( - 'Return a "loose", newly created ``<%s>`` element having no attri' - "butes, text, or children." % self._nsptagname - ) - self._add_to_class(self._new_method_name, creator) - - def _add_getter(self): - """Add a read-only ``{prop_name}`` property to the element class for this child - element.""" - property_ = property(self._getter, None, None) - # -- assign unconditionally to overwrite element name definition -- - setattr(self._element_cls, self._prop_name, property_) - - def _add_inserter(self): - """Add an ``_insert_x()`` method to the element class for this child element.""" - - def _insert_child(obj: BaseOxmlElement, child: BaseOxmlElement): - obj.insert_element_before(child, *self._successors) - return child - - _insert_child.__doc__ = ( - "Return the passed ``<%s>`` element after inserting it as a chil" - "d in the correct sequence." % self._nsptagname - ) - self._add_to_class(self._insert_method_name, _insert_child) - - def _add_list_getter(self): - """Add a read-only ``{prop_name}_lst`` property to the element class to retrieve - a list of child elements matching this type.""" - prop_name = "%s_lst" % self._prop_name - property_ = property(self._list_getter, None, None) - setattr(self._element_cls, prop_name, property_) - - @lazyproperty - def _add_method_name(self): - return "_add_%s" % self._prop_name - - def _add_public_adder(self): - """Add a public ``add_x()`` method to the parent element class.""" - - def add_child(obj: BaseOxmlElement): - private_add_method = getattr(obj, self._add_method_name) - child = private_add_method() - return child - - add_child.__doc__ = ( - "Add a new ``<%s>`` child element unconditionally, inserted in t" - "he correct sequence." % self._nsptagname - ) - self._add_to_class(self._public_add_method_name, add_child) - - def _add_to_class(self, name: str, method: Callable[..., Any]): - """Add `method` to the target class as `name`, unless `name` is already defined - on the class.""" - if hasattr(self._element_cls, name): - return - setattr(self._element_cls, name, method) - - @property - def _creator(self) -> Callable[[BaseOxmlElement], BaseOxmlElement]: - """Callable that creates an empty element of the right type, with no attrs.""" - from docx.oxml.parser import OxmlElement - - def new_child_element(obj: BaseOxmlElement): - return OxmlElement(self._nsptagname) - - return new_child_element - - @property - def _getter(self): - """Return a function object suitable for the "get" side of the property - descriptor. - - This default getter returns the child element with matching tag name or |None| - if not present. - """ - - def get_child_element(obj: BaseOxmlElement): - return obj.find(qn(self._nsptagname)) - - get_child_element.__doc__ = ( - "``<%s>`` child element or |None| if not present." % self._nsptagname - ) - return get_child_element - - @lazyproperty - def _insert_method_name(self): - return "_insert_%s" % self._prop_name - - @property - def _list_getter(self): - """Return a function object suitable for the "get" side of a list property - descriptor.""" - - def get_child_element_list(obj: BaseOxmlElement): - return obj.findall(qn(self._nsptagname)) - - get_child_element_list.__doc__ = ( - "A list containing each of the ``<%s>`` child elements, in the o" - "rder they appear." % self._nsptagname - ) - return get_child_element_list - - @lazyproperty - def _public_add_method_name(self): - """add_childElement() is public API for a repeating element, allowing new - elements to be added to the sequence. - - May be overridden to provide a friendlier API to clients having domain - appropriate parameter names for required attributes. - """ - return "add_%s" % self._prop_name - - @lazyproperty - def _remove_method_name(self): - return "_remove_%s" % self._prop_name - - @lazyproperty - def _new_method_name(self): - return "_new_%s" % self._prop_name - - -class Choice(_BaseChildElement): - """Defines a child element belonging to a group, only one of which may appear as a child.""" - - @property - def nsptagname(self): - return self._nsptagname - - def populate_class_members( # pyright: ignore[reportIncompatibleMethodOverride] - self, - element_cls: MetaOxmlElement, - group_prop_name: str, - successors: tuple[str, ...], - ) -> None: - """Add the appropriate methods to `element_cls`.""" - self._element_cls = element_cls - self._group_prop_name = group_prop_name - self._successors = successors - - self._add_getter() - self._add_creator() - self._add_inserter() - self._add_adder() - self._add_get_or_change_to_method() - - def _add_get_or_change_to_method(self): - """Add a ``get_or_change_to_x()`` method to the element class for this child - element.""" - - def get_or_change_to_child(obj: BaseOxmlElement): - child = getattr(obj, self._prop_name) - if child is not None: - return child - remove_group_method = getattr(obj, self._remove_group_method_name) - remove_group_method() - add_method = getattr(obj, self._add_method_name) - child = add_method() - return child - - get_or_change_to_child.__doc__ = ( - "Return the ``<%s>`` child, replacing any other group element if found." - ) % self._nsptagname - self._add_to_class(self._get_or_change_to_method_name, get_or_change_to_child) - - @property - def _prop_name(self): - """Property name computed from tag name, e.g. a:schemeClr -> schemeClr.""" - start = self._nsptagname.index(":") + 1 if ":" in self._nsptagname else 0 - return self._nsptagname[start:] - - @lazyproperty - def _get_or_change_to_method_name(self): - return "get_or_change_to_%s" % self._prop_name - - @lazyproperty - def _remove_group_method_name(self): - return "_remove_%s" % self._group_prop_name - - -class OneAndOnlyOne(_BaseChildElement): - """Defines a required child element for MetaOxmlElement.""" - - def __init__(self, nsptagname: str): - super(OneAndOnlyOne, self).__init__(nsptagname, ()) - - def populate_class_members(self, element_cls: MetaOxmlElement, prop_name: str) -> None: - """Add the appropriate methods to `element_cls`.""" - super(OneAndOnlyOne, self).populate_class_members(element_cls, prop_name) - self._add_getter() - - @property - def _getter(self): - """Return a function object suitable for the "get" side of the property - descriptor.""" - - def get_child_element(obj: BaseOxmlElement): - child = obj.find(qn(self._nsptagname)) - if child is None: - raise InvalidXmlError( - "required ``<%s>`` child element not present" % self._nsptagname - ) - return child - - get_child_element.__doc__ = "Required ``<%s>`` child element." % self._nsptagname - return get_child_element - - -class OneOrMore(_BaseChildElement): - """Defines a repeating child element for MetaOxmlElement that must appear at least - once.""" - - def populate_class_members(self, element_cls: MetaOxmlElement, prop_name: str) -> None: - """Add the appropriate methods to `element_cls`.""" - super(OneOrMore, self).populate_class_members(element_cls, prop_name) - self._add_list_getter() - self._add_creator() - self._add_inserter() - self._add_adder() - self._add_public_adder() - delattr(element_cls, prop_name) - - -class ZeroOrMore(_BaseChildElement): - """Defines an optional repeating child element for MetaOxmlElement.""" - - def populate_class_members(self, element_cls: MetaOxmlElement, prop_name: str) -> None: - """Add the appropriate methods to `element_cls`.""" - super(ZeroOrMore, self).populate_class_members(element_cls, prop_name) - self._add_list_getter() - self._add_creator() - self._add_inserter() - self._add_adder() - self._add_public_adder() - delattr(element_cls, prop_name) - - -class ZeroOrOne(_BaseChildElement): - """Defines an optional child element for MetaOxmlElement.""" - - def populate_class_members(self, element_cls: MetaOxmlElement, prop_name: str) -> None: - """Add the appropriate methods to `element_cls`.""" - super(ZeroOrOne, self).populate_class_members(element_cls, prop_name) - self._add_getter() - self._add_creator() - self._add_inserter() - self._add_adder() - self._add_get_or_adder() - self._add_remover() - - def _add_get_or_adder(self): - """Add a ``get_or_add_x()`` method to the element class for this child - element.""" - - def get_or_add_child(obj: BaseOxmlElement): - child = getattr(obj, self._prop_name) - if child is None: - add_method = getattr(obj, self._add_method_name) - child = add_method() - return child - - get_or_add_child.__doc__ = ( - "Return the ``<%s>`` child element, newly added if not present." - ) % self._nsptagname - self._add_to_class(self._get_or_add_method_name, get_or_add_child) - - def _add_remover(self): - """Add a ``_remove_x()`` method to the element class for this child element.""" - - def _remove_child(obj: BaseOxmlElement): - obj.remove_all(self._nsptagname) - - _remove_child.__doc__ = ("Remove all ``<%s>`` child elements.") % self._nsptagname - self._add_to_class(self._remove_method_name, _remove_child) - - @lazyproperty - def _get_or_add_method_name(self): - return "get_or_add_%s" % self._prop_name - - -class ZeroOrOneChoice(_BaseChildElement): - """Correspondes to an ``EG_*`` element group where at most one of its members may - appear as a child.""" - - def __init__(self, choices: Sequence[Choice], successors: tuple[str, ...] = ()): - self._choices = choices - self._successors = successors - - def populate_class_members(self, element_cls: MetaOxmlElement, prop_name: str) -> None: - """Add the appropriate methods to `element_cls`.""" - super(ZeroOrOneChoice, self).populate_class_members(element_cls, prop_name) - self._add_choice_getter() - for choice in self._choices: - choice.populate_class_members(element_cls, self._prop_name, self._successors) - self._add_group_remover() - - def _add_choice_getter(self): - """Add a read-only ``{prop_name}`` property to the element class that returns - the present member of this group, or |None| if none are present.""" - property_ = property(self._choice_getter, None, None) - # assign unconditionally to overwrite element name definition - setattr(self._element_cls, self._prop_name, property_) - - def _add_group_remover(self): - """Add a ``_remove_eg_x()`` method to the element class for this choice - group.""" - - def _remove_choice_group(obj: BaseOxmlElement): - for tagname in self._member_nsptagnames: - obj.remove_all(tagname) - - _remove_choice_group.__doc__ = "Remove the current choice group child element if present." - self._add_to_class(self._remove_choice_group_method_name, _remove_choice_group) - - @property - def _choice_getter(self): - """Return a function object suitable for the "get" side of the property - descriptor.""" - - def get_group_member_element(obj: BaseOxmlElement): - return obj.first_child_found_in(*self._member_nsptagnames) - - get_group_member_element.__doc__ = ( - "Return the child element belonging to this element group, or " - "|None| if no member child is present." - ) - return get_group_member_element - - @lazyproperty - def _member_nsptagnames(self): - """Sequence of namespace-prefixed tagnames, one for each of the member elements - of this choice group.""" - return [choice.nsptagname for choice in self._choices] - - @lazyproperty - def _remove_choice_group_method_name(self): - return "_remove_%s" % self._prop_name - - -# -- lxml typing isn't quite right here, just ignore this error on _Element -- -class BaseOxmlElement(etree.ElementBase, metaclass=MetaOxmlElement): - """Effective base class for all custom element classes. - - Adds standardized behavior to all classes in one place. - """ - - def __repr__(self): - return "<%s '<%s>' at 0x%0x>" % ( - self.__class__.__name__, - self._nsptag, - id(self), - ) - - def first_child_found_in(self, *tagnames: str) -> _Element | None: - """First child with tag in `tagnames`, or None if not found.""" - for tagname in tagnames: - child = self.find(qn(tagname)) - if child is not None: - return child - return None - - def insert_element_before(self, elm: ElementBase, *tagnames: str): - successor = self.first_child_found_in(*tagnames) - if successor is not None: - successor.addprevious(elm) - else: - self.append(elm) - return elm - - def remove_all(self, *tagnames: str) -> None: - """Remove child elements with tagname (e.g. "a:p") in `tagnames`.""" - for tagname in tagnames: - matching = self.findall(qn(tagname)) - for child in matching: - self.remove(child) - - @property - def xml(self) -> str: - """XML string for this element, suitable for testing purposes. - - Pretty printed for readability and without an XML declaration at the top. - """ - return serialize_for_reading(self) - - def xpath(self, xpath_str: str) -> Any: # pyright: ignore[reportIncompatibleMethodOverride] - """Override of `lxml` _Element.xpath() method. - - Provides standard Open XML namespace mapping (`nsmap`) in centralized location. - """ - return super().xpath(xpath_str, namespaces=nsmap) - - @property - def _nsptag(self) -> str: - return NamespacePrefixedTag.from_clark_name(self.tag) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/package.py b/.venv-docs/lib/python3.12/site-packages/docx/package.py deleted file mode 100644 index 7ea47e6e..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/package.py +++ /dev/null @@ -1,110 +0,0 @@ -"""WordprocessingML Package class and related objects.""" - -from __future__ import annotations - -from typing import IO, cast - -from docx.image.image import Image -from docx.opc.constants import RELATIONSHIP_TYPE as RT -from docx.opc.package import OpcPackage -from docx.opc.packuri import PackURI -from docx.parts.image import ImagePart -from docx.shared import lazyproperty - - -class Package(OpcPackage): - """Customizations specific to a WordprocessingML package.""" - - def after_unmarshal(self): - """Called by loading code after all parts and relationships have been loaded. - - This method affords the opportunity for any required post-processing. - """ - self._gather_image_parts() - - def get_or_add_image_part(self, image_descriptor: str | IO[bytes]) -> ImagePart: - """Return |ImagePart| containing image specified by `image_descriptor`. - - The image-part is newly created if a matching one is not already present in the - collection. - """ - return self.image_parts.get_or_add_image_part(image_descriptor) - - @lazyproperty - def image_parts(self) -> ImageParts: - """|ImageParts| collection object for this package.""" - return ImageParts() - - def _gather_image_parts(self): - """Load the image part collection with all the image parts in package.""" - for rel in self.iter_rels(): - if rel.is_external: - continue - if rel.reltype != RT.IMAGE: - continue - if rel.target_part in self.image_parts: - continue - self.image_parts.append(cast("ImagePart", rel.target_part)) - - -class ImageParts: - """Collection of |ImagePart| objects corresponding to images in the package.""" - - def __init__(self): - self._image_parts: list[ImagePart] = [] - - def __contains__(self, item: object): - return self._image_parts.__contains__(item) - - def __iter__(self): - return self._image_parts.__iter__() - - def __len__(self): - return self._image_parts.__len__() - - def append(self, item: ImagePart): - self._image_parts.append(item) - - def get_or_add_image_part(self, image_descriptor: str | IO[bytes]) -> ImagePart: - """Return |ImagePart| object containing image identified by `image_descriptor`. - - The image-part is newly created if a matching one is not present in the - collection. - """ - image = Image.from_file(image_descriptor) - matching_image_part = self._get_by_sha1(image.sha1) - if matching_image_part is not None: - return matching_image_part - return self._add_image_part(image) - - def _add_image_part(self, image: Image): - """Return |ImagePart| instance newly created from `image` and appended to the collection.""" - partname = self._next_image_partname(image.ext) - image_part = ImagePart.from_image(image, partname) - self.append(image_part) - return image_part - - def _get_by_sha1(self, sha1: str) -> ImagePart | None: - """Return the image part in this collection having a SHA1 hash matching `sha1`, - or |None| if not found.""" - for image_part in self._image_parts: - if image_part.sha1 == sha1: - return image_part - return None - - def _next_image_partname(self, ext: str) -> PackURI: - """The next available image partname, starting from ``/word/media/image1.{ext}`` - where unused numbers are reused. - - The partname is unique by number, without regard to the extension. `ext` does - not include the leading period. - """ - - def image_partname(n: int) -> PackURI: - return PackURI("/word/media/image%d.%s" % (n, ext)) - - used_numbers = [image_part.partname.idx for image_part in self] - for n in range(1, len(self) + 1): - if n not in used_numbers: - return image_partname(n) - return image_partname(len(self) + 1) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/parts/__init__.py b/.venv-docs/lib/python3.12/site-packages/docx/parts/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/.venv-docs/lib/python3.12/site-packages/docx/parts/__pycache__/__init__.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/parts/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index f009cd1a..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/parts/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/parts/__pycache__/comments.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/parts/__pycache__/comments.cpython-312.pyc deleted file mode 100644 index 85bfc070..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/parts/__pycache__/comments.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/parts/__pycache__/document.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/parts/__pycache__/document.cpython-312.pyc deleted file mode 100644 index f3dc31d2..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/parts/__pycache__/document.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/parts/__pycache__/hdrftr.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/parts/__pycache__/hdrftr.cpython-312.pyc deleted file mode 100644 index c37ad5e8..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/parts/__pycache__/hdrftr.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/parts/__pycache__/image.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/parts/__pycache__/image.cpython-312.pyc deleted file mode 100644 index c06c22d2..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/parts/__pycache__/image.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/parts/__pycache__/numbering.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/parts/__pycache__/numbering.cpython-312.pyc deleted file mode 100644 index 8f37664d..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/parts/__pycache__/numbering.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/parts/__pycache__/settings.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/parts/__pycache__/settings.cpython-312.pyc deleted file mode 100644 index cc64df98..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/parts/__pycache__/settings.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/parts/__pycache__/story.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/parts/__pycache__/story.cpython-312.pyc deleted file mode 100644 index 70d2262d..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/parts/__pycache__/story.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/parts/__pycache__/styles.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/parts/__pycache__/styles.cpython-312.pyc deleted file mode 100644 index 6ac7f401..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/parts/__pycache__/styles.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/parts/comments.py b/.venv-docs/lib/python3.12/site-packages/docx/parts/comments.py deleted file mode 100644 index 0e4cc743..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/parts/comments.py +++ /dev/null @@ -1,51 +0,0 @@ -"""Contains comments added to the document.""" - -from __future__ import annotations - -import os -from typing import TYPE_CHECKING, cast - -from typing_extensions import Self - -from docx.comments import Comments -from docx.opc.constants import CONTENT_TYPE as CT -from docx.opc.packuri import PackURI -from docx.oxml.comments import CT_Comments -from docx.oxml.parser import parse_xml -from docx.package import Package -from docx.parts.story import StoryPart - -if TYPE_CHECKING: - from docx.oxml.comments import CT_Comments - from docx.package import Package - - -class CommentsPart(StoryPart): - """Container part for comments added to the document.""" - - def __init__( - self, partname: PackURI, content_type: str, element: CT_Comments, package: Package - ): - super().__init__(partname, content_type, element, package) - self._comments = element - - @property - def comments(self) -> Comments: - """A |Comments| proxy object for the `w:comments` root element of this part.""" - return Comments(self._comments, self) - - @classmethod - def default(cls, package: Package) -> Self: - """A newly created comments part, containing a default empty `w:comments` element.""" - partname = PackURI("/word/comments.xml") - content_type = CT.WML_COMMENTS - element = cast("CT_Comments", parse_xml(cls._default_comments_xml())) - return cls(partname, content_type, element, package) - - @classmethod - def _default_comments_xml(cls) -> bytes: - """A byte-string containing XML for a default comments part.""" - path = os.path.join(os.path.split(__file__)[0], "..", "templates", "default-comments.xml") - with open(path, "rb") as f: - xml_bytes = f.read() - return xml_bytes diff --git a/.venv-docs/lib/python3.12/site-packages/docx/parts/document.py b/.venv-docs/lib/python3.12/site-packages/docx/parts/document.py deleted file mode 100644 index 4960264b..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/parts/document.py +++ /dev/null @@ -1,169 +0,0 @@ -"""|DocumentPart| and closely related objects.""" - -from __future__ import annotations - -from typing import IO, TYPE_CHECKING, cast - -from docx.document import Document -from docx.opc.constants import RELATIONSHIP_TYPE as RT -from docx.parts.comments import CommentsPart -from docx.parts.hdrftr import FooterPart, HeaderPart -from docx.parts.numbering import NumberingPart -from docx.parts.settings import SettingsPart -from docx.parts.story import StoryPart -from docx.parts.styles import StylesPart -from docx.shape import InlineShapes -from docx.shared import lazyproperty - -if TYPE_CHECKING: - from docx.comments import Comments - from docx.enum.style import WD_STYLE_TYPE - from docx.opc.coreprops import CoreProperties - from docx.settings import Settings - from docx.styles.style import BaseStyle - - -class DocumentPart(StoryPart): - """Main document part of a WordprocessingML (WML) package, aka a .docx file. - - Acts as broker to other parts such as image, core properties, and style parts. It - also acts as a convenient delegate when a mid-document object needs a service - involving a remote ancestor. The `Parented.part` property inherited by many content - objects provides access to this part object for that purpose. - """ - - def add_footer_part(self): - """Return (footer_part, rId) pair for newly-created footer part.""" - footer_part = FooterPart.new(self.package) - rId = self.relate_to(footer_part, RT.FOOTER) - return footer_part, rId - - def add_header_part(self): - """Return (header_part, rId) pair for newly-created header part.""" - header_part = HeaderPart.new(self.package) - rId = self.relate_to(header_part, RT.HEADER) - return header_part, rId - - @property - def comments(self) -> Comments: - """|Comments| object providing access to the comments added to this document.""" - return self._comments_part.comments - - @property - def core_properties(self) -> CoreProperties: - """A |CoreProperties| object providing read/write access to the core properties - of this document.""" - return self.package.core_properties - - @property - def document(self): - """A |Document| object providing access to the content of this document.""" - return Document(self._element, self) - - def drop_header_part(self, rId: str) -> None: - """Remove related header part identified by `rId`.""" - self.drop_rel(rId) - - def footer_part(self, rId: str): - """Return |FooterPart| related by `rId`.""" - return self.related_parts[rId] - - def get_style(self, style_id: str | None, style_type: WD_STYLE_TYPE) -> BaseStyle: - """Return the style in this document matching `style_id`. - - Returns the default style for `style_type` if `style_id` is |None| or does not - match a defined style of `style_type`. - """ - return self.styles.get_by_id(style_id, style_type) - - def get_style_id(self, style_or_name, style_type): - """Return the style_id (|str|) of the style of `style_type` matching - `style_or_name`. - - Returns |None| if the style resolves to the default style for `style_type` or if - `style_or_name` is itself |None|. Raises if `style_or_name` is a style of the - wrong type or names a style not present in the document. - """ - return self.styles.get_style_id(style_or_name, style_type) - - def header_part(self, rId: str): - """Return |HeaderPart| related by `rId`.""" - return self.related_parts[rId] - - @lazyproperty - def inline_shapes(self): - """The |InlineShapes| instance containing the inline shapes in the document.""" - return InlineShapes(self._element.body, self) - - @lazyproperty - def numbering_part(self) -> NumberingPart: - """A |NumberingPart| object providing access to the numbering definitions for this document. - - Creates an empty numbering part if one is not present. - """ - try: - return cast(NumberingPart, self.part_related_by(RT.NUMBERING)) - except KeyError: - numbering_part = NumberingPart.new() - self.relate_to(numbering_part, RT.NUMBERING) - return numbering_part - - def save(self, path_or_stream: str | IO[bytes]): - """Save this document to `path_or_stream`, which can be either a path to a - filesystem location (a string) or a file-like object.""" - self.package.save(path_or_stream) - - @property - def settings(self) -> Settings: - """A |Settings| object providing access to the settings in the settings part of - this document.""" - return self._settings_part.settings - - @property - def styles(self): - """A |Styles| object providing access to the styles in the styles part of this - document.""" - return self._styles_part.styles - - @property - def _comments_part(self) -> CommentsPart: - """A |CommentsPart| object providing access to the comments added to this document. - - Creates a default comments part if one is not present. - """ - try: - return cast(CommentsPart, self.part_related_by(RT.COMMENTS)) - except KeyError: - assert self.package is not None - comments_part = CommentsPart.default(self.package) - self.relate_to(comments_part, RT.COMMENTS) - return comments_part - - @property - def _settings_part(self) -> SettingsPart: - """A |SettingsPart| object providing access to the document-level settings for - this document. - - Creates a default settings part if one is not present. - """ - try: - return cast(SettingsPart, self.part_related_by(RT.SETTINGS)) - except KeyError: - settings_part = SettingsPart.default(self.package) - self.relate_to(settings_part, RT.SETTINGS) - return settings_part - - @property - def _styles_part(self) -> StylesPart: - """Instance of |StylesPart| for this document. - - Creates an empty styles part if one is not present. - """ - try: - return cast(StylesPart, self.part_related_by(RT.STYLES)) - except KeyError: - package = self.package - assert package is not None - styles_part = StylesPart.default(package) - self.relate_to(styles_part, RT.STYLES) - return styles_part diff --git a/.venv-docs/lib/python3.12/site-packages/docx/parts/hdrftr.py b/.venv-docs/lib/python3.12/site-packages/docx/parts/hdrftr.py deleted file mode 100644 index 35113801..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/parts/hdrftr.py +++ /dev/null @@ -1,53 +0,0 @@ -"""Header and footer part objects.""" - -from __future__ import annotations - -import os -from typing import TYPE_CHECKING - -from docx.opc.constants import CONTENT_TYPE as CT -from docx.oxml.parser import parse_xml -from docx.parts.story import StoryPart - -if TYPE_CHECKING: - from docx.package import Package - - -class FooterPart(StoryPart): - """Definition of a section footer.""" - - @classmethod - def new(cls, package: Package): - """Return newly created footer part.""" - partname = package.next_partname("/word/footer%d.xml") - content_type = CT.WML_FOOTER - element = parse_xml(cls._default_footer_xml()) - return cls(partname, content_type, element, package) - - @classmethod - def _default_footer_xml(cls): - """Return bytes containing XML for a default footer part.""" - path = os.path.join(os.path.split(__file__)[0], "..", "templates", "default-footer.xml") - with open(path, "rb") as f: - xml_bytes = f.read() - return xml_bytes - - -class HeaderPart(StoryPart): - """Definition of a section header.""" - - @classmethod - def new(cls, package: Package): - """Return newly created header part.""" - partname = package.next_partname("/word/header%d.xml") - content_type = CT.WML_HEADER - element = parse_xml(cls._default_header_xml()) - return cls(partname, content_type, element, package) - - @classmethod - def _default_header_xml(cls): - """Return bytes containing XML for a default header part.""" - path = os.path.join(os.path.split(__file__)[0], "..", "templates", "default-header.xml") - with open(path, "rb") as f: - xml_bytes = f.read() - return xml_bytes diff --git a/.venv-docs/lib/python3.12/site-packages/docx/parts/image.py b/.venv-docs/lib/python3.12/site-packages/docx/parts/image.py deleted file mode 100644 index 5aec0707..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/parts/image.py +++ /dev/null @@ -1,80 +0,0 @@ -"""The proxy class for an image part, and related objects.""" - -from __future__ import annotations - -import hashlib -from typing import TYPE_CHECKING - -from docx.image.image import Image -from docx.opc.part import Part -from docx.shared import Emu, Inches - -if TYPE_CHECKING: - from docx.opc.package import OpcPackage - from docx.opc.packuri import PackURI - - -class ImagePart(Part): - """An image part. - - Corresponds to the target part of a relationship with type RELATIONSHIP_TYPE.IMAGE. - """ - - def __init__( - self, partname: PackURI, content_type: str, blob: bytes, image: Image | None = None - ): - super(ImagePart, self).__init__(partname, content_type, blob) - self._image = image - - @property - def default_cx(self): - """Native width of this image, calculated from its width in pixels and - horizontal dots per inch (dpi).""" - px_width = self.image.px_width - horz_dpi = self.image.horz_dpi - width_in_inches = px_width / horz_dpi - return Inches(width_in_inches) - - @property - def default_cy(self): - """Native height of this image, calculated from its height in pixels and - vertical dots per inch (dpi).""" - px_height = self.image.px_height - horz_dpi = self.image.horz_dpi - height_in_emu = int(round(914400 * px_height / horz_dpi)) - return Emu(height_in_emu) - - @property - def filename(self): - """Filename from which this image part was originally created. - - A generic name, e.g. 'image.png', is substituted if no name is available, for - example when the image was loaded from an unnamed stream. In that case a default - extension is applied based on the detected MIME type of the image. - """ - if self._image is not None: - return self._image.filename - return "image.%s" % self.partname.ext - - @classmethod - def from_image(cls, image: Image, partname: PackURI): - """Return an |ImagePart| instance newly created from `image` and assigned - `partname`.""" - return ImagePart(partname, image.content_type, image.blob, image) - - @property - def image(self) -> Image: - if self._image is None: - self._image = Image.from_blob(self.blob) - return self._image - - @classmethod - def load(cls, partname: PackURI, content_type: str, blob: bytes, package: OpcPackage): - """Called by ``docx.opc.package.PartFactory`` to load an image part from a - package being opened by ``Document(...)`` call.""" - return cls(partname, content_type, blob) - - @property - def sha1(self): - """SHA1 hash digest of the blob of this image part.""" - return hashlib.sha1(self.blob).hexdigest() diff --git a/.venv-docs/lib/python3.12/site-packages/docx/parts/numbering.py b/.venv-docs/lib/python3.12/site-packages/docx/parts/numbering.py deleted file mode 100644 index 745c8458..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/parts/numbering.py +++ /dev/null @@ -1,32 +0,0 @@ -"""|NumberingPart| and closely related objects.""" - -from ..opc.part import XmlPart -from ..shared import lazyproperty - - -class NumberingPart(XmlPart): - """Proxy for the numbering.xml part containing numbering definitions for a document - or glossary.""" - - @classmethod - def new(cls) -> "NumberingPart": - """Newly created numbering part, containing only the root ```` element.""" - raise NotImplementedError - - @lazyproperty - def numbering_definitions(self): - """The |_NumberingDefinitions| instance containing the numbering definitions - ( element proxies) for this numbering part.""" - return _NumberingDefinitions(self._element) - - -class _NumberingDefinitions: - """Collection of |_NumberingDefinition| instances corresponding to the ```` - elements in a numbering part.""" - - def __init__(self, numbering_elm): - super(_NumberingDefinitions, self).__init__() - self._numbering = numbering_elm - - def __len__(self): - return len(self._numbering.num_lst) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/parts/settings.py b/.venv-docs/lib/python3.12/site-packages/docx/parts/settings.py deleted file mode 100644 index 7fe371f0..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/parts/settings.py +++ /dev/null @@ -1,50 +0,0 @@ -"""|SettingsPart| and closely related objects.""" - -from __future__ import annotations - -import os -from typing import TYPE_CHECKING, cast - -from docx.opc.constants import CONTENT_TYPE as CT -from docx.opc.packuri import PackURI -from docx.opc.part import XmlPart -from docx.oxml.parser import parse_xml -from docx.settings import Settings - -if TYPE_CHECKING: - from docx.oxml.settings import CT_Settings - from docx.package import Package - - -class SettingsPart(XmlPart): - """Document-level settings part of a WordprocessingML (WML) package.""" - - def __init__( - self, partname: PackURI, content_type: str, element: CT_Settings, package: Package - ): - super().__init__(partname, content_type, element, package) - self._settings = element - - @classmethod - def default(cls, package: Package): - """Return a newly created settings part, containing a default `w:settings` element tree.""" - partname = PackURI("/word/settings.xml") - content_type = CT.WML_SETTINGS - element = cast("CT_Settings", parse_xml(cls._default_settings_xml())) - return cls(partname, content_type, element, package) - - @property - def settings(self) -> Settings: - """A |Settings| proxy object for the `w:settings` element in this part. - - Contains the document-level settings for this document. - """ - return Settings(self._settings) - - @classmethod - def _default_settings_xml(cls): - """Return a bytestream containing XML for a default settings part.""" - path = os.path.join(os.path.split(__file__)[0], "..", "templates", "default-settings.xml") - with open(path, "rb") as f: - xml_bytes = f.read() - return xml_bytes diff --git a/.venv-docs/lib/python3.12/site-packages/docx/parts/story.py b/.venv-docs/lib/python3.12/site-packages/docx/parts/story.py deleted file mode 100644 index 7482c91a..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/parts/story.py +++ /dev/null @@ -1,95 +0,0 @@ -"""|StoryPart| and related objects.""" - -from __future__ import annotations - -from typing import IO, TYPE_CHECKING, Tuple, cast - -from docx.opc.constants import RELATIONSHIP_TYPE as RT -from docx.opc.part import XmlPart -from docx.oxml.shape import CT_Inline -from docx.shared import Length, lazyproperty - -if TYPE_CHECKING: - from docx.enum.style import WD_STYLE_TYPE - from docx.image.image import Image - from docx.parts.document import DocumentPart - from docx.styles.style import BaseStyle - - -class StoryPart(XmlPart): - """Base class for story parts. - - A story part is one that can contain textual content, such as the document-part and - header or footer parts. These all share content behaviors like `.paragraphs`, - `.add_paragraph()`, `.add_table()` etc. - """ - - def get_or_add_image(self, image_descriptor: str | IO[bytes]) -> Tuple[str, Image]: - """Return (rId, image) pair for image identified by `image_descriptor`. - - `rId` is the str key (often like "rId7") for the relationship between this story - part and the image part, reused if already present, newly created if not. - `image` is an |Image| instance providing access to the properties of the image, - such as dimensions and image type. - """ - package = self._package - assert package is not None - image_part = package.get_or_add_image_part(image_descriptor) - rId = self.relate_to(image_part, RT.IMAGE) - return rId, image_part.image - - def get_style(self, style_id: str | None, style_type: WD_STYLE_TYPE) -> BaseStyle: - """Return the style in this document matching `style_id`. - - Returns the default style for `style_type` if `style_id` is |None| or does not - match a defined style of `style_type`. - """ - return self._document_part.get_style(style_id, style_type) - - def get_style_id( - self, style_or_name: BaseStyle | str | None, style_type: WD_STYLE_TYPE - ) -> str | None: - """Return str style_id for `style_or_name` of `style_type`. - - Returns |None| if the style resolves to the default style for `style_type` or if - `style_or_name` is itself |None|. Raises if `style_or_name` is a style of the - wrong type or names a style not present in the document. - """ - return self._document_part.get_style_id(style_or_name, style_type) - - def new_pic_inline( - self, - image_descriptor: str | IO[bytes], - width: int | Length | None = None, - height: int | Length | None = None, - ) -> CT_Inline: - """Return a newly-created `w:inline` element. - - The element contains the image specified by `image_descriptor` and is scaled - based on the values of `width` and `height`. - """ - rId, image = self.get_or_add_image(image_descriptor) - cx, cy = image.scaled_dimensions(width, height) - shape_id, filename = self.next_id, image.filename - return CT_Inline.new_pic_inline(shape_id, rId, filename, cx, cy) - - @property - def next_id(self) -> int: - """Next available positive integer id value in this story XML document. - - The value is determined by incrementing the maximum existing id value. Gaps in - the existing id sequence are not filled. The id attribute value is unique in the - document, without regard to the element type it appears on. - """ - id_str_lst = self._element.xpath("//@id") - used_ids = [int(id_str) for id_str in id_str_lst if id_str.isdigit()] - if not used_ids: - return 1 - return max(used_ids) + 1 - - @lazyproperty - def _document_part(self) -> DocumentPart: - """|DocumentPart| object for this package.""" - package = self.package - assert package is not None - return cast("DocumentPart", package.main_document_part) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/parts/styles.py b/.venv-docs/lib/python3.12/site-packages/docx/parts/styles.py deleted file mode 100644 index 6e065bee..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/parts/styles.py +++ /dev/null @@ -1,42 +0,0 @@ -"""Provides StylesPart and related objects.""" - -from __future__ import annotations - -import os -from typing import TYPE_CHECKING - -from docx.opc.constants import CONTENT_TYPE as CT -from docx.opc.packuri import PackURI -from docx.opc.part import XmlPart -from docx.oxml.parser import parse_xml -from docx.styles.styles import Styles - -if TYPE_CHECKING: - from docx.opc.package import OpcPackage - - -class StylesPart(XmlPart): - """Proxy for the styles.xml part containing style definitions for a document or - glossary.""" - - @classmethod - def default(cls, package: OpcPackage) -> StylesPart: - """Return a newly created styles part, containing a default set of elements.""" - partname = PackURI("/word/styles.xml") - content_type = CT.WML_STYLES - element = parse_xml(cls._default_styles_xml()) - return cls(partname, content_type, element, package) - - @property - def styles(self): - """The |_Styles| instance containing the styles ( element proxies) for - this styles part.""" - return Styles(self.element) - - @classmethod - def _default_styles_xml(cls): - """Return a bytestream containing XML for a default styles part.""" - path = os.path.join(os.path.split(__file__)[0], "..", "templates", "default-styles.xml") - with open(path, "rb") as f: - xml_bytes = f.read() - return xml_bytes diff --git a/.venv-docs/lib/python3.12/site-packages/docx/py.typed b/.venv-docs/lib/python3.12/site-packages/docx/py.typed deleted file mode 100644 index e69de29b..00000000 diff --git a/.venv-docs/lib/python3.12/site-packages/docx/section.py b/.venv-docs/lib/python3.12/site-packages/docx/section.py deleted file mode 100644 index 982a1437..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/section.py +++ /dev/null @@ -1,479 +0,0 @@ -"""The |Section| object and related proxy classes.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Iterator, List, Sequence, overload - -from docx.blkcntnr import BlockItemContainer -from docx.enum.section import WD_HEADER_FOOTER -from docx.oxml.text.paragraph import CT_P -from docx.parts.hdrftr import FooterPart, HeaderPart -from docx.shared import lazyproperty -from docx.table import Table -from docx.text.paragraph import Paragraph - -if TYPE_CHECKING: - from docx.enum.section import WD_ORIENTATION, WD_SECTION_START - from docx.oxml.document import CT_Document - from docx.oxml.section import CT_SectPr - from docx.parts.document import DocumentPart - from docx.parts.story import StoryPart - from docx.shared import Length - - -class Section: - """Document section, providing access to section and page setup settings. - - Also provides access to headers and footers. - """ - - def __init__(self, sectPr: CT_SectPr, document_part: DocumentPart): - super(Section, self).__init__() - self._sectPr = sectPr - self._document_part = document_part - - @property - def bottom_margin(self) -> Length | None: - """Read/write. Bottom margin for pages in this section, in EMU. - - `None` when no bottom margin has been specified. Assigning |None| removes any - bottom-margin setting. - """ - return self._sectPr.bottom_margin - - @bottom_margin.setter - def bottom_margin(self, value: int | Length | None): - self._sectPr.bottom_margin = value - - @property - def different_first_page_header_footer(self) -> bool: - """True if this section displays a distinct first-page header and footer. - - Read/write. The definition of the first-page header and footer are accessed - using :attr:`.first_page_header` and :attr:`.first_page_footer` respectively. - """ - return self._sectPr.titlePg_val - - @different_first_page_header_footer.setter - def different_first_page_header_footer(self, value: bool): - self._sectPr.titlePg_val = value - - @property - def even_page_footer(self) -> _Footer: - """|_Footer| object defining footer content for even pages. - - The content of this footer definition is ignored unless the document setting - :attr:`~.Settings.odd_and_even_pages_header_footer` is set True. - """ - return _Footer(self._sectPr, self._document_part, WD_HEADER_FOOTER.EVEN_PAGE) - - @property - def even_page_header(self) -> _Header: - """|_Header| object defining header content for even pages. - - The content of this header definition is ignored unless the document setting - :attr:`~.Settings.odd_and_even_pages_header_footer` is set True. - """ - return _Header(self._sectPr, self._document_part, WD_HEADER_FOOTER.EVEN_PAGE) - - @property - def first_page_footer(self) -> _Footer: - """|_Footer| object defining footer content for the first page of this section. - - The content of this footer definition is ignored unless the property - :attr:`.different_first_page_header_footer` is set True. - """ - return _Footer(self._sectPr, self._document_part, WD_HEADER_FOOTER.FIRST_PAGE) - - @property - def first_page_header(self) -> _Header: - """|_Header| object defining header content for the first page of this section. - - The content of this header definition is ignored unless the property - :attr:`.different_first_page_header_footer` is set True. - """ - return _Header(self._sectPr, self._document_part, WD_HEADER_FOOTER.FIRST_PAGE) - - @lazyproperty - def footer(self) -> _Footer: - """|_Footer| object representing default page footer for this section. - - The default footer is used for odd-numbered pages when separate odd/even footers - are enabled. It is used for both odd and even-numbered pages otherwise. - """ - return _Footer(self._sectPr, self._document_part, WD_HEADER_FOOTER.PRIMARY) - - @property - def footer_distance(self) -> Length | None: - """Distance from bottom edge of page to bottom edge of the footer. - - Read/write. |None| if no setting is present in the XML. - """ - return self._sectPr.footer - - @footer_distance.setter - def footer_distance(self, value: int | Length | None): - self._sectPr.footer = value - - @property - def gutter(self) -> Length | None: - """|Length| object representing page gutter size in English Metric Units. - - Read/write. The page gutter is extra spacing added to the `inner` margin to - ensure even margins after page binding. Generally only used in book-bound - documents with double-sided and facing pages. - - This setting applies to all pages in this section. - - """ - return self._sectPr.gutter - - @gutter.setter - def gutter(self, value: int | Length | None): - self._sectPr.gutter = value - - @lazyproperty - def header(self) -> _Header: - """|_Header| object representing default page header for this section. - - The default header is used for odd-numbered pages when separate odd/even headers - are enabled. It is used for both odd and even-numbered pages otherwise. - """ - return _Header(self._sectPr, self._document_part, WD_HEADER_FOOTER.PRIMARY) - - @property - def header_distance(self) -> Length | None: - """Distance from top edge of page to top edge of header. - - Read/write. |None| if no setting is present in the XML. Assigning |None| causes - default value to be used. - """ - return self._sectPr.header - - @header_distance.setter - def header_distance(self, value: int | Length | None): - self._sectPr.header = value - - def iter_inner_content(self) -> Iterator[Paragraph | Table]: - """Generate each Paragraph or Table object in this `section`. - - Items appear in document order. - """ - for element in self._sectPr.iter_inner_content(): - yield (Paragraph(element, self) if isinstance(element, CT_P) else Table(element, self)) - - @property - def left_margin(self) -> Length | None: - """|Length| object representing the left margin for all pages in this section in - English Metric Units.""" - return self._sectPr.left_margin - - @left_margin.setter - def left_margin(self, value: int | Length | None): - self._sectPr.left_margin = value - - @property - def orientation(self) -> WD_ORIENTATION: - """:ref:`WdOrientation` member specifying page orientation for this section. - - One of ``WD_ORIENT.PORTRAIT`` or ``WD_ORIENT.LANDSCAPE``. - """ - return self._sectPr.orientation - - @orientation.setter - def orientation(self, value: WD_ORIENTATION | None): - self._sectPr.orientation = value - - @property - def page_height(self) -> Length | None: - """Total page height used for this section. - - This value is inclusive of all edge spacing values such as margins. - - Page orientation is taken into account, so for example, its expected value - would be ``Inches(8.5)`` for letter-sized paper when orientation is landscape. - """ - return self._sectPr.page_height - - @page_height.setter - def page_height(self, value: Length | None): - self._sectPr.page_height = value - - @property - def page_width(self) -> Length | None: - """Total page width used for this section. - - This value is like "paper size" and includes all edge spacing values such as - margins. - - Page orientation is taken into account, so for example, its expected value - would be ``Inches(11)`` for letter-sized paper when orientation is landscape. - """ - return self._sectPr.page_width - - @page_width.setter - def page_width(self, value: Length | None): - self._sectPr.page_width = value - - @property - def part(self) -> StoryPart: - return self._document_part - - @property - def right_margin(self) -> Length | None: - """|Length| object representing the right margin for all pages in this section - in English Metric Units.""" - return self._sectPr.right_margin - - @right_margin.setter - def right_margin(self, value: Length | None): - self._sectPr.right_margin = value - - @property - def start_type(self) -> WD_SECTION_START: - """Type of page-break (if any) inserted at the start of this section. - - For exmple, ``WD_SECTION_START.ODD_PAGE`` if the section should begin on the - next odd page, possibly inserting two page-breaks instead of one. - """ - return self._sectPr.start_type - - @start_type.setter - def start_type(self, value: WD_SECTION_START | None): - self._sectPr.start_type = value - - @property - def top_margin(self) -> Length | None: - """|Length| object representing the top margin for all pages in this section in - English Metric Units.""" - return self._sectPr.top_margin - - @top_margin.setter - def top_margin(self, value: Length | None): - self._sectPr.top_margin = value - - -class Sections(Sequence[Section]): - """Sequence of |Section| objects corresponding to the sections in the document. - - Supports ``len()``, iteration, and indexed access. - """ - - def __init__(self, document_elm: CT_Document, document_part: DocumentPart): - super(Sections, self).__init__() - self._document_elm = document_elm - self._document_part = document_part - - @overload - def __getitem__(self, key: int) -> Section: ... - - @overload - def __getitem__(self, key: slice) -> List[Section]: ... - - def __getitem__(self, key: int | slice) -> Section | List[Section]: - if isinstance(key, slice): - return [ - Section(sectPr, self._document_part) - for sectPr in self._document_elm.sectPr_lst[key] - ] - return Section(self._document_elm.sectPr_lst[key], self._document_part) - - def __iter__(self) -> Iterator[Section]: - for sectPr in self._document_elm.sectPr_lst: - yield Section(sectPr, self._document_part) - - def __len__(self) -> int: - return len(self._document_elm.sectPr_lst) - - -class _BaseHeaderFooter(BlockItemContainer): - """Base class for header and footer classes.""" - - def __init__( - self, - sectPr: CT_SectPr, - document_part: DocumentPart, - header_footer_index: WD_HEADER_FOOTER, - ): - self._sectPr = sectPr - self._document_part = document_part - self._hdrftr_index = header_footer_index - - @property - def is_linked_to_previous(self) -> bool: - """``True`` if this header/footer uses the definition from the prior section. - - ``False`` if this header/footer has an explicit definition. - - Assigning ``True`` to this property removes the header/footer definition for - this section, causing it to "inherit" the corresponding definition of the prior - section. Assigning ``False`` causes a new, empty definition to be added for this - section, but only if no definition is already present. - """ - # ---absence of a header/footer part indicates "linked" behavior--- - return not self._has_definition - - @is_linked_to_previous.setter - def is_linked_to_previous(self, value: bool) -> None: - new_state = bool(value) - # ---do nothing when value is not being changed--- - if new_state == self.is_linked_to_previous: - return - if new_state is True: - self._drop_definition() - else: - self._add_definition() - - @property - def part(self) -> HeaderPart | FooterPart: - """The |HeaderPart| or |FooterPart| for this header/footer. - - This overrides `BlockItemContainer.part` and is required to support image - insertion and perhaps other content like hyperlinks. - """ - # ---should not appear in documentation; - # ---not an interface property, even though public - return self._get_or_add_definition() - - def _add_definition(self) -> HeaderPart | FooterPart: - """Return newly-added header/footer part.""" - raise NotImplementedError("must be implemented by each subclass") - - @property - def _definition(self) -> HeaderPart | FooterPart: - """|HeaderPart| or |FooterPart| object containing header/footer content.""" - raise NotImplementedError("must be implemented by each subclass") - - def _drop_definition(self) -> None: - """Remove header/footer part containing the definition of this header/footer.""" - raise NotImplementedError("must be implemented by each subclass") - - @property - def _element(self): - """`w:hdr` or `w:ftr` element, root of header/footer part.""" - return self._get_or_add_definition().element - - def _get_or_add_definition(self) -> HeaderPart | FooterPart: - """Return HeaderPart or FooterPart object for this section. - - If this header/footer inherits its content, the part for the prior header/footer - is returned; this process continue recursively until a definition is found. If - the definition cannot be inherited (because the header/footer belongs to the - first section), a new definition is added for that first section and then - returned. - """ - # ---note this method is called recursively to access inherited definitions--- - # ---case-1: definition is not inherited--- - if self._has_definition: - return self._definition - # ---case-2: definition is inherited and belongs to second-or-later section--- - prior_headerfooter = self._prior_headerfooter - if prior_headerfooter: - return prior_headerfooter._get_or_add_definition() - # ---case-3: definition is inherited, but belongs to first section--- - return self._add_definition() - - @property - def _has_definition(self) -> bool: - """True if this header/footer has a related part containing its definition.""" - raise NotImplementedError("must be implemented by each subclass") - - @property - def _prior_headerfooter(self) -> _Header | _Footer | None: - """|_Header| or |_Footer| proxy on prior sectPr element. - - Returns None if this is first section. - """ - raise NotImplementedError("must be implemented by each subclass") - - -class _Footer(_BaseHeaderFooter): - """Page footer, used for all three types (default, even-page, and first-page). - - Note that, like a document or table cell, a footer must contain a minimum of one - paragraph and a new or otherwise "empty" footer contains a single empty paragraph. - This first paragraph can be accessed as `footer.paragraphs[0]` for purposes of - adding content to it. Using :meth:`add_paragraph()` by itself to add content will - leave an empty paragraph above the newly added one. - """ - - def _add_definition(self) -> FooterPart: - """Return newly-added footer part.""" - footer_part, rId = self._document_part.add_footer_part() - self._sectPr.add_footerReference(self._hdrftr_index, rId) - return footer_part - - @property - def _definition(self): - """|FooterPart| object containing content of this footer.""" - footerReference = self._sectPr.get_footerReference(self._hdrftr_index) - # -- currently this is never called when `._has_definition` evaluates False -- - assert footerReference is not None - return self._document_part.footer_part(footerReference.rId) - - def _drop_definition(self): - """Remove footer definition (footer part) associated with this section.""" - rId = self._sectPr.remove_footerReference(self._hdrftr_index) - self._document_part.drop_rel(rId) - - @property - def _has_definition(self) -> bool: - """True if a footer is defined for this section.""" - footerReference = self._sectPr.get_footerReference(self._hdrftr_index) - return footerReference is not None - - @property - def _prior_headerfooter(self): - """|_Footer| proxy on prior sectPr element or None if this is first section.""" - preceding_sectPr = self._sectPr.preceding_sectPr - return ( - None - if preceding_sectPr is None - else _Footer(preceding_sectPr, self._document_part, self._hdrftr_index) - ) - - -class _Header(_BaseHeaderFooter): - """Page header, used for all three types (default, even-page, and first-page). - - Note that, like a document or table cell, a header must contain a minimum of one - paragraph and a new or otherwise "empty" header contains a single empty paragraph. - This first paragraph can be accessed as `header.paragraphs[0]` for purposes of - adding content to it. Using :meth:`add_paragraph()` by itself to add content will - leave an empty paragraph above the newly added one. - """ - - def _add_definition(self): - """Return newly-added header part.""" - header_part, rId = self._document_part.add_header_part() - self._sectPr.add_headerReference(self._hdrftr_index, rId) - return header_part - - @property - def _definition(self): - """|HeaderPart| object containing content of this header.""" - headerReference = self._sectPr.get_headerReference(self._hdrftr_index) - # -- currently this is never called when `._has_definition` evaluates False -- - assert headerReference is not None - return self._document_part.header_part(headerReference.rId) - - def _drop_definition(self): - """Remove header definition associated with this section.""" - rId = self._sectPr.remove_headerReference(self._hdrftr_index) - self._document_part.drop_header_part(rId) - - @property - def _has_definition(self) -> bool: - """True if a header is explicitly defined for this section.""" - headerReference = self._sectPr.get_headerReference(self._hdrftr_index) - return headerReference is not None - - @property - def _prior_headerfooter(self): - """|_Header| proxy on prior sectPr element or None if this is first section.""" - preceding_sectPr = self._sectPr.preceding_sectPr - return ( - None - if preceding_sectPr is None - else _Header(preceding_sectPr, self._document_part, self._hdrftr_index) - ) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/settings.py b/.venv-docs/lib/python3.12/site-packages/docx/settings.py deleted file mode 100644 index 0a5aa2f3..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/settings.py +++ /dev/null @@ -1,35 +0,0 @@ -"""Settings object, providing access to document-level settings.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, cast - -from docx.shared import ElementProxy - -if TYPE_CHECKING: - import docx.types as t - from docx.oxml.settings import CT_Settings - from docx.oxml.xmlchemy import BaseOxmlElement - - -class Settings(ElementProxy): - """Provides access to document-level settings for a document. - - Accessed using the :attr:`.Document.settings` property. - """ - - def __init__(self, element: BaseOxmlElement, parent: t.ProvidesXmlPart | None = None): - super().__init__(element, parent) - self._settings = cast("CT_Settings", element) - - @property - def odd_and_even_pages_header_footer(self) -> bool: - """True if this document has distinct odd and even page headers and footers. - - Read/write. - """ - return self._settings.evenAndOddHeaders_val - - @odd_and_even_pages_header_footer.setter - def odd_and_even_pages_header_footer(self, value: bool): - self._settings.evenAndOddHeaders_val = value diff --git a/.venv-docs/lib/python3.12/site-packages/docx/shape.py b/.venv-docs/lib/python3.12/site-packages/docx/shape.py deleted file mode 100644 index cd35deb3..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/shape.py +++ /dev/null @@ -1,103 +0,0 @@ -"""Objects related to shapes. - -A shape is a visual object that appears on the drawing layer of a document. -""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from docx.enum.shape import WD_INLINE_SHAPE -from docx.oxml.ns import nsmap -from docx.shared import Parented - -if TYPE_CHECKING: - from docx.oxml.document import CT_Body - from docx.oxml.shape import CT_Inline - from docx.parts.story import StoryPart - from docx.shared import Length - - -class InlineShapes(Parented): - """Sequence of |InlineShape| instances, supporting len(), iteration, and indexed access.""" - - def __init__(self, body_elm: CT_Body, parent: StoryPart): - super(InlineShapes, self).__init__(parent) - self._body = body_elm - - def __getitem__(self, idx: int): - """Provide indexed access, e.g. 'inline_shapes[idx]'.""" - try: - inline = self._inline_lst[idx] - except IndexError: - msg = "inline shape index [%d] out of range" % idx - raise IndexError(msg) - - return InlineShape(inline) - - def __iter__(self): - return (InlineShape(inline) for inline in self._inline_lst) - - def __len__(self): - return len(self._inline_lst) - - @property - def _inline_lst(self): - body = self._body - xpath = "//w:p/w:r/w:drawing/wp:inline" - return body.xpath(xpath) - - -class InlineShape: - """Proxy for an ```` element, representing the container for an inline - graphical object.""" - - def __init__(self, inline: CT_Inline): - super(InlineShape, self).__init__() - self._inline = inline - - @property - def height(self) -> Length: - """Read/write. - - The display height of this inline shape as an |Emu| instance. - """ - return self._inline.extent.cy - - @height.setter - def height(self, cy: Length): - self._inline.extent.cy = cy - self._inline.graphic.graphicData.pic.spPr.cy = cy - - @property - def type(self): - """The type of this inline shape as a member of - ``docx.enum.shape.WD_INLINE_SHAPE``, e.g. ``LINKED_PICTURE``. - - Read-only. - """ - graphicData = self._inline.graphic.graphicData - uri = graphicData.uri - if uri == nsmap["pic"]: - blip = graphicData.pic.blipFill.blip - if blip.link is not None: - return WD_INLINE_SHAPE.LINKED_PICTURE - return WD_INLINE_SHAPE.PICTURE - if uri == nsmap["c"]: - return WD_INLINE_SHAPE.CHART - if uri == nsmap["dgm"]: - return WD_INLINE_SHAPE.SMART_ART - return WD_INLINE_SHAPE.NOT_IMPLEMENTED - - @property - def width(self): - """Read/write. - - The display width of this inline shape as an |Emu| instance. - """ - return self._inline.extent.cx - - @width.setter - def width(self, cx: Length): - self._inline.extent.cx = cx - self._inline.graphic.graphicData.pic.spPr.cx = cx diff --git a/.venv-docs/lib/python3.12/site-packages/docx/shared.py b/.venv-docs/lib/python3.12/site-packages/docx/shared.py deleted file mode 100644 index 6c12dc91..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/shared.py +++ /dev/null @@ -1,382 +0,0 @@ -"""Objects shared by docx modules.""" - -from __future__ import annotations - -import functools -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Generic, - Iterator, - List, - Tuple, - TypeVar, - cast, -) - -if TYPE_CHECKING: - import docx.types as t - from docx.opc.part import XmlPart - from docx.oxml.xmlchemy import BaseOxmlElement - from docx.parts.story import StoryPart - - -class Length(int): - """Base class for length constructor classes Inches, Cm, Mm, Px, and Emu. - - Behaves as an int count of English Metric Units, 914,400 to the inch, 36,000 to the - mm. Provides convenience unit conversion methods in the form of read-only - properties. Immutable. - """ - - _EMUS_PER_INCH = 914400 - _EMUS_PER_CM = 360000 - _EMUS_PER_MM = 36000 - _EMUS_PER_PT = 12700 - _EMUS_PER_TWIP = 635 - - def __new__(cls, emu: int): - return int.__new__(cls, emu) - - @property - def cm(self): - """The equivalent length expressed in centimeters (float).""" - return self / float(self._EMUS_PER_CM) - - @property - def emu(self): - """The equivalent length expressed in English Metric Units (int).""" - return self - - @property - def inches(self): - """The equivalent length expressed in inches (float).""" - return self / float(self._EMUS_PER_INCH) - - @property - def mm(self): - """The equivalent length expressed in millimeters (float).""" - return self / float(self._EMUS_PER_MM) - - @property - def pt(self): - """Floating point length in points.""" - return self / float(self._EMUS_PER_PT) - - @property - def twips(self): - """The equivalent length expressed in twips (int).""" - return int(round(self / float(self._EMUS_PER_TWIP))) - - -class Inches(Length): - """Convenience constructor for length in inches, e.g. ``width = Inches(0.5)``.""" - - def __new__(cls, inches: float): - emu = int(inches * Length._EMUS_PER_INCH) - return Length.__new__(cls, emu) - - -class Cm(Length): - """Convenience constructor for length in centimeters, e.g. ``height = Cm(12)``.""" - - def __new__(cls, cm: float): - emu = int(cm * Length._EMUS_PER_CM) - return Length.__new__(cls, emu) - - -class Emu(Length): - """Convenience constructor for length in English Metric Units, e.g. ``width = - Emu(457200)``.""" - - def __new__(cls, emu: int): - return Length.__new__(cls, int(emu)) - - -class Mm(Length): - """Convenience constructor for length in millimeters, e.g. ``width = Mm(240.5)``.""" - - def __new__(cls, mm: float): - emu = int(mm * Length._EMUS_PER_MM) - return Length.__new__(cls, emu) - - -class Pt(Length): - """Convenience value class for specifying a length in points.""" - - def __new__(cls, points: float): - emu = int(points * Length._EMUS_PER_PT) - return Length.__new__(cls, emu) - - -class Twips(Length): - """Convenience constructor for length in twips, e.g. ``width = Twips(42)``. - - A twip is a twentieth of a point, 635 EMU. - """ - - def __new__(cls, twips: float): - emu = int(twips * Length._EMUS_PER_TWIP) - return Length.__new__(cls, emu) - - -class RGBColor(Tuple[int, int, int]): - """Immutable value object defining a particular RGB color.""" - - def __new__(cls, r: int, g: int, b: int): - msg = "RGBColor() takes three integer values 0-255" - for val in (r, g, b): - if not isinstance(val, int): # pyright: ignore[reportUnnecessaryIsInstance] - raise TypeError(msg) - if val < 0 or val > 255: - raise ValueError(msg) - return super(RGBColor, cls).__new__(cls, (r, g, b)) - - def __repr__(self): - return "RGBColor(0x%02x, 0x%02x, 0x%02x)" % self - - def __str__(self): - """Return a hex string rgb value, like '3C2F80'.""" - return "%02X%02X%02X" % self - - @classmethod - def from_string(cls, rgb_hex_str: str) -> RGBColor: - """Return a new instance from an RGB color hex string like ``'3C2F80'``.""" - r = int(rgb_hex_str[:2], 16) - g = int(rgb_hex_str[2:4], 16) - b = int(rgb_hex_str[4:], 16) - return cls(r, g, b) - - -T = TypeVar("T") - - -class lazyproperty(Generic[T]): - """Decorator like @property, but evaluated only on first access. - - Like @property, this can only be used to decorate methods having only a `self` - parameter, and is accessed like an attribute on an instance, i.e. trailing - parentheses are not used. Unlike @property, the decorated method is only evaluated - on first access; the resulting value is cached and that same value returned on - second and later access without re-evaluation of the method. - - Like @property, this class produces a *data descriptor* object, which is stored in - the __dict__ of the *class* under the name of the decorated method ('fget' - nominally). The cached value is stored in the __dict__ of the *instance* under that - same name. - - Because it is a data descriptor (as opposed to a *non-data descriptor*), its - `__get__()` method is executed on each access of the decorated attribute; the - __dict__ item of the same name is "shadowed" by the descriptor. - - While this may represent a performance improvement over a property, its greater - benefit may be its other characteristics. One common use is to construct - collaborator objects, removing that "real work" from the constructor, while still - only executing once. It also de-couples client code from any sequencing - considerations; if it's accessed from more than one location, it's assured it will - be ready whenever needed. - - Loosely based on: https://stackoverflow.com/a/6849299/1902513. - - A lazyproperty is read-only. There is no counterpart to the optional "setter" (or - deleter) behavior of an @property. This is critically important to maintaining its - immutability and idempotence guarantees. Attempting to assign to a lazyproperty - raises AttributeError unconditionally. - - The parameter names in the methods below correspond to this usage example:: - - class Obj(object) - - @lazyproperty - def fget(self): - return 'some result' - - obj = Obj() - - Not suitable for wrapping a function (as opposed to a method) because it is not - callable.""" - - def __init__(self, fget: Callable[..., T]) -> None: - """*fget* is the decorated method (a "getter" function). - - A lazyproperty is read-only, so there is only an *fget* function (a regular - @property can also have an fset and fdel function). This name was chosen for - consistency with Python's `property` class which uses this name for the - corresponding parameter. - """ - # --- maintain a reference to the wrapped getter method - self._fget = fget - # --- and store the name of that decorated method - self._name = fget.__name__ - # --- adopt fget's __name__, __doc__, and other attributes - functools.update_wrapper(self, fget) # pyright: ignore - - def __get__(self, obj: Any, type: Any = None) -> T: - """Called on each access of 'fget' attribute on class or instance. - - *self* is this instance of a lazyproperty descriptor "wrapping" the property - method it decorates (`fget`, nominally). - - *obj* is the "host" object instance when the attribute is accessed from an - object instance, e.g. `obj = Obj(); obj.fget`. *obj* is None when accessed on - the class, e.g. `Obj.fget`. - - *type* is the class hosting the decorated getter method (`fget`) on both class - and instance attribute access. - """ - # --- when accessed on class, e.g. Obj.fget, just return this descriptor - # --- instance (patched above to look like fget). - if obj is None: - return self # type: ignore - - # --- when accessed on instance, start by checking instance __dict__ for - # --- item with key matching the wrapped function's name - value = obj.__dict__.get(self._name) - if value is None: - # --- on first access, the __dict__ item will be absent. Evaluate fget() - # --- and store that value in the (otherwise unused) host-object - # --- __dict__ value of same name ('fget' nominally) - value = self._fget(obj) - obj.__dict__[self._name] = value - return cast(T, value) - - def __set__(self, obj: Any, value: Any) -> None: - """Raises unconditionally, to preserve read-only behavior. - - This decorator is intended to implement immutable (and idempotent) object - attributes. For that reason, assignment to this property must be explicitly - prevented. - - If this __set__ method was not present, this descriptor would become a - *non-data descriptor*. That would be nice because the cached value would be - accessed directly once set (__dict__ attrs have precedence over non-data - descriptors on instance attribute lookup). The problem is, there would be - nothing to stop assignment to the cached value, which would overwrite the result - of `fget()` and break both the immutability and idempotence guarantees of this - decorator. - - The performance with this __set__() method in place was roughly 0.4 usec per - access when measured on a 2.8GHz development machine; so quite snappy and - probably not a rich target for optimization efforts. - """ - raise AttributeError("can't set attribute") - - -def write_only_property(f: Callable[[Any, Any], None]): - """@write_only_property decorator. - - Creates a property (descriptor attribute) that accepts assignment, but not getattr - (use in an expression). - """ - docstring = f.__doc__ - - return property(fset=f, doc=docstring) - - -class ElementProxy: - """Base class for lxml element proxy classes. - - An element proxy class is one whose primary responsibilities are fulfilled by - manipulating the attributes and child elements of an XML element. They are the most - common type of class in python-docx other than custom element (oxml) classes. - """ - - def __init__(self, element: BaseOxmlElement, parent: t.ProvidesXmlPart | None = None): - self._element = element - self._parent = parent - - def __eq__(self, other: object): - """Return |True| if this proxy object refers to the same oxml element as does - `other`. - - ElementProxy objects are value objects and should maintain no mutable local - state. Equality for proxy objects is defined as referring to the same XML - element, whether or not they are the same proxy object instance. - """ - if not isinstance(other, ElementProxy): - return False - return self._element is other._element - - def __ne__(self, other: object): - if not isinstance(other, ElementProxy): - return True - return self._element is not other._element - - @property - def element(self): - """The lxml element proxied by this object.""" - return self._element - - @property - def part(self) -> XmlPart: - """The package part containing this object.""" - if self._parent is None: - raise ValueError("part is not accessible from this element") - return self._parent.part - - -class Parented: - """Provides common services for document elements that occur below a part but may - occasionally require an ancestor object to provide a service, such as add or drop a - relationship. - - Provides ``self._parent`` attribute to subclasses. - """ - - def __init__(self, parent: t.ProvidesXmlPart): - self._parent = parent - - @property - def part(self) -> XmlPart: - """The package part containing this object.""" - return self._parent.part - - -class StoryChild: - """A document element within a story part. - - Story parts include DocumentPart and Header/FooterPart and can contain block items - (paragraphs and tables). Items from the block-item subtree occasionally require an - ancestor object to provide access to part-level or package-level items like styles - or images or to add or drop a relationship. - - Provides `self._parent` attribute to subclasses. - """ - - def __init__(self, parent: t.ProvidesStoryPart): - self._parent = parent - - @property - def part(self) -> StoryPart: - """The package part containing this object.""" - return self._parent.part - - -class TextAccumulator: - """Accepts `str` fragments and joins them together, in order, on `.pop(). - - Handy when text in a stream is broken up arbitrarily and you want to join it back - together within certain bounds. The optional `separator` argument determines how - the text fragments are punctuated, defaulting to the empty string. - """ - - def __init__(self, separator: str = ""): - self._separator = separator - self._texts: List[str] = [] - - def push(self, text: str) -> None: - """Add a text fragment to the accumulator.""" - self._texts.append(text) - - def pop(self) -> Iterator[str]: - """Generate sero-or-one str from those accumulated. - - Using `yield from accum.pop()` in a generator setting avoids producing an empty - string when no text is in the accumulator. - """ - if not self._texts: - return - text = self._separator.join(self._texts) - self._texts.clear() - yield text diff --git a/.venv-docs/lib/python3.12/site-packages/docx/styles/__init__.py b/.venv-docs/lib/python3.12/site-packages/docx/styles/__init__.py deleted file mode 100644 index 6358baf3..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/styles/__init__.py +++ /dev/null @@ -1,40 +0,0 @@ -"""Sub-package module for docx.styles sub-package.""" - -from __future__ import annotations - -from typing import Dict - - -class BabelFish: - """Translates special-case style names from UI name (e.g. Heading 1) to - internal/styles.xml name (e.g. heading 1) and back.""" - - style_aliases = ( - ("Caption", "caption"), - ("Footer", "footer"), - ("Header", "header"), - ("Heading 1", "heading 1"), - ("Heading 2", "heading 2"), - ("Heading 3", "heading 3"), - ("Heading 4", "heading 4"), - ("Heading 5", "heading 5"), - ("Heading 6", "heading 6"), - ("Heading 7", "heading 7"), - ("Heading 8", "heading 8"), - ("Heading 9", "heading 9"), - ) - - internal_style_names: Dict[str, str] = dict(style_aliases) - ui_style_names = {item[1]: item[0] for item in style_aliases} - - @classmethod - def ui2internal(cls, ui_style_name: str) -> str: - """Return the internal style name corresponding to `ui_style_name`, such as - 'heading 1' for 'Heading 1'.""" - return cls.internal_style_names.get(ui_style_name, ui_style_name) - - @classmethod - def internal2ui(cls, internal_style_name: str) -> str: - """Return the user interface style name corresponding to `internal_style_name`, - such as 'Heading 1' for 'heading 1'.""" - return cls.ui_style_names.get(internal_style_name, internal_style_name) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/styles/__pycache__/__init__.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/styles/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index b5afcbb6..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/styles/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/styles/__pycache__/latent.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/styles/__pycache__/latent.cpython-312.pyc deleted file mode 100644 index 790a8b2a..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/styles/__pycache__/latent.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/styles/__pycache__/style.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/styles/__pycache__/style.cpython-312.pyc deleted file mode 100644 index 0ea1e042..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/styles/__pycache__/style.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/styles/__pycache__/styles.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/styles/__pycache__/styles.cpython-312.pyc deleted file mode 100644 index b724ce65..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/styles/__pycache__/styles.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/styles/latent.py b/.venv-docs/lib/python3.12/site-packages/docx/styles/latent.py deleted file mode 100644 index c9db62f8..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/styles/latent.py +++ /dev/null @@ -1,198 +0,0 @@ -"""Latent style-related objects.""" - -from docx.shared import ElementProxy -from docx.styles import BabelFish - - -class LatentStyles(ElementProxy): - """Provides access to the default behaviors for latent styles in this document and - to the collection of |_LatentStyle| objects that define overrides of those defaults - for a particular named latent style.""" - - def __getitem__(self, key): - """Enables dictionary-style access to a latent style by name.""" - style_name = BabelFish.ui2internal(key) - lsdException = self._element.get_by_name(style_name) - if lsdException is None: - raise KeyError("no latent style with name '%s'" % key) - return _LatentStyle(lsdException) - - def __iter__(self): - return (_LatentStyle(ls) for ls in self._element.lsdException_lst) - - def __len__(self): - return len(self._element.lsdException_lst) - - def add_latent_style(self, name): - """Return a newly added |_LatentStyle| object to override the inherited defaults - defined in this latent styles object for the built-in style having `name`.""" - lsdException = self._element.add_lsdException() - lsdException.name = BabelFish.ui2internal(name) - return _LatentStyle(lsdException) - - @property - def default_priority(self): - """Integer between 0 and 99 inclusive specifying the default sort order for - latent styles in style lists and the style gallery. - - |None| if no value is assigned, which causes Word to use the default value 99. - """ - return self._element.defUIPriority - - @default_priority.setter - def default_priority(self, value): - self._element.defUIPriority = value - - @property - def default_to_hidden(self): - """Boolean specifying whether the default behavior for latent styles is to be - hidden. - - A hidden style does not appear in the recommended list or in the style gallery. - """ - return self._element.bool_prop("defSemiHidden") - - @default_to_hidden.setter - def default_to_hidden(self, value): - self._element.set_bool_prop("defSemiHidden", value) - - @property - def default_to_locked(self): - """Boolean specifying whether the default behavior for latent styles is to be - locked. - - A locked style does not appear in the styles panel or the style gallery and - cannot be applied to document content. This behavior is only active when - formatting protection is turned on for the document (via the Developer menu). - """ - return self._element.bool_prop("defLockedState") - - @default_to_locked.setter - def default_to_locked(self, value): - self._element.set_bool_prop("defLockedState", value) - - @property - def default_to_quick_style(self): - """Boolean specifying whether the default behavior for latent styles is to - appear in the style gallery when not hidden.""" - return self._element.bool_prop("defQFormat") - - @default_to_quick_style.setter - def default_to_quick_style(self, value): - self._element.set_bool_prop("defQFormat", value) - - @property - def default_to_unhide_when_used(self): - """Boolean specifying whether the default behavior for latent styles is to be - unhidden when first applied to content.""" - return self._element.bool_prop("defUnhideWhenUsed") - - @default_to_unhide_when_used.setter - def default_to_unhide_when_used(self, value): - self._element.set_bool_prop("defUnhideWhenUsed", value) - - @property - def load_count(self): - """Integer specifying the number of built-in styles to initialize to the - defaults specified in this |LatentStyles| object. - - |None| if there is no setting in the XML (very uncommon). The default Word 2011 - template sets this value to 276, accounting for the built-in styles in Word - 2010. - """ - return self._element.count - - @load_count.setter - def load_count(self, value): - self._element.count = value - - -class _LatentStyle(ElementProxy): - """Proxy for an `w:lsdException` element, which specifies display behaviors for a - built-in style when no definition for that style is stored yet in the `styles.xml` - part. - - The values in this element override the defaults specified in the parent - `w:latentStyles` element. - """ - - def delete(self): - """Remove this latent style definition such that the defaults defined in the - containing |LatentStyles| object provide the effective value for each of its - attributes. - - Attempting to access any attributes on this object after calling this method - will raise |AttributeError|. - """ - self._element.delete() - self._element = None - - @property - def hidden(self): - """Tri-state value specifying whether this latent style should appear in the - recommended list. - - |None| indicates the effective value is inherited from the parent - ```` element. - """ - return self._element.on_off_prop("semiHidden") - - @hidden.setter - def hidden(self, value): - self._element.set_on_off_prop("semiHidden", value) - - @property - def locked(self): - """Tri-state value specifying whether this latent styles is locked. - - A locked style does not appear in the styles panel or the style gallery and - cannot be applied to document content. This behavior is only active when - formatting protection is turned on for the document (via the Developer menu). - """ - return self._element.on_off_prop("locked") - - @locked.setter - def locked(self, value): - self._element.set_on_off_prop("locked", value) - - @property - def name(self): - """The name of the built-in style this exception applies to.""" - return BabelFish.internal2ui(self._element.name) - - @property - def priority(self): - """The integer sort key for this latent style in the Word UI.""" - return self._element.uiPriority - - @priority.setter - def priority(self, value): - self._element.uiPriority = value - - @property - def quick_style(self): - """Tri-state value specifying whether this latent style should appear in the - Word styles gallery when not hidden. - - |None| indicates the effective value should be inherited from the default values - in its parent |LatentStyles| object. - """ - return self._element.on_off_prop("qFormat") - - @quick_style.setter - def quick_style(self, value): - self._element.set_on_off_prop("qFormat", value) - - @property - def unhide_when_used(self): - """Tri-state value specifying whether this style should have its :attr:`hidden` - attribute set |False| the next time the style is applied to content. - - |None| indicates the effective value should be inherited from the default - specified by its parent |LatentStyles| object. - """ - return self._element.on_off_prop("unhideWhenUsed") - - @unhide_when_used.setter - def unhide_when_used(self, value): - self._element.set_on_off_prop("unhideWhenUsed", value) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/styles/style.py b/.venv-docs/lib/python3.12/site-packages/docx/styles/style.py deleted file mode 100644 index aa175ea8..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/styles/style.py +++ /dev/null @@ -1,254 +0,0 @@ -"""Style object hierarchy.""" - -from __future__ import annotations - -from typing import Type - -from docx.enum.style import WD_STYLE_TYPE -from docx.oxml.styles import CT_Style -from docx.shared import ElementProxy -from docx.styles import BabelFish -from docx.text.font import Font -from docx.text.parfmt import ParagraphFormat - - -def StyleFactory(style_elm: CT_Style) -> BaseStyle: - """Return `Style` object of appropriate |BaseStyle| subclass for `style_elm`.""" - style_cls: Type[BaseStyle] = { - WD_STYLE_TYPE.PARAGRAPH: ParagraphStyle, - WD_STYLE_TYPE.CHARACTER: CharacterStyle, - WD_STYLE_TYPE.TABLE: _TableStyle, - WD_STYLE_TYPE.LIST: _NumberingStyle, - }[style_elm.type] - - return style_cls(style_elm) - - -class BaseStyle(ElementProxy): - """Base class for the various types of style object, paragraph, character, table, - and numbering. - - These properties and methods are inherited by all style objects. - """ - - def __init__(self, style_elm: CT_Style): - super().__init__(style_elm) - self._style_elm = style_elm - - @property - def builtin(self): - """Read-only. - - |True| if this style is a built-in style. |False| indicates it is a custom - (user-defined) style. Note this value is based on the presence of a - `customStyle` attribute in the XML, not on specific knowledge of which styles - are built into Word. - """ - return not self._element.customStyle - - def delete(self): - """Remove this style definition from the document. - - Note that calling this method does not remove or change the style applied to any - document content. Content items having the deleted style will be rendered using - the default style, as is any content with a style not defined in the document. - """ - self._element.delete() - self._element = None - - @property - def hidden(self): - """|True| if display of this style in the style gallery and list of recommended - styles is suppressed. - - |False| otherwise. In order to be shown in the style gallery, this value must be - |False| and :attr:`.quick_style` must be |True|. - """ - return self._element.semiHidden_val - - @hidden.setter - def hidden(self, value): - self._element.semiHidden_val = value - - @property - def locked(self): - """Read/write Boolean. - - |True| if this style is locked. A locked style does not appear in the styles - panel or the style gallery and cannot be applied to document content. This - behavior is only active when formatting protection is turned on for the document - (via the Developer menu). - """ - return self._element.locked_val - - @locked.setter - def locked(self, value): - self._element.locked_val = value - - @property - def name(self): - """The UI name of this style.""" - name = self._element.name_val - if name is None: - return None - return BabelFish.internal2ui(name) - - @name.setter - def name(self, value): - self._element.name_val = value - - @property - def priority(self): - """The integer sort key governing display sequence of this style in the Word UI. - - |None| indicates no setting is defined, causing Word to use the default value of - 0. Style name is used as a secondary sort key to resolve ordering of styles - having the same priority value. - """ - return self._element.uiPriority_val - - @priority.setter - def priority(self, value): - self._element.uiPriority_val = value - - @property - def quick_style(self): - """|True| if this style should be displayed in the style gallery when - :attr:`.hidden` is |False|. - - Read/write Boolean. - """ - return self._element.qFormat_val - - @quick_style.setter - def quick_style(self, value): - self._element.qFormat_val = value - - @property - def style_id(self) -> str: - """The unique key name (string) for this style. - - This value is subject to rewriting by Word and should generally not be changed - unless you are familiar with the internals involved. - """ - return self._style_elm.styleId - - @style_id.setter - def style_id(self, value): - self._element.styleId = value - - @property - def type(self): - """Member of :ref:`WdStyleType` corresponding to the type of this style, e.g. - ``WD_STYLE_TYPE.PARAGRAPH``.""" - type = self._style_elm.type - if type is None: - return WD_STYLE_TYPE.PARAGRAPH - return type - - @property - def unhide_when_used(self): - """|True| if an application should make this style visible the next time it is - applied to content. - - False otherwise. Note that |docx| does not automatically unhide a style having - |True| for this attribute when it is applied to content. - """ - return self._element.unhideWhenUsed_val - - @unhide_when_used.setter - def unhide_when_used(self, value): - self._element.unhideWhenUsed_val = value - - -class CharacterStyle(BaseStyle): - """A character style. - - A character style is applied to a |Run| object and primarily provides character- - level formatting via the |Font| object in its :attr:`.font` property. - """ - - @property - def base_style(self): - """Style object this style inherits from or |None| if this style is not based on - another style.""" - base_style = self._element.base_style - if base_style is None: - return None - return StyleFactory(base_style) - - @base_style.setter - def base_style(self, style): - style_id = style.style_id if style is not None else None - self._element.basedOn_val = style_id - - @property - def font(self): - """The |Font| object providing access to the character formatting properties for - this style, such as font name and size.""" - return Font(self._element) - - -# -- just in case someone uses the old name in an extension function -- -_CharacterStyle = CharacterStyle - - -class ParagraphStyle(CharacterStyle): - """A paragraph style. - - A paragraph style provides both character formatting and paragraph formatting such - as indentation and line-spacing. - """ - - def __repr__(self): - return "_ParagraphStyle('%s') id: %s" % (self.name, id(self)) - - @property - def next_paragraph_style(self): - """|_ParagraphStyle| object representing the style to be applied automatically - to a new paragraph inserted after a paragraph of this style. - - Returns self if no next paragraph style is defined. Assigning |None| or `self` - removes the setting such that new paragraphs are created using this same style. - """ - next_style_elm = self._element.next_style - if next_style_elm is None: - return self - if next_style_elm.type != WD_STYLE_TYPE.PARAGRAPH: - return self - return StyleFactory(next_style_elm) - - @next_paragraph_style.setter - def next_paragraph_style(self, style): - if style is None or style.style_id == self.style_id: - self._element._remove_next() - else: - self._element.get_or_add_next().val = style.style_id - - @property - def paragraph_format(self): - """The |ParagraphFormat| object providing access to the paragraph formatting - properties for this style such as indentation.""" - return ParagraphFormat(self._element) - - -# -- just in case someone uses the old name in an extension function -- -_ParagraphStyle = ParagraphStyle - - -class _TableStyle(ParagraphStyle): - """A table style. - - A table style provides character and paragraph formatting for its contents as well - as special table formatting properties. - """ - - def __repr__(self): - return "_TableStyle('%s') id: %s" % (self.name, id(self)) - - -class _NumberingStyle(BaseStyle): - """A numbering style. - - Not yet implemented. - """ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/styles/styles.py b/.venv-docs/lib/python3.12/site-packages/docx/styles/styles.py deleted file mode 100644 index b05b3ebb..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/styles/styles.py +++ /dev/null @@ -1,136 +0,0 @@ -"""Styles object, container for all objects in the styles part.""" - -from __future__ import annotations - -from warnings import warn - -from docx.enum.style import WD_STYLE_TYPE -from docx.oxml.styles import CT_Styles -from docx.shared import ElementProxy -from docx.styles import BabelFish -from docx.styles.latent import LatentStyles -from docx.styles.style import BaseStyle, StyleFactory - - -class Styles(ElementProxy): - """Provides access to the styles defined in a document. - - Accessed using the :attr:`.Document.styles` property. Supports ``len()``, iteration, - and dictionary-style access by style name. - """ - - def __init__(self, styles: CT_Styles): - super().__init__(styles) - self._element = styles - - def __contains__(self, name): - """Enables `in` operator on style name.""" - internal_name = BabelFish.ui2internal(name) - return any(style.name_val == internal_name for style in self._element.style_lst) - - def __getitem__(self, key: str): - """Enables dictionary-style access by UI name. - - Lookup by style id is deprecated, triggers a warning, and will be removed in a - near-future release. - """ - style_elm = self._element.get_by_name(BabelFish.ui2internal(key)) - if style_elm is not None: - return StyleFactory(style_elm) - - style_elm = self._element.get_by_id(key) - if style_elm is not None: - msg = "style lookup by style_id is deprecated. Use style name as key instead." - warn(msg, UserWarning, stacklevel=2) - return StyleFactory(style_elm) - - raise KeyError("no style with name '%s'" % key) - - def __iter__(self): - return (StyleFactory(style) for style in self._element.style_lst) - - def __len__(self): - return len(self._element.style_lst) - - def add_style(self, name, style_type, builtin=False): - """Return a newly added style object of `style_type` and identified by `name`. - - A builtin style can be defined by passing True for the optional `builtin` - argument. - """ - style_name = BabelFish.ui2internal(name) - if style_name in self: - raise ValueError("document already contains style '%s'" % name) - style = self._element.add_style_of_type(style_name, style_type, builtin) - return StyleFactory(style) - - def default(self, style_type: WD_STYLE_TYPE): - """Return the default style for `style_type` or |None| if no default is defined - for that type (not common).""" - style = self._element.default_for(style_type) - if style is None: - return None - return StyleFactory(style) - - def get_by_id(self, style_id: str | None, style_type: WD_STYLE_TYPE): - """Return the style of `style_type` matching `style_id`. - - Returns the default for `style_type` if `style_id` is not found or is |None|, or - if the style having `style_id` is not of `style_type`. - """ - if style_id is None: - return self.default(style_type) - return self._get_by_id(style_id, style_type) - - def get_style_id(self, style_or_name, style_type): - """Return the id of the style corresponding to `style_or_name`, or |None| if - `style_or_name` is |None|. - - If `style_or_name` is not a style object, the style is looked up using - `style_or_name` as a style name, raising |ValueError| if no style with that name - is defined. Raises |ValueError| if the target style is not of `style_type`. - """ - if style_or_name is None: - return None - elif isinstance(style_or_name, BaseStyle): - return self._get_style_id_from_style(style_or_name, style_type) - else: - return self._get_style_id_from_name(style_or_name, style_type) - - @property - def latent_styles(self): - """A |LatentStyles| object providing access to the default behaviors for latent - styles and the collection of |_LatentStyle| objects that define overrides of - those defaults for a particular named latent style.""" - return LatentStyles(self._element.get_or_add_latentStyles()) - - def _get_by_id(self, style_id: str | None, style_type: WD_STYLE_TYPE): - """Return the style of `style_type` matching `style_id`. - - Returns the default for `style_type` if `style_id` is not found or if the style - having `style_id` is not of `style_type`. - """ - style = self._element.get_by_id(style_id) if style_id else None - if style is None or style.type != style_type: - return self.default(style_type) - return StyleFactory(style) - - def _get_style_id_from_name(self, style_name: str, style_type: WD_STYLE_TYPE) -> str | None: - """Return the id of the style of `style_type` corresponding to `style_name`. - - Returns |None| if that style is the default style for `style_type`. Raises - |ValueError| if the named style is not found in the document or does not match - `style_type`. - """ - return self._get_style_id_from_style(self[style_name], style_type) - - def _get_style_id_from_style(self, style: BaseStyle, style_type: WD_STYLE_TYPE) -> str | None: - """Id of `style`, or |None| if it is the default style of `style_type`. - - Raises |ValueError| if style is not of `style_type`. - """ - if style.type != style_type: - raise ValueError("assigned style is type %s, need type %s" % (style.type, style_type)) - if style == self.default(style_type): - return None - return style.style_id diff --git a/.venv-docs/lib/python3.12/site-packages/docx/table.py b/.venv-docs/lib/python3.12/site-packages/docx/table.py deleted file mode 100644 index 545c4688..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/table.py +++ /dev/null @@ -1,537 +0,0 @@ -"""The |Table| object and related proxy classes.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Iterator, cast, overload - -from typing_extensions import TypeAlias - -from docx.blkcntnr import BlockItemContainer -from docx.enum.style import WD_STYLE_TYPE -from docx.enum.table import WD_CELL_VERTICAL_ALIGNMENT -from docx.oxml.simpletypes import ST_Merge -from docx.oxml.table import CT_TblGridCol -from docx.shared import Inches, Parented, StoryChild, lazyproperty - -if TYPE_CHECKING: - import docx.types as t - from docx.enum.table import WD_ROW_HEIGHT_RULE, WD_TABLE_ALIGNMENT, WD_TABLE_DIRECTION - from docx.oxml.table import CT_Row, CT_Tbl, CT_TblPr, CT_Tc - from docx.shared import Length - from docx.styles.style import ( - ParagraphStyle, - _TableStyle, # pyright: ignore[reportPrivateUsage] - ) - -TableParent: TypeAlias = "Table | _Columns | _Rows" - - -class Table(StoryChild): - """Proxy class for a WordprocessingML ```` element.""" - - def __init__(self, tbl: CT_Tbl, parent: t.ProvidesStoryPart): - super(Table, self).__init__(parent) - self._element = tbl - self._tbl = tbl - - def add_column(self, width: Length): - """Return a |_Column| object of `width`, newly added rightmost to the table.""" - tblGrid = self._tbl.tblGrid - gridCol = tblGrid.add_gridCol() - gridCol.w = width - for tr in self._tbl.tr_lst: - tc = tr.add_tc() - tc.width = width - return _Column(gridCol, self) - - def add_row(self): - """Return a |_Row| instance, newly added bottom-most to the table.""" - tbl = self._tbl - tr = tbl.add_tr() - for gridCol in tbl.tblGrid.gridCol_lst: - tc = tr.add_tc() - if gridCol.w is not None: - tc.width = gridCol.w - return _Row(tr, self) - - @property - def alignment(self) -> WD_TABLE_ALIGNMENT | None: - """Read/write. - - A member of :ref:`WdRowAlignment` or None, specifying the positioning of this - table between the page margins. |None| if no setting is specified, causing the - effective value to be inherited from the style hierarchy. - """ - return self._tblPr.alignment - - @alignment.setter - def alignment(self, value: WD_TABLE_ALIGNMENT | None): - self._tblPr.alignment = value - - @property - def autofit(self) -> bool: - """|True| if column widths can be automatically adjusted to improve the fit of - cell contents. - - |False| if table layout is fixed. Column widths are adjusted in either case if - total column width exceeds page width. Read/write boolean. - """ - return self._tblPr.autofit - - @autofit.setter - def autofit(self, value: bool): - self._tblPr.autofit = value - - def cell(self, row_idx: int, col_idx: int) -> _Cell: - """|_Cell| at `row_idx`, `col_idx` intersection. - - (0, 0) is the top, left-most cell. - """ - cell_idx = col_idx + (row_idx * self._column_count) - return self._cells[cell_idx] - - def column_cells(self, column_idx: int) -> list[_Cell]: - """Sequence of cells in the column at `column_idx` in this table.""" - cells = self._cells - idxs = range(column_idx, len(cells), self._column_count) - return [cells[idx] for idx in idxs] - - @lazyproperty - def columns(self): - """|_Columns| instance representing the sequence of columns in this table.""" - return _Columns(self._tbl, self) - - def row_cells(self, row_idx: int) -> list[_Cell]: - """DEPRECATED: Use `table.rows[row_idx].cells` instead. - - Sequence of cells in the row at `row_idx` in this table. - """ - column_count = self._column_count - start = row_idx * column_count - end = start + column_count - return self._cells[start:end] - - @lazyproperty - def rows(self) -> _Rows: - """|_Rows| instance containing the sequence of rows in this table.""" - return _Rows(self._tbl, self) - - @property - def style(self) -> _TableStyle | None: - """|_TableStyle| object representing the style applied to this table. - - Read/write. The default table style for the document (often `Normal Table`) is - returned if the table has no directly-applied style. Assigning |None| to this - property removes any directly-applied table style causing it to inherit the - default table style of the document. - - Note that the style name of a table style differs slightly from that displayed - in the user interface; a hyphen, if it appears, must be removed. For example, - `Light Shading - Accent 1` becomes `Light Shading Accent 1`. - """ - style_id = self._tbl.tblStyle_val - return cast("_TableStyle | None", self.part.get_style(style_id, WD_STYLE_TYPE.TABLE)) - - @style.setter - def style(self, style_or_name: _TableStyle | str | None): - style_id = self.part.get_style_id(style_or_name, WD_STYLE_TYPE.TABLE) - self._tbl.tblStyle_val = style_id - - @property - def table(self): - """Provide child objects with reference to the |Table| object they belong to, - without them having to know their direct parent is a |Table| object. - - This is the terminus of a series of `parent._table` calls from an arbitrary - child through its ancestors. - """ - return self - - @property - def table_direction(self) -> WD_TABLE_DIRECTION | None: - """Member of :ref:`WdTableDirection` indicating cell-ordering direction. - - For example: `WD_TABLE_DIRECTION.LTR`. |None| indicates the value is inherited - from the style hierarchy. - """ - return cast("WD_TABLE_DIRECTION | None", self._tbl.bidiVisual_val) - - @table_direction.setter - def table_direction(self, value: WD_TABLE_DIRECTION | None): - self._element.bidiVisual_val = value - - @property - def _cells(self) -> list[_Cell]: - """A sequence of |_Cell| objects, one for each cell of the layout grid. - - If the table contains a span, one or more |_Cell| object references are - repeated. - """ - col_count = self._column_count - cells: list[_Cell] = [] - for tc in self._tbl.iter_tcs(): - for grid_span_idx in range(tc.grid_span): - if tc.vMerge == ST_Merge.CONTINUE: - cells.append(cells[-col_count]) - elif grid_span_idx > 0: - cells.append(cells[-1]) - else: - cells.append(_Cell(tc, self)) - return cells - - @property - def _column_count(self): - """The number of grid columns in this table.""" - return self._tbl.col_count - - @property - def _tblPr(self) -> CT_TblPr: - return self._tbl.tblPr - - -class _Cell(BlockItemContainer): - """Table cell.""" - - def __init__(self, tc: CT_Tc, parent: TableParent): - super(_Cell, self).__init__(tc, cast("t.ProvidesStoryPart", parent)) - self._parent = parent - self._tc = self._element = tc - - def add_paragraph(self, text: str = "", style: str | ParagraphStyle | None = None): - """Return a paragraph newly added to the end of the content in this cell. - - If present, `text` is added to the paragraph in a single run. If specified, the - paragraph style `style` is applied. If `style` is not specified or is |None|, - the result is as though the 'Normal' style was applied. Note that the formatting - of text in a cell can be influenced by the table style. `text` can contain tab - (``\\t``) characters, which are converted to the appropriate XML form for a tab. - `text` can also include newline (``\\n``) or carriage return (``\\r``) - characters, each of which is converted to a line break. - """ - return super(_Cell, self).add_paragraph(text, style) - - def add_table( # pyright: ignore[reportIncompatibleMethodOverride] - self, rows: int, cols: int - ) -> Table: - """Return a table newly added to this cell after any existing cell content. - - The new table will have `rows` rows and `cols` columns. - - An empty paragraph is added after the table because Word requires a paragraph - element as the last element in every cell. - """ - width = self.width if self.width is not None else Inches(1) - table = super(_Cell, self).add_table(rows, cols, width) - self.add_paragraph() - return table - - @property - def grid_span(self) -> int: - """Number of layout-grid cells this cell spans horizontally. - - A "normal" cell has a grid-span of 1. A horizontally merged cell has a grid-span of 2 or - more. - """ - return self._tc.grid_span - - def merge(self, other_cell: _Cell): - """Return a merged cell created by spanning the rectangular region having this - cell and `other_cell` as diagonal corners. - - Raises |InvalidSpanError| if the cells do not define a rectangular region. - """ - tc, tc_2 = self._tc, other_cell._tc - merged_tc = tc.merge(tc_2) - return _Cell(merged_tc, self._parent) - - @property - def paragraphs(self): - """List of paragraphs in the cell. - - A table cell is required to contain at least one block-level element and end - with a paragraph. By default, a new cell contains a single paragraph. Read-only - """ - return super(_Cell, self).paragraphs - - @property - def tables(self): - """List of tables in the cell, in the order they appear. - - Read-only. - """ - return super(_Cell, self).tables - - @property - def text(self) -> str: - """The entire contents of this cell as a string of text. - - Assigning a string to this property replaces all existing content with a single - paragraph containing the assigned text in a single run. - """ - return "\n".join(p.text for p in self.paragraphs) - - @text.setter - def text(self, text: str): - """Write-only. - - Set entire contents of cell to the string `text`. Any existing content or - revisions are replaced. - """ - tc = self._tc - tc.clear_content() - p = tc.add_p() - r = p.add_r() - r.text = text - - @property - def vertical_alignment(self): - """Member of :ref:`WdCellVerticalAlignment` or None. - - A value of |None| indicates vertical alignment for this cell is inherited. - Assigning |None| causes any explicitly defined vertical alignment to be removed, - restoring inheritance. - """ - tcPr = self._element.tcPr - if tcPr is None: - return None - return tcPr.vAlign_val - - @vertical_alignment.setter - def vertical_alignment(self, value: WD_CELL_VERTICAL_ALIGNMENT | None): - tcPr = self._element.get_or_add_tcPr() - tcPr.vAlign_val = value - - @property - def width(self): - """The width of this cell in EMU, or |None| if no explicit width is set.""" - return self._tc.width - - @width.setter - def width(self, value: Length): - self._tc.width = value - - -class _Column(Parented): - """Table column.""" - - def __init__(self, gridCol: CT_TblGridCol, parent: TableParent): - super(_Column, self).__init__(parent) - self._parent = parent - self._gridCol = gridCol - - @property - def cells(self) -> tuple[_Cell, ...]: - """Sequence of |_Cell| instances corresponding to cells in this column.""" - return tuple(self.table.column_cells(self._index)) - - @property - def table(self) -> Table: - """Reference to the |Table| object this column belongs to.""" - return self._parent.table - - @property - def width(self) -> Length | None: - """The width of this column in EMU, or |None| if no explicit width is set.""" - return self._gridCol.w - - @width.setter - def width(self, value: Length | None): - self._gridCol.w = value - - @property - def _index(self): - """Index of this column in its table, starting from zero.""" - return self._gridCol.gridCol_idx - - -class _Columns(Parented): - """Sequence of |_Column| instances corresponding to the columns in a table. - - Supports ``len()``, iteration and indexed access. - """ - - def __init__(self, tbl: CT_Tbl, parent: TableParent): - super(_Columns, self).__init__(parent) - self._parent = parent - self._tbl = tbl - - def __getitem__(self, idx: int): - """Provide indexed access, e.g. 'columns[0]'.""" - try: - gridCol = self._gridCol_lst[idx] - except IndexError: - msg = "column index [%d] is out of range" % idx - raise IndexError(msg) - return _Column(gridCol, self) - - def __iter__(self): - for gridCol in self._gridCol_lst: - yield _Column(gridCol, self) - - def __len__(self): - return len(self._gridCol_lst) - - @property - def table(self) -> Table: - """Reference to the |Table| object this column collection belongs to.""" - return self._parent.table - - @property - def _gridCol_lst(self): - """Sequence containing ```` elements for this table, each - representing a table column.""" - tblGrid = self._tbl.tblGrid - return tblGrid.gridCol_lst - - -class _Row(Parented): - """Table row.""" - - def __init__(self, tr: CT_Row, parent: TableParent): - super(_Row, self).__init__(parent) - self._parent = parent - self._tr = self._element = tr - - @property - def cells(self) -> tuple[_Cell, ...]: - """Sequence of |_Cell| instances corresponding to cells in this row. - - Note that Word allows table rows to start later than the first column and end before the - last column. - - - Only cells actually present are included in the return value. - - This implies the length of this cell sequence may differ between rows of the same table. - - If you are reading the cells from each row to form a rectangular "matrix" data structure - of the table cell values, you will need to account for empty leading and/or trailing - layout-grid positions using `.grid_cols_before` and `.grid_cols_after`. - - """ - - def iter_tc_cells(tc: CT_Tc) -> Iterator[_Cell]: - """Generate a cell object for each layout-grid cell in `tc`. - - In particular, a `` element with a horizontal "span" with generate the same cell - multiple times, one for each grid-cell being spanned. This approximates a row in a - "uniform" table, where each row has a cell for each column in the table. - """ - # -- a cell comprising the second or later row of a vertical span is indicated by - # -- tc.vMerge="continue" (the default value of the `w:vMerge` attribute, when it is - # -- present in the XML). The `w:tc` element at the same grid-offset in the prior row - # -- is guaranteed to be the same width (gridSpan). So we can delegate content - # -- discovery to that prior-row `w:tc` element (recursively) until we arrive at the - # -- "root" cell -- for the vertical span. - if tc.vMerge == "continue": - yield from iter_tc_cells(tc._tc_above) # pyright: ignore[reportPrivateUsage] - return - - # -- Otherwise, vMerge is either "restart" or None, meaning this `tc` holds the actual - # -- content of the cell (whether it is vertically merged or not). - cell = _Cell(tc, self.table) - for _ in range(tc.grid_span): - yield cell - - def _iter_row_cells() -> Iterator[_Cell]: - """Generate `_Cell` instance for each populated layout-grid cell in this row.""" - for tc in self._tr.tc_lst: - yield from iter_tc_cells(tc) - - return tuple(_iter_row_cells()) - - @property - def grid_cols_after(self) -> int: - """Count of unpopulated grid-columns after the last cell in this row. - - Word allows a row to "end early", meaning that one or more cells are not present at the - end of that row. - - Note these are not simply "empty" cells. The renderer reads this value and "skips" this - many columns after drawing the last cell. - - Note this also implies that not all rows are guaranteed to have the same number of cells, - e.g. `_Row.cells` could have length `n` for one row and `n - m` for the next row in the same - table. Visually this appears as a column (at the beginning or end, not in the middle) with - one or more cells missing. - """ - return self._tr.grid_after - - @property - def grid_cols_before(self) -> int: - """Count of unpopulated grid-columns before the first cell in this row. - - Word allows a row to "start late", meaning that one or more cells are not present at the - beginning of that row. - - Note these are not simply "empty" cells. The renderer reads this value and skips forward to - the table layout-grid position of the first cell in this row; the renderer "skips" this many - columns before drawing the first cell. - - Note this also implies that not all rows are guaranteed to have the same number of cells, - e.g. `_Row.cells` could have length `n` for one row and `n - m` for the next row in the same - table. - """ - return self._tr.grid_before - - @property - def height(self) -> Length | None: - """Return a |Length| object representing the height of this cell, or |None| if - no explicit height is set.""" - return self._tr.trHeight_val - - @height.setter - def height(self, value: Length | None): - self._tr.trHeight_val = value - - @property - def height_rule(self) -> WD_ROW_HEIGHT_RULE | None: - """Return the height rule of this cell as a member of the :ref:`WdRowHeightRule`. - - This value is |None| if no explicit height_rule is set. - """ - return self._tr.trHeight_hRule - - @height_rule.setter - def height_rule(self, value: WD_ROW_HEIGHT_RULE | None): - self._tr.trHeight_hRule = value - - @property - def table(self) -> Table: - """Reference to the |Table| object this row belongs to.""" - return self._parent.table - - @property - def _index(self) -> int: - """Index of this row in its table, starting from zero.""" - return self._tr.tr_idx - - -class _Rows(Parented): - """Sequence of |_Row| objects corresponding to the rows in a table. - - Supports ``len()``, iteration, indexed access, and slicing. - """ - - def __init__(self, tbl: CT_Tbl, parent: TableParent): - super(_Rows, self).__init__(parent) - self._parent = parent - self._tbl = tbl - - @overload - def __getitem__(self, idx: int) -> _Row: ... - - @overload - def __getitem__(self, idx: slice) -> list[_Row]: ... - - def __getitem__(self, idx: int | slice) -> _Row | list[_Row]: - """Provide indexed access, (e.g. `rows[0]` or `rows[1:3]`)""" - return list(self)[idx] - - def __iter__(self): - return (_Row(tr, self) for tr in self._tbl.tr_lst) - - def __len__(self): - return len(self._tbl.tr_lst) - - @property - def table(self) -> Table: - """Reference to the |Table| object this row collection belongs to.""" - return self._parent.table diff --git a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-comments.xml b/.venv-docs/lib/python3.12/site-packages/docx/templates/default-comments.xml deleted file mode 100644 index 2a36ca98..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-comments.xml +++ /dev/null @@ -1,12 +0,0 @@ - - diff --git a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/[Content_Types].xml b/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/[Content_Types].xml deleted file mode 100644 index a2e37e89..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/[Content_Types].xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/_rels/.rels b/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/_rels/.rels deleted file mode 100644 index 40657c77..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/_rels/.rels +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/customXml/_rels/item1.xml.rels b/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/customXml/_rels/item1.xml.rels deleted file mode 100644 index 8a65d212..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/customXml/_rels/item1.xml.rels +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/customXml/item1.xml b/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/customXml/item1.xml deleted file mode 100644 index 94c8babd..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/customXml/item1.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/customXml/itemProps1.xml b/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/customXml/itemProps1.xml deleted file mode 100644 index 6664fc08..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/customXml/itemProps1.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/docProps/app.xml b/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/docProps/app.xml deleted file mode 100644 index bf89187e..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/docProps/app.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - 0 - 1 - 0 - 0 - Microsoft Macintosh Word - 0 - 0 - 0 - false - - - - Title - - - 1 - - - - - - - - - - - false - 0 - false - - false - 14.0000 - diff --git a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/docProps/core.xml b/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/docProps/core.xml deleted file mode 100644 index 55109c76..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/docProps/core.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - python-docx - - generated by python-docx - - 1 - 2013-12-23T23:15:00Z - 2013-12-23T23:15:00Z - - diff --git a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/docProps/thumbnail.jpeg b/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/docProps/thumbnail.jpeg deleted file mode 100644 index d0c4f1f5..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/docProps/thumbnail.jpeg and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/word/_rels/document.xml.rels b/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/word/_rels/document.xml.rels deleted file mode 100644 index de8ebbc5..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/word/_rels/document.xml.rels +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/word/document.xml b/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/word/document.xml deleted file mode 100644 index 3edb08e8..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/word/document.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/word/fontTable.xml b/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/word/fontTable.xml deleted file mode 100644 index a2e06581..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/word/fontTable.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/word/numbering.xml b/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/word/numbering.xml deleted file mode 100644 index cba57e24..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/word/numbering.xml +++ /dev/null @@ -1,201 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/word/settings.xml b/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/word/settings.xml deleted file mode 100644 index 1e84bf18..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/word/settings.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/word/styles.xml b/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/word/styles.xml deleted file mode 100644 index 46d1e98f..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/word/styles.xml +++ /dev/null @@ -1,11844 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/word/stylesWithEffects.xml b/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/word/stylesWithEffects.xml deleted file mode 100644 index 91c1734e..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/word/stylesWithEffects.xml +++ /dev/null @@ -1,11800 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/word/theme/theme1.xml b/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/word/theme/theme1.xml deleted file mode 100644 index 2b30074a..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/word/theme/theme1.xml +++ /dev/null @@ -1,318 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/word/webSettings.xml b/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/word/webSettings.xml deleted file mode 100644 index 189a20a2..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-docx-template/word/webSettings.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-footer.xml b/.venv-docs/lib/python3.12/site-packages/docx/templates/default-footer.xml deleted file mode 100644 index 944ae1a6..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-footer.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - diff --git a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-header.xml b/.venv-docs/lib/python3.12/site-packages/docx/templates/default-header.xml deleted file mode 100644 index df6848c0..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-header.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - diff --git a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-settings.xml b/.venv-docs/lib/python3.12/site-packages/docx/templates/default-settings.xml deleted file mode 100644 index fda1adef..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-settings.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-styles.xml b/.venv-docs/lib/python3.12/site-packages/docx/templates/default-styles.xml deleted file mode 100644 index b8b97bc7..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/templates/default-styles.xml +++ /dev/null @@ -1,190 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.venv-docs/lib/python3.12/site-packages/docx/templates/default.docx b/.venv-docs/lib/python3.12/site-packages/docx/templates/default.docx deleted file mode 100644 index c22ff362..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/templates/default.docx and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/text/__init__.py b/.venv-docs/lib/python3.12/site-packages/docx/text/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/.venv-docs/lib/python3.12/site-packages/docx/text/__pycache__/__init__.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/text/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 012e595d..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/text/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/text/__pycache__/font.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/text/__pycache__/font.cpython-312.pyc deleted file mode 100644 index 378bfc7a..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/text/__pycache__/font.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/text/__pycache__/hyperlink.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/text/__pycache__/hyperlink.cpython-312.pyc deleted file mode 100644 index 9608dfb9..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/text/__pycache__/hyperlink.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/text/__pycache__/pagebreak.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/text/__pycache__/pagebreak.cpython-312.pyc deleted file mode 100644 index 324a566f..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/text/__pycache__/pagebreak.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/text/__pycache__/paragraph.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/text/__pycache__/paragraph.cpython-312.pyc deleted file mode 100644 index d189e906..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/text/__pycache__/paragraph.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/text/__pycache__/parfmt.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/text/__pycache__/parfmt.cpython-312.pyc deleted file mode 100644 index fd47cebb..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/text/__pycache__/parfmt.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/text/__pycache__/run.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/text/__pycache__/run.cpython-312.pyc deleted file mode 100644 index 9c03e348..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/text/__pycache__/run.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/text/__pycache__/tabstops.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/docx/text/__pycache__/tabstops.cpython-312.pyc deleted file mode 100644 index 6e938869..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/docx/text/__pycache__/tabstops.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/docx/text/font.py b/.venv-docs/lib/python3.12/site-packages/docx/text/font.py deleted file mode 100644 index 0439f454..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/text/font.py +++ /dev/null @@ -1,428 +0,0 @@ -"""Font-related proxy objects.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Any - -from docx.dml.color import ColorFormat -from docx.enum.text import WD_UNDERLINE -from docx.shared import ElementProxy, Emu - -if TYPE_CHECKING: - from docx.enum.text import WD_COLOR_INDEX - from docx.oxml.text.run import CT_R - from docx.shared import Length - - -class Font(ElementProxy): - """Proxy object for parent of a `` element and providing access to - character properties such as font name, font size, bold, and subscript.""" - - def __init__(self, r: CT_R, parent: Any | None = None): - super().__init__(r, parent) - self._element = r - self._r = r - - @property - def all_caps(self) -> bool | None: - """Read/write. - - Causes text in this font to appear in capital letters. - """ - return self._get_bool_prop("caps") - - @all_caps.setter - def all_caps(self, value: bool | None) -> None: - self._set_bool_prop("caps", value) - - @property - def bold(self) -> bool | None: - """Read/write. - - Causes text in this font to appear in bold. - """ - return self._get_bool_prop("b") - - @bold.setter - def bold(self, value: bool | None) -> None: - self._set_bool_prop("b", value) - - @property - def color(self): - """A |ColorFormat| object providing a way to get and set the text color for this - font.""" - return ColorFormat(self._element) - - @property - def complex_script(self) -> bool | None: - """Read/write tri-state value. - - When |True|, causes the characters in the run to be treated as complex script - regardless of their Unicode values. - """ - return self._get_bool_prop("cs") - - @complex_script.setter - def complex_script(self, value: bool | None) -> None: - self._set_bool_prop("cs", value) - - @property - def cs_bold(self) -> bool | None: - """Read/write tri-state value. - - When |True|, causes the complex script characters in the run to be displayed in - bold typeface. - """ - return self._get_bool_prop("bCs") - - @cs_bold.setter - def cs_bold(self, value: bool | None) -> None: - self._set_bool_prop("bCs", value) - - @property - def cs_italic(self) -> bool | None: - """Read/write tri-state value. - - When |True|, causes the complex script characters in the run to be displayed in - italic typeface. - """ - return self._get_bool_prop("iCs") - - @cs_italic.setter - def cs_italic(self, value: bool | None) -> None: - self._set_bool_prop("iCs", value) - - @property - def double_strike(self) -> bool | None: - """Read/write tri-state value. - - When |True|, causes the text in the run to appear with double strikethrough. - """ - return self._get_bool_prop("dstrike") - - @double_strike.setter - def double_strike(self, value: bool | None) -> None: - self._set_bool_prop("dstrike", value) - - @property - def emboss(self) -> bool | None: - """Read/write tri-state value. - - When |True|, causes the text in the run to appear as if raised off the page in - relief. - """ - return self._get_bool_prop("emboss") - - @emboss.setter - def emboss(self, value: bool | None) -> None: - self._set_bool_prop("emboss", value) - - @property - def hidden(self) -> bool | None: - """Read/write tri-state value. - - When |True|, causes the text in the run to be hidden from display, unless - applications settings force hidden text to be shown. - """ - return self._get_bool_prop("vanish") - - @hidden.setter - def hidden(self, value: bool | None) -> None: - self._set_bool_prop("vanish", value) - - @property - def highlight_color(self) -> WD_COLOR_INDEX | None: - """Color of highlighing applied or |None| if not highlighted.""" - rPr = self._element.rPr - if rPr is None: - return None - return rPr.highlight_val - - @highlight_color.setter - def highlight_color(self, value: WD_COLOR_INDEX | None): - rPr = self._element.get_or_add_rPr() - rPr.highlight_val = value - - @property - def italic(self) -> bool | None: - """Read/write tri-state value. - - When |True|, causes the text of the run to appear in italics. |None| indicates - the effective value is inherited from the style hierarchy. - """ - return self._get_bool_prop("i") - - @italic.setter - def italic(self, value: bool | None) -> None: - self._set_bool_prop("i", value) - - @property - def imprint(self) -> bool | None: - """Read/write tri-state value. - - When |True|, causes the text in the run to appear as if pressed into the page. - """ - return self._get_bool_prop("imprint") - - @imprint.setter - def imprint(self, value: bool | None) -> None: - self._set_bool_prop("imprint", value) - - @property - def math(self) -> bool | None: - """Read/write tri-state value. - - When |True|, specifies this run contains WML that should be handled as though it - was Office Open XML Math. - """ - return self._get_bool_prop("oMath") - - @math.setter - def math(self, value: bool | None) -> None: - self._set_bool_prop("oMath", value) - - @property - def name(self) -> str | None: - """The typeface name for this |Font|. - - Causes the text it controls to appear in the named font, if a matching font is - found. |None| indicates the typeface is inherited from the style hierarchy. - """ - rPr = self._element.rPr - if rPr is None: - return None - return rPr.rFonts_ascii - - @name.setter - def name(self, value: str | None) -> None: - rPr = self._element.get_or_add_rPr() - rPr.rFonts_ascii = value - rPr.rFonts_hAnsi = value - - @property - def no_proof(self) -> bool | None: - """Read/write tri-state value. - - When |True|, specifies that the contents of this run should not report any - errors when the document is scanned for spelling and grammar. - """ - return self._get_bool_prop("noProof") - - @no_proof.setter - def no_proof(self, value: bool | None) -> None: - self._set_bool_prop("noProof", value) - - @property - def outline(self) -> bool | None: - """Read/write tri-state value. - - When |True| causes the characters in the run to appear as if they have an - outline, by drawing a one pixel wide border around the inside and outside - borders of each character glyph. - """ - return self._get_bool_prop("outline") - - @outline.setter - def outline(self, value: bool | None) -> None: - self._set_bool_prop("outline", value) - - @property - def rtl(self) -> bool | None: - """Read/write tri-state value. - - When |True| causes the text in the run to have right-to-left characteristics. - """ - return self._get_bool_prop("rtl") - - @rtl.setter - def rtl(self, value: bool | None) -> None: - self._set_bool_prop("rtl", value) - - @property - def shadow(self) -> bool | None: - """Read/write tri-state value. - - When |True| causes the text in the run to appear as if each character has a - shadow. - """ - return self._get_bool_prop("shadow") - - @shadow.setter - def shadow(self, value: bool | None) -> None: - self._set_bool_prop("shadow", value) - - @property - def size(self) -> Length | None: - """Font height in English Metric Units (EMU). - - |None| indicates the font size should be inherited from the style hierarchy. - |Length| is a subclass of |int| having properties for convenient conversion into - points or other length units. The :class:`docx.shared.Pt` class allows - convenient specification of point values:: - - >>> font.size = Pt(24) - >>> font.size - 304800 - >>> font.size.pt - 24.0 - - """ - rPr = self._element.rPr - if rPr is None: - return None - return rPr.sz_val - - @size.setter - def size(self, emu: int | Length | None) -> None: - rPr = self._element.get_or_add_rPr() - rPr.sz_val = None if emu is None else Emu(emu) - - @property - def small_caps(self) -> bool | None: - """Read/write tri-state value. - - When |True| causes the lowercase characters in the run to appear as capital - letters two points smaller than the font size specified for the run. - """ - return self._get_bool_prop("smallCaps") - - @small_caps.setter - def small_caps(self, value: bool | None) -> None: - self._set_bool_prop("smallCaps", value) - - @property - def snap_to_grid(self) -> bool | None: - """Read/write tri-state value. - - When |True| causes the run to use the document grid characters per line settings - defined in the docGrid element when laying out the characters in this run. - """ - return self._get_bool_prop("snapToGrid") - - @snap_to_grid.setter - def snap_to_grid(self, value: bool | None) -> None: - self._set_bool_prop("snapToGrid", value) - - @property - def spec_vanish(self) -> bool | None: - """Read/write tri-state value. - - When |True|, specifies that the given run shall always behave as if it is - hidden, even when hidden text is being displayed in the current document. The - property has a very narrow, specialized use related to the table of contents. - Consult the spec (§17.3.2.36) for more details. - """ - return self._get_bool_prop("specVanish") - - @spec_vanish.setter - def spec_vanish(self, value: bool | None) -> None: - self._set_bool_prop("specVanish", value) - - @property - def strike(self) -> bool | None: - """Read/write tri-state value. - - When |True| causes the text in the run to appear with a single horizontal line - through the center of the line. - """ - return self._get_bool_prop("strike") - - @strike.setter - def strike(self, value: bool | None) -> None: - self._set_bool_prop("strike", value) - - @property - def subscript(self) -> bool | None: - """Boolean indicating whether the characters in this |Font| appear as subscript. - - |None| indicates the subscript/subscript value is inherited from the style - hierarchy. - """ - rPr = self._element.rPr - if rPr is None: - return None - return rPr.subscript - - @subscript.setter - def subscript(self, value: bool | None) -> None: - rPr = self._element.get_or_add_rPr() - rPr.subscript = value - - @property - def superscript(self) -> bool | None: - """Boolean indicating whether the characters in this |Font| appear as - superscript. - - |None| indicates the subscript/superscript value is inherited from the style - hierarchy. - """ - rPr = self._element.rPr - if rPr is None: - return None - return rPr.superscript - - @superscript.setter - def superscript(self, value: bool | None) -> None: - rPr = self._element.get_or_add_rPr() - rPr.superscript = value - - @property - def underline(self) -> bool | WD_UNDERLINE | None: - """The underline style for this |Font|. - - The value is one of |None|, |True|, |False|, or a member of :ref:`WdUnderline`. - - |None| indicates the font inherits its underline value from the style hierarchy. - |False| indicates no underline. |True| indicates single underline. The values - from :ref:`WdUnderline` are used to specify other outline styles such as double, - wavy, and dotted. - """ - rPr = self._element.rPr - if rPr is None: - return None - val = rPr.u_val - return ( - None - if val == WD_UNDERLINE.INHERITED - else True - if val == WD_UNDERLINE.SINGLE - else False - if val == WD_UNDERLINE.NONE - else val - ) - - @underline.setter - def underline(self, value: bool | WD_UNDERLINE | None) -> None: - rPr = self._element.get_or_add_rPr() - # -- works fine without these two mappings, but only because True == 1 and - # -- False == 0, which happen to match the mapping for WD_UNDERLINE.SINGLE - # -- and .NONE respectively. - val = ( - WD_UNDERLINE.SINGLE if value is True else WD_UNDERLINE.NONE if value is False else value - ) - rPr.u_val = val - - @property - def web_hidden(self) -> bool | None: - """Read/write tri-state value. - - When |True|, specifies that the contents of this run shall be hidden when the - document is displayed in web page view. - """ - return self._get_bool_prop("webHidden") - - @web_hidden.setter - def web_hidden(self, value: bool | None) -> None: - self._set_bool_prop("webHidden", value) - - def _get_bool_prop(self, name: str) -> bool | None: - """Return the value of boolean child of `w:rPr` having `name`.""" - rPr = self._element.rPr - if rPr is None: - return None - return rPr._get_bool_val(name) # pyright: ignore[reportPrivateUsage] - - def _set_bool_prop(self, name: str, value: bool | None): - """Assign `value` to the boolean child `name` of `w:rPr`.""" - rPr = self._element.get_or_add_rPr() - rPr._set_bool_val(name, value) # pyright: ignore[reportPrivateUsage] diff --git a/.venv-docs/lib/python3.12/site-packages/docx/text/hyperlink.py b/.venv-docs/lib/python3.12/site-packages/docx/text/hyperlink.py deleted file mode 100644 index a23df1c7..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/text/hyperlink.py +++ /dev/null @@ -1,121 +0,0 @@ -"""Hyperlink-related proxy objects for python-docx, Hyperlink in particular. - -A hyperlink occurs in a paragraph, at the same level as a Run, and a hyperlink itself -contains runs, which is where the visible text of the hyperlink is stored. So it's kind -of in-between, less than a paragraph and more than a run. So it gets its own module. -""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from docx.shared import Parented -from docx.text.run import Run - -if TYPE_CHECKING: - import docx.types as t - from docx.oxml.text.hyperlink import CT_Hyperlink - - -class Hyperlink(Parented): - """Proxy object wrapping a `` element. - - A hyperlink occurs as a child of a paragraph, at the same level as a Run. A - hyperlink itself contains runs, which is where the visible text of the hyperlink is - stored. - """ - - def __init__(self, hyperlink: CT_Hyperlink, parent: t.ProvidesStoryPart): - super().__init__(parent) - self._parent = parent - self._hyperlink = self._element = hyperlink - - @property - def address(self) -> str: - """The "URL" of the hyperlink (but not necessarily a web link). - - While commonly a web link like "https://google.com" the hyperlink address can - take a variety of forms including "internal links" to bookmarked locations - within the document. When this hyperlink is an internal "jump" to for example a - heading from the table-of-contents (TOC), the address is blank. The bookmark - reference (like "_Toc147925734") is stored in the `.fragment` property. - """ - rId = self._hyperlink.rId - return self._parent.part.rels[rId].target_ref if rId else "" - - @property - def contains_page_break(self) -> bool: - """True when the text of this hyperlink is broken across page boundaries. - - This is not uncommon and can happen for example when the hyperlink text is - multiple words and occurs in the last line of a page. Theoretically, a hyperlink - can contain more than one page break but that would be extremely uncommon in - practice. Still, this value should be understood to mean that "one-or-more" - rendered page breaks are present. - """ - return bool(self._hyperlink.lastRenderedPageBreaks) - - @property - def fragment(self) -> str: - """Reference like `#glossary` at end of URL that refers to a sub-resource. - - Note that this value does not include the fragment-separator character ("#"). - - This value is known as a "named anchor" in an HTML context and "anchor" in the - MS API, but an "anchor" element (``) represents a full hyperlink in HTML so - we avoid confusion by using the more precise RFC 3986 naming "URI fragment". - - These are also used to refer to bookmarks within the same document, in which - case the `.address` value with be blank ("") and this property will hold a - value like "_Toc147925734". - - To reliably get an entire web URL you will need to concatenate this with the - `.address` value, separated by "#" when both are present. Consider using the - `.url` property for that purpose. - - Word sometimes stores a fragment in this property (an XML attribute) and - sometimes with the address, depending on how the URL is inserted, so don't - depend on this field being empty to indicate no fragment is present. - """ - return self._hyperlink.anchor or "" - - @property - def runs(self) -> list[Run]: - """List of |Run| instances in this hyperlink. - - Together these define the visible text of the hyperlink. The text of a hyperlink - is typically contained in a single run will be broken into multiple runs if for - example part of the hyperlink is bold or the text was changed after the document - was saved. - """ - return [Run(r, self._parent) for r in self._hyperlink.r_lst] - - @property - def text(self) -> str: - """String formed by concatenating the text of each run in the hyperlink. - - Tabs and line breaks in the XML are mapped to ``\\t`` and ``\\n`` characters - respectively. Note that rendered page-breaks can occur within a hyperlink but - they are not reflected in this text. - """ - return self._hyperlink.text - - @property - def url(self) -> str: - """Convenience property to get web URLs from hyperlinks that contain them. - - This value is the empty string ("") when there is no address portion, so its - boolean value can also be used to distinguish external URIs from internal "jump" - hyperlinks like those found in a table-of-contents. - - Note that this value may also be a link to a file, so if you only want web-urls - you'll need to check for a protocol prefix like `https://`. - - When both an address and fragment are present, the return value joins the two - separated by the fragment-separator hash ("#"). Otherwise this value is the same - as that of the `.address` property. - """ - address, fragment = self.address, self.fragment - if not address: - return "" - return f"{address}#{fragment}" if fragment else address diff --git a/.venv-docs/lib/python3.12/site-packages/docx/text/pagebreak.py b/.venv-docs/lib/python3.12/site-packages/docx/text/pagebreak.py deleted file mode 100644 index 0977ccea..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/text/pagebreak.py +++ /dev/null @@ -1,104 +0,0 @@ -"""Proxy objects related to rendered page-breaks.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from docx.oxml.text.pagebreak import CT_LastRenderedPageBreak -from docx.shared import Parented - -if TYPE_CHECKING: - import docx.types as t - from docx.text.paragraph import Paragraph - - -class RenderedPageBreak(Parented): - """A page-break inserted by Word during page-layout for print or display purposes. - - This usually does not correspond to a "hard" page-break inserted by the document - author, rather just that Word ran out of room on one page and needed to start - another. The position of these can change depending on the printer and page-size, as - well as margins, etc. They also will change in response to edits, but not until Word - loads and saves the document. - - Note these are never inserted by `python-docx` because it has no rendering function. - These are generally only useful for text-extraction of existing documents when - `python-docx` is being used solely as a document "reader". - - NOTE: a rendered page-break can occur within a hyperlink; consider a multi-word - hyperlink like "excellent Wikipedia article on LLMs" that happens to fall close to - the end of the last line on a page such that the page breaks between "Wikipedia" and - "article". In such a "page-breaks-in-hyperlink" case, THESE METHODS WILL "MOVE" THE - PAGE-BREAK to occur after the hyperlink, such that the entire hyperlink appears in - the paragraph returned by `.preceding_paragraph_fragment`. While this places the - "tail" text of the hyperlink on the "wrong" page, it avoids having two hyperlinks - each with a fragment of the actual text and pointing to the same address. - """ - - def __init__( - self, - lastRenderedPageBreak: CT_LastRenderedPageBreak, - parent: t.ProvidesStoryPart, - ): - super().__init__(parent) - self._element = lastRenderedPageBreak - self._lastRenderedPageBreak = lastRenderedPageBreak - - @property - def preceding_paragraph_fragment(self) -> Paragraph | None: - """A "loose" paragraph containing the content preceding this page-break. - - Compare `.following_paragraph_fragment` as these two are intended to be used - together. - - This value is `None` when no content precedes this page-break. This case is - common and occurs whenever a page breaks on an even paragraph boundary. - Returning `None` for this case avoids "inserting" a non-existent paragraph into - the content stream. Note that content can include DrawingML items like images or - charts. - - Note the returned paragraph *is divorced from the document body*. Any changes - made to it will not be reflected in the document. It is intended to provide a - familiar container (`Paragraph`) to interrogate for the content preceding this - page-break in the paragraph in which it occured. - - Contains the entire hyperlink when this break occurs within a hyperlink. - """ - if self._lastRenderedPageBreak.precedes_all_content: - return None - - from docx.text.paragraph import Paragraph - - return Paragraph(self._lastRenderedPageBreak.preceding_fragment_p, self._parent) - - @property - def following_paragraph_fragment(self) -> Paragraph | None: - """A "loose" paragraph containing the content following this page-break. - - HAS POTENTIALLY SURPRISING BEHAVIORS so read carefully to be sure this is what - you want. This is primarily targeted toward text-extraction use-cases for which - precisely associating text with the page it occurs on is important. - - Compare `.preceding_paragraph_fragment` as these two are intended to be used - together. - - This value is `None` when no content follows this page-break. This case is - unlikely to occur in practice because Word places even-paragraph-boundary - page-breaks on the paragraph *following* the page-break. Still, it is possible - and must be checked for. Returning `None` for this case avoids "inserting" an - extra, non-existent paragraph into the content stream. Note that content can - include DrawingML items like images or charts, not just text. - - The returned paragraph *is divorced from the document body*. Any changes made to - it will not be reflected in the document. It is intended to provide a container - (`Paragraph`) with familiar properties and methods that can be used to - characterize the paragraph content following a mid-paragraph page-break. - - Contains no portion of the hyperlink when this break occurs within a hyperlink. - """ - if self._lastRenderedPageBreak.follows_all_content: - return None - - from docx.text.paragraph import Paragraph - - return Paragraph(self._lastRenderedPageBreak.following_fragment_p, self._parent) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/text/paragraph.py b/.venv-docs/lib/python3.12/site-packages/docx/text/paragraph.py deleted file mode 100644 index 234ea66c..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/text/paragraph.py +++ /dev/null @@ -1,173 +0,0 @@ -"""Paragraph-related proxy types.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Iterator, List, cast - -from docx.enum.style import WD_STYLE_TYPE -from docx.oxml.text.run import CT_R -from docx.shared import StoryChild -from docx.styles.style import ParagraphStyle -from docx.text.hyperlink import Hyperlink -from docx.text.pagebreak import RenderedPageBreak -from docx.text.parfmt import ParagraphFormat -from docx.text.run import Run - -if TYPE_CHECKING: - import docx.types as t - from docx.enum.text import WD_PARAGRAPH_ALIGNMENT - from docx.oxml.text.paragraph import CT_P - from docx.styles.style import CharacterStyle - - -class Paragraph(StoryChild): - """Proxy object wrapping a `` element.""" - - def __init__(self, p: CT_P, parent: t.ProvidesStoryPart): - super(Paragraph, self).__init__(parent) - self._p = self._element = p - - def add_run(self, text: str | None = None, style: str | CharacterStyle | None = None) -> Run: - """Append run containing `text` and having character-style `style`. - - `text` can contain tab (``\\t``) characters, which are converted to the - appropriate XML form for a tab. `text` can also include newline (``\\n``) or - carriage return (``\\r``) characters, each of which is converted to a line - break. When `text` is `None`, the new run is empty. - """ - r = self._p.add_r() - run = Run(r, self) - if text: - run.text = text - if style: - run.style = style - return run - - @property - def alignment(self) -> WD_PARAGRAPH_ALIGNMENT | None: - """A member of the :ref:`WdParagraphAlignment` enumeration specifying the - justification setting for this paragraph. - - A value of |None| indicates the paragraph has no directly-applied alignment - value and will inherit its alignment value from its style hierarchy. Assigning - |None| to this property removes any directly-applied alignment value. - """ - return self._p.alignment - - @alignment.setter - def alignment(self, value: WD_PARAGRAPH_ALIGNMENT): - self._p.alignment = value - - def clear(self): - """Return this same paragraph after removing all its content. - - Paragraph-level formatting, such as style, is preserved. - """ - self._p.clear_content() - return self - - @property - def contains_page_break(self) -> bool: - """`True` when one or more rendered page-breaks occur in this paragraph.""" - return bool(self._p.lastRenderedPageBreaks) - - @property - def hyperlinks(self) -> List[Hyperlink]: - """A |Hyperlink| instance for each hyperlink in this paragraph.""" - return [Hyperlink(hyperlink, self) for hyperlink in self._p.hyperlink_lst] - - def insert_paragraph_before( - self, text: str | None = None, style: str | ParagraphStyle | None = None - ) -> Paragraph: - """Return a newly created paragraph, inserted directly before this paragraph. - - If `text` is supplied, the new paragraph contains that text in a single run. If - `style` is provided, that style is assigned to the new paragraph. - """ - paragraph = self._insert_paragraph_before() - if text: - paragraph.add_run(text) - if style is not None: - paragraph.style = style - return paragraph - - def iter_inner_content(self) -> Iterator[Run | Hyperlink]: - """Generate the runs and hyperlinks in this paragraph, in the order they appear. - - The content in a paragraph consists of both runs and hyperlinks. This method - allows accessing each of those separately, in document order, for when the - precise position of the hyperlink within the paragraph text is important. Note - that a hyperlink itself contains runs. - """ - for r_or_hlink in self._p.inner_content_elements: - yield ( - Run(r_or_hlink, self) - if isinstance(r_or_hlink, CT_R) - else Hyperlink(r_or_hlink, self) - ) - - @property - def paragraph_format(self): - """The |ParagraphFormat| object providing access to the formatting properties - for this paragraph, such as line spacing and indentation.""" - return ParagraphFormat(self._element) - - @property - def rendered_page_breaks(self) -> List[RenderedPageBreak]: - """All rendered page-breaks in this paragraph. - - Most often an empty list, sometimes contains one page-break, but can contain - more than one is rare or contrived cases. - """ - return [RenderedPageBreak(lrpb, self) for lrpb in self._p.lastRenderedPageBreaks] - - @property - def runs(self) -> List[Run]: - """Sequence of |Run| instances corresponding to the elements in this - paragraph.""" - return [Run(r, self) for r in self._p.r_lst] - - @property - def style(self) -> ParagraphStyle | None: - """Read/Write. - - |_ParagraphStyle| object representing the style assigned to this paragraph. If - no explicit style is assigned to this paragraph, its value is the default - paragraph style for the document. A paragraph style name can be assigned in lieu - of a paragraph style object. Assigning |None| removes any applied style, making - its effective value the default paragraph style for the document. - """ - style_id = self._p.style - style = self.part.get_style(style_id, WD_STYLE_TYPE.PARAGRAPH) - return cast(ParagraphStyle, style) - - @style.setter - def style(self, style_or_name: str | ParagraphStyle | None): - style_id = self.part.get_style_id(style_or_name, WD_STYLE_TYPE.PARAGRAPH) - self._p.style = style_id - - @property - def text(self) -> str: - """The textual content of this paragraph. - - The text includes the visible-text portion of any hyperlinks in the paragraph. - Tabs and line breaks in the XML are mapped to ``\\t`` and ``\\n`` characters - respectively. - - Assigning text to this property causes all existing paragraph content to be - replaced with a single run containing the assigned text. A ``\\t`` character in - the text is mapped to a ```` element and each ``\\n`` or ``\\r`` - character is mapped to a line break. Paragraph-level formatting, such as style, - is preserved. All run-level formatting, such as bold or italic, is removed. - """ - return self._p.text - - @text.setter - def text(self, text: str | None): - self.clear() - self.add_run(text) - - def _insert_paragraph_before(self): - """Return a newly created paragraph, inserted directly before this paragraph.""" - p = self._p.add_p_before() - return Paragraph(p, self._parent) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/text/parfmt.py b/.venv-docs/lib/python3.12/site-packages/docx/text/parfmt.py deleted file mode 100644 index ea374373..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/text/parfmt.py +++ /dev/null @@ -1,286 +0,0 @@ -"""Paragraph-related proxy types.""" - -from docx.enum.text import WD_LINE_SPACING -from docx.shared import ElementProxy, Emu, Length, Pt, Twips, lazyproperty -from docx.text.tabstops import TabStops - - -class ParagraphFormat(ElementProxy): - """Provides access to paragraph formatting such as justification, indentation, line - spacing, space before and after, and widow/orphan control.""" - - @property - def alignment(self): - """A member of the :ref:`WdParagraphAlignment` enumeration specifying the - justification setting for this paragraph. - - A value of |None| indicates paragraph alignment is inherited from the style - hierarchy. - """ - pPr = self._element.pPr - if pPr is None: - return None - return pPr.jc_val - - @alignment.setter - def alignment(self, value): - pPr = self._element.get_or_add_pPr() - pPr.jc_val = value - - @property - def first_line_indent(self): - """|Length| value specifying the relative difference in indentation for the - first line of the paragraph. - - A positive value causes the first line to be indented. A negative value produces - a hanging indent. |None| indicates first line indentation is inherited from the - style hierarchy. - """ - pPr = self._element.pPr - if pPr is None: - return None - return pPr.first_line_indent - - @first_line_indent.setter - def first_line_indent(self, value): - pPr = self._element.get_or_add_pPr() - pPr.first_line_indent = value - - @property - def keep_together(self): - """|True| if the paragraph should be kept "in one piece" and not broken across a - page boundary when the document is rendered. - - |None| indicates its effective value is inherited from the style hierarchy. - """ - pPr = self._element.pPr - if pPr is None: - return None - return pPr.keepLines_val - - @keep_together.setter - def keep_together(self, value): - self._element.get_or_add_pPr().keepLines_val = value - - @property - def keep_with_next(self): - """|True| if the paragraph should be kept on the same page as the subsequent - paragraph when the document is rendered. - - For example, this property could be used to keep a section heading on the same - page as its first paragraph. |None| indicates its effective value is inherited - from the style hierarchy. - """ - pPr = self._element.pPr - if pPr is None: - return None - return pPr.keepNext_val - - @keep_with_next.setter - def keep_with_next(self, value): - self._element.get_or_add_pPr().keepNext_val = value - - @property - def left_indent(self): - """|Length| value specifying the space between the left margin and the left side - of the paragraph. - - |None| indicates the left indent value is inherited from the style hierarchy. - Use an |Inches| value object as a convenient way to apply indentation in units - of inches. - """ - pPr = self._element.pPr - if pPr is None: - return None - return pPr.ind_left - - @left_indent.setter - def left_indent(self, value): - pPr = self._element.get_or_add_pPr() - pPr.ind_left = value - - @property - def line_spacing(self): - """|float| or |Length| value specifying the space between baselines in - successive lines of the paragraph. - - A value of |None| indicates line spacing is inherited from the style hierarchy. - A float value, e.g. ``2.0`` or ``1.75``, indicates spacing is applied in - multiples of line heights. A |Length| value such as ``Pt(12)`` indicates spacing - is a fixed height. The |Pt| value class is a convenient way to apply line - spacing in units of points. Assigning |None| resets line spacing to inherit from - the style hierarchy. - """ - pPr = self._element.pPr - if pPr is None: - return None - return self._line_spacing(pPr.spacing_line, pPr.spacing_lineRule) - - @line_spacing.setter - def line_spacing(self, value): - pPr = self._element.get_or_add_pPr() - if value is None: - pPr.spacing_line = None - pPr.spacing_lineRule = None - elif isinstance(value, Length): - pPr.spacing_line = value - if pPr.spacing_lineRule != WD_LINE_SPACING.AT_LEAST: - pPr.spacing_lineRule = WD_LINE_SPACING.EXACTLY - else: - pPr.spacing_line = Emu(value * Twips(240)) - pPr.spacing_lineRule = WD_LINE_SPACING.MULTIPLE - - @property - def line_spacing_rule(self): - """A member of the :ref:`WdLineSpacing` enumeration indicating how the value of - :attr:`line_spacing` should be interpreted. - - Assigning any of the :ref:`WdLineSpacing` members :attr:`SINGLE`, - :attr:`DOUBLE`, or :attr:`ONE_POINT_FIVE` will cause the value of - :attr:`line_spacing` to be updated to produce the corresponding line spacing. - """ - pPr = self._element.pPr - if pPr is None: - return None - return self._line_spacing_rule(pPr.spacing_line, pPr.spacing_lineRule) - - @line_spacing_rule.setter - def line_spacing_rule(self, value): - pPr = self._element.get_or_add_pPr() - if value == WD_LINE_SPACING.SINGLE: - pPr.spacing_line = Twips(240) - pPr.spacing_lineRule = WD_LINE_SPACING.MULTIPLE - elif value == WD_LINE_SPACING.ONE_POINT_FIVE: - pPr.spacing_line = Twips(360) - pPr.spacing_lineRule = WD_LINE_SPACING.MULTIPLE - elif value == WD_LINE_SPACING.DOUBLE: - pPr.spacing_line = Twips(480) - pPr.spacing_lineRule = WD_LINE_SPACING.MULTIPLE - else: - pPr.spacing_lineRule = value - - @property - def page_break_before(self): - """|True| if the paragraph should appear at the top of the page following the - prior paragraph. - - |None| indicates its effective value is inherited from the style hierarchy. - """ - pPr = self._element.pPr - if pPr is None: - return None - return pPr.pageBreakBefore_val - - @page_break_before.setter - def page_break_before(self, value): - self._element.get_or_add_pPr().pageBreakBefore_val = value - - @property - def right_indent(self): - """|Length| value specifying the space between the right margin and the right - side of the paragraph. - - |None| indicates the right indent value is inherited from the style hierarchy. - Use a |Cm| value object as a convenient way to apply indentation in units of - centimeters. - """ - pPr = self._element.pPr - if pPr is None: - return None - return pPr.ind_right - - @right_indent.setter - def right_indent(self, value): - pPr = self._element.get_or_add_pPr() - pPr.ind_right = value - - @property - def space_after(self): - """|Length| value specifying the spacing to appear between this paragraph and - the subsequent paragraph. - - |None| indicates this value is inherited from the style hierarchy. |Length| - objects provide convenience properties, such as :attr:`~.Length.pt` and - :attr:`~.Length.inches`, that allow easy conversion to various length units. - """ - pPr = self._element.pPr - if pPr is None: - return None - return pPr.spacing_after - - @space_after.setter - def space_after(self, value): - self._element.get_or_add_pPr().spacing_after = value - - @property - def space_before(self): - """|Length| value specifying the spacing to appear between this paragraph and - the prior paragraph. - - |None| indicates this value is inherited from the style hierarchy. |Length| - objects provide convenience properties, such as :attr:`~.Length.pt` and - :attr:`~.Length.cm`, that allow easy conversion to various length units. - """ - pPr = self._element.pPr - if pPr is None: - return None - return pPr.spacing_before - - @space_before.setter - def space_before(self, value): - self._element.get_or_add_pPr().spacing_before = value - - @lazyproperty - def tab_stops(self): - """|TabStops| object providing access to the tab stops defined for this - paragraph format.""" - pPr = self._element.get_or_add_pPr() - return TabStops(pPr) - - @property - def widow_control(self): - """|True| if the first and last lines in the paragraph remain on the same page - as the rest of the paragraph when Word repaginates the document. - - |None| indicates its effective value is inherited from the style hierarchy. - """ - pPr = self._element.pPr - if pPr is None: - return None - return pPr.widowControl_val - - @widow_control.setter - def widow_control(self, value): - self._element.get_or_add_pPr().widowControl_val = value - - @staticmethod - def _line_spacing(spacing_line, spacing_lineRule): - """Return the line spacing value calculated from the combination of - `spacing_line` and `spacing_lineRule`. - - Returns a |float| number of lines when `spacing_lineRule` is - ``WD_LINE_SPACING.MULTIPLE``, otherwise a |Length| object of absolute line - height is returned. Returns |None| when `spacing_line` is |None|. - """ - if spacing_line is None: - return None - if spacing_lineRule == WD_LINE_SPACING.MULTIPLE: - return spacing_line / Pt(12) - return spacing_line - - @staticmethod - def _line_spacing_rule(line, lineRule): - """Return the line spacing rule value calculated from the combination of `line` - and `lineRule`. - - Returns special members of the :ref:`WdLineSpacing` enumeration when line - spacing is single, double, or 1.5 lines. - """ - if lineRule == WD_LINE_SPACING.MULTIPLE: - if line == Twips(240): - return WD_LINE_SPACING.SINGLE - if line == Twips(360): - return WD_LINE_SPACING.ONE_POINT_FIVE - if line == Twips(480): - return WD_LINE_SPACING.DOUBLE - return lineRule diff --git a/.venv-docs/lib/python3.12/site-packages/docx/text/run.py b/.venv-docs/lib/python3.12/site-packages/docx/text/run.py deleted file mode 100644 index 57ea31fa..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/text/run.py +++ /dev/null @@ -1,257 +0,0 @@ -"""Run-related proxy objects for python-docx, Run in particular.""" - -from __future__ import annotations - -from typing import IO, TYPE_CHECKING, Iterator, cast - -from docx.drawing import Drawing -from docx.enum.style import WD_STYLE_TYPE -from docx.enum.text import WD_BREAK -from docx.oxml.drawing import CT_Drawing -from docx.oxml.text.pagebreak import CT_LastRenderedPageBreak -from docx.shape import InlineShape -from docx.shared import StoryChild -from docx.styles.style import CharacterStyle -from docx.text.font import Font -from docx.text.pagebreak import RenderedPageBreak - -if TYPE_CHECKING: - import docx.types as t - from docx.enum.text import WD_UNDERLINE - from docx.oxml.text.run import CT_R, CT_Text - from docx.shared import Length - - -class Run(StoryChild): - """Proxy object wrapping `` element. - - Several of the properties on Run take a tri-state value, |True|, |False|, or |None|. - |True| and |False| correspond to on and off respectively. |None| indicates the - property is not specified directly on the run and its effective value is taken from - the style hierarchy. - """ - - def __init__(self, r: CT_R, parent: t.ProvidesStoryPart): - super().__init__(parent) - self._r = self._element = self.element = r - - def add_break(self, break_type: WD_BREAK = WD_BREAK.LINE): - """Add a break element of `break_type` to this run. - - `break_type` can take the values `WD_BREAK.LINE`, `WD_BREAK.PAGE`, and - `WD_BREAK.COLUMN` where `WD_BREAK` is imported from `docx.enum.text`. - `break_type` defaults to `WD_BREAK.LINE`. - """ - type_, clear = { - WD_BREAK.LINE: (None, None), - WD_BREAK.PAGE: ("page", None), - WD_BREAK.COLUMN: ("column", None), - WD_BREAK.LINE_CLEAR_LEFT: ("textWrapping", "left"), - WD_BREAK.LINE_CLEAR_RIGHT: ("textWrapping", "right"), - WD_BREAK.LINE_CLEAR_ALL: ("textWrapping", "all"), - }[break_type] - br = self._r.add_br() - if type_ is not None: - br.type = type_ - if clear is not None: - br.clear = clear - - def add_picture( - self, - image_path_or_stream: str | IO[bytes], - width: int | Length | None = None, - height: int | Length | None = None, - ) -> InlineShape: - """Return |InlineShape| containing image identified by `image_path_or_stream`. - - The picture is added to the end of this run. - - `image_path_or_stream` can be a path (a string) or a file-like object containing - a binary image. - - If neither width nor height is specified, the picture appears at - its native size. If only one is specified, it is used to compute a scaling - factor that is then applied to the unspecified dimension, preserving the aspect - ratio of the image. The native size of the picture is calculated using the dots- - per-inch (dpi) value specified in the image file, defaulting to 72 dpi if no - value is specified, as is often the case. - """ - inline = self.part.new_pic_inline(image_path_or_stream, width, height) - self._r.add_drawing(inline) - return InlineShape(inline) - - def add_tab(self) -> None: - """Add a ```` element at the end of the run, which Word interprets as a - tab character.""" - self._r.add_tab() - - def add_text(self, text: str): - """Returns a newly appended |_Text| object (corresponding to a new ```` - child element) to the run, containing `text`. - - Compare with the possibly more friendly approach of assigning text to the - :attr:`Run.text` property. - """ - t = self._r.add_t(text) - return _Text(t) - - @property - def bold(self) -> bool | None: - """Read/write tri-state value. - - When |True|, causes the text of the run to appear in bold face. When |False|, - the text unconditionally appears non-bold. When |None| the bold setting for this - run is inherited from the style hierarchy. - """ - return self.font.bold - - @bold.setter - def bold(self, value: bool | None): - self.font.bold = value - - def clear(self): - """Return reference to this run after removing all its content. - - All run formatting is preserved. - """ - self._r.clear_content() - return self - - @property - def contains_page_break(self) -> bool: - """`True` when one or more rendered page-breaks occur in this run. - - Note that "hard" page-breaks inserted by the author are not included. A hard - page-break gives rise to a rendered page-break in the right position so if those - were included that page-break would be "double-counted". - - It would be very rare for multiple rendered page-breaks to occur in a single - run, but it is possible. - """ - return bool(self._r.lastRenderedPageBreaks) - - @property - def font(self) -> Font: - """The |Font| object providing access to the character formatting properties for - this run, such as font name and size.""" - return Font(self._element) - - @property - def italic(self) -> bool | None: - """Read/write tri-state value. - - When |True|, causes the text of the run to appear in italics. When |False|, the - text unconditionally appears non-italic. When |None| the italic setting for this - run is inherited from the style hierarchy. - """ - return self.font.italic - - @italic.setter - def italic(self, value: bool | None): - self.font.italic = value - - def iter_inner_content(self) -> Iterator[str | Drawing | RenderedPageBreak]: - """Generate the content-items in this run in the order they appear. - - NOTE: only content-types currently supported by `python-docx` are generated. In - this version, that is text and rendered page-breaks. Drawing is included but - currently only provides access to its XML element (CT_Drawing) on its - `._drawing` attribute. `Drawing` attributes and methods may be expanded in - future releases. - - There are a number of element-types that can appear inside a run, but most of - those (w:br, w:cr, w:noBreakHyphen, w:t, w:tab) have a clear plain-text - equivalent. Any contiguous range of such elements is generated as a single - `str`. Rendered page-break and drawing elements are generated individually. Any - other elements are ignored. - """ - for item in self._r.inner_content_items: - if isinstance(item, str): - yield item - elif isinstance(item, CT_LastRenderedPageBreak): - yield RenderedPageBreak(item, self) - elif isinstance(item, CT_Drawing): # pyright: ignore[reportUnnecessaryIsInstance] - yield Drawing(item, self) - - def mark_comment_range(self, last_run: Run, comment_id: int) -> None: - """Mark the range of runs from this run to `last_run` (inclusive) as belonging to a comment. - - `comment_id` identfies the comment that references this range. - """ - # -- insert `w:commentRangeStart` with `comment_id` before this (first) run -- - self._r.insert_comment_range_start_above(comment_id) - - # -- insert `w:commentRangeEnd` and `w:commentReference` run with `comment_id` after - # -- `last_run` - last_run._r.insert_comment_range_end_and_reference_below(comment_id) - - @property - def style(self) -> CharacterStyle: - """Read/write. - - A |CharacterStyle| object representing the character style applied to this run. - The default character style for the document (often `Default Character Font`) is - returned if the run has no directly-applied character style. Setting this - property to |None| removes any directly-applied character style. - """ - style_id = self._r.style - return cast(CharacterStyle, self.part.get_style(style_id, WD_STYLE_TYPE.CHARACTER)) - - @style.setter - def style(self, style_or_name: str | CharacterStyle | None): - style_id = self.part.get_style_id(style_or_name, WD_STYLE_TYPE.CHARACTER) - self._r.style = style_id - - @property - def text(self) -> str: - """String formed by concatenating the text equivalent of each run. - - Each `` element adds the text characters it contains. A `` element - adds a `\\t` character. A `` or `` element each add a `\\n` - character. Note that a `` element can indicate a page break or column - break as well as a line break. Only line-break `` elements translate to - a `\\n` character. Others are ignored. All other content child elements, such as - ``, are ignored. - - Assigning text to this property has the reverse effect, translating each `\\t` - character to a `` element and each `\\n` or `\\r` character to a - `` element. Any existing run content is replaced. Run formatting is - preserved. - """ - return self._r.text - - @text.setter - def text(self, text: str): - self._r.text = text - - @property - def underline(self) -> bool | WD_UNDERLINE | None: - """The underline style for this |Run|. - - Value is one of |None|, |True|, |False|, or a member of :ref:`WdUnderline`. - - A value of |None| indicates the run has no directly-applied underline value and - so will inherit the underline value of its containing paragraph. Assigning - |None| to this property removes any directly-applied underline value. - - A value of |False| indicates a directly-applied setting of no underline, - overriding any inherited value. - - A value of |True| indicates single underline. - - The values from :ref:`WdUnderline` are used to specify other outline styles such - as double, wavy, and dotted. - """ - return self.font.underline - - @underline.setter - def underline(self, value: bool | WD_UNDERLINE | None): - self.font.underline = value - - -class _Text: - """Proxy object wrapping `` element.""" - - def __init__(self, t_elm: CT_Text): - super(_Text, self).__init__() - self._t = t_elm diff --git a/.venv-docs/lib/python3.12/site-packages/docx/text/tabstops.py b/.venv-docs/lib/python3.12/site-packages/docx/text/tabstops.py deleted file mode 100644 index 0f8c22c9..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/text/tabstops.py +++ /dev/null @@ -1,123 +0,0 @@ -"""Tabstop-related proxy types.""" - -from docx.enum.text import WD_TAB_ALIGNMENT, WD_TAB_LEADER -from docx.shared import ElementProxy - - -class TabStops(ElementProxy): - """A sequence of |TabStop| objects providing access to the tab stops of a paragraph - or paragraph style. - - Supports iteration, indexed access, del, and len(). It is accesed using the - :attr:`~.ParagraphFormat.tab_stops` property of ParagraphFormat; it is not intended - to be constructed directly. - """ - - def __init__(self, element): - super(TabStops, self).__init__(element, None) - self._pPr = element - - def __delitem__(self, idx): - """Remove the tab at offset `idx` in this sequence.""" - tabs = self._pPr.tabs - try: - tabs.remove(tabs[idx]) - except (AttributeError, IndexError): - raise IndexError("tab index out of range") - - if len(tabs) == 0: - self._pPr.remove(tabs) - - def __getitem__(self, idx): - """Enables list-style access by index.""" - tabs = self._pPr.tabs - if tabs is None: - raise IndexError("TabStops object is empty") - tab = tabs.tab_lst[idx] - return TabStop(tab) - - def __iter__(self): - """Generate a TabStop object for each of the w:tab elements, in XML document - order.""" - tabs = self._pPr.tabs - if tabs is not None: - for tab in tabs.tab_lst: - yield TabStop(tab) - - def __len__(self): - tabs = self._pPr.tabs - if tabs is None: - return 0 - return len(tabs.tab_lst) - - def add_tab_stop(self, position, alignment=WD_TAB_ALIGNMENT.LEFT, leader=WD_TAB_LEADER.SPACES): - """Add a new tab stop at `position`, a |Length| object specifying the location - of the tab stop relative to the paragraph edge. - - A negative `position` value is valid and appears in hanging indentation. Tab - alignment defaults to left, but may be specified by passing a member of the - :ref:`WdTabAlignment` enumeration as `alignment`. An optional leader character - can be specified by passing a member of the :ref:`WdTabLeader` enumeration as - `leader`. - """ - tabs = self._pPr.get_or_add_tabs() - tab = tabs.insert_tab_in_order(position, alignment, leader) - return TabStop(tab) - - def clear_all(self): - """Remove all custom tab stops.""" - self._pPr._remove_tabs() - - -class TabStop(ElementProxy): - """An individual tab stop applying to a paragraph or style. - - Accessed using list semantics on its containing |TabStops| object. - """ - - def __init__(self, element): - super(TabStop, self).__init__(element, None) - self._tab = element - - @property - def alignment(self): - """A member of :ref:`WdTabAlignment` specifying the alignment setting for this - tab stop. - - Read/write. - """ - return self._tab.val - - @alignment.setter - def alignment(self, value): - self._tab.val = value - - @property - def leader(self): - """A member of :ref:`WdTabLeader` specifying a repeating character used as a - "leader", filling in the space spanned by this tab. - - Assigning |None| produces the same result as assigning `WD_TAB_LEADER.SPACES`. - Read/write. - """ - return self._tab.leader - - @leader.setter - def leader(self, value): - self._tab.leader = value - - @property - def position(self): - """A |Length| object representing the distance of this tab stop from the inside - edge of the paragraph. - - May be positive or negative. Read/write. - """ - return self._tab.pos - - @position.setter - def position(self, value): - tab = self._tab - tabs = tab.getparent() - self._tab = tabs.insert_tab_in_order(value, tab.val, tab.leader) - tabs.remove(tab) diff --git a/.venv-docs/lib/python3.12/site-packages/docx/types.py b/.venv-docs/lib/python3.12/site-packages/docx/types.py deleted file mode 100644 index 06d1a571..00000000 --- a/.venv-docs/lib/python3.12/site-packages/docx/types.py +++ /dev/null @@ -1,34 +0,0 @@ -"""Abstract types used by `python-docx`.""" - -from __future__ import annotations - -from typing import TYPE_CHECKING - -from typing_extensions import Protocol - -if TYPE_CHECKING: - from docx.opc.part import XmlPart - from docx.parts.story import StoryPart - - -class ProvidesStoryPart(Protocol): - """An object that provides access to the StoryPart. - - This type is for objects that have a story part like document or header as their - root part. - """ - - @property - def part(self) -> StoryPart: ... - - -class ProvidesXmlPart(Protocol): - """An object that provides access to its XmlPart. - - This type is for objects that need access to their part but it either isn't a - StoryPart or they don't care, possibly because they just need access to the package - or related parts. - """ - - @property - def part(self) -> XmlPart: ... diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/__init__.py b/.venv-docs/lib/python3.12/site-packages/fontTools/__init__.py deleted file mode 100644 index 7d2f5af5..00000000 --- a/.venv-docs/lib/python3.12/site-packages/fontTools/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -import logging -from fontTools.misc.loggingTools import configLogger - -log = logging.getLogger(__name__) - -version = __version__ = "4.60.1" - -__all__ = ["version", "log", "configLogger"] diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/__main__.py b/.venv-docs/lib/python3.12/site-packages/fontTools/__main__.py deleted file mode 100644 index 7c74ad3c..00000000 --- a/.venv-docs/lib/python3.12/site-packages/fontTools/__main__.py +++ /dev/null @@ -1,35 +0,0 @@ -import sys - - -def main(args=None): - if args is None: - args = sys.argv[1:] - - # TODO Handle library-wide options. Eg.: - # --unicodedata - # --verbose / other logging stuff - - # TODO Allow a way to run arbitrary modules? Useful for setting - # library-wide options and calling another library. Eg.: - # - # $ fonttools --unicodedata=... fontmake ... - # - # This allows for a git-like command where thirdparty commands - # can be added. Should we just try importing the fonttools - # module first and try without if it fails? - - if len(sys.argv) < 2: - sys.argv.append("help") - if sys.argv[1] == "-h" or sys.argv[1] == "--help": - sys.argv[1] = "help" - mod = "fontTools." + sys.argv[1] - sys.argv[1] = sys.argv[0] + " " + sys.argv[1] - del sys.argv[0] - - import runpy - - runpy.run_module(mod, run_name="__main__") - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/__init__.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index fba82b82..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/__main__.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/__main__.cpython-312.pyc deleted file mode 100644 index 4b7dbc49..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/__main__.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/afmLib.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/afmLib.cpython-312.pyc deleted file mode 100644 index 861a41a7..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/afmLib.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/agl.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/agl.cpython-312.pyc deleted file mode 100644 index af1a5d36..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/agl.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/annotations.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/annotations.cpython-312.pyc deleted file mode 100644 index 1e512a9d..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/annotations.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/fontBuilder.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/fontBuilder.cpython-312.pyc deleted file mode 100644 index 4c92bcaf..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/fontBuilder.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/help.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/help.cpython-312.pyc deleted file mode 100644 index 0e667d4f..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/help.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/tfmLib.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/tfmLib.cpython-312.pyc deleted file mode 100644 index 53b52ed5..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/tfmLib.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/ttx.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/ttx.cpython-312.pyc deleted file mode 100644 index 0b099593..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/ttx.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/unicode.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/unicode.cpython-312.pyc deleted file mode 100644 index 3035e366..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/__pycache__/unicode.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/afmLib.py b/.venv-docs/lib/python3.12/site-packages/fontTools/afmLib.py deleted file mode 100644 index 0aabf7f6..00000000 --- a/.venv-docs/lib/python3.12/site-packages/fontTools/afmLib.py +++ /dev/null @@ -1,439 +0,0 @@ -"""Module for reading and writing AFM (Adobe Font Metrics) files. - -Note that this has been designed to read in AFM files generated by Fontographer -and has not been tested on many other files. In particular, it does not -implement the whole Adobe AFM specification [#f1]_ but, it should read most -"common" AFM files. - -Here is an example of using `afmLib` to read, modify and write an AFM file: - - >>> from fontTools.afmLib import AFM - >>> f = AFM("Tests/afmLib/data/TestAFM.afm") - >>> - >>> # Accessing a pair gets you the kern value - >>> f[("V","A")] - -60 - >>> - >>> # Accessing a glyph name gets you metrics - >>> f["A"] - (65, 668, (8, -25, 660, 666)) - >>> # (charnum, width, bounding box) - >>> - >>> # Accessing an attribute gets you metadata - >>> f.FontName - 'TestFont-Regular' - >>> f.FamilyName - 'TestFont' - >>> f.Weight - 'Regular' - >>> f.XHeight - 500 - >>> f.Ascender - 750 - >>> - >>> # Attributes and items can also be set - >>> f[("A","V")] = -150 # Tighten kerning - >>> f.FontName = "TestFont Squished" - >>> - >>> # And the font written out again (remove the # in front) - >>> #f.write("testfont-squished.afm") - -.. rubric:: Footnotes - -.. [#f1] `Adobe Technote 5004 `_, - Adobe Font Metrics File Format Specification. - -""" - -import re - -# every single line starts with a "word" -identifierRE = re.compile(r"^([A-Za-z]+).*") - -# regular expression to parse char lines -charRE = re.compile( - r"(-?\d+)" # charnum - r"\s*;\s*WX\s+" # ; WX - r"(-?\d+)" # width - r"\s*;\s*N\s+" # ; N - r"([.A-Za-z0-9_]+)" # charname - r"\s*;\s*B\s+" # ; B - r"(-?\d+)" # left - r"\s+" - r"(-?\d+)" # bottom - r"\s+" - r"(-?\d+)" # right - r"\s+" - r"(-?\d+)" # top - r"\s*;\s*" # ; -) - -# regular expression to parse kerning lines -kernRE = re.compile( - r"([.A-Za-z0-9_]+)" # leftchar - r"\s+" - r"([.A-Za-z0-9_]+)" # rightchar - r"\s+" - r"(-?\d+)" # value - r"\s*" -) - -# regular expressions to parse composite info lines of the form: -# Aacute 2 ; PCC A 0 0 ; PCC acute 182 211 ; -compositeRE = re.compile( - r"([.A-Za-z0-9_]+)" # char name - r"\s+" - r"(\d+)" # number of parts - r"\s*;\s*" -) -componentRE = re.compile( - r"PCC\s+" # PPC - r"([.A-Za-z0-9_]+)" # base char name - r"\s+" - r"(-?\d+)" # x offset - r"\s+" - r"(-?\d+)" # y offset - r"\s*;\s*" -) - -preferredAttributeOrder = [ - "FontName", - "FullName", - "FamilyName", - "Weight", - "ItalicAngle", - "IsFixedPitch", - "FontBBox", - "UnderlinePosition", - "UnderlineThickness", - "Version", - "Notice", - "EncodingScheme", - "CapHeight", - "XHeight", - "Ascender", - "Descender", -] - - -class error(Exception): - pass - - -class AFM(object): - _attrs = None - - _keywords = [ - "StartFontMetrics", - "EndFontMetrics", - "StartCharMetrics", - "EndCharMetrics", - "StartKernData", - "StartKernPairs", - "EndKernPairs", - "EndKernData", - "StartComposites", - "EndComposites", - ] - - def __init__(self, path=None): - """AFM file reader. - - Instantiating an object with a path name will cause the file to be opened, - read, and parsed. Alternatively the path can be left unspecified, and a - file can be parsed later with the :meth:`read` method.""" - self._attrs = {} - self._chars = {} - self._kerning = {} - self._index = {} - self._comments = [] - self._composites = {} - if path is not None: - self.read(path) - - def read(self, path): - """Opens, reads and parses a file.""" - lines = readlines(path) - for line in lines: - if not line.strip(): - continue - m = identifierRE.match(line) - if m is None: - raise error("syntax error in AFM file: " + repr(line)) - - pos = m.regs[1][1] - word = line[:pos] - rest = line[pos:].strip() - if word in self._keywords: - continue - if word == "C": - self.parsechar(rest) - elif word == "KPX": - self.parsekernpair(rest) - elif word == "CC": - self.parsecomposite(rest) - else: - self.parseattr(word, rest) - - def parsechar(self, rest): - m = charRE.match(rest) - if m is None: - raise error("syntax error in AFM file: " + repr(rest)) - things = [] - for fr, to in m.regs[1:]: - things.append(rest[fr:to]) - charname = things[2] - del things[2] - charnum, width, l, b, r, t = (int(thing) for thing in things) - self._chars[charname] = charnum, width, (l, b, r, t) - - def parsekernpair(self, rest): - m = kernRE.match(rest) - if m is None: - raise error("syntax error in AFM file: " + repr(rest)) - things = [] - for fr, to in m.regs[1:]: - things.append(rest[fr:to]) - leftchar, rightchar, value = things - value = int(value) - self._kerning[(leftchar, rightchar)] = value - - def parseattr(self, word, rest): - if word == "FontBBox": - l, b, r, t = [int(thing) for thing in rest.split()] - self._attrs[word] = l, b, r, t - elif word == "Comment": - self._comments.append(rest) - else: - try: - value = int(rest) - except (ValueError, OverflowError): - self._attrs[word] = rest - else: - self._attrs[word] = value - - def parsecomposite(self, rest): - m = compositeRE.match(rest) - if m is None: - raise error("syntax error in AFM file: " + repr(rest)) - charname = m.group(1) - ncomponents = int(m.group(2)) - rest = rest[m.regs[0][1] :] - components = [] - while True: - m = componentRE.match(rest) - if m is None: - raise error("syntax error in AFM file: " + repr(rest)) - basechar = m.group(1) - xoffset = int(m.group(2)) - yoffset = int(m.group(3)) - components.append((basechar, xoffset, yoffset)) - rest = rest[m.regs[0][1] :] - if not rest: - break - assert len(components) == ncomponents - self._composites[charname] = components - - def write(self, path, sep="\r"): - """Writes out an AFM font to the given path.""" - import time - - lines = [ - "StartFontMetrics 2.0", - "Comment Generated by afmLib; at %s" - % (time.strftime("%m/%d/%Y %H:%M:%S", time.localtime(time.time()))), - ] - - # write comments, assuming (possibly wrongly!) they should - # all appear at the top - for comment in self._comments: - lines.append("Comment " + comment) - - # write attributes, first the ones we know about, in - # a preferred order - attrs = self._attrs - for attr in preferredAttributeOrder: - if attr in attrs: - value = attrs[attr] - if attr == "FontBBox": - value = "%s %s %s %s" % value - lines.append(attr + " " + str(value)) - # then write the attributes we don't know about, - # in alphabetical order - items = sorted(attrs.items()) - for attr, value in items: - if attr in preferredAttributeOrder: - continue - lines.append(attr + " " + str(value)) - - # write char metrics - lines.append("StartCharMetrics " + repr(len(self._chars))) - items = [ - (charnum, (charname, width, box)) - for charname, (charnum, width, box) in self._chars.items() - ] - - def myKey(a): - """Custom key function to make sure unencoded chars (-1) - end up at the end of the list after sorting.""" - if a[0] == -1: - a = (0xFFFF,) + a[1:] # 0xffff is an arbitrary large number - return a - - items.sort(key=myKey) - - for charnum, (charname, width, (l, b, r, t)) in items: - lines.append( - "C %d ; WX %d ; N %s ; B %d %d %d %d ;" - % (charnum, width, charname, l, b, r, t) - ) - lines.append("EndCharMetrics") - - # write kerning info - lines.append("StartKernData") - lines.append("StartKernPairs " + repr(len(self._kerning))) - items = sorted(self._kerning.items()) - for (leftchar, rightchar), value in items: - lines.append("KPX %s %s %d" % (leftchar, rightchar, value)) - lines.append("EndKernPairs") - lines.append("EndKernData") - - if self._composites: - composites = sorted(self._composites.items()) - lines.append("StartComposites %s" % len(self._composites)) - for charname, components in composites: - line = "CC %s %s ;" % (charname, len(components)) - for basechar, xoffset, yoffset in components: - line = line + " PCC %s %s %s ;" % (basechar, xoffset, yoffset) - lines.append(line) - lines.append("EndComposites") - - lines.append("EndFontMetrics") - - writelines(path, lines, sep) - - def has_kernpair(self, pair): - """Returns `True` if the given glyph pair (specified as a tuple) exists - in the kerning dictionary.""" - return pair in self._kerning - - def kernpairs(self): - """Returns a list of all kern pairs in the kerning dictionary.""" - return list(self._kerning.keys()) - - def has_char(self, char): - """Returns `True` if the given glyph exists in the font.""" - return char in self._chars - - def chars(self): - """Returns a list of all glyph names in the font.""" - return list(self._chars.keys()) - - def comments(self): - """Returns all comments from the file.""" - return self._comments - - def addComment(self, comment): - """Adds a new comment to the file.""" - self._comments.append(comment) - - def addComposite(self, glyphName, components): - """Specifies that the glyph `glyphName` is made up of the given components. - The components list should be of the following form:: - - [ - (glyphname, xOffset, yOffset), - ... - ] - - """ - self._composites[glyphName] = components - - def __getattr__(self, attr): - if attr in self._attrs: - return self._attrs[attr] - else: - raise AttributeError(attr) - - def __setattr__(self, attr, value): - # all attrs *not* starting with "_" are consider to be AFM keywords - if attr[:1] == "_": - self.__dict__[attr] = value - else: - self._attrs[attr] = value - - def __delattr__(self, attr): - # all attrs *not* starting with "_" are consider to be AFM keywords - if attr[:1] == "_": - try: - del self.__dict__[attr] - except KeyError: - raise AttributeError(attr) - else: - try: - del self._attrs[attr] - except KeyError: - raise AttributeError(attr) - - def __getitem__(self, key): - if isinstance(key, tuple): - # key is a tuple, return the kernpair - return self._kerning[key] - else: - # return the metrics instead - return self._chars[key] - - def __setitem__(self, key, value): - if isinstance(key, tuple): - # key is a tuple, set kernpair - self._kerning[key] = value - else: - # set char metrics - self._chars[key] = value - - def __delitem__(self, key): - if isinstance(key, tuple): - # key is a tuple, del kernpair - del self._kerning[key] - else: - # del char metrics - del self._chars[key] - - def __repr__(self): - if hasattr(self, "FullName"): - return "" % self.FullName - else: - return "" % id(self) - - -def readlines(path): - with open(path, "r", encoding="ascii") as f: - data = f.read() - return data.splitlines() - - -def writelines(path, lines, sep="\r"): - with open(path, "w", encoding="ascii", newline=sep) as f: - f.write("\n".join(lines) + "\n") - - -if __name__ == "__main__": - import EasyDialogs - - path = EasyDialogs.AskFileForOpen() - if path: - afm = AFM(path) - char = "A" - if afm.has_char(char): - print(afm[char]) # print charnum, width and boundingbox - pair = ("A", "V") - if afm.has_kernpair(pair): - print(afm[pair]) # print kerning value for pair - print(afm.Version) # various other afm entries have become attributes - print(afm.Weight) - # afm.comments() returns a list of all Comment lines found in the AFM - print(afm.comments()) - # print afm.chars() - # print afm.kernpairs() - print(afm) - afm.write(path + ".muck") diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/agl.py b/.venv-docs/lib/python3.12/site-packages/fontTools/agl.py deleted file mode 100644 index d6994628..00000000 --- a/.venv-docs/lib/python3.12/site-packages/fontTools/agl.py +++ /dev/null @@ -1,5233 +0,0 @@ -# -*- coding: utf-8 -*- -# The tables below are taken from -# https://github.com/adobe-type-tools/agl-aglfn/raw/4036a9ca80a62f64f9de4f7321a9a045ad0ecfd6/glyphlist.txt -# and -# https://github.com/adobe-type-tools/agl-aglfn/raw/4036a9ca80a62f64f9de4f7321a9a045ad0ecfd6/aglfn.txt -""" -Interface to the Adobe Glyph List - -This module exists to convert glyph names from the Adobe Glyph List -to their Unicode equivalents. Example usage: - - >>> from fontTools.agl import toUnicode - >>> toUnicode("nahiragana") - 'な' - -It also contains two dictionaries, ``UV2AGL`` and ``AGL2UV``, which map from -Unicode codepoints to AGL names and vice versa: - - >>> import fontTools - >>> fontTools.agl.UV2AGL[ord("?")] - 'question' - >>> fontTools.agl.AGL2UV["wcircumflex"] - 373 - -This is used by fontTools when it has to construct glyph names for a font which -doesn't include any (e.g. format 3.0 post tables). -""" - -from fontTools.misc.textTools import tostr -import re - - -_aglText = """\ -# ----------------------------------------------------------- -# Copyright 2002-2019 Adobe (http://www.adobe.com/). -# -# Redistribution and use in source and binary forms, with or -# without modification, are permitted provided that the -# following conditions are met: -# -# Redistributions of source code must retain the above -# copyright notice, this list of conditions and the following -# disclaimer. -# -# Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following -# disclaimer in the documentation and/or other materials -# provided with the distribution. -# -# Neither the name of Adobe nor the names of its contributors -# may be used to endorse or promote products derived from this -# software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND -# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# ----------------------------------------------------------- -# Name: Adobe Glyph List -# Table version: 2.0 -# Date: September 20, 2002 -# URL: https://github.com/adobe-type-tools/agl-aglfn -# -# Format: two semicolon-delimited fields: -# (1) glyph name--upper/lowercase letters and digits -# (2) Unicode scalar value--four uppercase hexadecimal digits -# -A;0041 -AE;00C6 -AEacute;01FC -AEmacron;01E2 -AEsmall;F7E6 -Aacute;00C1 -Aacutesmall;F7E1 -Abreve;0102 -Abreveacute;1EAE -Abrevecyrillic;04D0 -Abrevedotbelow;1EB6 -Abrevegrave;1EB0 -Abrevehookabove;1EB2 -Abrevetilde;1EB4 -Acaron;01CD -Acircle;24B6 -Acircumflex;00C2 -Acircumflexacute;1EA4 -Acircumflexdotbelow;1EAC -Acircumflexgrave;1EA6 -Acircumflexhookabove;1EA8 -Acircumflexsmall;F7E2 -Acircumflextilde;1EAA -Acute;F6C9 -Acutesmall;F7B4 -Acyrillic;0410 -Adblgrave;0200 -Adieresis;00C4 -Adieresiscyrillic;04D2 -Adieresismacron;01DE -Adieresissmall;F7E4 -Adotbelow;1EA0 -Adotmacron;01E0 -Agrave;00C0 -Agravesmall;F7E0 -Ahookabove;1EA2 -Aiecyrillic;04D4 -Ainvertedbreve;0202 -Alpha;0391 -Alphatonos;0386 -Amacron;0100 -Amonospace;FF21 -Aogonek;0104 -Aring;00C5 -Aringacute;01FA -Aringbelow;1E00 -Aringsmall;F7E5 -Asmall;F761 -Atilde;00C3 -Atildesmall;F7E3 -Aybarmenian;0531 -B;0042 -Bcircle;24B7 -Bdotaccent;1E02 -Bdotbelow;1E04 -Becyrillic;0411 -Benarmenian;0532 -Beta;0392 -Bhook;0181 -Blinebelow;1E06 -Bmonospace;FF22 -Brevesmall;F6F4 -Bsmall;F762 -Btopbar;0182 -C;0043 -Caarmenian;053E -Cacute;0106 -Caron;F6CA -Caronsmall;F6F5 -Ccaron;010C -Ccedilla;00C7 -Ccedillaacute;1E08 -Ccedillasmall;F7E7 -Ccircle;24B8 -Ccircumflex;0108 -Cdot;010A -Cdotaccent;010A -Cedillasmall;F7B8 -Chaarmenian;0549 -Cheabkhasiancyrillic;04BC -Checyrillic;0427 -Chedescenderabkhasiancyrillic;04BE -Chedescendercyrillic;04B6 -Chedieresiscyrillic;04F4 -Cheharmenian;0543 -Chekhakassiancyrillic;04CB -Cheverticalstrokecyrillic;04B8 -Chi;03A7 -Chook;0187 -Circumflexsmall;F6F6 -Cmonospace;FF23 -Coarmenian;0551 -Csmall;F763 -D;0044 -DZ;01F1 -DZcaron;01C4 -Daarmenian;0534 -Dafrican;0189 -Dcaron;010E -Dcedilla;1E10 -Dcircle;24B9 -Dcircumflexbelow;1E12 -Dcroat;0110 -Ddotaccent;1E0A -Ddotbelow;1E0C -Decyrillic;0414 -Deicoptic;03EE -Delta;2206 -Deltagreek;0394 -Dhook;018A -Dieresis;F6CB -DieresisAcute;F6CC -DieresisGrave;F6CD -Dieresissmall;F7A8 -Digammagreek;03DC -Djecyrillic;0402 -Dlinebelow;1E0E -Dmonospace;FF24 -Dotaccentsmall;F6F7 -Dslash;0110 -Dsmall;F764 -Dtopbar;018B -Dz;01F2 -Dzcaron;01C5 -Dzeabkhasiancyrillic;04E0 -Dzecyrillic;0405 -Dzhecyrillic;040F -E;0045 -Eacute;00C9 -Eacutesmall;F7E9 -Ebreve;0114 -Ecaron;011A -Ecedillabreve;1E1C -Echarmenian;0535 -Ecircle;24BA -Ecircumflex;00CA -Ecircumflexacute;1EBE -Ecircumflexbelow;1E18 -Ecircumflexdotbelow;1EC6 -Ecircumflexgrave;1EC0 -Ecircumflexhookabove;1EC2 -Ecircumflexsmall;F7EA -Ecircumflextilde;1EC4 -Ecyrillic;0404 -Edblgrave;0204 -Edieresis;00CB -Edieresissmall;F7EB -Edot;0116 -Edotaccent;0116 -Edotbelow;1EB8 -Efcyrillic;0424 -Egrave;00C8 -Egravesmall;F7E8 -Eharmenian;0537 -Ehookabove;1EBA -Eightroman;2167 -Einvertedbreve;0206 -Eiotifiedcyrillic;0464 -Elcyrillic;041B -Elevenroman;216A -Emacron;0112 -Emacronacute;1E16 -Emacrongrave;1E14 -Emcyrillic;041C -Emonospace;FF25 -Encyrillic;041D -Endescendercyrillic;04A2 -Eng;014A -Enghecyrillic;04A4 -Enhookcyrillic;04C7 -Eogonek;0118 -Eopen;0190 -Epsilon;0395 -Epsilontonos;0388 -Ercyrillic;0420 -Ereversed;018E -Ereversedcyrillic;042D -Escyrillic;0421 -Esdescendercyrillic;04AA -Esh;01A9 -Esmall;F765 -Eta;0397 -Etarmenian;0538 -Etatonos;0389 -Eth;00D0 -Ethsmall;F7F0 -Etilde;1EBC -Etildebelow;1E1A -Euro;20AC -Ezh;01B7 -Ezhcaron;01EE -Ezhreversed;01B8 -F;0046 -Fcircle;24BB -Fdotaccent;1E1E -Feharmenian;0556 -Feicoptic;03E4 -Fhook;0191 -Fitacyrillic;0472 -Fiveroman;2164 -Fmonospace;FF26 -Fourroman;2163 -Fsmall;F766 -G;0047 -GBsquare;3387 -Gacute;01F4 -Gamma;0393 -Gammaafrican;0194 -Gangiacoptic;03EA -Gbreve;011E -Gcaron;01E6 -Gcedilla;0122 -Gcircle;24BC -Gcircumflex;011C -Gcommaaccent;0122 -Gdot;0120 -Gdotaccent;0120 -Gecyrillic;0413 -Ghadarmenian;0542 -Ghemiddlehookcyrillic;0494 -Ghestrokecyrillic;0492 -Gheupturncyrillic;0490 -Ghook;0193 -Gimarmenian;0533 -Gjecyrillic;0403 -Gmacron;1E20 -Gmonospace;FF27 -Grave;F6CE -Gravesmall;F760 -Gsmall;F767 -Gsmallhook;029B -Gstroke;01E4 -H;0048 -H18533;25CF -H18543;25AA -H18551;25AB -H22073;25A1 -HPsquare;33CB -Haabkhasiancyrillic;04A8 -Hadescendercyrillic;04B2 -Hardsigncyrillic;042A -Hbar;0126 -Hbrevebelow;1E2A -Hcedilla;1E28 -Hcircle;24BD -Hcircumflex;0124 -Hdieresis;1E26 -Hdotaccent;1E22 -Hdotbelow;1E24 -Hmonospace;FF28 -Hoarmenian;0540 -Horicoptic;03E8 -Hsmall;F768 -Hungarumlaut;F6CF -Hungarumlautsmall;F6F8 -Hzsquare;3390 -I;0049 -IAcyrillic;042F -IJ;0132 -IUcyrillic;042E -Iacute;00CD -Iacutesmall;F7ED -Ibreve;012C -Icaron;01CF -Icircle;24BE -Icircumflex;00CE -Icircumflexsmall;F7EE -Icyrillic;0406 -Idblgrave;0208 -Idieresis;00CF -Idieresisacute;1E2E -Idieresiscyrillic;04E4 -Idieresissmall;F7EF -Idot;0130 -Idotaccent;0130 -Idotbelow;1ECA -Iebrevecyrillic;04D6 -Iecyrillic;0415 -Ifraktur;2111 -Igrave;00CC -Igravesmall;F7EC -Ihookabove;1EC8 -Iicyrillic;0418 -Iinvertedbreve;020A -Iishortcyrillic;0419 -Imacron;012A -Imacroncyrillic;04E2 -Imonospace;FF29 -Iniarmenian;053B -Iocyrillic;0401 -Iogonek;012E -Iota;0399 -Iotaafrican;0196 -Iotadieresis;03AA -Iotatonos;038A -Ismall;F769 -Istroke;0197 -Itilde;0128 -Itildebelow;1E2C -Izhitsacyrillic;0474 -Izhitsadblgravecyrillic;0476 -J;004A -Jaarmenian;0541 -Jcircle;24BF -Jcircumflex;0134 -Jecyrillic;0408 -Jheharmenian;054B -Jmonospace;FF2A -Jsmall;F76A -K;004B -KBsquare;3385 -KKsquare;33CD -Kabashkircyrillic;04A0 -Kacute;1E30 -Kacyrillic;041A -Kadescendercyrillic;049A -Kahookcyrillic;04C3 -Kappa;039A -Kastrokecyrillic;049E -Kaverticalstrokecyrillic;049C -Kcaron;01E8 -Kcedilla;0136 -Kcircle;24C0 -Kcommaaccent;0136 -Kdotbelow;1E32 -Keharmenian;0554 -Kenarmenian;053F -Khacyrillic;0425 -Kheicoptic;03E6 -Khook;0198 -Kjecyrillic;040C -Klinebelow;1E34 -Kmonospace;FF2B -Koppacyrillic;0480 -Koppagreek;03DE -Ksicyrillic;046E -Ksmall;F76B -L;004C -LJ;01C7 -LL;F6BF -Lacute;0139 -Lambda;039B -Lcaron;013D -Lcedilla;013B -Lcircle;24C1 -Lcircumflexbelow;1E3C -Lcommaaccent;013B -Ldot;013F -Ldotaccent;013F -Ldotbelow;1E36 -Ldotbelowmacron;1E38 -Liwnarmenian;053C -Lj;01C8 -Ljecyrillic;0409 -Llinebelow;1E3A -Lmonospace;FF2C -Lslash;0141 -Lslashsmall;F6F9 -Lsmall;F76C -M;004D -MBsquare;3386 -Macron;F6D0 -Macronsmall;F7AF -Macute;1E3E -Mcircle;24C2 -Mdotaccent;1E40 -Mdotbelow;1E42 -Menarmenian;0544 -Mmonospace;FF2D -Msmall;F76D -Mturned;019C -Mu;039C -N;004E -NJ;01CA -Nacute;0143 -Ncaron;0147 -Ncedilla;0145 -Ncircle;24C3 -Ncircumflexbelow;1E4A -Ncommaaccent;0145 -Ndotaccent;1E44 -Ndotbelow;1E46 -Nhookleft;019D -Nineroman;2168 -Nj;01CB -Njecyrillic;040A -Nlinebelow;1E48 -Nmonospace;FF2E -Nowarmenian;0546 -Nsmall;F76E -Ntilde;00D1 -Ntildesmall;F7F1 -Nu;039D -O;004F -OE;0152 -OEsmall;F6FA -Oacute;00D3 -Oacutesmall;F7F3 -Obarredcyrillic;04E8 -Obarreddieresiscyrillic;04EA -Obreve;014E -Ocaron;01D1 -Ocenteredtilde;019F -Ocircle;24C4 -Ocircumflex;00D4 -Ocircumflexacute;1ED0 -Ocircumflexdotbelow;1ED8 -Ocircumflexgrave;1ED2 -Ocircumflexhookabove;1ED4 -Ocircumflexsmall;F7F4 -Ocircumflextilde;1ED6 -Ocyrillic;041E -Odblacute;0150 -Odblgrave;020C -Odieresis;00D6 -Odieresiscyrillic;04E6 -Odieresissmall;F7F6 -Odotbelow;1ECC -Ogoneksmall;F6FB -Ograve;00D2 -Ogravesmall;F7F2 -Oharmenian;0555 -Ohm;2126 -Ohookabove;1ECE -Ohorn;01A0 -Ohornacute;1EDA -Ohorndotbelow;1EE2 -Ohorngrave;1EDC -Ohornhookabove;1EDE -Ohorntilde;1EE0 -Ohungarumlaut;0150 -Oi;01A2 -Oinvertedbreve;020E -Omacron;014C -Omacronacute;1E52 -Omacrongrave;1E50 -Omega;2126 -Omegacyrillic;0460 -Omegagreek;03A9 -Omegaroundcyrillic;047A -Omegatitlocyrillic;047C -Omegatonos;038F -Omicron;039F -Omicrontonos;038C -Omonospace;FF2F -Oneroman;2160 -Oogonek;01EA -Oogonekmacron;01EC -Oopen;0186 -Oslash;00D8 -Oslashacute;01FE -Oslashsmall;F7F8 -Osmall;F76F -Ostrokeacute;01FE -Otcyrillic;047E -Otilde;00D5 -Otildeacute;1E4C -Otildedieresis;1E4E -Otildesmall;F7F5 -P;0050 -Pacute;1E54 -Pcircle;24C5 -Pdotaccent;1E56 -Pecyrillic;041F -Peharmenian;054A -Pemiddlehookcyrillic;04A6 -Phi;03A6 -Phook;01A4 -Pi;03A0 -Piwrarmenian;0553 -Pmonospace;FF30 -Psi;03A8 -Psicyrillic;0470 -Psmall;F770 -Q;0051 -Qcircle;24C6 -Qmonospace;FF31 -Qsmall;F771 -R;0052 -Raarmenian;054C -Racute;0154 -Rcaron;0158 -Rcedilla;0156 -Rcircle;24C7 -Rcommaaccent;0156 -Rdblgrave;0210 -Rdotaccent;1E58 -Rdotbelow;1E5A -Rdotbelowmacron;1E5C -Reharmenian;0550 -Rfraktur;211C -Rho;03A1 -Ringsmall;F6FC -Rinvertedbreve;0212 -Rlinebelow;1E5E -Rmonospace;FF32 -Rsmall;F772 -Rsmallinverted;0281 -Rsmallinvertedsuperior;02B6 -S;0053 -SF010000;250C -SF020000;2514 -SF030000;2510 -SF040000;2518 -SF050000;253C -SF060000;252C -SF070000;2534 -SF080000;251C -SF090000;2524 -SF100000;2500 -SF110000;2502 -SF190000;2561 -SF200000;2562 -SF210000;2556 -SF220000;2555 -SF230000;2563 -SF240000;2551 -SF250000;2557 -SF260000;255D -SF270000;255C -SF280000;255B -SF360000;255E -SF370000;255F -SF380000;255A -SF390000;2554 -SF400000;2569 -SF410000;2566 -SF420000;2560 -SF430000;2550 -SF440000;256C -SF450000;2567 -SF460000;2568 -SF470000;2564 -SF480000;2565 -SF490000;2559 -SF500000;2558 -SF510000;2552 -SF520000;2553 -SF530000;256B -SF540000;256A -Sacute;015A -Sacutedotaccent;1E64 -Sampigreek;03E0 -Scaron;0160 -Scarondotaccent;1E66 -Scaronsmall;F6FD -Scedilla;015E -Schwa;018F -Schwacyrillic;04D8 -Schwadieresiscyrillic;04DA -Scircle;24C8 -Scircumflex;015C -Scommaaccent;0218 -Sdotaccent;1E60 -Sdotbelow;1E62 -Sdotbelowdotaccent;1E68 -Seharmenian;054D -Sevenroman;2166 -Shaarmenian;0547 -Shacyrillic;0428 -Shchacyrillic;0429 -Sheicoptic;03E2 -Shhacyrillic;04BA -Shimacoptic;03EC -Sigma;03A3 -Sixroman;2165 -Smonospace;FF33 -Softsigncyrillic;042C -Ssmall;F773 -Stigmagreek;03DA -T;0054 -Tau;03A4 -Tbar;0166 -Tcaron;0164 -Tcedilla;0162 -Tcircle;24C9 -Tcircumflexbelow;1E70 -Tcommaaccent;0162 -Tdotaccent;1E6A -Tdotbelow;1E6C -Tecyrillic;0422 -Tedescendercyrillic;04AC -Tenroman;2169 -Tetsecyrillic;04B4 -Theta;0398 -Thook;01AC -Thorn;00DE -Thornsmall;F7FE -Threeroman;2162 -Tildesmall;F6FE -Tiwnarmenian;054F -Tlinebelow;1E6E -Tmonospace;FF34 -Toarmenian;0539 -Tonefive;01BC -Tonesix;0184 -Tonetwo;01A7 -Tretroflexhook;01AE -Tsecyrillic;0426 -Tshecyrillic;040B -Tsmall;F774 -Twelveroman;216B -Tworoman;2161 -U;0055 -Uacute;00DA -Uacutesmall;F7FA -Ubreve;016C -Ucaron;01D3 -Ucircle;24CA -Ucircumflex;00DB -Ucircumflexbelow;1E76 -Ucircumflexsmall;F7FB -Ucyrillic;0423 -Udblacute;0170 -Udblgrave;0214 -Udieresis;00DC -Udieresisacute;01D7 -Udieresisbelow;1E72 -Udieresiscaron;01D9 -Udieresiscyrillic;04F0 -Udieresisgrave;01DB -Udieresismacron;01D5 -Udieresissmall;F7FC -Udotbelow;1EE4 -Ugrave;00D9 -Ugravesmall;F7F9 -Uhookabove;1EE6 -Uhorn;01AF -Uhornacute;1EE8 -Uhorndotbelow;1EF0 -Uhorngrave;1EEA -Uhornhookabove;1EEC -Uhorntilde;1EEE -Uhungarumlaut;0170 -Uhungarumlautcyrillic;04F2 -Uinvertedbreve;0216 -Ukcyrillic;0478 -Umacron;016A -Umacroncyrillic;04EE -Umacrondieresis;1E7A -Umonospace;FF35 -Uogonek;0172 -Upsilon;03A5 -Upsilon1;03D2 -Upsilonacutehooksymbolgreek;03D3 -Upsilonafrican;01B1 -Upsilondieresis;03AB -Upsilondieresishooksymbolgreek;03D4 -Upsilonhooksymbol;03D2 -Upsilontonos;038E -Uring;016E -Ushortcyrillic;040E -Usmall;F775 -Ustraightcyrillic;04AE -Ustraightstrokecyrillic;04B0 -Utilde;0168 -Utildeacute;1E78 -Utildebelow;1E74 -V;0056 -Vcircle;24CB -Vdotbelow;1E7E -Vecyrillic;0412 -Vewarmenian;054E -Vhook;01B2 -Vmonospace;FF36 -Voarmenian;0548 -Vsmall;F776 -Vtilde;1E7C -W;0057 -Wacute;1E82 -Wcircle;24CC -Wcircumflex;0174 -Wdieresis;1E84 -Wdotaccent;1E86 -Wdotbelow;1E88 -Wgrave;1E80 -Wmonospace;FF37 -Wsmall;F777 -X;0058 -Xcircle;24CD -Xdieresis;1E8C -Xdotaccent;1E8A -Xeharmenian;053D -Xi;039E -Xmonospace;FF38 -Xsmall;F778 -Y;0059 -Yacute;00DD -Yacutesmall;F7FD -Yatcyrillic;0462 -Ycircle;24CE -Ycircumflex;0176 -Ydieresis;0178 -Ydieresissmall;F7FF -Ydotaccent;1E8E -Ydotbelow;1EF4 -Yericyrillic;042B -Yerudieresiscyrillic;04F8 -Ygrave;1EF2 -Yhook;01B3 -Yhookabove;1EF6 -Yiarmenian;0545 -Yicyrillic;0407 -Yiwnarmenian;0552 -Ymonospace;FF39 -Ysmall;F779 -Ytilde;1EF8 -Yusbigcyrillic;046A -Yusbigiotifiedcyrillic;046C -Yuslittlecyrillic;0466 -Yuslittleiotifiedcyrillic;0468 -Z;005A -Zaarmenian;0536 -Zacute;0179 -Zcaron;017D -Zcaronsmall;F6FF -Zcircle;24CF -Zcircumflex;1E90 -Zdot;017B -Zdotaccent;017B -Zdotbelow;1E92 -Zecyrillic;0417 -Zedescendercyrillic;0498 -Zedieresiscyrillic;04DE -Zeta;0396 -Zhearmenian;053A -Zhebrevecyrillic;04C1 -Zhecyrillic;0416 -Zhedescendercyrillic;0496 -Zhedieresiscyrillic;04DC -Zlinebelow;1E94 -Zmonospace;FF3A -Zsmall;F77A -Zstroke;01B5 -a;0061 -aabengali;0986 -aacute;00E1 -aadeva;0906 -aagujarati;0A86 -aagurmukhi;0A06 -aamatragurmukhi;0A3E -aarusquare;3303 -aavowelsignbengali;09BE -aavowelsigndeva;093E -aavowelsigngujarati;0ABE -abbreviationmarkarmenian;055F -abbreviationsigndeva;0970 -abengali;0985 -abopomofo;311A -abreve;0103 -abreveacute;1EAF -abrevecyrillic;04D1 -abrevedotbelow;1EB7 -abrevegrave;1EB1 -abrevehookabove;1EB3 -abrevetilde;1EB5 -acaron;01CE -acircle;24D0 -acircumflex;00E2 -acircumflexacute;1EA5 -acircumflexdotbelow;1EAD -acircumflexgrave;1EA7 -acircumflexhookabove;1EA9 -acircumflextilde;1EAB -acute;00B4 -acutebelowcmb;0317 -acutecmb;0301 -acutecomb;0301 -acutedeva;0954 -acutelowmod;02CF -acutetonecmb;0341 -acyrillic;0430 -adblgrave;0201 -addakgurmukhi;0A71 -adeva;0905 -adieresis;00E4 -adieresiscyrillic;04D3 -adieresismacron;01DF -adotbelow;1EA1 -adotmacron;01E1 -ae;00E6 -aeacute;01FD -aekorean;3150 -aemacron;01E3 -afii00208;2015 -afii08941;20A4 -afii10017;0410 -afii10018;0411 -afii10019;0412 -afii10020;0413 -afii10021;0414 -afii10022;0415 -afii10023;0401 -afii10024;0416 -afii10025;0417 -afii10026;0418 -afii10027;0419 -afii10028;041A -afii10029;041B -afii10030;041C -afii10031;041D -afii10032;041E -afii10033;041F -afii10034;0420 -afii10035;0421 -afii10036;0422 -afii10037;0423 -afii10038;0424 -afii10039;0425 -afii10040;0426 -afii10041;0427 -afii10042;0428 -afii10043;0429 -afii10044;042A -afii10045;042B -afii10046;042C -afii10047;042D -afii10048;042E -afii10049;042F -afii10050;0490 -afii10051;0402 -afii10052;0403 -afii10053;0404 -afii10054;0405 -afii10055;0406 -afii10056;0407 -afii10057;0408 -afii10058;0409 -afii10059;040A -afii10060;040B -afii10061;040C -afii10062;040E -afii10063;F6C4 -afii10064;F6C5 -afii10065;0430 -afii10066;0431 -afii10067;0432 -afii10068;0433 -afii10069;0434 -afii10070;0435 -afii10071;0451 -afii10072;0436 -afii10073;0437 -afii10074;0438 -afii10075;0439 -afii10076;043A -afii10077;043B -afii10078;043C -afii10079;043D -afii10080;043E -afii10081;043F -afii10082;0440 -afii10083;0441 -afii10084;0442 -afii10085;0443 -afii10086;0444 -afii10087;0445 -afii10088;0446 -afii10089;0447 -afii10090;0448 -afii10091;0449 -afii10092;044A -afii10093;044B -afii10094;044C -afii10095;044D -afii10096;044E -afii10097;044F -afii10098;0491 -afii10099;0452 -afii10100;0453 -afii10101;0454 -afii10102;0455 -afii10103;0456 -afii10104;0457 -afii10105;0458 -afii10106;0459 -afii10107;045A -afii10108;045B -afii10109;045C -afii10110;045E -afii10145;040F -afii10146;0462 -afii10147;0472 -afii10148;0474 -afii10192;F6C6 -afii10193;045F -afii10194;0463 -afii10195;0473 -afii10196;0475 -afii10831;F6C7 -afii10832;F6C8 -afii10846;04D9 -afii299;200E -afii300;200F -afii301;200D -afii57381;066A -afii57388;060C -afii57392;0660 -afii57393;0661 -afii57394;0662 -afii57395;0663 -afii57396;0664 -afii57397;0665 -afii57398;0666 -afii57399;0667 -afii57400;0668 -afii57401;0669 -afii57403;061B -afii57407;061F -afii57409;0621 -afii57410;0622 -afii57411;0623 -afii57412;0624 -afii57413;0625 -afii57414;0626 -afii57415;0627 -afii57416;0628 -afii57417;0629 -afii57418;062A -afii57419;062B -afii57420;062C -afii57421;062D -afii57422;062E -afii57423;062F -afii57424;0630 -afii57425;0631 -afii57426;0632 -afii57427;0633 -afii57428;0634 -afii57429;0635 -afii57430;0636 -afii57431;0637 -afii57432;0638 -afii57433;0639 -afii57434;063A -afii57440;0640 -afii57441;0641 -afii57442;0642 -afii57443;0643 -afii57444;0644 -afii57445;0645 -afii57446;0646 -afii57448;0648 -afii57449;0649 -afii57450;064A -afii57451;064B -afii57452;064C -afii57453;064D -afii57454;064E -afii57455;064F -afii57456;0650 -afii57457;0651 -afii57458;0652 -afii57470;0647 -afii57505;06A4 -afii57506;067E -afii57507;0686 -afii57508;0698 -afii57509;06AF -afii57511;0679 -afii57512;0688 -afii57513;0691 -afii57514;06BA -afii57519;06D2 -afii57534;06D5 -afii57636;20AA -afii57645;05BE -afii57658;05C3 -afii57664;05D0 -afii57665;05D1 -afii57666;05D2 -afii57667;05D3 -afii57668;05D4 -afii57669;05D5 -afii57670;05D6 -afii57671;05D7 -afii57672;05D8 -afii57673;05D9 -afii57674;05DA -afii57675;05DB -afii57676;05DC -afii57677;05DD -afii57678;05DE -afii57679;05DF -afii57680;05E0 -afii57681;05E1 -afii57682;05E2 -afii57683;05E3 -afii57684;05E4 -afii57685;05E5 -afii57686;05E6 -afii57687;05E7 -afii57688;05E8 -afii57689;05E9 -afii57690;05EA -afii57694;FB2A -afii57695;FB2B -afii57700;FB4B -afii57705;FB1F -afii57716;05F0 -afii57717;05F1 -afii57718;05F2 -afii57723;FB35 -afii57793;05B4 -afii57794;05B5 -afii57795;05B6 -afii57796;05BB -afii57797;05B8 -afii57798;05B7 -afii57799;05B0 -afii57800;05B2 -afii57801;05B1 -afii57802;05B3 -afii57803;05C2 -afii57804;05C1 -afii57806;05B9 -afii57807;05BC -afii57839;05BD -afii57841;05BF -afii57842;05C0 -afii57929;02BC -afii61248;2105 -afii61289;2113 -afii61352;2116 -afii61573;202C -afii61574;202D -afii61575;202E -afii61664;200C -afii63167;066D -afii64937;02BD -agrave;00E0 -agujarati;0A85 -agurmukhi;0A05 -ahiragana;3042 -ahookabove;1EA3 -aibengali;0990 -aibopomofo;311E -aideva;0910 -aiecyrillic;04D5 -aigujarati;0A90 -aigurmukhi;0A10 -aimatragurmukhi;0A48 -ainarabic;0639 -ainfinalarabic;FECA -aininitialarabic;FECB -ainmedialarabic;FECC -ainvertedbreve;0203 -aivowelsignbengali;09C8 -aivowelsigndeva;0948 -aivowelsigngujarati;0AC8 -akatakana;30A2 -akatakanahalfwidth;FF71 -akorean;314F -alef;05D0 -alefarabic;0627 -alefdageshhebrew;FB30 -aleffinalarabic;FE8E -alefhamzaabovearabic;0623 -alefhamzaabovefinalarabic;FE84 -alefhamzabelowarabic;0625 -alefhamzabelowfinalarabic;FE88 -alefhebrew;05D0 -aleflamedhebrew;FB4F -alefmaddaabovearabic;0622 -alefmaddaabovefinalarabic;FE82 -alefmaksuraarabic;0649 -alefmaksurafinalarabic;FEF0 -alefmaksurainitialarabic;FEF3 -alefmaksuramedialarabic;FEF4 -alefpatahhebrew;FB2E -alefqamatshebrew;FB2F -aleph;2135 -allequal;224C -alpha;03B1 -alphatonos;03AC -amacron;0101 -amonospace;FF41 -ampersand;0026 -ampersandmonospace;FF06 -ampersandsmall;F726 -amsquare;33C2 -anbopomofo;3122 -angbopomofo;3124 -angkhankhuthai;0E5A -angle;2220 -anglebracketleft;3008 -anglebracketleftvertical;FE3F -anglebracketright;3009 -anglebracketrightvertical;FE40 -angleleft;2329 -angleright;232A -angstrom;212B -anoteleia;0387 -anudattadeva;0952 -anusvarabengali;0982 -anusvaradeva;0902 -anusvaragujarati;0A82 -aogonek;0105 -apaatosquare;3300 -aparen;249C -apostrophearmenian;055A -apostrophemod;02BC -apple;F8FF -approaches;2250 -approxequal;2248 -approxequalorimage;2252 -approximatelyequal;2245 -araeaekorean;318E -araeakorean;318D -arc;2312 -arighthalfring;1E9A -aring;00E5 -aringacute;01FB -aringbelow;1E01 -arrowboth;2194 -arrowdashdown;21E3 -arrowdashleft;21E0 -arrowdashright;21E2 -arrowdashup;21E1 -arrowdblboth;21D4 -arrowdbldown;21D3 -arrowdblleft;21D0 -arrowdblright;21D2 -arrowdblup;21D1 -arrowdown;2193 -arrowdownleft;2199 -arrowdownright;2198 -arrowdownwhite;21E9 -arrowheaddownmod;02C5 -arrowheadleftmod;02C2 -arrowheadrightmod;02C3 -arrowheadupmod;02C4 -arrowhorizex;F8E7 -arrowleft;2190 -arrowleftdbl;21D0 -arrowleftdblstroke;21CD -arrowleftoverright;21C6 -arrowleftwhite;21E6 -arrowright;2192 -arrowrightdblstroke;21CF -arrowrightheavy;279E -arrowrightoverleft;21C4 -arrowrightwhite;21E8 -arrowtableft;21E4 -arrowtabright;21E5 -arrowup;2191 -arrowupdn;2195 -arrowupdnbse;21A8 -arrowupdownbase;21A8 -arrowupleft;2196 -arrowupleftofdown;21C5 -arrowupright;2197 -arrowupwhite;21E7 -arrowvertex;F8E6 -asciicircum;005E -asciicircummonospace;FF3E -asciitilde;007E -asciitildemonospace;FF5E -ascript;0251 -ascriptturned;0252 -asmallhiragana;3041 -asmallkatakana;30A1 -asmallkatakanahalfwidth;FF67 -asterisk;002A -asteriskaltonearabic;066D -asteriskarabic;066D -asteriskmath;2217 -asteriskmonospace;FF0A -asterisksmall;FE61 -asterism;2042 -asuperior;F6E9 -asymptoticallyequal;2243 -at;0040 -atilde;00E3 -atmonospace;FF20 -atsmall;FE6B -aturned;0250 -aubengali;0994 -aubopomofo;3120 -audeva;0914 -augujarati;0A94 -augurmukhi;0A14 -aulengthmarkbengali;09D7 -aumatragurmukhi;0A4C -auvowelsignbengali;09CC -auvowelsigndeva;094C -auvowelsigngujarati;0ACC -avagrahadeva;093D -aybarmenian;0561 -ayin;05E2 -ayinaltonehebrew;FB20 -ayinhebrew;05E2 -b;0062 -babengali;09AC -backslash;005C -backslashmonospace;FF3C -badeva;092C -bagujarati;0AAC -bagurmukhi;0A2C -bahiragana;3070 -bahtthai;0E3F -bakatakana;30D0 -bar;007C -barmonospace;FF5C -bbopomofo;3105 -bcircle;24D1 -bdotaccent;1E03 -bdotbelow;1E05 -beamedsixteenthnotes;266C -because;2235 -becyrillic;0431 -beharabic;0628 -behfinalarabic;FE90 -behinitialarabic;FE91 -behiragana;3079 -behmedialarabic;FE92 -behmeeminitialarabic;FC9F -behmeemisolatedarabic;FC08 -behnoonfinalarabic;FC6D -bekatakana;30D9 -benarmenian;0562 -bet;05D1 -beta;03B2 -betasymbolgreek;03D0 -betdagesh;FB31 -betdageshhebrew;FB31 -bethebrew;05D1 -betrafehebrew;FB4C -bhabengali;09AD -bhadeva;092D -bhagujarati;0AAD -bhagurmukhi;0A2D -bhook;0253 -bihiragana;3073 -bikatakana;30D3 -bilabialclick;0298 -bindigurmukhi;0A02 -birusquare;3331 -blackcircle;25CF -blackdiamond;25C6 -blackdownpointingtriangle;25BC -blackleftpointingpointer;25C4 -blackleftpointingtriangle;25C0 -blacklenticularbracketleft;3010 -blacklenticularbracketleftvertical;FE3B -blacklenticularbracketright;3011 -blacklenticularbracketrightvertical;FE3C -blacklowerlefttriangle;25E3 -blacklowerrighttriangle;25E2 -blackrectangle;25AC -blackrightpointingpointer;25BA -blackrightpointingtriangle;25B6 -blacksmallsquare;25AA -blacksmilingface;263B -blacksquare;25A0 -blackstar;2605 -blackupperlefttriangle;25E4 -blackupperrighttriangle;25E5 -blackuppointingsmalltriangle;25B4 -blackuppointingtriangle;25B2 -blank;2423 -blinebelow;1E07 -block;2588 -bmonospace;FF42 -bobaimaithai;0E1A -bohiragana;307C -bokatakana;30DC -bparen;249D -bqsquare;33C3 -braceex;F8F4 -braceleft;007B -braceleftbt;F8F3 -braceleftmid;F8F2 -braceleftmonospace;FF5B -braceleftsmall;FE5B -bracelefttp;F8F1 -braceleftvertical;FE37 -braceright;007D -bracerightbt;F8FE -bracerightmid;F8FD -bracerightmonospace;FF5D -bracerightsmall;FE5C -bracerighttp;F8FC -bracerightvertical;FE38 -bracketleft;005B -bracketleftbt;F8F0 -bracketleftex;F8EF -bracketleftmonospace;FF3B -bracketlefttp;F8EE -bracketright;005D -bracketrightbt;F8FB -bracketrightex;F8FA -bracketrightmonospace;FF3D -bracketrighttp;F8F9 -breve;02D8 -brevebelowcmb;032E -brevecmb;0306 -breveinvertedbelowcmb;032F -breveinvertedcmb;0311 -breveinverteddoublecmb;0361 -bridgebelowcmb;032A -bridgeinvertedbelowcmb;033A -brokenbar;00A6 -bstroke;0180 -bsuperior;F6EA -btopbar;0183 -buhiragana;3076 -bukatakana;30D6 -bullet;2022 -bulletinverse;25D8 -bulletoperator;2219 -bullseye;25CE -c;0063 -caarmenian;056E -cabengali;099A -cacute;0107 -cadeva;091A -cagujarati;0A9A -cagurmukhi;0A1A -calsquare;3388 -candrabindubengali;0981 -candrabinducmb;0310 -candrabindudeva;0901 -candrabindugujarati;0A81 -capslock;21EA -careof;2105 -caron;02C7 -caronbelowcmb;032C -caroncmb;030C -carriagereturn;21B5 -cbopomofo;3118 -ccaron;010D -ccedilla;00E7 -ccedillaacute;1E09 -ccircle;24D2 -ccircumflex;0109 -ccurl;0255 -cdot;010B -cdotaccent;010B -cdsquare;33C5 -cedilla;00B8 -cedillacmb;0327 -cent;00A2 -centigrade;2103 -centinferior;F6DF -centmonospace;FFE0 -centoldstyle;F7A2 -centsuperior;F6E0 -chaarmenian;0579 -chabengali;099B -chadeva;091B -chagujarati;0A9B -chagurmukhi;0A1B -chbopomofo;3114 -cheabkhasiancyrillic;04BD -checkmark;2713 -checyrillic;0447 -chedescenderabkhasiancyrillic;04BF -chedescendercyrillic;04B7 -chedieresiscyrillic;04F5 -cheharmenian;0573 -chekhakassiancyrillic;04CC -cheverticalstrokecyrillic;04B9 -chi;03C7 -chieuchacirclekorean;3277 -chieuchaparenkorean;3217 -chieuchcirclekorean;3269 -chieuchkorean;314A -chieuchparenkorean;3209 -chochangthai;0E0A -chochanthai;0E08 -chochingthai;0E09 -chochoethai;0E0C -chook;0188 -cieucacirclekorean;3276 -cieucaparenkorean;3216 -cieuccirclekorean;3268 -cieuckorean;3148 -cieucparenkorean;3208 -cieucuparenkorean;321C -circle;25CB -circlemultiply;2297 -circleot;2299 -circleplus;2295 -circlepostalmark;3036 -circlewithlefthalfblack;25D0 -circlewithrighthalfblack;25D1 -circumflex;02C6 -circumflexbelowcmb;032D -circumflexcmb;0302 -clear;2327 -clickalveolar;01C2 -clickdental;01C0 -clicklateral;01C1 -clickretroflex;01C3 -club;2663 -clubsuitblack;2663 -clubsuitwhite;2667 -cmcubedsquare;33A4 -cmonospace;FF43 -cmsquaredsquare;33A0 -coarmenian;0581 -colon;003A -colonmonetary;20A1 -colonmonospace;FF1A -colonsign;20A1 -colonsmall;FE55 -colontriangularhalfmod;02D1 -colontriangularmod;02D0 -comma;002C -commaabovecmb;0313 -commaaboverightcmb;0315 -commaaccent;F6C3 -commaarabic;060C -commaarmenian;055D -commainferior;F6E1 -commamonospace;FF0C -commareversedabovecmb;0314 -commareversedmod;02BD -commasmall;FE50 -commasuperior;F6E2 -commaturnedabovecmb;0312 -commaturnedmod;02BB -compass;263C -congruent;2245 -contourintegral;222E -control;2303 -controlACK;0006 -controlBEL;0007 -controlBS;0008 -controlCAN;0018 -controlCR;000D -controlDC1;0011 -controlDC2;0012 -controlDC3;0013 -controlDC4;0014 -controlDEL;007F -controlDLE;0010 -controlEM;0019 -controlENQ;0005 -controlEOT;0004 -controlESC;001B -controlETB;0017 -controlETX;0003 -controlFF;000C -controlFS;001C -controlGS;001D -controlHT;0009 -controlLF;000A -controlNAK;0015 -controlRS;001E -controlSI;000F -controlSO;000E -controlSOT;0002 -controlSTX;0001 -controlSUB;001A -controlSYN;0016 -controlUS;001F -controlVT;000B -copyright;00A9 -copyrightsans;F8E9 -copyrightserif;F6D9 -cornerbracketleft;300C -cornerbracketlefthalfwidth;FF62 -cornerbracketleftvertical;FE41 -cornerbracketright;300D -cornerbracketrighthalfwidth;FF63 -cornerbracketrightvertical;FE42 -corporationsquare;337F -cosquare;33C7 -coverkgsquare;33C6 -cparen;249E -cruzeiro;20A2 -cstretched;0297 -curlyand;22CF -curlyor;22CE -currency;00A4 -cyrBreve;F6D1 -cyrFlex;F6D2 -cyrbreve;F6D4 -cyrflex;F6D5 -d;0064 -daarmenian;0564 -dabengali;09A6 -dadarabic;0636 -dadeva;0926 -dadfinalarabic;FEBE -dadinitialarabic;FEBF -dadmedialarabic;FEC0 -dagesh;05BC -dageshhebrew;05BC -dagger;2020 -daggerdbl;2021 -dagujarati;0AA6 -dagurmukhi;0A26 -dahiragana;3060 -dakatakana;30C0 -dalarabic;062F -dalet;05D3 -daletdagesh;FB33 -daletdageshhebrew;FB33 -dalethatafpatah;05D3 05B2 -dalethatafpatahhebrew;05D3 05B2 -dalethatafsegol;05D3 05B1 -dalethatafsegolhebrew;05D3 05B1 -dalethebrew;05D3 -dalethiriq;05D3 05B4 -dalethiriqhebrew;05D3 05B4 -daletholam;05D3 05B9 -daletholamhebrew;05D3 05B9 -daletpatah;05D3 05B7 -daletpatahhebrew;05D3 05B7 -daletqamats;05D3 05B8 -daletqamatshebrew;05D3 05B8 -daletqubuts;05D3 05BB -daletqubutshebrew;05D3 05BB -daletsegol;05D3 05B6 -daletsegolhebrew;05D3 05B6 -daletsheva;05D3 05B0 -daletshevahebrew;05D3 05B0 -dalettsere;05D3 05B5 -dalettserehebrew;05D3 05B5 -dalfinalarabic;FEAA -dammaarabic;064F -dammalowarabic;064F -dammatanaltonearabic;064C -dammatanarabic;064C -danda;0964 -dargahebrew;05A7 -dargalefthebrew;05A7 -dasiapneumatacyrilliccmb;0485 -dblGrave;F6D3 -dblanglebracketleft;300A -dblanglebracketleftvertical;FE3D -dblanglebracketright;300B -dblanglebracketrightvertical;FE3E -dblarchinvertedbelowcmb;032B -dblarrowleft;21D4 -dblarrowright;21D2 -dbldanda;0965 -dblgrave;F6D6 -dblgravecmb;030F -dblintegral;222C -dbllowline;2017 -dbllowlinecmb;0333 -dbloverlinecmb;033F -dblprimemod;02BA -dblverticalbar;2016 -dblverticallineabovecmb;030E -dbopomofo;3109 -dbsquare;33C8 -dcaron;010F -dcedilla;1E11 -dcircle;24D3 -dcircumflexbelow;1E13 -dcroat;0111 -ddabengali;09A1 -ddadeva;0921 -ddagujarati;0AA1 -ddagurmukhi;0A21 -ddalarabic;0688 -ddalfinalarabic;FB89 -dddhadeva;095C -ddhabengali;09A2 -ddhadeva;0922 -ddhagujarati;0AA2 -ddhagurmukhi;0A22 -ddotaccent;1E0B -ddotbelow;1E0D -decimalseparatorarabic;066B -decimalseparatorpersian;066B -decyrillic;0434 -degree;00B0 -dehihebrew;05AD -dehiragana;3067 -deicoptic;03EF -dekatakana;30C7 -deleteleft;232B -deleteright;2326 -delta;03B4 -deltaturned;018D -denominatorminusonenumeratorbengali;09F8 -dezh;02A4 -dhabengali;09A7 -dhadeva;0927 -dhagujarati;0AA7 -dhagurmukhi;0A27 -dhook;0257 -dialytikatonos;0385 -dialytikatonoscmb;0344 -diamond;2666 -diamondsuitwhite;2662 -dieresis;00A8 -dieresisacute;F6D7 -dieresisbelowcmb;0324 -dieresiscmb;0308 -dieresisgrave;F6D8 -dieresistonos;0385 -dihiragana;3062 -dikatakana;30C2 -dittomark;3003 -divide;00F7 -divides;2223 -divisionslash;2215 -djecyrillic;0452 -dkshade;2593 -dlinebelow;1E0F -dlsquare;3397 -dmacron;0111 -dmonospace;FF44 -dnblock;2584 -dochadathai;0E0E -dodekthai;0E14 -dohiragana;3069 -dokatakana;30C9 -dollar;0024 -dollarinferior;F6E3 -dollarmonospace;FF04 -dollaroldstyle;F724 -dollarsmall;FE69 -dollarsuperior;F6E4 -dong;20AB -dorusquare;3326 -dotaccent;02D9 -dotaccentcmb;0307 -dotbelowcmb;0323 -dotbelowcomb;0323 -dotkatakana;30FB -dotlessi;0131 -dotlessj;F6BE -dotlessjstrokehook;0284 -dotmath;22C5 -dottedcircle;25CC -doubleyodpatah;FB1F -doubleyodpatahhebrew;FB1F -downtackbelowcmb;031E -downtackmod;02D5 -dparen;249F -dsuperior;F6EB -dtail;0256 -dtopbar;018C -duhiragana;3065 -dukatakana;30C5 -dz;01F3 -dzaltone;02A3 -dzcaron;01C6 -dzcurl;02A5 -dzeabkhasiancyrillic;04E1 -dzecyrillic;0455 -dzhecyrillic;045F -e;0065 -eacute;00E9 -earth;2641 -ebengali;098F -ebopomofo;311C -ebreve;0115 -ecandradeva;090D -ecandragujarati;0A8D -ecandravowelsigndeva;0945 -ecandravowelsigngujarati;0AC5 -ecaron;011B -ecedillabreve;1E1D -echarmenian;0565 -echyiwnarmenian;0587 -ecircle;24D4 -ecircumflex;00EA -ecircumflexacute;1EBF -ecircumflexbelow;1E19 -ecircumflexdotbelow;1EC7 -ecircumflexgrave;1EC1 -ecircumflexhookabove;1EC3 -ecircumflextilde;1EC5 -ecyrillic;0454 -edblgrave;0205 -edeva;090F -edieresis;00EB -edot;0117 -edotaccent;0117 -edotbelow;1EB9 -eegurmukhi;0A0F -eematragurmukhi;0A47 -efcyrillic;0444 -egrave;00E8 -egujarati;0A8F -eharmenian;0567 -ehbopomofo;311D -ehiragana;3048 -ehookabove;1EBB -eibopomofo;311F -eight;0038 -eightarabic;0668 -eightbengali;09EE -eightcircle;2467 -eightcircleinversesansserif;2791 -eightdeva;096E -eighteencircle;2471 -eighteenparen;2485 -eighteenperiod;2499 -eightgujarati;0AEE -eightgurmukhi;0A6E -eighthackarabic;0668 -eighthangzhou;3028 -eighthnotebeamed;266B -eightideographicparen;3227 -eightinferior;2088 -eightmonospace;FF18 -eightoldstyle;F738 -eightparen;247B -eightperiod;248F -eightpersian;06F8 -eightroman;2177 -eightsuperior;2078 -eightthai;0E58 -einvertedbreve;0207 -eiotifiedcyrillic;0465 -ekatakana;30A8 -ekatakanahalfwidth;FF74 -ekonkargurmukhi;0A74 -ekorean;3154 -elcyrillic;043B -element;2208 -elevencircle;246A -elevenparen;247E -elevenperiod;2492 -elevenroman;217A -ellipsis;2026 -ellipsisvertical;22EE -emacron;0113 -emacronacute;1E17 -emacrongrave;1E15 -emcyrillic;043C -emdash;2014 -emdashvertical;FE31 -emonospace;FF45 -emphasismarkarmenian;055B -emptyset;2205 -enbopomofo;3123 -encyrillic;043D -endash;2013 -endashvertical;FE32 -endescendercyrillic;04A3 -eng;014B -engbopomofo;3125 -enghecyrillic;04A5 -enhookcyrillic;04C8 -enspace;2002 -eogonek;0119 -eokorean;3153 -eopen;025B -eopenclosed;029A -eopenreversed;025C -eopenreversedclosed;025E -eopenreversedhook;025D -eparen;24A0 -epsilon;03B5 -epsilontonos;03AD -equal;003D -equalmonospace;FF1D -equalsmall;FE66 -equalsuperior;207C -equivalence;2261 -erbopomofo;3126 -ercyrillic;0440 -ereversed;0258 -ereversedcyrillic;044D -escyrillic;0441 -esdescendercyrillic;04AB -esh;0283 -eshcurl;0286 -eshortdeva;090E -eshortvowelsigndeva;0946 -eshreversedloop;01AA -eshsquatreversed;0285 -esmallhiragana;3047 -esmallkatakana;30A7 -esmallkatakanahalfwidth;FF6A -estimated;212E -esuperior;F6EC -eta;03B7 -etarmenian;0568 -etatonos;03AE -eth;00F0 -etilde;1EBD -etildebelow;1E1B -etnahtafoukhhebrew;0591 -etnahtafoukhlefthebrew;0591 -etnahtahebrew;0591 -etnahtalefthebrew;0591 -eturned;01DD -eukorean;3161 -euro;20AC -evowelsignbengali;09C7 -evowelsigndeva;0947 -evowelsigngujarati;0AC7 -exclam;0021 -exclamarmenian;055C -exclamdbl;203C -exclamdown;00A1 -exclamdownsmall;F7A1 -exclammonospace;FF01 -exclamsmall;F721 -existential;2203 -ezh;0292 -ezhcaron;01EF -ezhcurl;0293 -ezhreversed;01B9 -ezhtail;01BA -f;0066 -fadeva;095E -fagurmukhi;0A5E -fahrenheit;2109 -fathaarabic;064E -fathalowarabic;064E -fathatanarabic;064B -fbopomofo;3108 -fcircle;24D5 -fdotaccent;1E1F -feharabic;0641 -feharmenian;0586 -fehfinalarabic;FED2 -fehinitialarabic;FED3 -fehmedialarabic;FED4 -feicoptic;03E5 -female;2640 -ff;FB00 -ffi;FB03 -ffl;FB04 -fi;FB01 -fifteencircle;246E -fifteenparen;2482 -fifteenperiod;2496 -figuredash;2012 -filledbox;25A0 -filledrect;25AC -finalkaf;05DA -finalkafdagesh;FB3A -finalkafdageshhebrew;FB3A -finalkafhebrew;05DA -finalkafqamats;05DA 05B8 -finalkafqamatshebrew;05DA 05B8 -finalkafsheva;05DA 05B0 -finalkafshevahebrew;05DA 05B0 -finalmem;05DD -finalmemhebrew;05DD -finalnun;05DF -finalnunhebrew;05DF -finalpe;05E3 -finalpehebrew;05E3 -finaltsadi;05E5 -finaltsadihebrew;05E5 -firsttonechinese;02C9 -fisheye;25C9 -fitacyrillic;0473 -five;0035 -fivearabic;0665 -fivebengali;09EB -fivecircle;2464 -fivecircleinversesansserif;278E -fivedeva;096B -fiveeighths;215D -fivegujarati;0AEB -fivegurmukhi;0A6B -fivehackarabic;0665 -fivehangzhou;3025 -fiveideographicparen;3224 -fiveinferior;2085 -fivemonospace;FF15 -fiveoldstyle;F735 -fiveparen;2478 -fiveperiod;248C -fivepersian;06F5 -fiveroman;2174 -fivesuperior;2075 -fivethai;0E55 -fl;FB02 -florin;0192 -fmonospace;FF46 -fmsquare;3399 -fofanthai;0E1F -fofathai;0E1D -fongmanthai;0E4F -forall;2200 -four;0034 -fourarabic;0664 -fourbengali;09EA -fourcircle;2463 -fourcircleinversesansserif;278D -fourdeva;096A -fourgujarati;0AEA -fourgurmukhi;0A6A -fourhackarabic;0664 -fourhangzhou;3024 -fourideographicparen;3223 -fourinferior;2084 -fourmonospace;FF14 -fournumeratorbengali;09F7 -fouroldstyle;F734 -fourparen;2477 -fourperiod;248B -fourpersian;06F4 -fourroman;2173 -foursuperior;2074 -fourteencircle;246D -fourteenparen;2481 -fourteenperiod;2495 -fourthai;0E54 -fourthtonechinese;02CB -fparen;24A1 -fraction;2044 -franc;20A3 -g;0067 -gabengali;0997 -gacute;01F5 -gadeva;0917 -gafarabic;06AF -gaffinalarabic;FB93 -gafinitialarabic;FB94 -gafmedialarabic;FB95 -gagujarati;0A97 -gagurmukhi;0A17 -gahiragana;304C -gakatakana;30AC -gamma;03B3 -gammalatinsmall;0263 -gammasuperior;02E0 -gangiacoptic;03EB -gbopomofo;310D -gbreve;011F -gcaron;01E7 -gcedilla;0123 -gcircle;24D6 -gcircumflex;011D -gcommaaccent;0123 -gdot;0121 -gdotaccent;0121 -gecyrillic;0433 -gehiragana;3052 -gekatakana;30B2 -geometricallyequal;2251 -gereshaccenthebrew;059C -gereshhebrew;05F3 -gereshmuqdamhebrew;059D -germandbls;00DF -gershayimaccenthebrew;059E -gershayimhebrew;05F4 -getamark;3013 -ghabengali;0998 -ghadarmenian;0572 -ghadeva;0918 -ghagujarati;0A98 -ghagurmukhi;0A18 -ghainarabic;063A -ghainfinalarabic;FECE -ghaininitialarabic;FECF -ghainmedialarabic;FED0 -ghemiddlehookcyrillic;0495 -ghestrokecyrillic;0493 -gheupturncyrillic;0491 -ghhadeva;095A -ghhagurmukhi;0A5A -ghook;0260 -ghzsquare;3393 -gihiragana;304E -gikatakana;30AE -gimarmenian;0563 -gimel;05D2 -gimeldagesh;FB32 -gimeldageshhebrew;FB32 -gimelhebrew;05D2 -gjecyrillic;0453 -glottalinvertedstroke;01BE -glottalstop;0294 -glottalstopinverted;0296 -glottalstopmod;02C0 -glottalstopreversed;0295 -glottalstopreversedmod;02C1 -glottalstopreversedsuperior;02E4 -glottalstopstroke;02A1 -glottalstopstrokereversed;02A2 -gmacron;1E21 -gmonospace;FF47 -gohiragana;3054 -gokatakana;30B4 -gparen;24A2 -gpasquare;33AC -gradient;2207 -grave;0060 -gravebelowcmb;0316 -gravecmb;0300 -gravecomb;0300 -gravedeva;0953 -gravelowmod;02CE -gravemonospace;FF40 -gravetonecmb;0340 -greater;003E -greaterequal;2265 -greaterequalorless;22DB -greatermonospace;FF1E -greaterorequivalent;2273 -greaterorless;2277 -greateroverequal;2267 -greatersmall;FE65 -gscript;0261 -gstroke;01E5 -guhiragana;3050 -guillemotleft;00AB -guillemotright;00BB -guilsinglleft;2039 -guilsinglright;203A -gukatakana;30B0 -guramusquare;3318 -gysquare;33C9 -h;0068 -haabkhasiancyrillic;04A9 -haaltonearabic;06C1 -habengali;09B9 -hadescendercyrillic;04B3 -hadeva;0939 -hagujarati;0AB9 -hagurmukhi;0A39 -haharabic;062D -hahfinalarabic;FEA2 -hahinitialarabic;FEA3 -hahiragana;306F -hahmedialarabic;FEA4 -haitusquare;332A -hakatakana;30CF -hakatakanahalfwidth;FF8A -halantgurmukhi;0A4D -hamzaarabic;0621 -hamzadammaarabic;0621 064F -hamzadammatanarabic;0621 064C -hamzafathaarabic;0621 064E -hamzafathatanarabic;0621 064B -hamzalowarabic;0621 -hamzalowkasraarabic;0621 0650 -hamzalowkasratanarabic;0621 064D -hamzasukunarabic;0621 0652 -hangulfiller;3164 -hardsigncyrillic;044A -harpoonleftbarbup;21BC -harpoonrightbarbup;21C0 -hasquare;33CA -hatafpatah;05B2 -hatafpatah16;05B2 -hatafpatah23;05B2 -hatafpatah2f;05B2 -hatafpatahhebrew;05B2 -hatafpatahnarrowhebrew;05B2 -hatafpatahquarterhebrew;05B2 -hatafpatahwidehebrew;05B2 -hatafqamats;05B3 -hatafqamats1b;05B3 -hatafqamats28;05B3 -hatafqamats34;05B3 -hatafqamatshebrew;05B3 -hatafqamatsnarrowhebrew;05B3 -hatafqamatsquarterhebrew;05B3 -hatafqamatswidehebrew;05B3 -hatafsegol;05B1 -hatafsegol17;05B1 -hatafsegol24;05B1 -hatafsegol30;05B1 -hatafsegolhebrew;05B1 -hatafsegolnarrowhebrew;05B1 -hatafsegolquarterhebrew;05B1 -hatafsegolwidehebrew;05B1 -hbar;0127 -hbopomofo;310F -hbrevebelow;1E2B -hcedilla;1E29 -hcircle;24D7 -hcircumflex;0125 -hdieresis;1E27 -hdotaccent;1E23 -hdotbelow;1E25 -he;05D4 -heart;2665 -heartsuitblack;2665 -heartsuitwhite;2661 -hedagesh;FB34 -hedageshhebrew;FB34 -hehaltonearabic;06C1 -heharabic;0647 -hehebrew;05D4 -hehfinalaltonearabic;FBA7 -hehfinalalttwoarabic;FEEA -hehfinalarabic;FEEA -hehhamzaabovefinalarabic;FBA5 -hehhamzaaboveisolatedarabic;FBA4 -hehinitialaltonearabic;FBA8 -hehinitialarabic;FEEB -hehiragana;3078 -hehmedialaltonearabic;FBA9 -hehmedialarabic;FEEC -heiseierasquare;337B -hekatakana;30D8 -hekatakanahalfwidth;FF8D -hekutaarusquare;3336 -henghook;0267 -herutusquare;3339 -het;05D7 -hethebrew;05D7 -hhook;0266 -hhooksuperior;02B1 -hieuhacirclekorean;327B -hieuhaparenkorean;321B -hieuhcirclekorean;326D -hieuhkorean;314E -hieuhparenkorean;320D -hihiragana;3072 -hikatakana;30D2 -hikatakanahalfwidth;FF8B -hiriq;05B4 -hiriq14;05B4 -hiriq21;05B4 -hiriq2d;05B4 -hiriqhebrew;05B4 -hiriqnarrowhebrew;05B4 -hiriqquarterhebrew;05B4 -hiriqwidehebrew;05B4 -hlinebelow;1E96 -hmonospace;FF48 -hoarmenian;0570 -hohipthai;0E2B -hohiragana;307B -hokatakana;30DB -hokatakanahalfwidth;FF8E -holam;05B9 -holam19;05B9 -holam26;05B9 -holam32;05B9 -holamhebrew;05B9 -holamnarrowhebrew;05B9 -holamquarterhebrew;05B9 -holamwidehebrew;05B9 -honokhukthai;0E2E -hookabovecomb;0309 -hookcmb;0309 -hookpalatalizedbelowcmb;0321 -hookretroflexbelowcmb;0322 -hoonsquare;3342 -horicoptic;03E9 -horizontalbar;2015 -horncmb;031B -hotsprings;2668 -house;2302 -hparen;24A3 -hsuperior;02B0 -hturned;0265 -huhiragana;3075 -huiitosquare;3333 -hukatakana;30D5 -hukatakanahalfwidth;FF8C -hungarumlaut;02DD -hungarumlautcmb;030B -hv;0195 -hyphen;002D -hypheninferior;F6E5 -hyphenmonospace;FF0D -hyphensmall;FE63 -hyphensuperior;F6E6 -hyphentwo;2010 -i;0069 -iacute;00ED -iacyrillic;044F -ibengali;0987 -ibopomofo;3127 -ibreve;012D -icaron;01D0 -icircle;24D8 -icircumflex;00EE -icyrillic;0456 -idblgrave;0209 -ideographearthcircle;328F -ideographfirecircle;328B -ideographicallianceparen;323F -ideographiccallparen;323A -ideographiccentrecircle;32A5 -ideographicclose;3006 -ideographiccomma;3001 -ideographiccommaleft;FF64 -ideographiccongratulationparen;3237 -ideographiccorrectcircle;32A3 -ideographicearthparen;322F -ideographicenterpriseparen;323D -ideographicexcellentcircle;329D -ideographicfestivalparen;3240 -ideographicfinancialcircle;3296 -ideographicfinancialparen;3236 -ideographicfireparen;322B -ideographichaveparen;3232 -ideographichighcircle;32A4 -ideographiciterationmark;3005 -ideographiclaborcircle;3298 -ideographiclaborparen;3238 -ideographicleftcircle;32A7 -ideographiclowcircle;32A6 -ideographicmedicinecircle;32A9 -ideographicmetalparen;322E -ideographicmoonparen;322A -ideographicnameparen;3234 -ideographicperiod;3002 -ideographicprintcircle;329E -ideographicreachparen;3243 -ideographicrepresentparen;3239 -ideographicresourceparen;323E -ideographicrightcircle;32A8 -ideographicsecretcircle;3299 -ideographicselfparen;3242 -ideographicsocietyparen;3233 -ideographicspace;3000 -ideographicspecialparen;3235 -ideographicstockparen;3231 -ideographicstudyparen;323B -ideographicsunparen;3230 -ideographicsuperviseparen;323C -ideographicwaterparen;322C -ideographicwoodparen;322D -ideographiczero;3007 -ideographmetalcircle;328E -ideographmooncircle;328A -ideographnamecircle;3294 -ideographsuncircle;3290 -ideographwatercircle;328C -ideographwoodcircle;328D -ideva;0907 -idieresis;00EF -idieresisacute;1E2F -idieresiscyrillic;04E5 -idotbelow;1ECB -iebrevecyrillic;04D7 -iecyrillic;0435 -ieungacirclekorean;3275 -ieungaparenkorean;3215 -ieungcirclekorean;3267 -ieungkorean;3147 -ieungparenkorean;3207 -igrave;00EC -igujarati;0A87 -igurmukhi;0A07 -ihiragana;3044 -ihookabove;1EC9 -iibengali;0988 -iicyrillic;0438 -iideva;0908 -iigujarati;0A88 -iigurmukhi;0A08 -iimatragurmukhi;0A40 -iinvertedbreve;020B -iishortcyrillic;0439 -iivowelsignbengali;09C0 -iivowelsigndeva;0940 -iivowelsigngujarati;0AC0 -ij;0133 -ikatakana;30A4 -ikatakanahalfwidth;FF72 -ikorean;3163 -ilde;02DC -iluyhebrew;05AC -imacron;012B -imacroncyrillic;04E3 -imageorapproximatelyequal;2253 -imatragurmukhi;0A3F -imonospace;FF49 -increment;2206 -infinity;221E -iniarmenian;056B -integral;222B -integralbottom;2321 -integralbt;2321 -integralex;F8F5 -integraltop;2320 -integraltp;2320 -intersection;2229 -intisquare;3305 -invbullet;25D8 -invcircle;25D9 -invsmileface;263B -iocyrillic;0451 -iogonek;012F -iota;03B9 -iotadieresis;03CA -iotadieresistonos;0390 -iotalatin;0269 -iotatonos;03AF -iparen;24A4 -irigurmukhi;0A72 -ismallhiragana;3043 -ismallkatakana;30A3 -ismallkatakanahalfwidth;FF68 -issharbengali;09FA -istroke;0268 -isuperior;F6ED -iterationhiragana;309D -iterationkatakana;30FD -itilde;0129 -itildebelow;1E2D -iubopomofo;3129 -iucyrillic;044E -ivowelsignbengali;09BF -ivowelsigndeva;093F -ivowelsigngujarati;0ABF -izhitsacyrillic;0475 -izhitsadblgravecyrillic;0477 -j;006A -jaarmenian;0571 -jabengali;099C -jadeva;091C -jagujarati;0A9C -jagurmukhi;0A1C -jbopomofo;3110 -jcaron;01F0 -jcircle;24D9 -jcircumflex;0135 -jcrossedtail;029D -jdotlessstroke;025F -jecyrillic;0458 -jeemarabic;062C -jeemfinalarabic;FE9E -jeeminitialarabic;FE9F -jeemmedialarabic;FEA0 -jeharabic;0698 -jehfinalarabic;FB8B -jhabengali;099D -jhadeva;091D -jhagujarati;0A9D -jhagurmukhi;0A1D -jheharmenian;057B -jis;3004 -jmonospace;FF4A -jparen;24A5 -jsuperior;02B2 -k;006B -kabashkircyrillic;04A1 -kabengali;0995 -kacute;1E31 -kacyrillic;043A -kadescendercyrillic;049B -kadeva;0915 -kaf;05DB -kafarabic;0643 -kafdagesh;FB3B -kafdageshhebrew;FB3B -kaffinalarabic;FEDA -kafhebrew;05DB -kafinitialarabic;FEDB -kafmedialarabic;FEDC -kafrafehebrew;FB4D -kagujarati;0A95 -kagurmukhi;0A15 -kahiragana;304B -kahookcyrillic;04C4 -kakatakana;30AB -kakatakanahalfwidth;FF76 -kappa;03BA -kappasymbolgreek;03F0 -kapyeounmieumkorean;3171 -kapyeounphieuphkorean;3184 -kapyeounpieupkorean;3178 -kapyeounssangpieupkorean;3179 -karoriisquare;330D -kashidaautoarabic;0640 -kashidaautonosidebearingarabic;0640 -kasmallkatakana;30F5 -kasquare;3384 -kasraarabic;0650 -kasratanarabic;064D -kastrokecyrillic;049F -katahiraprolongmarkhalfwidth;FF70 -kaverticalstrokecyrillic;049D -kbopomofo;310E -kcalsquare;3389 -kcaron;01E9 -kcedilla;0137 -kcircle;24DA -kcommaaccent;0137 -kdotbelow;1E33 -keharmenian;0584 -kehiragana;3051 -kekatakana;30B1 -kekatakanahalfwidth;FF79 -kenarmenian;056F -kesmallkatakana;30F6 -kgreenlandic;0138 -khabengali;0996 -khacyrillic;0445 -khadeva;0916 -khagujarati;0A96 -khagurmukhi;0A16 -khaharabic;062E -khahfinalarabic;FEA6 -khahinitialarabic;FEA7 -khahmedialarabic;FEA8 -kheicoptic;03E7 -khhadeva;0959 -khhagurmukhi;0A59 -khieukhacirclekorean;3278 -khieukhaparenkorean;3218 -khieukhcirclekorean;326A -khieukhkorean;314B -khieukhparenkorean;320A -khokhaithai;0E02 -khokhonthai;0E05 -khokhuatthai;0E03 -khokhwaithai;0E04 -khomutthai;0E5B -khook;0199 -khorakhangthai;0E06 -khzsquare;3391 -kihiragana;304D -kikatakana;30AD -kikatakanahalfwidth;FF77 -kiroguramusquare;3315 -kiromeetorusquare;3316 -kirosquare;3314 -kiyeokacirclekorean;326E -kiyeokaparenkorean;320E -kiyeokcirclekorean;3260 -kiyeokkorean;3131 -kiyeokparenkorean;3200 -kiyeoksioskorean;3133 -kjecyrillic;045C -klinebelow;1E35 -klsquare;3398 -kmcubedsquare;33A6 -kmonospace;FF4B -kmsquaredsquare;33A2 -kohiragana;3053 -kohmsquare;33C0 -kokaithai;0E01 -kokatakana;30B3 -kokatakanahalfwidth;FF7A -kooposquare;331E -koppacyrillic;0481 -koreanstandardsymbol;327F -koroniscmb;0343 -kparen;24A6 -kpasquare;33AA -ksicyrillic;046F -ktsquare;33CF -kturned;029E -kuhiragana;304F -kukatakana;30AF -kukatakanahalfwidth;FF78 -kvsquare;33B8 -kwsquare;33BE -l;006C -labengali;09B2 -lacute;013A -ladeva;0932 -lagujarati;0AB2 -lagurmukhi;0A32 -lakkhangyaothai;0E45 -lamaleffinalarabic;FEFC -lamalefhamzaabovefinalarabic;FEF8 -lamalefhamzaaboveisolatedarabic;FEF7 -lamalefhamzabelowfinalarabic;FEFA -lamalefhamzabelowisolatedarabic;FEF9 -lamalefisolatedarabic;FEFB -lamalefmaddaabovefinalarabic;FEF6 -lamalefmaddaaboveisolatedarabic;FEF5 -lamarabic;0644 -lambda;03BB -lambdastroke;019B -lamed;05DC -lameddagesh;FB3C -lameddageshhebrew;FB3C -lamedhebrew;05DC -lamedholam;05DC 05B9 -lamedholamdagesh;05DC 05B9 05BC -lamedholamdageshhebrew;05DC 05B9 05BC -lamedholamhebrew;05DC 05B9 -lamfinalarabic;FEDE -lamhahinitialarabic;FCCA -laminitialarabic;FEDF -lamjeeminitialarabic;FCC9 -lamkhahinitialarabic;FCCB -lamlamhehisolatedarabic;FDF2 -lammedialarabic;FEE0 -lammeemhahinitialarabic;FD88 -lammeeminitialarabic;FCCC -lammeemjeeminitialarabic;FEDF FEE4 FEA0 -lammeemkhahinitialarabic;FEDF FEE4 FEA8 -largecircle;25EF -lbar;019A -lbelt;026C -lbopomofo;310C -lcaron;013E -lcedilla;013C -lcircle;24DB -lcircumflexbelow;1E3D -lcommaaccent;013C -ldot;0140 -ldotaccent;0140 -ldotbelow;1E37 -ldotbelowmacron;1E39 -leftangleabovecmb;031A -lefttackbelowcmb;0318 -less;003C -lessequal;2264 -lessequalorgreater;22DA -lessmonospace;FF1C -lessorequivalent;2272 -lessorgreater;2276 -lessoverequal;2266 -lesssmall;FE64 -lezh;026E -lfblock;258C -lhookretroflex;026D -lira;20A4 -liwnarmenian;056C -lj;01C9 -ljecyrillic;0459 -ll;F6C0 -lladeva;0933 -llagujarati;0AB3 -llinebelow;1E3B -llladeva;0934 -llvocalicbengali;09E1 -llvocalicdeva;0961 -llvocalicvowelsignbengali;09E3 -llvocalicvowelsigndeva;0963 -lmiddletilde;026B -lmonospace;FF4C -lmsquare;33D0 -lochulathai;0E2C -logicaland;2227 -logicalnot;00AC -logicalnotreversed;2310 -logicalor;2228 -lolingthai;0E25 -longs;017F -lowlinecenterline;FE4E -lowlinecmb;0332 -lowlinedashed;FE4D -lozenge;25CA -lparen;24A7 -lslash;0142 -lsquare;2113 -lsuperior;F6EE -ltshade;2591 -luthai;0E26 -lvocalicbengali;098C -lvocalicdeva;090C -lvocalicvowelsignbengali;09E2 -lvocalicvowelsigndeva;0962 -lxsquare;33D3 -m;006D -mabengali;09AE -macron;00AF -macronbelowcmb;0331 -macroncmb;0304 -macronlowmod;02CD -macronmonospace;FFE3 -macute;1E3F -madeva;092E -magujarati;0AAE -magurmukhi;0A2E -mahapakhhebrew;05A4 -mahapakhlefthebrew;05A4 -mahiragana;307E -maichattawalowleftthai;F895 -maichattawalowrightthai;F894 -maichattawathai;0E4B -maichattawaupperleftthai;F893 -maieklowleftthai;F88C -maieklowrightthai;F88B -maiekthai;0E48 -maiekupperleftthai;F88A -maihanakatleftthai;F884 -maihanakatthai;0E31 -maitaikhuleftthai;F889 -maitaikhuthai;0E47 -maitholowleftthai;F88F -maitholowrightthai;F88E -maithothai;0E49 -maithoupperleftthai;F88D -maitrilowleftthai;F892 -maitrilowrightthai;F891 -maitrithai;0E4A -maitriupperleftthai;F890 -maiyamokthai;0E46 -makatakana;30DE -makatakanahalfwidth;FF8F -male;2642 -mansyonsquare;3347 -maqafhebrew;05BE -mars;2642 -masoracirclehebrew;05AF -masquare;3383 -mbopomofo;3107 -mbsquare;33D4 -mcircle;24DC -mcubedsquare;33A5 -mdotaccent;1E41 -mdotbelow;1E43 -meemarabic;0645 -meemfinalarabic;FEE2 -meeminitialarabic;FEE3 -meemmedialarabic;FEE4 -meemmeeminitialarabic;FCD1 -meemmeemisolatedarabic;FC48 -meetorusquare;334D -mehiragana;3081 -meizierasquare;337E -mekatakana;30E1 -mekatakanahalfwidth;FF92 -mem;05DE -memdagesh;FB3E -memdageshhebrew;FB3E -memhebrew;05DE -menarmenian;0574 -merkhahebrew;05A5 -merkhakefulahebrew;05A6 -merkhakefulalefthebrew;05A6 -merkhalefthebrew;05A5 -mhook;0271 -mhzsquare;3392 -middledotkatakanahalfwidth;FF65 -middot;00B7 -mieumacirclekorean;3272 -mieumaparenkorean;3212 -mieumcirclekorean;3264 -mieumkorean;3141 -mieumpansioskorean;3170 -mieumparenkorean;3204 -mieumpieupkorean;316E -mieumsioskorean;316F -mihiragana;307F -mikatakana;30DF -mikatakanahalfwidth;FF90 -minus;2212 -minusbelowcmb;0320 -minuscircle;2296 -minusmod;02D7 -minusplus;2213 -minute;2032 -miribaarusquare;334A -mirisquare;3349 -mlonglegturned;0270 -mlsquare;3396 -mmcubedsquare;33A3 -mmonospace;FF4D -mmsquaredsquare;339F -mohiragana;3082 -mohmsquare;33C1 -mokatakana;30E2 -mokatakanahalfwidth;FF93 -molsquare;33D6 -momathai;0E21 -moverssquare;33A7 -moverssquaredsquare;33A8 -mparen;24A8 -mpasquare;33AB -mssquare;33B3 -msuperior;F6EF -mturned;026F -mu;00B5 -mu1;00B5 -muasquare;3382 -muchgreater;226B -muchless;226A -mufsquare;338C -mugreek;03BC -mugsquare;338D -muhiragana;3080 -mukatakana;30E0 -mukatakanahalfwidth;FF91 -mulsquare;3395 -multiply;00D7 -mumsquare;339B -munahhebrew;05A3 -munahlefthebrew;05A3 -musicalnote;266A -musicalnotedbl;266B -musicflatsign;266D -musicsharpsign;266F -mussquare;33B2 -muvsquare;33B6 -muwsquare;33BC -mvmegasquare;33B9 -mvsquare;33B7 -mwmegasquare;33BF -mwsquare;33BD -n;006E -nabengali;09A8 -nabla;2207 -nacute;0144 -nadeva;0928 -nagujarati;0AA8 -nagurmukhi;0A28 -nahiragana;306A -nakatakana;30CA -nakatakanahalfwidth;FF85 -napostrophe;0149 -nasquare;3381 -nbopomofo;310B -nbspace;00A0 -ncaron;0148 -ncedilla;0146 -ncircle;24DD -ncircumflexbelow;1E4B -ncommaaccent;0146 -ndotaccent;1E45 -ndotbelow;1E47 -nehiragana;306D -nekatakana;30CD -nekatakanahalfwidth;FF88 -newsheqelsign;20AA -nfsquare;338B -ngabengali;0999 -ngadeva;0919 -ngagujarati;0A99 -ngagurmukhi;0A19 -ngonguthai;0E07 -nhiragana;3093 -nhookleft;0272 -nhookretroflex;0273 -nieunacirclekorean;326F -nieunaparenkorean;320F -nieuncieuckorean;3135 -nieuncirclekorean;3261 -nieunhieuhkorean;3136 -nieunkorean;3134 -nieunpansioskorean;3168 -nieunparenkorean;3201 -nieunsioskorean;3167 -nieuntikeutkorean;3166 -nihiragana;306B -nikatakana;30CB -nikatakanahalfwidth;FF86 -nikhahitleftthai;F899 -nikhahitthai;0E4D -nine;0039 -ninearabic;0669 -ninebengali;09EF -ninecircle;2468 -ninecircleinversesansserif;2792 -ninedeva;096F -ninegujarati;0AEF -ninegurmukhi;0A6F -ninehackarabic;0669 -ninehangzhou;3029 -nineideographicparen;3228 -nineinferior;2089 -ninemonospace;FF19 -nineoldstyle;F739 -nineparen;247C -nineperiod;2490 -ninepersian;06F9 -nineroman;2178 -ninesuperior;2079 -nineteencircle;2472 -nineteenparen;2486 -nineteenperiod;249A -ninethai;0E59 -nj;01CC -njecyrillic;045A -nkatakana;30F3 -nkatakanahalfwidth;FF9D -nlegrightlong;019E -nlinebelow;1E49 -nmonospace;FF4E -nmsquare;339A -nnabengali;09A3 -nnadeva;0923 -nnagujarati;0AA3 -nnagurmukhi;0A23 -nnnadeva;0929 -nohiragana;306E -nokatakana;30CE -nokatakanahalfwidth;FF89 -nonbreakingspace;00A0 -nonenthai;0E13 -nonuthai;0E19 -noonarabic;0646 -noonfinalarabic;FEE6 -noonghunnaarabic;06BA -noonghunnafinalarabic;FB9F -noonhehinitialarabic;FEE7 FEEC -nooninitialarabic;FEE7 -noonjeeminitialarabic;FCD2 -noonjeemisolatedarabic;FC4B -noonmedialarabic;FEE8 -noonmeeminitialarabic;FCD5 -noonmeemisolatedarabic;FC4E -noonnoonfinalarabic;FC8D -notcontains;220C -notelement;2209 -notelementof;2209 -notequal;2260 -notgreater;226F -notgreaternorequal;2271 -notgreaternorless;2279 -notidentical;2262 -notless;226E -notlessnorequal;2270 -notparallel;2226 -notprecedes;2280 -notsubset;2284 -notsucceeds;2281 -notsuperset;2285 -nowarmenian;0576 -nparen;24A9 -nssquare;33B1 -nsuperior;207F -ntilde;00F1 -nu;03BD -nuhiragana;306C -nukatakana;30CC -nukatakanahalfwidth;FF87 -nuktabengali;09BC -nuktadeva;093C -nuktagujarati;0ABC -nuktagurmukhi;0A3C -numbersign;0023 -numbersignmonospace;FF03 -numbersignsmall;FE5F -numeralsigngreek;0374 -numeralsignlowergreek;0375 -numero;2116 -nun;05E0 -nundagesh;FB40 -nundageshhebrew;FB40 -nunhebrew;05E0 -nvsquare;33B5 -nwsquare;33BB -nyabengali;099E -nyadeva;091E -nyagujarati;0A9E -nyagurmukhi;0A1E -o;006F -oacute;00F3 -oangthai;0E2D -obarred;0275 -obarredcyrillic;04E9 -obarreddieresiscyrillic;04EB -obengali;0993 -obopomofo;311B -obreve;014F -ocandradeva;0911 -ocandragujarati;0A91 -ocandravowelsigndeva;0949 -ocandravowelsigngujarati;0AC9 -ocaron;01D2 -ocircle;24DE -ocircumflex;00F4 -ocircumflexacute;1ED1 -ocircumflexdotbelow;1ED9 -ocircumflexgrave;1ED3 -ocircumflexhookabove;1ED5 -ocircumflextilde;1ED7 -ocyrillic;043E -odblacute;0151 -odblgrave;020D -odeva;0913 -odieresis;00F6 -odieresiscyrillic;04E7 -odotbelow;1ECD -oe;0153 -oekorean;315A -ogonek;02DB -ogonekcmb;0328 -ograve;00F2 -ogujarati;0A93 -oharmenian;0585 -ohiragana;304A -ohookabove;1ECF -ohorn;01A1 -ohornacute;1EDB -ohorndotbelow;1EE3 -ohorngrave;1EDD -ohornhookabove;1EDF -ohorntilde;1EE1 -ohungarumlaut;0151 -oi;01A3 -oinvertedbreve;020F -okatakana;30AA -okatakanahalfwidth;FF75 -okorean;3157 -olehebrew;05AB -omacron;014D -omacronacute;1E53 -omacrongrave;1E51 -omdeva;0950 -omega;03C9 -omega1;03D6 -omegacyrillic;0461 -omegalatinclosed;0277 -omegaroundcyrillic;047B -omegatitlocyrillic;047D -omegatonos;03CE -omgujarati;0AD0 -omicron;03BF -omicrontonos;03CC -omonospace;FF4F -one;0031 -onearabic;0661 -onebengali;09E7 -onecircle;2460 -onecircleinversesansserif;278A -onedeva;0967 -onedotenleader;2024 -oneeighth;215B -onefitted;F6DC -onegujarati;0AE7 -onegurmukhi;0A67 -onehackarabic;0661 -onehalf;00BD -onehangzhou;3021 -oneideographicparen;3220 -oneinferior;2081 -onemonospace;FF11 -onenumeratorbengali;09F4 -oneoldstyle;F731 -oneparen;2474 -oneperiod;2488 -onepersian;06F1 -onequarter;00BC -oneroman;2170 -onesuperior;00B9 -onethai;0E51 -onethird;2153 -oogonek;01EB -oogonekmacron;01ED -oogurmukhi;0A13 -oomatragurmukhi;0A4B -oopen;0254 -oparen;24AA -openbullet;25E6 -option;2325 -ordfeminine;00AA -ordmasculine;00BA -orthogonal;221F -oshortdeva;0912 -oshortvowelsigndeva;094A -oslash;00F8 -oslashacute;01FF -osmallhiragana;3049 -osmallkatakana;30A9 -osmallkatakanahalfwidth;FF6B -ostrokeacute;01FF -osuperior;F6F0 -otcyrillic;047F -otilde;00F5 -otildeacute;1E4D -otildedieresis;1E4F -oubopomofo;3121 -overline;203E -overlinecenterline;FE4A -overlinecmb;0305 -overlinedashed;FE49 -overlinedblwavy;FE4C -overlinewavy;FE4B -overscore;00AF -ovowelsignbengali;09CB -ovowelsigndeva;094B -ovowelsigngujarati;0ACB -p;0070 -paampssquare;3380 -paasentosquare;332B -pabengali;09AA -pacute;1E55 -padeva;092A -pagedown;21DF -pageup;21DE -pagujarati;0AAA -pagurmukhi;0A2A -pahiragana;3071 -paiyannoithai;0E2F -pakatakana;30D1 -palatalizationcyrilliccmb;0484 -palochkacyrillic;04C0 -pansioskorean;317F -paragraph;00B6 -parallel;2225 -parenleft;0028 -parenleftaltonearabic;FD3E -parenleftbt;F8ED -parenleftex;F8EC -parenleftinferior;208D -parenleftmonospace;FF08 -parenleftsmall;FE59 -parenleftsuperior;207D -parenlefttp;F8EB -parenleftvertical;FE35 -parenright;0029 -parenrightaltonearabic;FD3F -parenrightbt;F8F8 -parenrightex;F8F7 -parenrightinferior;208E -parenrightmonospace;FF09 -parenrightsmall;FE5A -parenrightsuperior;207E -parenrighttp;F8F6 -parenrightvertical;FE36 -partialdiff;2202 -paseqhebrew;05C0 -pashtahebrew;0599 -pasquare;33A9 -patah;05B7 -patah11;05B7 -patah1d;05B7 -patah2a;05B7 -patahhebrew;05B7 -patahnarrowhebrew;05B7 -patahquarterhebrew;05B7 -patahwidehebrew;05B7 -pazerhebrew;05A1 -pbopomofo;3106 -pcircle;24DF -pdotaccent;1E57 -pe;05E4 -pecyrillic;043F -pedagesh;FB44 -pedageshhebrew;FB44 -peezisquare;333B -pefinaldageshhebrew;FB43 -peharabic;067E -peharmenian;057A -pehebrew;05E4 -pehfinalarabic;FB57 -pehinitialarabic;FB58 -pehiragana;307A -pehmedialarabic;FB59 -pekatakana;30DA -pemiddlehookcyrillic;04A7 -perafehebrew;FB4E -percent;0025 -percentarabic;066A -percentmonospace;FF05 -percentsmall;FE6A -period;002E -periodarmenian;0589 -periodcentered;00B7 -periodhalfwidth;FF61 -periodinferior;F6E7 -periodmonospace;FF0E -periodsmall;FE52 -periodsuperior;F6E8 -perispomenigreekcmb;0342 -perpendicular;22A5 -perthousand;2030 -peseta;20A7 -pfsquare;338A -phabengali;09AB -phadeva;092B -phagujarati;0AAB -phagurmukhi;0A2B -phi;03C6 -phi1;03D5 -phieuphacirclekorean;327A -phieuphaparenkorean;321A -phieuphcirclekorean;326C -phieuphkorean;314D -phieuphparenkorean;320C -philatin;0278 -phinthuthai;0E3A -phisymbolgreek;03D5 -phook;01A5 -phophanthai;0E1E -phophungthai;0E1C -phosamphaothai;0E20 -pi;03C0 -pieupacirclekorean;3273 -pieupaparenkorean;3213 -pieupcieuckorean;3176 -pieupcirclekorean;3265 -pieupkiyeokkorean;3172 -pieupkorean;3142 -pieupparenkorean;3205 -pieupsioskiyeokkorean;3174 -pieupsioskorean;3144 -pieupsiostikeutkorean;3175 -pieupthieuthkorean;3177 -pieuptikeutkorean;3173 -pihiragana;3074 -pikatakana;30D4 -pisymbolgreek;03D6 -piwrarmenian;0583 -plus;002B -plusbelowcmb;031F -pluscircle;2295 -plusminus;00B1 -plusmod;02D6 -plusmonospace;FF0B -plussmall;FE62 -plussuperior;207A -pmonospace;FF50 -pmsquare;33D8 -pohiragana;307D -pointingindexdownwhite;261F -pointingindexleftwhite;261C -pointingindexrightwhite;261E -pointingindexupwhite;261D -pokatakana;30DD -poplathai;0E1B -postalmark;3012 -postalmarkface;3020 -pparen;24AB -precedes;227A -prescription;211E -primemod;02B9 -primereversed;2035 -product;220F -projective;2305 -prolongedkana;30FC -propellor;2318 -propersubset;2282 -propersuperset;2283 -proportion;2237 -proportional;221D -psi;03C8 -psicyrillic;0471 -psilipneumatacyrilliccmb;0486 -pssquare;33B0 -puhiragana;3077 -pukatakana;30D7 -pvsquare;33B4 -pwsquare;33BA -q;0071 -qadeva;0958 -qadmahebrew;05A8 -qafarabic;0642 -qaffinalarabic;FED6 -qafinitialarabic;FED7 -qafmedialarabic;FED8 -qamats;05B8 -qamats10;05B8 -qamats1a;05B8 -qamats1c;05B8 -qamats27;05B8 -qamats29;05B8 -qamats33;05B8 -qamatsde;05B8 -qamatshebrew;05B8 -qamatsnarrowhebrew;05B8 -qamatsqatanhebrew;05B8 -qamatsqatannarrowhebrew;05B8 -qamatsqatanquarterhebrew;05B8 -qamatsqatanwidehebrew;05B8 -qamatsquarterhebrew;05B8 -qamatswidehebrew;05B8 -qarneyparahebrew;059F -qbopomofo;3111 -qcircle;24E0 -qhook;02A0 -qmonospace;FF51 -qof;05E7 -qofdagesh;FB47 -qofdageshhebrew;FB47 -qofhatafpatah;05E7 05B2 -qofhatafpatahhebrew;05E7 05B2 -qofhatafsegol;05E7 05B1 -qofhatafsegolhebrew;05E7 05B1 -qofhebrew;05E7 -qofhiriq;05E7 05B4 -qofhiriqhebrew;05E7 05B4 -qofholam;05E7 05B9 -qofholamhebrew;05E7 05B9 -qofpatah;05E7 05B7 -qofpatahhebrew;05E7 05B7 -qofqamats;05E7 05B8 -qofqamatshebrew;05E7 05B8 -qofqubuts;05E7 05BB -qofqubutshebrew;05E7 05BB -qofsegol;05E7 05B6 -qofsegolhebrew;05E7 05B6 -qofsheva;05E7 05B0 -qofshevahebrew;05E7 05B0 -qoftsere;05E7 05B5 -qoftserehebrew;05E7 05B5 -qparen;24AC -quarternote;2669 -qubuts;05BB -qubuts18;05BB -qubuts25;05BB -qubuts31;05BB -qubutshebrew;05BB -qubutsnarrowhebrew;05BB -qubutsquarterhebrew;05BB -qubutswidehebrew;05BB -question;003F -questionarabic;061F -questionarmenian;055E -questiondown;00BF -questiondownsmall;F7BF -questiongreek;037E -questionmonospace;FF1F -questionsmall;F73F -quotedbl;0022 -quotedblbase;201E -quotedblleft;201C -quotedblmonospace;FF02 -quotedblprime;301E -quotedblprimereversed;301D -quotedblright;201D -quoteleft;2018 -quoteleftreversed;201B -quotereversed;201B -quoteright;2019 -quoterightn;0149 -quotesinglbase;201A -quotesingle;0027 -quotesinglemonospace;FF07 -r;0072 -raarmenian;057C -rabengali;09B0 -racute;0155 -radeva;0930 -radical;221A -radicalex;F8E5 -radoverssquare;33AE -radoverssquaredsquare;33AF -radsquare;33AD -rafe;05BF -rafehebrew;05BF -ragujarati;0AB0 -ragurmukhi;0A30 -rahiragana;3089 -rakatakana;30E9 -rakatakanahalfwidth;FF97 -ralowerdiagonalbengali;09F1 -ramiddlediagonalbengali;09F0 -ramshorn;0264 -ratio;2236 -rbopomofo;3116 -rcaron;0159 -rcedilla;0157 -rcircle;24E1 -rcommaaccent;0157 -rdblgrave;0211 -rdotaccent;1E59 -rdotbelow;1E5B -rdotbelowmacron;1E5D -referencemark;203B -reflexsubset;2286 -reflexsuperset;2287 -registered;00AE -registersans;F8E8 -registerserif;F6DA -reharabic;0631 -reharmenian;0580 -rehfinalarabic;FEAE -rehiragana;308C -rehyehaleflamarabic;0631 FEF3 FE8E 0644 -rekatakana;30EC -rekatakanahalfwidth;FF9A -resh;05E8 -reshdageshhebrew;FB48 -reshhatafpatah;05E8 05B2 -reshhatafpatahhebrew;05E8 05B2 -reshhatafsegol;05E8 05B1 -reshhatafsegolhebrew;05E8 05B1 -reshhebrew;05E8 -reshhiriq;05E8 05B4 -reshhiriqhebrew;05E8 05B4 -reshholam;05E8 05B9 -reshholamhebrew;05E8 05B9 -reshpatah;05E8 05B7 -reshpatahhebrew;05E8 05B7 -reshqamats;05E8 05B8 -reshqamatshebrew;05E8 05B8 -reshqubuts;05E8 05BB -reshqubutshebrew;05E8 05BB -reshsegol;05E8 05B6 -reshsegolhebrew;05E8 05B6 -reshsheva;05E8 05B0 -reshshevahebrew;05E8 05B0 -reshtsere;05E8 05B5 -reshtserehebrew;05E8 05B5 -reversedtilde;223D -reviahebrew;0597 -reviamugrashhebrew;0597 -revlogicalnot;2310 -rfishhook;027E -rfishhookreversed;027F -rhabengali;09DD -rhadeva;095D -rho;03C1 -rhook;027D -rhookturned;027B -rhookturnedsuperior;02B5 -rhosymbolgreek;03F1 -rhotichookmod;02DE -rieulacirclekorean;3271 -rieulaparenkorean;3211 -rieulcirclekorean;3263 -rieulhieuhkorean;3140 -rieulkiyeokkorean;313A -rieulkiyeoksioskorean;3169 -rieulkorean;3139 -rieulmieumkorean;313B -rieulpansioskorean;316C -rieulparenkorean;3203 -rieulphieuphkorean;313F -rieulpieupkorean;313C -rieulpieupsioskorean;316B -rieulsioskorean;313D -rieulthieuthkorean;313E -rieultikeutkorean;316A -rieulyeorinhieuhkorean;316D -rightangle;221F -righttackbelowcmb;0319 -righttriangle;22BF -rihiragana;308A -rikatakana;30EA -rikatakanahalfwidth;FF98 -ring;02DA -ringbelowcmb;0325 -ringcmb;030A -ringhalfleft;02BF -ringhalfleftarmenian;0559 -ringhalfleftbelowcmb;031C -ringhalfleftcentered;02D3 -ringhalfright;02BE -ringhalfrightbelowcmb;0339 -ringhalfrightcentered;02D2 -rinvertedbreve;0213 -rittorusquare;3351 -rlinebelow;1E5F -rlongleg;027C -rlonglegturned;027A -rmonospace;FF52 -rohiragana;308D -rokatakana;30ED -rokatakanahalfwidth;FF9B -roruathai;0E23 -rparen;24AD -rrabengali;09DC -rradeva;0931 -rragurmukhi;0A5C -rreharabic;0691 -rrehfinalarabic;FB8D -rrvocalicbengali;09E0 -rrvocalicdeva;0960 -rrvocalicgujarati;0AE0 -rrvocalicvowelsignbengali;09C4 -rrvocalicvowelsigndeva;0944 -rrvocalicvowelsigngujarati;0AC4 -rsuperior;F6F1 -rtblock;2590 -rturned;0279 -rturnedsuperior;02B4 -ruhiragana;308B -rukatakana;30EB -rukatakanahalfwidth;FF99 -rupeemarkbengali;09F2 -rupeesignbengali;09F3 -rupiah;F6DD -ruthai;0E24 -rvocalicbengali;098B -rvocalicdeva;090B -rvocalicgujarati;0A8B -rvocalicvowelsignbengali;09C3 -rvocalicvowelsigndeva;0943 -rvocalicvowelsigngujarati;0AC3 -s;0073 -sabengali;09B8 -sacute;015B -sacutedotaccent;1E65 -sadarabic;0635 -sadeva;0938 -sadfinalarabic;FEBA -sadinitialarabic;FEBB -sadmedialarabic;FEBC -sagujarati;0AB8 -sagurmukhi;0A38 -sahiragana;3055 -sakatakana;30B5 -sakatakanahalfwidth;FF7B -sallallahoualayhewasallamarabic;FDFA -samekh;05E1 -samekhdagesh;FB41 -samekhdageshhebrew;FB41 -samekhhebrew;05E1 -saraaathai;0E32 -saraaethai;0E41 -saraaimaimalaithai;0E44 -saraaimaimuanthai;0E43 -saraamthai;0E33 -saraathai;0E30 -saraethai;0E40 -saraiileftthai;F886 -saraiithai;0E35 -saraileftthai;F885 -saraithai;0E34 -saraothai;0E42 -saraueeleftthai;F888 -saraueethai;0E37 -saraueleftthai;F887 -sarauethai;0E36 -sarauthai;0E38 -sarauuthai;0E39 -sbopomofo;3119 -scaron;0161 -scarondotaccent;1E67 -scedilla;015F -schwa;0259 -schwacyrillic;04D9 -schwadieresiscyrillic;04DB -schwahook;025A -scircle;24E2 -scircumflex;015D -scommaaccent;0219 -sdotaccent;1E61 -sdotbelow;1E63 -sdotbelowdotaccent;1E69 -seagullbelowcmb;033C -second;2033 -secondtonechinese;02CA -section;00A7 -seenarabic;0633 -seenfinalarabic;FEB2 -seeninitialarabic;FEB3 -seenmedialarabic;FEB4 -segol;05B6 -segol13;05B6 -segol1f;05B6 -segol2c;05B6 -segolhebrew;05B6 -segolnarrowhebrew;05B6 -segolquarterhebrew;05B6 -segoltahebrew;0592 -segolwidehebrew;05B6 -seharmenian;057D -sehiragana;305B -sekatakana;30BB -sekatakanahalfwidth;FF7E -semicolon;003B -semicolonarabic;061B -semicolonmonospace;FF1B -semicolonsmall;FE54 -semivoicedmarkkana;309C -semivoicedmarkkanahalfwidth;FF9F -sentisquare;3322 -sentosquare;3323 -seven;0037 -sevenarabic;0667 -sevenbengali;09ED -sevencircle;2466 -sevencircleinversesansserif;2790 -sevendeva;096D -seveneighths;215E -sevengujarati;0AED -sevengurmukhi;0A6D -sevenhackarabic;0667 -sevenhangzhou;3027 -sevenideographicparen;3226 -seveninferior;2087 -sevenmonospace;FF17 -sevenoldstyle;F737 -sevenparen;247A -sevenperiod;248E -sevenpersian;06F7 -sevenroman;2176 -sevensuperior;2077 -seventeencircle;2470 -seventeenparen;2484 -seventeenperiod;2498 -seventhai;0E57 -sfthyphen;00AD -shaarmenian;0577 -shabengali;09B6 -shacyrillic;0448 -shaddaarabic;0651 -shaddadammaarabic;FC61 -shaddadammatanarabic;FC5E -shaddafathaarabic;FC60 -shaddafathatanarabic;0651 064B -shaddakasraarabic;FC62 -shaddakasratanarabic;FC5F -shade;2592 -shadedark;2593 -shadelight;2591 -shademedium;2592 -shadeva;0936 -shagujarati;0AB6 -shagurmukhi;0A36 -shalshelethebrew;0593 -shbopomofo;3115 -shchacyrillic;0449 -sheenarabic;0634 -sheenfinalarabic;FEB6 -sheeninitialarabic;FEB7 -sheenmedialarabic;FEB8 -sheicoptic;03E3 -sheqel;20AA -sheqelhebrew;20AA -sheva;05B0 -sheva115;05B0 -sheva15;05B0 -sheva22;05B0 -sheva2e;05B0 -shevahebrew;05B0 -shevanarrowhebrew;05B0 -shevaquarterhebrew;05B0 -shevawidehebrew;05B0 -shhacyrillic;04BB -shimacoptic;03ED -shin;05E9 -shindagesh;FB49 -shindageshhebrew;FB49 -shindageshshindot;FB2C -shindageshshindothebrew;FB2C -shindageshsindot;FB2D -shindageshsindothebrew;FB2D -shindothebrew;05C1 -shinhebrew;05E9 -shinshindot;FB2A -shinshindothebrew;FB2A -shinsindot;FB2B -shinsindothebrew;FB2B -shook;0282 -sigma;03C3 -sigma1;03C2 -sigmafinal;03C2 -sigmalunatesymbolgreek;03F2 -sihiragana;3057 -sikatakana;30B7 -sikatakanahalfwidth;FF7C -siluqhebrew;05BD -siluqlefthebrew;05BD -similar;223C -sindothebrew;05C2 -siosacirclekorean;3274 -siosaparenkorean;3214 -sioscieuckorean;317E -sioscirclekorean;3266 -sioskiyeokkorean;317A -sioskorean;3145 -siosnieunkorean;317B -siosparenkorean;3206 -siospieupkorean;317D -siostikeutkorean;317C -six;0036 -sixarabic;0666 -sixbengali;09EC -sixcircle;2465 -sixcircleinversesansserif;278F -sixdeva;096C -sixgujarati;0AEC -sixgurmukhi;0A6C -sixhackarabic;0666 -sixhangzhou;3026 -sixideographicparen;3225 -sixinferior;2086 -sixmonospace;FF16 -sixoldstyle;F736 -sixparen;2479 -sixperiod;248D -sixpersian;06F6 -sixroman;2175 -sixsuperior;2076 -sixteencircle;246F -sixteencurrencydenominatorbengali;09F9 -sixteenparen;2483 -sixteenperiod;2497 -sixthai;0E56 -slash;002F -slashmonospace;FF0F -slong;017F -slongdotaccent;1E9B -smileface;263A -smonospace;FF53 -sofpasuqhebrew;05C3 -softhyphen;00AD -softsigncyrillic;044C -sohiragana;305D -sokatakana;30BD -sokatakanahalfwidth;FF7F -soliduslongoverlaycmb;0338 -solidusshortoverlaycmb;0337 -sorusithai;0E29 -sosalathai;0E28 -sosothai;0E0B -sosuathai;0E2A -space;0020 -spacehackarabic;0020 -spade;2660 -spadesuitblack;2660 -spadesuitwhite;2664 -sparen;24AE -squarebelowcmb;033B -squarecc;33C4 -squarecm;339D -squarediagonalcrosshatchfill;25A9 -squarehorizontalfill;25A4 -squarekg;338F -squarekm;339E -squarekmcapital;33CE -squareln;33D1 -squarelog;33D2 -squaremg;338E -squaremil;33D5 -squaremm;339C -squaremsquared;33A1 -squareorthogonalcrosshatchfill;25A6 -squareupperlefttolowerrightfill;25A7 -squareupperrighttolowerleftfill;25A8 -squareverticalfill;25A5 -squarewhitewithsmallblack;25A3 -srsquare;33DB -ssabengali;09B7 -ssadeva;0937 -ssagujarati;0AB7 -ssangcieuckorean;3149 -ssanghieuhkorean;3185 -ssangieungkorean;3180 -ssangkiyeokkorean;3132 -ssangnieunkorean;3165 -ssangpieupkorean;3143 -ssangsioskorean;3146 -ssangtikeutkorean;3138 -ssuperior;F6F2 -sterling;00A3 -sterlingmonospace;FFE1 -strokelongoverlaycmb;0336 -strokeshortoverlaycmb;0335 -subset;2282 -subsetnotequal;228A -subsetorequal;2286 -succeeds;227B -suchthat;220B -suhiragana;3059 -sukatakana;30B9 -sukatakanahalfwidth;FF7D -sukunarabic;0652 -summation;2211 -sun;263C -superset;2283 -supersetnotequal;228B -supersetorequal;2287 -svsquare;33DC -syouwaerasquare;337C -t;0074 -tabengali;09A4 -tackdown;22A4 -tackleft;22A3 -tadeva;0924 -tagujarati;0AA4 -tagurmukhi;0A24 -taharabic;0637 -tahfinalarabic;FEC2 -tahinitialarabic;FEC3 -tahiragana;305F -tahmedialarabic;FEC4 -taisyouerasquare;337D -takatakana;30BF -takatakanahalfwidth;FF80 -tatweelarabic;0640 -tau;03C4 -tav;05EA -tavdages;FB4A -tavdagesh;FB4A -tavdageshhebrew;FB4A -tavhebrew;05EA -tbar;0167 -tbopomofo;310A -tcaron;0165 -tccurl;02A8 -tcedilla;0163 -tcheharabic;0686 -tchehfinalarabic;FB7B -tchehinitialarabic;FB7C -tchehmedialarabic;FB7D -tchehmeeminitialarabic;FB7C FEE4 -tcircle;24E3 -tcircumflexbelow;1E71 -tcommaaccent;0163 -tdieresis;1E97 -tdotaccent;1E6B -tdotbelow;1E6D -tecyrillic;0442 -tedescendercyrillic;04AD -teharabic;062A -tehfinalarabic;FE96 -tehhahinitialarabic;FCA2 -tehhahisolatedarabic;FC0C -tehinitialarabic;FE97 -tehiragana;3066 -tehjeeminitialarabic;FCA1 -tehjeemisolatedarabic;FC0B -tehmarbutaarabic;0629 -tehmarbutafinalarabic;FE94 -tehmedialarabic;FE98 -tehmeeminitialarabic;FCA4 -tehmeemisolatedarabic;FC0E -tehnoonfinalarabic;FC73 -tekatakana;30C6 -tekatakanahalfwidth;FF83 -telephone;2121 -telephoneblack;260E -telishagedolahebrew;05A0 -telishaqetanahebrew;05A9 -tencircle;2469 -tenideographicparen;3229 -tenparen;247D -tenperiod;2491 -tenroman;2179 -tesh;02A7 -tet;05D8 -tetdagesh;FB38 -tetdageshhebrew;FB38 -tethebrew;05D8 -tetsecyrillic;04B5 -tevirhebrew;059B -tevirlefthebrew;059B -thabengali;09A5 -thadeva;0925 -thagujarati;0AA5 -thagurmukhi;0A25 -thalarabic;0630 -thalfinalarabic;FEAC -thanthakhatlowleftthai;F898 -thanthakhatlowrightthai;F897 -thanthakhatthai;0E4C -thanthakhatupperleftthai;F896 -theharabic;062B -thehfinalarabic;FE9A -thehinitialarabic;FE9B -thehmedialarabic;FE9C -thereexists;2203 -therefore;2234 -theta;03B8 -theta1;03D1 -thetasymbolgreek;03D1 -thieuthacirclekorean;3279 -thieuthaparenkorean;3219 -thieuthcirclekorean;326B -thieuthkorean;314C -thieuthparenkorean;320B -thirteencircle;246C -thirteenparen;2480 -thirteenperiod;2494 -thonangmonthothai;0E11 -thook;01AD -thophuthaothai;0E12 -thorn;00FE -thothahanthai;0E17 -thothanthai;0E10 -thothongthai;0E18 -thothungthai;0E16 -thousandcyrillic;0482 -thousandsseparatorarabic;066C -thousandsseparatorpersian;066C -three;0033 -threearabic;0663 -threebengali;09E9 -threecircle;2462 -threecircleinversesansserif;278C -threedeva;0969 -threeeighths;215C -threegujarati;0AE9 -threegurmukhi;0A69 -threehackarabic;0663 -threehangzhou;3023 -threeideographicparen;3222 -threeinferior;2083 -threemonospace;FF13 -threenumeratorbengali;09F6 -threeoldstyle;F733 -threeparen;2476 -threeperiod;248A -threepersian;06F3 -threequarters;00BE -threequartersemdash;F6DE -threeroman;2172 -threesuperior;00B3 -threethai;0E53 -thzsquare;3394 -tihiragana;3061 -tikatakana;30C1 -tikatakanahalfwidth;FF81 -tikeutacirclekorean;3270 -tikeutaparenkorean;3210 -tikeutcirclekorean;3262 -tikeutkorean;3137 -tikeutparenkorean;3202 -tilde;02DC -tildebelowcmb;0330 -tildecmb;0303 -tildecomb;0303 -tildedoublecmb;0360 -tildeoperator;223C -tildeoverlaycmb;0334 -tildeverticalcmb;033E -timescircle;2297 -tipehahebrew;0596 -tipehalefthebrew;0596 -tippigurmukhi;0A70 -titlocyrilliccmb;0483 -tiwnarmenian;057F -tlinebelow;1E6F -tmonospace;FF54 -toarmenian;0569 -tohiragana;3068 -tokatakana;30C8 -tokatakanahalfwidth;FF84 -tonebarextrahighmod;02E5 -tonebarextralowmod;02E9 -tonebarhighmod;02E6 -tonebarlowmod;02E8 -tonebarmidmod;02E7 -tonefive;01BD -tonesix;0185 -tonetwo;01A8 -tonos;0384 -tonsquare;3327 -topatakthai;0E0F -tortoiseshellbracketleft;3014 -tortoiseshellbracketleftsmall;FE5D -tortoiseshellbracketleftvertical;FE39 -tortoiseshellbracketright;3015 -tortoiseshellbracketrightsmall;FE5E -tortoiseshellbracketrightvertical;FE3A -totaothai;0E15 -tpalatalhook;01AB -tparen;24AF -trademark;2122 -trademarksans;F8EA -trademarkserif;F6DB -tretroflexhook;0288 -triagdn;25BC -triaglf;25C4 -triagrt;25BA -triagup;25B2 -ts;02A6 -tsadi;05E6 -tsadidagesh;FB46 -tsadidageshhebrew;FB46 -tsadihebrew;05E6 -tsecyrillic;0446 -tsere;05B5 -tsere12;05B5 -tsere1e;05B5 -tsere2b;05B5 -tserehebrew;05B5 -tserenarrowhebrew;05B5 -tserequarterhebrew;05B5 -tserewidehebrew;05B5 -tshecyrillic;045B -tsuperior;F6F3 -ttabengali;099F -ttadeva;091F -ttagujarati;0A9F -ttagurmukhi;0A1F -tteharabic;0679 -ttehfinalarabic;FB67 -ttehinitialarabic;FB68 -ttehmedialarabic;FB69 -tthabengali;09A0 -tthadeva;0920 -tthagujarati;0AA0 -tthagurmukhi;0A20 -tturned;0287 -tuhiragana;3064 -tukatakana;30C4 -tukatakanahalfwidth;FF82 -tusmallhiragana;3063 -tusmallkatakana;30C3 -tusmallkatakanahalfwidth;FF6F -twelvecircle;246B -twelveparen;247F -twelveperiod;2493 -twelveroman;217B -twentycircle;2473 -twentyhangzhou;5344 -twentyparen;2487 -twentyperiod;249B -two;0032 -twoarabic;0662 -twobengali;09E8 -twocircle;2461 -twocircleinversesansserif;278B -twodeva;0968 -twodotenleader;2025 -twodotleader;2025 -twodotleadervertical;FE30 -twogujarati;0AE8 -twogurmukhi;0A68 -twohackarabic;0662 -twohangzhou;3022 -twoideographicparen;3221 -twoinferior;2082 -twomonospace;FF12 -twonumeratorbengali;09F5 -twooldstyle;F732 -twoparen;2475 -twoperiod;2489 -twopersian;06F2 -tworoman;2171 -twostroke;01BB -twosuperior;00B2 -twothai;0E52 -twothirds;2154 -u;0075 -uacute;00FA -ubar;0289 -ubengali;0989 -ubopomofo;3128 -ubreve;016D -ucaron;01D4 -ucircle;24E4 -ucircumflex;00FB -ucircumflexbelow;1E77 -ucyrillic;0443 -udattadeva;0951 -udblacute;0171 -udblgrave;0215 -udeva;0909 -udieresis;00FC -udieresisacute;01D8 -udieresisbelow;1E73 -udieresiscaron;01DA -udieresiscyrillic;04F1 -udieresisgrave;01DC -udieresismacron;01D6 -udotbelow;1EE5 -ugrave;00F9 -ugujarati;0A89 -ugurmukhi;0A09 -uhiragana;3046 -uhookabove;1EE7 -uhorn;01B0 -uhornacute;1EE9 -uhorndotbelow;1EF1 -uhorngrave;1EEB -uhornhookabove;1EED -uhorntilde;1EEF -uhungarumlaut;0171 -uhungarumlautcyrillic;04F3 -uinvertedbreve;0217 -ukatakana;30A6 -ukatakanahalfwidth;FF73 -ukcyrillic;0479 -ukorean;315C -umacron;016B -umacroncyrillic;04EF -umacrondieresis;1E7B -umatragurmukhi;0A41 -umonospace;FF55 -underscore;005F -underscoredbl;2017 -underscoremonospace;FF3F -underscorevertical;FE33 -underscorewavy;FE4F -union;222A -universal;2200 -uogonek;0173 -uparen;24B0 -upblock;2580 -upperdothebrew;05C4 -upsilon;03C5 -upsilondieresis;03CB -upsilondieresistonos;03B0 -upsilonlatin;028A -upsilontonos;03CD -uptackbelowcmb;031D -uptackmod;02D4 -uragurmukhi;0A73 -uring;016F -ushortcyrillic;045E -usmallhiragana;3045 -usmallkatakana;30A5 -usmallkatakanahalfwidth;FF69 -ustraightcyrillic;04AF -ustraightstrokecyrillic;04B1 -utilde;0169 -utildeacute;1E79 -utildebelow;1E75 -uubengali;098A -uudeva;090A -uugujarati;0A8A -uugurmukhi;0A0A -uumatragurmukhi;0A42 -uuvowelsignbengali;09C2 -uuvowelsigndeva;0942 -uuvowelsigngujarati;0AC2 -uvowelsignbengali;09C1 -uvowelsigndeva;0941 -uvowelsigngujarati;0AC1 -v;0076 -vadeva;0935 -vagujarati;0AB5 -vagurmukhi;0A35 -vakatakana;30F7 -vav;05D5 -vavdagesh;FB35 -vavdagesh65;FB35 -vavdageshhebrew;FB35 -vavhebrew;05D5 -vavholam;FB4B -vavholamhebrew;FB4B -vavvavhebrew;05F0 -vavyodhebrew;05F1 -vcircle;24E5 -vdotbelow;1E7F -vecyrillic;0432 -veharabic;06A4 -vehfinalarabic;FB6B -vehinitialarabic;FB6C -vehmedialarabic;FB6D -vekatakana;30F9 -venus;2640 -verticalbar;007C -verticallineabovecmb;030D -verticallinebelowcmb;0329 -verticallinelowmod;02CC -verticallinemod;02C8 -vewarmenian;057E -vhook;028B -vikatakana;30F8 -viramabengali;09CD -viramadeva;094D -viramagujarati;0ACD -visargabengali;0983 -visargadeva;0903 -visargagujarati;0A83 -vmonospace;FF56 -voarmenian;0578 -voicediterationhiragana;309E -voicediterationkatakana;30FE -voicedmarkkana;309B -voicedmarkkanahalfwidth;FF9E -vokatakana;30FA -vparen;24B1 -vtilde;1E7D -vturned;028C -vuhiragana;3094 -vukatakana;30F4 -w;0077 -wacute;1E83 -waekorean;3159 -wahiragana;308F -wakatakana;30EF -wakatakanahalfwidth;FF9C -wakorean;3158 -wasmallhiragana;308E -wasmallkatakana;30EE -wattosquare;3357 -wavedash;301C -wavyunderscorevertical;FE34 -wawarabic;0648 -wawfinalarabic;FEEE -wawhamzaabovearabic;0624 -wawhamzaabovefinalarabic;FE86 -wbsquare;33DD -wcircle;24E6 -wcircumflex;0175 -wdieresis;1E85 -wdotaccent;1E87 -wdotbelow;1E89 -wehiragana;3091 -weierstrass;2118 -wekatakana;30F1 -wekorean;315E -weokorean;315D -wgrave;1E81 -whitebullet;25E6 -whitecircle;25CB -whitecircleinverse;25D9 -whitecornerbracketleft;300E -whitecornerbracketleftvertical;FE43 -whitecornerbracketright;300F -whitecornerbracketrightvertical;FE44 -whitediamond;25C7 -whitediamondcontainingblacksmalldiamond;25C8 -whitedownpointingsmalltriangle;25BF -whitedownpointingtriangle;25BD -whiteleftpointingsmalltriangle;25C3 -whiteleftpointingtriangle;25C1 -whitelenticularbracketleft;3016 -whitelenticularbracketright;3017 -whiterightpointingsmalltriangle;25B9 -whiterightpointingtriangle;25B7 -whitesmallsquare;25AB -whitesmilingface;263A -whitesquare;25A1 -whitestar;2606 -whitetelephone;260F -whitetortoiseshellbracketleft;3018 -whitetortoiseshellbracketright;3019 -whiteuppointingsmalltriangle;25B5 -whiteuppointingtriangle;25B3 -wihiragana;3090 -wikatakana;30F0 -wikorean;315F -wmonospace;FF57 -wohiragana;3092 -wokatakana;30F2 -wokatakanahalfwidth;FF66 -won;20A9 -wonmonospace;FFE6 -wowaenthai;0E27 -wparen;24B2 -wring;1E98 -wsuperior;02B7 -wturned;028D -wynn;01BF -x;0078 -xabovecmb;033D -xbopomofo;3112 -xcircle;24E7 -xdieresis;1E8D -xdotaccent;1E8B -xeharmenian;056D -xi;03BE -xmonospace;FF58 -xparen;24B3 -xsuperior;02E3 -y;0079 -yaadosquare;334E -yabengali;09AF -yacute;00FD -yadeva;092F -yaekorean;3152 -yagujarati;0AAF -yagurmukhi;0A2F -yahiragana;3084 -yakatakana;30E4 -yakatakanahalfwidth;FF94 -yakorean;3151 -yamakkanthai;0E4E -yasmallhiragana;3083 -yasmallkatakana;30E3 -yasmallkatakanahalfwidth;FF6C -yatcyrillic;0463 -ycircle;24E8 -ycircumflex;0177 -ydieresis;00FF -ydotaccent;1E8F -ydotbelow;1EF5 -yeharabic;064A -yehbarreearabic;06D2 -yehbarreefinalarabic;FBAF -yehfinalarabic;FEF2 -yehhamzaabovearabic;0626 -yehhamzaabovefinalarabic;FE8A -yehhamzaaboveinitialarabic;FE8B -yehhamzaabovemedialarabic;FE8C -yehinitialarabic;FEF3 -yehmedialarabic;FEF4 -yehmeeminitialarabic;FCDD -yehmeemisolatedarabic;FC58 -yehnoonfinalarabic;FC94 -yehthreedotsbelowarabic;06D1 -yekorean;3156 -yen;00A5 -yenmonospace;FFE5 -yeokorean;3155 -yeorinhieuhkorean;3186 -yerahbenyomohebrew;05AA -yerahbenyomolefthebrew;05AA -yericyrillic;044B -yerudieresiscyrillic;04F9 -yesieungkorean;3181 -yesieungpansioskorean;3183 -yesieungsioskorean;3182 -yetivhebrew;059A -ygrave;1EF3 -yhook;01B4 -yhookabove;1EF7 -yiarmenian;0575 -yicyrillic;0457 -yikorean;3162 -yinyang;262F -yiwnarmenian;0582 -ymonospace;FF59 -yod;05D9 -yoddagesh;FB39 -yoddageshhebrew;FB39 -yodhebrew;05D9 -yodyodhebrew;05F2 -yodyodpatahhebrew;FB1F -yohiragana;3088 -yoikorean;3189 -yokatakana;30E8 -yokatakanahalfwidth;FF96 -yokorean;315B -yosmallhiragana;3087 -yosmallkatakana;30E7 -yosmallkatakanahalfwidth;FF6E -yotgreek;03F3 -yoyaekorean;3188 -yoyakorean;3187 -yoyakthai;0E22 -yoyingthai;0E0D -yparen;24B4 -ypogegrammeni;037A -ypogegrammenigreekcmb;0345 -yr;01A6 -yring;1E99 -ysuperior;02B8 -ytilde;1EF9 -yturned;028E -yuhiragana;3086 -yuikorean;318C -yukatakana;30E6 -yukatakanahalfwidth;FF95 -yukorean;3160 -yusbigcyrillic;046B -yusbigiotifiedcyrillic;046D -yuslittlecyrillic;0467 -yuslittleiotifiedcyrillic;0469 -yusmallhiragana;3085 -yusmallkatakana;30E5 -yusmallkatakanahalfwidth;FF6D -yuyekorean;318B -yuyeokorean;318A -yyabengali;09DF -yyadeva;095F -z;007A -zaarmenian;0566 -zacute;017A -zadeva;095B -zagurmukhi;0A5B -zaharabic;0638 -zahfinalarabic;FEC6 -zahinitialarabic;FEC7 -zahiragana;3056 -zahmedialarabic;FEC8 -zainarabic;0632 -zainfinalarabic;FEB0 -zakatakana;30B6 -zaqefgadolhebrew;0595 -zaqefqatanhebrew;0594 -zarqahebrew;0598 -zayin;05D6 -zayindagesh;FB36 -zayindageshhebrew;FB36 -zayinhebrew;05D6 -zbopomofo;3117 -zcaron;017E -zcircle;24E9 -zcircumflex;1E91 -zcurl;0291 -zdot;017C -zdotaccent;017C -zdotbelow;1E93 -zecyrillic;0437 -zedescendercyrillic;0499 -zedieresiscyrillic;04DF -zehiragana;305C -zekatakana;30BC -zero;0030 -zeroarabic;0660 -zerobengali;09E6 -zerodeva;0966 -zerogujarati;0AE6 -zerogurmukhi;0A66 -zerohackarabic;0660 -zeroinferior;2080 -zeromonospace;FF10 -zerooldstyle;F730 -zeropersian;06F0 -zerosuperior;2070 -zerothai;0E50 -zerowidthjoiner;FEFF -zerowidthnonjoiner;200C -zerowidthspace;200B -zeta;03B6 -zhbopomofo;3113 -zhearmenian;056A -zhebrevecyrillic;04C2 -zhecyrillic;0436 -zhedescendercyrillic;0497 -zhedieresiscyrillic;04DD -zihiragana;3058 -zikatakana;30B8 -zinorhebrew;05AE -zlinebelow;1E95 -zmonospace;FF5A -zohiragana;305E -zokatakana;30BE -zparen;24B5 -zretroflexhook;0290 -zstroke;01B6 -zuhiragana;305A -zukatakana;30BA -# END -""" - - -_aglfnText = """\ -# ----------------------------------------------------------- -# Copyright 2002-2019 Adobe (http://www.adobe.com/). -# -# Redistribution and use in source and binary forms, with or -# without modification, are permitted provided that the -# following conditions are met: -# -# Redistributions of source code must retain the above -# copyright notice, this list of conditions and the following -# disclaimer. -# -# Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following -# disclaimer in the documentation and/or other materials -# provided with the distribution. -# -# Neither the name of Adobe nor the names of its contributors -# may be used to endorse or promote products derived from this -# software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND -# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# ----------------------------------------------------------- -# Name: Adobe Glyph List For New Fonts -# Table version: 1.7 -# Date: November 6, 2008 -# URL: https://github.com/adobe-type-tools/agl-aglfn -# -# Description: -# -# AGLFN (Adobe Glyph List For New Fonts) provides a list of base glyph -# names that are recommended for new fonts, which are compatible with -# the AGL (Adobe Glyph List) Specification, and which should be used -# as described in Section 6 of that document. AGLFN comprises the set -# of glyph names from AGL that map via the AGL Specification rules to -# the semantically correct UV (Unicode Value). For example, "Asmall" -# is omitted because AGL maps this glyph name to the PUA (Private Use -# Area) value U+F761, rather than to the UV that maps from the glyph -# name "A." Also omitted is "ffi," because AGL maps this to the -# Alphabetic Presentation Forms value U+FB03, rather than decomposing -# it into the following sequence of three UVs: U+0066, U+0066, and -# U+0069. The name "arrowvertex" has been omitted because this glyph -# now has a real UV, and AGL is now incorrect in mapping it to the PUA -# value U+F8E6. If you do not find an appropriate name for your glyph -# in this list, then please refer to Section 6 of the AGL -# Specification. -# -# Format: three semicolon-delimited fields: -# (1) Standard UV or CUS UV--four uppercase hexadecimal digits -# (2) Glyph name--upper/lowercase letters and digits -# (3) Character names: Unicode character names for standard UVs, and -# descriptive names for CUS UVs--uppercase letters, hyphen, and -# space -# -# The records are sorted by glyph name in increasing ASCII order, -# entries with the same glyph name are sorted in decreasing priority -# order, the UVs and Unicode character names are provided for -# convenience, lines starting with "#" are comments, and blank lines -# should be ignored. -# -# Revision History: -# -# 1.7 [6 November 2008] -# - Reverted to the original 1.4 and earlier mappings for Delta, -# Omega, and mu. -# - Removed mappings for "afii" names. These should now be assigned -# "uni" names. -# - Removed mappings for "commaaccent" names. These should now be -# assigned "uni" names. -# -# 1.6 [30 January 2006] -# - Completed work intended in 1.5. -# -# 1.5 [23 November 2005] -# - Removed duplicated block at end of file. -# - Changed mappings: -# 2206;Delta;INCREMENT changed to 0394;Delta;GREEK CAPITAL LETTER DELTA -# 2126;Omega;OHM SIGN changed to 03A9;Omega;GREEK CAPITAL LETTER OMEGA -# 03BC;mu;MICRO SIGN changed to 03BC;mu;GREEK SMALL LETTER MU -# - Corrected statement above about why "ffi" is omitted. -# -# 1.4 [24 September 2003] -# - Changed version to 1.4, to avoid confusion with the AGL 1.3. -# - Fixed spelling errors in the header. -# - Fully removed "arrowvertex," as it is mapped only to a PUA Unicode -# value in some fonts. -# -# 1.1 [17 April 2003] -# - Renamed [Tt]cedilla back to [Tt]commaaccent. -# -# 1.0 [31 January 2003] -# - Original version. -# - Derived from the AGLv1.2 by: -# removing the PUA area codes; -# removing duplicate Unicode mappings; and -# renaming "tcommaaccent" to "tcedilla" and "Tcommaaccent" to "Tcedilla" -# -0041;A;LATIN CAPITAL LETTER A -00C6;AE;LATIN CAPITAL LETTER AE -01FC;AEacute;LATIN CAPITAL LETTER AE WITH ACUTE -00C1;Aacute;LATIN CAPITAL LETTER A WITH ACUTE -0102;Abreve;LATIN CAPITAL LETTER A WITH BREVE -00C2;Acircumflex;LATIN CAPITAL LETTER A WITH CIRCUMFLEX -00C4;Adieresis;LATIN CAPITAL LETTER A WITH DIAERESIS -00C0;Agrave;LATIN CAPITAL LETTER A WITH GRAVE -0391;Alpha;GREEK CAPITAL LETTER ALPHA -0386;Alphatonos;GREEK CAPITAL LETTER ALPHA WITH TONOS -0100;Amacron;LATIN CAPITAL LETTER A WITH MACRON -0104;Aogonek;LATIN CAPITAL LETTER A WITH OGONEK -00C5;Aring;LATIN CAPITAL LETTER A WITH RING ABOVE -01FA;Aringacute;LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE -00C3;Atilde;LATIN CAPITAL LETTER A WITH TILDE -0042;B;LATIN CAPITAL LETTER B -0392;Beta;GREEK CAPITAL LETTER BETA -0043;C;LATIN CAPITAL LETTER C -0106;Cacute;LATIN CAPITAL LETTER C WITH ACUTE -010C;Ccaron;LATIN CAPITAL LETTER C WITH CARON -00C7;Ccedilla;LATIN CAPITAL LETTER C WITH CEDILLA -0108;Ccircumflex;LATIN CAPITAL LETTER C WITH CIRCUMFLEX -010A;Cdotaccent;LATIN CAPITAL LETTER C WITH DOT ABOVE -03A7;Chi;GREEK CAPITAL LETTER CHI -0044;D;LATIN CAPITAL LETTER D -010E;Dcaron;LATIN CAPITAL LETTER D WITH CARON -0110;Dcroat;LATIN CAPITAL LETTER D WITH STROKE -2206;Delta;INCREMENT -0045;E;LATIN CAPITAL LETTER E -00C9;Eacute;LATIN CAPITAL LETTER E WITH ACUTE -0114;Ebreve;LATIN CAPITAL LETTER E WITH BREVE -011A;Ecaron;LATIN CAPITAL LETTER E WITH CARON -00CA;Ecircumflex;LATIN CAPITAL LETTER E WITH CIRCUMFLEX -00CB;Edieresis;LATIN CAPITAL LETTER E WITH DIAERESIS -0116;Edotaccent;LATIN CAPITAL LETTER E WITH DOT ABOVE -00C8;Egrave;LATIN CAPITAL LETTER E WITH GRAVE -0112;Emacron;LATIN CAPITAL LETTER E WITH MACRON -014A;Eng;LATIN CAPITAL LETTER ENG -0118;Eogonek;LATIN CAPITAL LETTER E WITH OGONEK -0395;Epsilon;GREEK CAPITAL LETTER EPSILON -0388;Epsilontonos;GREEK CAPITAL LETTER EPSILON WITH TONOS -0397;Eta;GREEK CAPITAL LETTER ETA -0389;Etatonos;GREEK CAPITAL LETTER ETA WITH TONOS -00D0;Eth;LATIN CAPITAL LETTER ETH -20AC;Euro;EURO SIGN -0046;F;LATIN CAPITAL LETTER F -0047;G;LATIN CAPITAL LETTER G -0393;Gamma;GREEK CAPITAL LETTER GAMMA -011E;Gbreve;LATIN CAPITAL LETTER G WITH BREVE -01E6;Gcaron;LATIN CAPITAL LETTER G WITH CARON -011C;Gcircumflex;LATIN CAPITAL LETTER G WITH CIRCUMFLEX -0120;Gdotaccent;LATIN CAPITAL LETTER G WITH DOT ABOVE -0048;H;LATIN CAPITAL LETTER H -25CF;H18533;BLACK CIRCLE -25AA;H18543;BLACK SMALL SQUARE -25AB;H18551;WHITE SMALL SQUARE -25A1;H22073;WHITE SQUARE -0126;Hbar;LATIN CAPITAL LETTER H WITH STROKE -0124;Hcircumflex;LATIN CAPITAL LETTER H WITH CIRCUMFLEX -0049;I;LATIN CAPITAL LETTER I -0132;IJ;LATIN CAPITAL LIGATURE IJ -00CD;Iacute;LATIN CAPITAL LETTER I WITH ACUTE -012C;Ibreve;LATIN CAPITAL LETTER I WITH BREVE -00CE;Icircumflex;LATIN CAPITAL LETTER I WITH CIRCUMFLEX -00CF;Idieresis;LATIN CAPITAL LETTER I WITH DIAERESIS -0130;Idotaccent;LATIN CAPITAL LETTER I WITH DOT ABOVE -2111;Ifraktur;BLACK-LETTER CAPITAL I -00CC;Igrave;LATIN CAPITAL LETTER I WITH GRAVE -012A;Imacron;LATIN CAPITAL LETTER I WITH MACRON -012E;Iogonek;LATIN CAPITAL LETTER I WITH OGONEK -0399;Iota;GREEK CAPITAL LETTER IOTA -03AA;Iotadieresis;GREEK CAPITAL LETTER IOTA WITH DIALYTIKA -038A;Iotatonos;GREEK CAPITAL LETTER IOTA WITH TONOS -0128;Itilde;LATIN CAPITAL LETTER I WITH TILDE -004A;J;LATIN CAPITAL LETTER J -0134;Jcircumflex;LATIN CAPITAL LETTER J WITH CIRCUMFLEX -004B;K;LATIN CAPITAL LETTER K -039A;Kappa;GREEK CAPITAL LETTER KAPPA -004C;L;LATIN CAPITAL LETTER L -0139;Lacute;LATIN CAPITAL LETTER L WITH ACUTE -039B;Lambda;GREEK CAPITAL LETTER LAMDA -013D;Lcaron;LATIN CAPITAL LETTER L WITH CARON -013F;Ldot;LATIN CAPITAL LETTER L WITH MIDDLE DOT -0141;Lslash;LATIN CAPITAL LETTER L WITH STROKE -004D;M;LATIN CAPITAL LETTER M -039C;Mu;GREEK CAPITAL LETTER MU -004E;N;LATIN CAPITAL LETTER N -0143;Nacute;LATIN CAPITAL LETTER N WITH ACUTE -0147;Ncaron;LATIN CAPITAL LETTER N WITH CARON -00D1;Ntilde;LATIN CAPITAL LETTER N WITH TILDE -039D;Nu;GREEK CAPITAL LETTER NU -004F;O;LATIN CAPITAL LETTER O -0152;OE;LATIN CAPITAL LIGATURE OE -00D3;Oacute;LATIN CAPITAL LETTER O WITH ACUTE -014E;Obreve;LATIN CAPITAL LETTER O WITH BREVE -00D4;Ocircumflex;LATIN CAPITAL LETTER O WITH CIRCUMFLEX -00D6;Odieresis;LATIN CAPITAL LETTER O WITH DIAERESIS -00D2;Ograve;LATIN CAPITAL LETTER O WITH GRAVE -01A0;Ohorn;LATIN CAPITAL LETTER O WITH HORN -0150;Ohungarumlaut;LATIN CAPITAL LETTER O WITH DOUBLE ACUTE -014C;Omacron;LATIN CAPITAL LETTER O WITH MACRON -2126;Omega;OHM SIGN -038F;Omegatonos;GREEK CAPITAL LETTER OMEGA WITH TONOS -039F;Omicron;GREEK CAPITAL LETTER OMICRON -038C;Omicrontonos;GREEK CAPITAL LETTER OMICRON WITH TONOS -00D8;Oslash;LATIN CAPITAL LETTER O WITH STROKE -01FE;Oslashacute;LATIN CAPITAL LETTER O WITH STROKE AND ACUTE -00D5;Otilde;LATIN CAPITAL LETTER O WITH TILDE -0050;P;LATIN CAPITAL LETTER P -03A6;Phi;GREEK CAPITAL LETTER PHI -03A0;Pi;GREEK CAPITAL LETTER PI -03A8;Psi;GREEK CAPITAL LETTER PSI -0051;Q;LATIN CAPITAL LETTER Q -0052;R;LATIN CAPITAL LETTER R -0154;Racute;LATIN CAPITAL LETTER R WITH ACUTE -0158;Rcaron;LATIN CAPITAL LETTER R WITH CARON -211C;Rfraktur;BLACK-LETTER CAPITAL R -03A1;Rho;GREEK CAPITAL LETTER RHO -0053;S;LATIN CAPITAL LETTER S -250C;SF010000;BOX DRAWINGS LIGHT DOWN AND RIGHT -2514;SF020000;BOX DRAWINGS LIGHT UP AND RIGHT -2510;SF030000;BOX DRAWINGS LIGHT DOWN AND LEFT -2518;SF040000;BOX DRAWINGS LIGHT UP AND LEFT -253C;SF050000;BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL -252C;SF060000;BOX DRAWINGS LIGHT DOWN AND HORIZONTAL -2534;SF070000;BOX DRAWINGS LIGHT UP AND HORIZONTAL -251C;SF080000;BOX DRAWINGS LIGHT VERTICAL AND RIGHT -2524;SF090000;BOX DRAWINGS LIGHT VERTICAL AND LEFT -2500;SF100000;BOX DRAWINGS LIGHT HORIZONTAL -2502;SF110000;BOX DRAWINGS LIGHT VERTICAL -2561;SF190000;BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE -2562;SF200000;BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE -2556;SF210000;BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE -2555;SF220000;BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE -2563;SF230000;BOX DRAWINGS DOUBLE VERTICAL AND LEFT -2551;SF240000;BOX DRAWINGS DOUBLE VERTICAL -2557;SF250000;BOX DRAWINGS DOUBLE DOWN AND LEFT -255D;SF260000;BOX DRAWINGS DOUBLE UP AND LEFT -255C;SF270000;BOX DRAWINGS UP DOUBLE AND LEFT SINGLE -255B;SF280000;BOX DRAWINGS UP SINGLE AND LEFT DOUBLE -255E;SF360000;BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE -255F;SF370000;BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE -255A;SF380000;BOX DRAWINGS DOUBLE UP AND RIGHT -2554;SF390000;BOX DRAWINGS DOUBLE DOWN AND RIGHT -2569;SF400000;BOX DRAWINGS DOUBLE UP AND HORIZONTAL -2566;SF410000;BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL -2560;SF420000;BOX DRAWINGS DOUBLE VERTICAL AND RIGHT -2550;SF430000;BOX DRAWINGS DOUBLE HORIZONTAL -256C;SF440000;BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL -2567;SF450000;BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE -2568;SF460000;BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE -2564;SF470000;BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE -2565;SF480000;BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE -2559;SF490000;BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE -2558;SF500000;BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE -2552;SF510000;BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE -2553;SF520000;BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE -256B;SF530000;BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE -256A;SF540000;BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE -015A;Sacute;LATIN CAPITAL LETTER S WITH ACUTE -0160;Scaron;LATIN CAPITAL LETTER S WITH CARON -015E;Scedilla;LATIN CAPITAL LETTER S WITH CEDILLA -015C;Scircumflex;LATIN CAPITAL LETTER S WITH CIRCUMFLEX -03A3;Sigma;GREEK CAPITAL LETTER SIGMA -0054;T;LATIN CAPITAL LETTER T -03A4;Tau;GREEK CAPITAL LETTER TAU -0166;Tbar;LATIN CAPITAL LETTER T WITH STROKE -0164;Tcaron;LATIN CAPITAL LETTER T WITH CARON -0398;Theta;GREEK CAPITAL LETTER THETA -00DE;Thorn;LATIN CAPITAL LETTER THORN -0055;U;LATIN CAPITAL LETTER U -00DA;Uacute;LATIN CAPITAL LETTER U WITH ACUTE -016C;Ubreve;LATIN CAPITAL LETTER U WITH BREVE -00DB;Ucircumflex;LATIN CAPITAL LETTER U WITH CIRCUMFLEX -00DC;Udieresis;LATIN CAPITAL LETTER U WITH DIAERESIS -00D9;Ugrave;LATIN CAPITAL LETTER U WITH GRAVE -01AF;Uhorn;LATIN CAPITAL LETTER U WITH HORN -0170;Uhungarumlaut;LATIN CAPITAL LETTER U WITH DOUBLE ACUTE -016A;Umacron;LATIN CAPITAL LETTER U WITH MACRON -0172;Uogonek;LATIN CAPITAL LETTER U WITH OGONEK -03A5;Upsilon;GREEK CAPITAL LETTER UPSILON -03D2;Upsilon1;GREEK UPSILON WITH HOOK SYMBOL -03AB;Upsilondieresis;GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA -038E;Upsilontonos;GREEK CAPITAL LETTER UPSILON WITH TONOS -016E;Uring;LATIN CAPITAL LETTER U WITH RING ABOVE -0168;Utilde;LATIN CAPITAL LETTER U WITH TILDE -0056;V;LATIN CAPITAL LETTER V -0057;W;LATIN CAPITAL LETTER W -1E82;Wacute;LATIN CAPITAL LETTER W WITH ACUTE -0174;Wcircumflex;LATIN CAPITAL LETTER W WITH CIRCUMFLEX -1E84;Wdieresis;LATIN CAPITAL LETTER W WITH DIAERESIS -1E80;Wgrave;LATIN CAPITAL LETTER W WITH GRAVE -0058;X;LATIN CAPITAL LETTER X -039E;Xi;GREEK CAPITAL LETTER XI -0059;Y;LATIN CAPITAL LETTER Y -00DD;Yacute;LATIN CAPITAL LETTER Y WITH ACUTE -0176;Ycircumflex;LATIN CAPITAL LETTER Y WITH CIRCUMFLEX -0178;Ydieresis;LATIN CAPITAL LETTER Y WITH DIAERESIS -1EF2;Ygrave;LATIN CAPITAL LETTER Y WITH GRAVE -005A;Z;LATIN CAPITAL LETTER Z -0179;Zacute;LATIN CAPITAL LETTER Z WITH ACUTE -017D;Zcaron;LATIN CAPITAL LETTER Z WITH CARON -017B;Zdotaccent;LATIN CAPITAL LETTER Z WITH DOT ABOVE -0396;Zeta;GREEK CAPITAL LETTER ZETA -0061;a;LATIN SMALL LETTER A -00E1;aacute;LATIN SMALL LETTER A WITH ACUTE -0103;abreve;LATIN SMALL LETTER A WITH BREVE -00E2;acircumflex;LATIN SMALL LETTER A WITH CIRCUMFLEX -00B4;acute;ACUTE ACCENT -0301;acutecomb;COMBINING ACUTE ACCENT -00E4;adieresis;LATIN SMALL LETTER A WITH DIAERESIS -00E6;ae;LATIN SMALL LETTER AE -01FD;aeacute;LATIN SMALL LETTER AE WITH ACUTE -00E0;agrave;LATIN SMALL LETTER A WITH GRAVE -2135;aleph;ALEF SYMBOL -03B1;alpha;GREEK SMALL LETTER ALPHA -03AC;alphatonos;GREEK SMALL LETTER ALPHA WITH TONOS -0101;amacron;LATIN SMALL LETTER A WITH MACRON -0026;ampersand;AMPERSAND -2220;angle;ANGLE -2329;angleleft;LEFT-POINTING ANGLE BRACKET -232A;angleright;RIGHT-POINTING ANGLE BRACKET -0387;anoteleia;GREEK ANO TELEIA -0105;aogonek;LATIN SMALL LETTER A WITH OGONEK -2248;approxequal;ALMOST EQUAL TO -00E5;aring;LATIN SMALL LETTER A WITH RING ABOVE -01FB;aringacute;LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE -2194;arrowboth;LEFT RIGHT ARROW -21D4;arrowdblboth;LEFT RIGHT DOUBLE ARROW -21D3;arrowdbldown;DOWNWARDS DOUBLE ARROW -21D0;arrowdblleft;LEFTWARDS DOUBLE ARROW -21D2;arrowdblright;RIGHTWARDS DOUBLE ARROW -21D1;arrowdblup;UPWARDS DOUBLE ARROW -2193;arrowdown;DOWNWARDS ARROW -2190;arrowleft;LEFTWARDS ARROW -2192;arrowright;RIGHTWARDS ARROW -2191;arrowup;UPWARDS ARROW -2195;arrowupdn;UP DOWN ARROW -21A8;arrowupdnbse;UP DOWN ARROW WITH BASE -005E;asciicircum;CIRCUMFLEX ACCENT -007E;asciitilde;TILDE -002A;asterisk;ASTERISK -2217;asteriskmath;ASTERISK OPERATOR -0040;at;COMMERCIAL AT -00E3;atilde;LATIN SMALL LETTER A WITH TILDE -0062;b;LATIN SMALL LETTER B -005C;backslash;REVERSE SOLIDUS -007C;bar;VERTICAL LINE -03B2;beta;GREEK SMALL LETTER BETA -2588;block;FULL BLOCK -007B;braceleft;LEFT CURLY BRACKET -007D;braceright;RIGHT CURLY BRACKET -005B;bracketleft;LEFT SQUARE BRACKET -005D;bracketright;RIGHT SQUARE BRACKET -02D8;breve;BREVE -00A6;brokenbar;BROKEN BAR -2022;bullet;BULLET -0063;c;LATIN SMALL LETTER C -0107;cacute;LATIN SMALL LETTER C WITH ACUTE -02C7;caron;CARON -21B5;carriagereturn;DOWNWARDS ARROW WITH CORNER LEFTWARDS -010D;ccaron;LATIN SMALL LETTER C WITH CARON -00E7;ccedilla;LATIN SMALL LETTER C WITH CEDILLA -0109;ccircumflex;LATIN SMALL LETTER C WITH CIRCUMFLEX -010B;cdotaccent;LATIN SMALL LETTER C WITH DOT ABOVE -00B8;cedilla;CEDILLA -00A2;cent;CENT SIGN -03C7;chi;GREEK SMALL LETTER CHI -25CB;circle;WHITE CIRCLE -2297;circlemultiply;CIRCLED TIMES -2295;circleplus;CIRCLED PLUS -02C6;circumflex;MODIFIER LETTER CIRCUMFLEX ACCENT -2663;club;BLACK CLUB SUIT -003A;colon;COLON -20A1;colonmonetary;COLON SIGN -002C;comma;COMMA -2245;congruent;APPROXIMATELY EQUAL TO -00A9;copyright;COPYRIGHT SIGN -00A4;currency;CURRENCY SIGN -0064;d;LATIN SMALL LETTER D -2020;dagger;DAGGER -2021;daggerdbl;DOUBLE DAGGER -010F;dcaron;LATIN SMALL LETTER D WITH CARON -0111;dcroat;LATIN SMALL LETTER D WITH STROKE -00B0;degree;DEGREE SIGN -03B4;delta;GREEK SMALL LETTER DELTA -2666;diamond;BLACK DIAMOND SUIT -00A8;dieresis;DIAERESIS -0385;dieresistonos;GREEK DIALYTIKA TONOS -00F7;divide;DIVISION SIGN -2593;dkshade;DARK SHADE -2584;dnblock;LOWER HALF BLOCK -0024;dollar;DOLLAR SIGN -20AB;dong;DONG SIGN -02D9;dotaccent;DOT ABOVE -0323;dotbelowcomb;COMBINING DOT BELOW -0131;dotlessi;LATIN SMALL LETTER DOTLESS I -22C5;dotmath;DOT OPERATOR -0065;e;LATIN SMALL LETTER E -00E9;eacute;LATIN SMALL LETTER E WITH ACUTE -0115;ebreve;LATIN SMALL LETTER E WITH BREVE -011B;ecaron;LATIN SMALL LETTER E WITH CARON -00EA;ecircumflex;LATIN SMALL LETTER E WITH CIRCUMFLEX -00EB;edieresis;LATIN SMALL LETTER E WITH DIAERESIS -0117;edotaccent;LATIN SMALL LETTER E WITH DOT ABOVE -00E8;egrave;LATIN SMALL LETTER E WITH GRAVE -0038;eight;DIGIT EIGHT -2208;element;ELEMENT OF -2026;ellipsis;HORIZONTAL ELLIPSIS -0113;emacron;LATIN SMALL LETTER E WITH MACRON -2014;emdash;EM DASH -2205;emptyset;EMPTY SET -2013;endash;EN DASH -014B;eng;LATIN SMALL LETTER ENG -0119;eogonek;LATIN SMALL LETTER E WITH OGONEK -03B5;epsilon;GREEK SMALL LETTER EPSILON -03AD;epsilontonos;GREEK SMALL LETTER EPSILON WITH TONOS -003D;equal;EQUALS SIGN -2261;equivalence;IDENTICAL TO -212E;estimated;ESTIMATED SYMBOL -03B7;eta;GREEK SMALL LETTER ETA -03AE;etatonos;GREEK SMALL LETTER ETA WITH TONOS -00F0;eth;LATIN SMALL LETTER ETH -0021;exclam;EXCLAMATION MARK -203C;exclamdbl;DOUBLE EXCLAMATION MARK -00A1;exclamdown;INVERTED EXCLAMATION MARK -2203;existential;THERE EXISTS -0066;f;LATIN SMALL LETTER F -2640;female;FEMALE SIGN -2012;figuredash;FIGURE DASH -25A0;filledbox;BLACK SQUARE -25AC;filledrect;BLACK RECTANGLE -0035;five;DIGIT FIVE -215D;fiveeighths;VULGAR FRACTION FIVE EIGHTHS -0192;florin;LATIN SMALL LETTER F WITH HOOK -0034;four;DIGIT FOUR -2044;fraction;FRACTION SLASH -20A3;franc;FRENCH FRANC SIGN -0067;g;LATIN SMALL LETTER G -03B3;gamma;GREEK SMALL LETTER GAMMA -011F;gbreve;LATIN SMALL LETTER G WITH BREVE -01E7;gcaron;LATIN SMALL LETTER G WITH CARON -011D;gcircumflex;LATIN SMALL LETTER G WITH CIRCUMFLEX -0121;gdotaccent;LATIN SMALL LETTER G WITH DOT ABOVE -00DF;germandbls;LATIN SMALL LETTER SHARP S -2207;gradient;NABLA -0060;grave;GRAVE ACCENT -0300;gravecomb;COMBINING GRAVE ACCENT -003E;greater;GREATER-THAN SIGN -2265;greaterequal;GREATER-THAN OR EQUAL TO -00AB;guillemotleft;LEFT-POINTING DOUBLE ANGLE QUOTATION MARK -00BB;guillemotright;RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK -2039;guilsinglleft;SINGLE LEFT-POINTING ANGLE QUOTATION MARK -203A;guilsinglright;SINGLE RIGHT-POINTING ANGLE QUOTATION MARK -0068;h;LATIN SMALL LETTER H -0127;hbar;LATIN SMALL LETTER H WITH STROKE -0125;hcircumflex;LATIN SMALL LETTER H WITH CIRCUMFLEX -2665;heart;BLACK HEART SUIT -0309;hookabovecomb;COMBINING HOOK ABOVE -2302;house;HOUSE -02DD;hungarumlaut;DOUBLE ACUTE ACCENT -002D;hyphen;HYPHEN-MINUS -0069;i;LATIN SMALL LETTER I -00ED;iacute;LATIN SMALL LETTER I WITH ACUTE -012D;ibreve;LATIN SMALL LETTER I WITH BREVE -00EE;icircumflex;LATIN SMALL LETTER I WITH CIRCUMFLEX -00EF;idieresis;LATIN SMALL LETTER I WITH DIAERESIS -00EC;igrave;LATIN SMALL LETTER I WITH GRAVE -0133;ij;LATIN SMALL LIGATURE IJ -012B;imacron;LATIN SMALL LETTER I WITH MACRON -221E;infinity;INFINITY -222B;integral;INTEGRAL -2321;integralbt;BOTTOM HALF INTEGRAL -2320;integraltp;TOP HALF INTEGRAL -2229;intersection;INTERSECTION -25D8;invbullet;INVERSE BULLET -25D9;invcircle;INVERSE WHITE CIRCLE -263B;invsmileface;BLACK SMILING FACE -012F;iogonek;LATIN SMALL LETTER I WITH OGONEK -03B9;iota;GREEK SMALL LETTER IOTA -03CA;iotadieresis;GREEK SMALL LETTER IOTA WITH DIALYTIKA -0390;iotadieresistonos;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS -03AF;iotatonos;GREEK SMALL LETTER IOTA WITH TONOS -0129;itilde;LATIN SMALL LETTER I WITH TILDE -006A;j;LATIN SMALL LETTER J -0135;jcircumflex;LATIN SMALL LETTER J WITH CIRCUMFLEX -006B;k;LATIN SMALL LETTER K -03BA;kappa;GREEK SMALL LETTER KAPPA -0138;kgreenlandic;LATIN SMALL LETTER KRA -006C;l;LATIN SMALL LETTER L -013A;lacute;LATIN SMALL LETTER L WITH ACUTE -03BB;lambda;GREEK SMALL LETTER LAMDA -013E;lcaron;LATIN SMALL LETTER L WITH CARON -0140;ldot;LATIN SMALL LETTER L WITH MIDDLE DOT -003C;less;LESS-THAN SIGN -2264;lessequal;LESS-THAN OR EQUAL TO -258C;lfblock;LEFT HALF BLOCK -20A4;lira;LIRA SIGN -2227;logicaland;LOGICAL AND -00AC;logicalnot;NOT SIGN -2228;logicalor;LOGICAL OR -017F;longs;LATIN SMALL LETTER LONG S -25CA;lozenge;LOZENGE -0142;lslash;LATIN SMALL LETTER L WITH STROKE -2591;ltshade;LIGHT SHADE -006D;m;LATIN SMALL LETTER M -00AF;macron;MACRON -2642;male;MALE SIGN -2212;minus;MINUS SIGN -2032;minute;PRIME -00B5;mu;MICRO SIGN -00D7;multiply;MULTIPLICATION SIGN -266A;musicalnote;EIGHTH NOTE -266B;musicalnotedbl;BEAMED EIGHTH NOTES -006E;n;LATIN SMALL LETTER N -0144;nacute;LATIN SMALL LETTER N WITH ACUTE -0149;napostrophe;LATIN SMALL LETTER N PRECEDED BY APOSTROPHE -0148;ncaron;LATIN SMALL LETTER N WITH CARON -0039;nine;DIGIT NINE -2209;notelement;NOT AN ELEMENT OF -2260;notequal;NOT EQUAL TO -2284;notsubset;NOT A SUBSET OF -00F1;ntilde;LATIN SMALL LETTER N WITH TILDE -03BD;nu;GREEK SMALL LETTER NU -0023;numbersign;NUMBER SIGN -006F;o;LATIN SMALL LETTER O -00F3;oacute;LATIN SMALL LETTER O WITH ACUTE -014F;obreve;LATIN SMALL LETTER O WITH BREVE -00F4;ocircumflex;LATIN SMALL LETTER O WITH CIRCUMFLEX -00F6;odieresis;LATIN SMALL LETTER O WITH DIAERESIS -0153;oe;LATIN SMALL LIGATURE OE -02DB;ogonek;OGONEK -00F2;ograve;LATIN SMALL LETTER O WITH GRAVE -01A1;ohorn;LATIN SMALL LETTER O WITH HORN -0151;ohungarumlaut;LATIN SMALL LETTER O WITH DOUBLE ACUTE -014D;omacron;LATIN SMALL LETTER O WITH MACRON -03C9;omega;GREEK SMALL LETTER OMEGA -03D6;omega1;GREEK PI SYMBOL -03CE;omegatonos;GREEK SMALL LETTER OMEGA WITH TONOS -03BF;omicron;GREEK SMALL LETTER OMICRON -03CC;omicrontonos;GREEK SMALL LETTER OMICRON WITH TONOS -0031;one;DIGIT ONE -2024;onedotenleader;ONE DOT LEADER -215B;oneeighth;VULGAR FRACTION ONE EIGHTH -00BD;onehalf;VULGAR FRACTION ONE HALF -00BC;onequarter;VULGAR FRACTION ONE QUARTER -2153;onethird;VULGAR FRACTION ONE THIRD -25E6;openbullet;WHITE BULLET -00AA;ordfeminine;FEMININE ORDINAL INDICATOR -00BA;ordmasculine;MASCULINE ORDINAL INDICATOR -221F;orthogonal;RIGHT ANGLE -00F8;oslash;LATIN SMALL LETTER O WITH STROKE -01FF;oslashacute;LATIN SMALL LETTER O WITH STROKE AND ACUTE -00F5;otilde;LATIN SMALL LETTER O WITH TILDE -0070;p;LATIN SMALL LETTER P -00B6;paragraph;PILCROW SIGN -0028;parenleft;LEFT PARENTHESIS -0029;parenright;RIGHT PARENTHESIS -2202;partialdiff;PARTIAL DIFFERENTIAL -0025;percent;PERCENT SIGN -002E;period;FULL STOP -00B7;periodcentered;MIDDLE DOT -22A5;perpendicular;UP TACK -2030;perthousand;PER MILLE SIGN -20A7;peseta;PESETA SIGN -03C6;phi;GREEK SMALL LETTER PHI -03D5;phi1;GREEK PHI SYMBOL -03C0;pi;GREEK SMALL LETTER PI -002B;plus;PLUS SIGN -00B1;plusminus;PLUS-MINUS SIGN -211E;prescription;PRESCRIPTION TAKE -220F;product;N-ARY PRODUCT -2282;propersubset;SUBSET OF -2283;propersuperset;SUPERSET OF -221D;proportional;PROPORTIONAL TO -03C8;psi;GREEK SMALL LETTER PSI -0071;q;LATIN SMALL LETTER Q -003F;question;QUESTION MARK -00BF;questiondown;INVERTED QUESTION MARK -0022;quotedbl;QUOTATION MARK -201E;quotedblbase;DOUBLE LOW-9 QUOTATION MARK -201C;quotedblleft;LEFT DOUBLE QUOTATION MARK -201D;quotedblright;RIGHT DOUBLE QUOTATION MARK -2018;quoteleft;LEFT SINGLE QUOTATION MARK -201B;quotereversed;SINGLE HIGH-REVERSED-9 QUOTATION MARK -2019;quoteright;RIGHT SINGLE QUOTATION MARK -201A;quotesinglbase;SINGLE LOW-9 QUOTATION MARK -0027;quotesingle;APOSTROPHE -0072;r;LATIN SMALL LETTER R -0155;racute;LATIN SMALL LETTER R WITH ACUTE -221A;radical;SQUARE ROOT -0159;rcaron;LATIN SMALL LETTER R WITH CARON -2286;reflexsubset;SUBSET OF OR EQUAL TO -2287;reflexsuperset;SUPERSET OF OR EQUAL TO -00AE;registered;REGISTERED SIGN -2310;revlogicalnot;REVERSED NOT SIGN -03C1;rho;GREEK SMALL LETTER RHO -02DA;ring;RING ABOVE -2590;rtblock;RIGHT HALF BLOCK -0073;s;LATIN SMALL LETTER S -015B;sacute;LATIN SMALL LETTER S WITH ACUTE -0161;scaron;LATIN SMALL LETTER S WITH CARON -015F;scedilla;LATIN SMALL LETTER S WITH CEDILLA -015D;scircumflex;LATIN SMALL LETTER S WITH CIRCUMFLEX -2033;second;DOUBLE PRIME -00A7;section;SECTION SIGN -003B;semicolon;SEMICOLON -0037;seven;DIGIT SEVEN -215E;seveneighths;VULGAR FRACTION SEVEN EIGHTHS -2592;shade;MEDIUM SHADE -03C3;sigma;GREEK SMALL LETTER SIGMA -03C2;sigma1;GREEK SMALL LETTER FINAL SIGMA -223C;similar;TILDE OPERATOR -0036;six;DIGIT SIX -002F;slash;SOLIDUS -263A;smileface;WHITE SMILING FACE -0020;space;SPACE -2660;spade;BLACK SPADE SUIT -00A3;sterling;POUND SIGN -220B;suchthat;CONTAINS AS MEMBER -2211;summation;N-ARY SUMMATION -263C;sun;WHITE SUN WITH RAYS -0074;t;LATIN SMALL LETTER T -03C4;tau;GREEK SMALL LETTER TAU -0167;tbar;LATIN SMALL LETTER T WITH STROKE -0165;tcaron;LATIN SMALL LETTER T WITH CARON -2234;therefore;THEREFORE -03B8;theta;GREEK SMALL LETTER THETA -03D1;theta1;GREEK THETA SYMBOL -00FE;thorn;LATIN SMALL LETTER THORN -0033;three;DIGIT THREE -215C;threeeighths;VULGAR FRACTION THREE EIGHTHS -00BE;threequarters;VULGAR FRACTION THREE QUARTERS -02DC;tilde;SMALL TILDE -0303;tildecomb;COMBINING TILDE -0384;tonos;GREEK TONOS -2122;trademark;TRADE MARK SIGN -25BC;triagdn;BLACK DOWN-POINTING TRIANGLE -25C4;triaglf;BLACK LEFT-POINTING POINTER -25BA;triagrt;BLACK RIGHT-POINTING POINTER -25B2;triagup;BLACK UP-POINTING TRIANGLE -0032;two;DIGIT TWO -2025;twodotenleader;TWO DOT LEADER -2154;twothirds;VULGAR FRACTION TWO THIRDS -0075;u;LATIN SMALL LETTER U -00FA;uacute;LATIN SMALL LETTER U WITH ACUTE -016D;ubreve;LATIN SMALL LETTER U WITH BREVE -00FB;ucircumflex;LATIN SMALL LETTER U WITH CIRCUMFLEX -00FC;udieresis;LATIN SMALL LETTER U WITH DIAERESIS -00F9;ugrave;LATIN SMALL LETTER U WITH GRAVE -01B0;uhorn;LATIN SMALL LETTER U WITH HORN -0171;uhungarumlaut;LATIN SMALL LETTER U WITH DOUBLE ACUTE -016B;umacron;LATIN SMALL LETTER U WITH MACRON -005F;underscore;LOW LINE -2017;underscoredbl;DOUBLE LOW LINE -222A;union;UNION -2200;universal;FOR ALL -0173;uogonek;LATIN SMALL LETTER U WITH OGONEK -2580;upblock;UPPER HALF BLOCK -03C5;upsilon;GREEK SMALL LETTER UPSILON -03CB;upsilondieresis;GREEK SMALL LETTER UPSILON WITH DIALYTIKA -03B0;upsilondieresistonos;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS -03CD;upsilontonos;GREEK SMALL LETTER UPSILON WITH TONOS -016F;uring;LATIN SMALL LETTER U WITH RING ABOVE -0169;utilde;LATIN SMALL LETTER U WITH TILDE -0076;v;LATIN SMALL LETTER V -0077;w;LATIN SMALL LETTER W -1E83;wacute;LATIN SMALL LETTER W WITH ACUTE -0175;wcircumflex;LATIN SMALL LETTER W WITH CIRCUMFLEX -1E85;wdieresis;LATIN SMALL LETTER W WITH DIAERESIS -2118;weierstrass;SCRIPT CAPITAL P -1E81;wgrave;LATIN SMALL LETTER W WITH GRAVE -0078;x;LATIN SMALL LETTER X -03BE;xi;GREEK SMALL LETTER XI -0079;y;LATIN SMALL LETTER Y -00FD;yacute;LATIN SMALL LETTER Y WITH ACUTE -0177;ycircumflex;LATIN SMALL LETTER Y WITH CIRCUMFLEX -00FF;ydieresis;LATIN SMALL LETTER Y WITH DIAERESIS -00A5;yen;YEN SIGN -1EF3;ygrave;LATIN SMALL LETTER Y WITH GRAVE -007A;z;LATIN SMALL LETTER Z -017A;zacute;LATIN SMALL LETTER Z WITH ACUTE -017E;zcaron;LATIN SMALL LETTER Z WITH CARON -017C;zdotaccent;LATIN SMALL LETTER Z WITH DOT ABOVE -0030;zero;DIGIT ZERO -03B6;zeta;GREEK SMALL LETTER ZETA -# END -""" - - -class AGLError(Exception): - pass - - -LEGACY_AGL2UV = {} -AGL2UV = {} -UV2AGL = {} - - -def _builddicts(): - import re - - lines = _aglText.splitlines() - - parseAGL_RE = re.compile("([A-Za-z0-9]+);((?:[0-9A-F]{4})(?: (?:[0-9A-F]{4}))*)$") - - for line in lines: - if not line or line[:1] == "#": - continue - m = parseAGL_RE.match(line) - if not m: - raise AGLError("syntax error in glyphlist.txt: %s" % repr(line[:20])) - unicodes = m.group(2) - assert len(unicodes) % 5 == 4 - unicodes = [int(unicode, 16) for unicode in unicodes.split()] - glyphName = tostr(m.group(1)) - LEGACY_AGL2UV[glyphName] = unicodes - - lines = _aglfnText.splitlines() - - parseAGLFN_RE = re.compile("([0-9A-F]{4});([A-Za-z0-9]+);.*?$") - - for line in lines: - if not line or line[:1] == "#": - continue - m = parseAGLFN_RE.match(line) - if not m: - raise AGLError("syntax error in aglfn.txt: %s" % repr(line[:20])) - unicode = m.group(1) - assert len(unicode) == 4 - unicode = int(unicode, 16) - glyphName = tostr(m.group(2)) - AGL2UV[glyphName] = unicode - UV2AGL[unicode] = glyphName - - -_builddicts() - - -def toUnicode(glyph, isZapfDingbats=False): - """Convert glyph names to Unicode, such as ``'longs_t.oldstyle'`` --> ``u'ſt'`` - - If ``isZapfDingbats`` is ``True``, the implementation recognizes additional - glyph names (as required by the AGL specification). - """ - # https://github.com/adobe-type-tools/agl-specification#2-the-mapping - # - # 1. Drop all the characters from the glyph name starting with - # the first occurrence of a period (U+002E; FULL STOP), if any. - glyph = glyph.split(".", 1)[0] - - # 2. Split the remaining string into a sequence of components, - # using underscore (U+005F; LOW LINE) as the delimiter. - components = glyph.split("_") - - # 3. Map each component to a character string according to the - # procedure below, and concatenate those strings; the result - # is the character string to which the glyph name is mapped. - result = [_glyphComponentToUnicode(c, isZapfDingbats) for c in components] - return "".join(result) - - -def _glyphComponentToUnicode(component, isZapfDingbats): - # If the font is Zapf Dingbats (PostScript FontName: ZapfDingbats), - # and the component is in the ITC Zapf Dingbats Glyph List, then - # map it to the corresponding character in that list. - dingbat = _zapfDingbatsToUnicode(component) if isZapfDingbats else None - if dingbat: - return dingbat - - # Otherwise, if the component is in AGL, then map it - # to the corresponding character in that list. - uchars = LEGACY_AGL2UV.get(component) - if uchars: - return "".join(map(chr, uchars)) - - # Otherwise, if the component is of the form "uni" (U+0075, - # U+006E, and U+0069) followed by a sequence of uppercase - # hexadecimal digits (0–9 and A–F, meaning U+0030 through - # U+0039 and U+0041 through U+0046), if the length of that - # sequence is a multiple of four, and if each group of four - # digits represents a value in the ranges 0000 through D7FF - # or E000 through FFFF, then interpret each as a Unicode scalar - # value and map the component to the string made of those - # scalar values. Note that the range and digit-length - # restrictions mean that the "uni" glyph name prefix can be - # used only with UVs in the Basic Multilingual Plane (BMP). - uni = _uniToUnicode(component) - if uni: - return uni - - # Otherwise, if the component is of the form "u" (U+0075) - # followed by a sequence of four to six uppercase hexadecimal - # digits (0–9 and A–F, meaning U+0030 through U+0039 and - # U+0041 through U+0046), and those digits represents a value - # in the ranges 0000 through D7FF or E000 through 10FFFF, then - # interpret it as a Unicode scalar value and map the component - # to the string made of this scalar value. - uni = _uToUnicode(component) - if uni: - return uni - - # Otherwise, map the component to an empty string. - return "" - - -# https://github.com/adobe-type-tools/agl-aglfn/blob/master/zapfdingbats.txt -_AGL_ZAPF_DINGBATS = ( - " ✁✂✄☎✆✝✞✟✠✡☛☞✌✍✎✏✑✒✓✔✕✖✗✘✙✚✛✜✢✣✤✥✦✧★✩✪✫✬✭✮✯✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀" - "❁❂❃❄❅❆❇❈❉❊❋●❍■❏❑▲▼◆❖ ◗❘❙❚❯❱❲❳❨❩❬❭❪❫❴❵❛❜❝❞❡❢❣❤✐❥❦❧♠♥♦♣ ✉✈✇" - "①②③④⑤⑥⑦⑧⑨⑩❶❷❸❹❺❻❼❽❾❿➀➁➂➃➄➅➆➇➈➉➊➋➌➍➎➏➐➑➒➓➔→➣↔" - "↕➙➛➜➝➞➟➠➡➢➤➥➦➧➨➩➫➭➯➲➳➵➸➺➻➼➽➾➚➪➶➹➘➴➷➬➮➱✃❐❒❮❰" -) - - -def _zapfDingbatsToUnicode(glyph): - """Helper for toUnicode().""" - if len(glyph) < 2 or glyph[0] != "a": - return None - try: - gid = int(glyph[1:]) - except ValueError: - return None - if gid < 0 or gid >= len(_AGL_ZAPF_DINGBATS): - return None - uchar = _AGL_ZAPF_DINGBATS[gid] - return uchar if uchar != " " else None - - -_re_uni = re.compile("^uni([0-9A-F]+)$") - - -def _uniToUnicode(component): - """Helper for toUnicode() to handle "uniABCD" components.""" - match = _re_uni.match(component) - if match is None: - return None - digits = match.group(1) - if len(digits) % 4 != 0: - return None - chars = [int(digits[i : i + 4], 16) for i in range(0, len(digits), 4)] - if any(c >= 0xD800 and c <= 0xDFFF for c in chars): - # The AGL specification explicitly excluded surrogate pairs. - return None - return "".join([chr(c) for c in chars]) - - -_re_u = re.compile("^u([0-9A-F]{4,6})$") - - -def _uToUnicode(component): - """Helper for toUnicode() to handle "u1ABCD" components.""" - match = _re_u.match(component) - if match is None: - return None - digits = match.group(1) - try: - value = int(digits, 16) - except ValueError: - return None - if (value >= 0x0000 and value <= 0xD7FF) or (value >= 0xE000 and value <= 0x10FFFF): - return chr(value) - return None diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/annotations.py b/.venv-docs/lib/python3.12/site-packages/fontTools/annotations.py deleted file mode 100644 index 5ff5972e..00000000 --- a/.venv-docs/lib/python3.12/site-packages/fontTools/annotations.py +++ /dev/null @@ -1,30 +0,0 @@ -from __future__ import annotations -from typing import TYPE_CHECKING, Iterable, Optional, TypeVar, Union -from collections.abc import Callable, Sequence -from fontTools.misc.filesystem._base import FS -from os import PathLike -from xml.etree.ElementTree import Element as ElementTreeElement - -if TYPE_CHECKING: - from fontTools.ufoLib import UFOFormatVersion - from fontTools.ufoLib.glifLib import GLIFFormatVersion - from lxml.etree import _Element as LxmlElement - - -T = TypeVar("T") # Generic type -K = TypeVar("K") # Generic dict key type -V = TypeVar("V") # Generic dict value type - -GlyphNameToFileNameFunc = Optional[Callable[[str, set[str]], str]] -ElementType = Union[ElementTreeElement, "LxmlElement"] -FormatVersion = Union[int, tuple[int, int]] -FormatVersions = Optional[Iterable[FormatVersion]] -GLIFFormatVersionInput = Optional[Union[int, tuple[int, int], "GLIFFormatVersion"]] -UFOFormatVersionInput = Optional[Union[int, tuple[int, int], "UFOFormatVersion"]] -IntFloat = Union[int, float] -KerningPair = tuple[str, str] -KerningDict = dict[KerningPair, IntFloat] -KerningGroups = dict[str, Sequence[str]] -KerningNested = dict[str, dict[str, IntFloat]] -PathStr = Union[str, PathLike[str]] -PathOrFS = Union[PathStr, FS] diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/CFF2ToCFF.py b/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/CFF2ToCFF.py deleted file mode 100644 index e0ec956b..00000000 --- a/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/CFF2ToCFF.py +++ /dev/null @@ -1,233 +0,0 @@ -"""CFF2 to CFF converter.""" - -from fontTools.ttLib import TTFont, newTable -from fontTools.misc.cliTools import makeOutputFileName -from fontTools.misc.psCharStrings import T2StackUseExtractor -from fontTools.cffLib import ( - TopDictIndex, - buildOrder, - buildDefaults, - topDictOperators, - privateDictOperators, - FDSelect, -) -from .transforms import desubroutinizeCharString -from .specializer import specializeProgram -from .width import optimizeWidths -from collections import defaultdict -import logging - - -__all__ = ["convertCFF2ToCFF", "main"] - - -log = logging.getLogger("fontTools.cffLib") - - -def _convertCFF2ToCFF(cff, otFont): - """Converts this object from CFF2 format to CFF format. This conversion - is done 'in-place'. The conversion cannot be reversed. - - The CFF2 font cannot be variable. (TODO Accept those and convert to the - default instance?) - - This assumes a decompiled CFF2 table. (i.e. that the object has been - filled via :meth:`decompile` and e.g. not loaded from XML.)""" - - cff.major = 1 - - topDictData = TopDictIndex(None) - for item in cff.topDictIndex: - # Iterate over, such that all are decompiled - item.cff2GetGlyphOrder = None - topDictData.append(item) - cff.topDictIndex = topDictData - topDict = topDictData[0] - - if hasattr(topDict, "VarStore"): - raise ValueError("Variable CFF2 font cannot be converted to CFF format.") - - opOrder = buildOrder(topDictOperators) - topDict.order = opOrder - for key in topDict.rawDict.keys(): - if key not in opOrder: - del topDict.rawDict[key] - if hasattr(topDict, key): - delattr(topDict, key) - - charStrings = topDict.CharStrings - - fdArray = topDict.FDArray - if not hasattr(topDict, "FDSelect"): - # FDSelect is optional in CFF2, but required in CFF. - fdSelect = topDict.FDSelect = FDSelect() - fdSelect.gidArray = [0] * len(charStrings.charStrings) - - defaults = buildDefaults(privateDictOperators) - order = buildOrder(privateDictOperators) - for fd in fdArray: - fd.setCFF2(False) - privateDict = fd.Private - privateDict.order = order - for key in order: - if key not in privateDict.rawDict and key in defaults: - privateDict.rawDict[key] = defaults[key] - for key in privateDict.rawDict.keys(): - if key not in order: - del privateDict.rawDict[key] - if hasattr(privateDict, key): - delattr(privateDict, key) - - # Add ending operators - for cs in charStrings.values(): - cs.decompile() - cs.program.append("endchar") - for subrSets in [cff.GlobalSubrs] + [ - getattr(fd.Private, "Subrs", []) for fd in fdArray - ]: - for cs in subrSets: - cs.program.append("return") - - # Add (optimal) width to CharStrings that need it. - widths = defaultdict(list) - metrics = otFont["hmtx"].metrics - for glyphName in charStrings.keys(): - cs, fdIndex = charStrings.getItemAndSelector(glyphName) - if fdIndex == None: - fdIndex = 0 - widths[fdIndex].append(metrics[glyphName][0]) - for fdIndex, widthList in widths.items(): - bestDefault, bestNominal = optimizeWidths(widthList) - private = fdArray[fdIndex].Private - private.defaultWidthX = bestDefault - private.nominalWidthX = bestNominal - for glyphName in charStrings.keys(): - cs, fdIndex = charStrings.getItemAndSelector(glyphName) - if fdIndex == None: - fdIndex = 0 - private = fdArray[fdIndex].Private - width = metrics[glyphName][0] - if width != private.defaultWidthX: - cs.program.insert(0, width - private.nominalWidthX) - - # Handle stack use since stack-depth is lower in CFF than in CFF2. - for glyphName in charStrings.keys(): - cs, fdIndex = charStrings.getItemAndSelector(glyphName) - if fdIndex is None: - fdIndex = 0 - private = fdArray[fdIndex].Private - extractor = T2StackUseExtractor( - getattr(private, "Subrs", []), cff.GlobalSubrs, private=private - ) - stackUse = extractor.execute(cs) - if stackUse > 48: # CFF stack depth is 48 - desubroutinizeCharString(cs) - cs.program = specializeProgram(cs.program) - - # Unused subroutines are still in CFF2 (ie. lacking 'return' operator) - # because they were not decompiled when we added the 'return'. - # Moreover, some used subroutines may have become unused after the - # stack-use fixup. So we remove all unused subroutines now. - cff.remove_unused_subroutines() - - mapping = { - name: ("cid" + str(n).zfill(5) if n else ".notdef") - for n, name in enumerate(topDict.charset) - } - topDict.charset = [ - "cid" + str(n).zfill(5) if n else ".notdef" for n in range(len(topDict.charset)) - ] - charStrings.charStrings = { - mapping[name]: v for name, v in charStrings.charStrings.items() - } - - topDict.ROS = ("Adobe", "Identity", 0) - - -def convertCFF2ToCFF(font, *, updatePostTable=True): - if "CFF2" not in font: - raise ValueError("Input font does not contain a CFF2 table.") - cff = font["CFF2"].cff - _convertCFF2ToCFF(cff, font) - del font["CFF2"] - table = font["CFF "] = newTable("CFF ") - table.cff = cff - - if updatePostTable and "post" in font: - # Only version supported for fonts with CFF table is 0x00030000 not 0x20000 - post = font["post"] - if post.formatType == 2.0: - post.formatType = 3.0 - - -def main(args=None): - """Convert CFF2 OTF font to CFF OTF font""" - if args is None: - import sys - - args = sys.argv[1:] - - import argparse - - parser = argparse.ArgumentParser( - "fonttools cffLib.CFF2ToCFF", - description="Convert a non-variable CFF2 font to CFF.", - ) - parser.add_argument( - "input", metavar="INPUT.ttf", help="Input OTF file with CFF table." - ) - parser.add_argument( - "-o", - "--output", - metavar="OUTPUT.ttf", - default=None, - help="Output instance OTF file (default: INPUT-CFF2.ttf).", - ) - parser.add_argument( - "--no-recalc-timestamp", - dest="recalc_timestamp", - action="store_false", - help="Don't set the output font's timestamp to the current time.", - ) - loggingGroup = parser.add_mutually_exclusive_group(required=False) - loggingGroup.add_argument( - "-v", "--verbose", action="store_true", help="Run more verbosely." - ) - loggingGroup.add_argument( - "-q", "--quiet", action="store_true", help="Turn verbosity off." - ) - options = parser.parse_args(args) - - from fontTools import configLogger - - configLogger( - level=("DEBUG" if options.verbose else "ERROR" if options.quiet else "INFO") - ) - - import os - - infile = options.input - if not os.path.isfile(infile): - parser.error("No such file '{}'".format(infile)) - - outfile = ( - makeOutputFileName(infile, overWrite=True, suffix="-CFF") - if not options.output - else options.output - ) - - font = TTFont(infile, recalcTimestamp=options.recalc_timestamp, recalcBBoxes=False) - - convertCFF2ToCFF(font) - - log.info( - "Saving %s", - outfile, - ) - font.save(outfile) - - -if __name__ == "__main__": - import sys - - sys.exit(main(sys.argv[1:])) diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/CFFToCFF2.py b/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/CFFToCFF2.py deleted file mode 100644 index 2555f0b2..00000000 --- a/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/CFFToCFF2.py +++ /dev/null @@ -1,305 +0,0 @@ -"""CFF to CFF2 converter.""" - -from fontTools.ttLib import TTFont, newTable -from fontTools.misc.cliTools import makeOutputFileName -from fontTools.misc.psCharStrings import T2WidthExtractor -from fontTools.cffLib import ( - TopDictIndex, - FDArrayIndex, - FontDict, - buildOrder, - topDictOperators, - privateDictOperators, - topDictOperators2, - privateDictOperators2, -) -from io import BytesIO -import logging - -__all__ = ["convertCFFToCFF2", "main"] - - -log = logging.getLogger("fontTools.cffLib") - - -class _NominalWidthUsedError(Exception): - def __add__(self, other): - raise self - - def __radd__(self, other): - raise self - - -def _convertCFFToCFF2(cff, otFont): - """Converts this object from CFF format to CFF2 format. This conversion - is done 'in-place'. The conversion cannot be reversed. - - This assumes a decompiled CFF table. (i.e. that the object has been - filled via :meth:`decompile` and e.g. not loaded from XML.)""" - - # Clean up T2CharStrings - - topDict = cff.topDictIndex[0] - fdArray = topDict.FDArray if hasattr(topDict, "FDArray") else None - charStrings = topDict.CharStrings - globalSubrs = cff.GlobalSubrs - localSubrs = ( - [getattr(fd.Private, "Subrs", []) for fd in fdArray] - if fdArray - else ( - [topDict.Private.Subrs] - if hasattr(topDict, "Private") and hasattr(topDict.Private, "Subrs") - else [] - ) - ) - - for glyphName in charStrings.keys(): - cs, fdIndex = charStrings.getItemAndSelector(glyphName) - cs.decompile() - - # Clean up subroutines first - for subrs in [globalSubrs] + localSubrs: - for subr in subrs: - program = subr.program - i = j = len(program) - try: - i = program.index("return") - except ValueError: - pass - try: - j = program.index("endchar") - except ValueError: - pass - program[min(i, j) :] = [] - - # Clean up glyph charstrings - removeUnusedSubrs = False - nominalWidthXError = _NominalWidthUsedError() - for glyphName in charStrings.keys(): - cs, fdIndex = charStrings.getItemAndSelector(glyphName) - program = cs.program - - thisLocalSubrs = ( - localSubrs[fdIndex] - if fdIndex is not None - else ( - getattr(topDict.Private, "Subrs", []) - if hasattr(topDict, "Private") - else [] - ) - ) - - # Intentionally use custom type for nominalWidthX, such that any - # CharString that has an explicit width encoded will throw back to us. - extractor = T2WidthExtractor( - thisLocalSubrs, - globalSubrs, - nominalWidthXError, - 0, - ) - try: - extractor.execute(cs) - except _NominalWidthUsedError: - # Program has explicit width. We want to drop it, but can't - # just pop the first number since it may be a subroutine call. - # Instead, when seeing that, we embed the subroutine and recurse. - # If this ever happened, we later prune unused subroutines. - while len(program) >= 2 and program[1] in ["callsubr", "callgsubr"]: - removeUnusedSubrs = True - subrNumber = program.pop(0) - assert isinstance(subrNumber, int), subrNumber - op = program.pop(0) - bias = extractor.localBias if op == "callsubr" else extractor.globalBias - subrNumber += bias - subrSet = thisLocalSubrs if op == "callsubr" else globalSubrs - subrProgram = subrSet[subrNumber].program - program[:0] = subrProgram - # Now pop the actual width - assert len(program) >= 1, program - program.pop(0) - - if program and program[-1] == "endchar": - program.pop() - - if removeUnusedSubrs: - cff.remove_unused_subroutines() - - # Upconvert TopDict - - cff.major = 2 - cff2GetGlyphOrder = cff.otFont.getGlyphOrder - topDictData = TopDictIndex(None, cff2GetGlyphOrder) - for item in cff.topDictIndex: - # Iterate over, such that all are decompiled - topDictData.append(item) - cff.topDictIndex = topDictData - topDict = topDictData[0] - if hasattr(topDict, "Private"): - privateDict = topDict.Private - else: - privateDict = None - opOrder = buildOrder(topDictOperators2) - topDict.order = opOrder - topDict.cff2GetGlyphOrder = cff2GetGlyphOrder - - if not hasattr(topDict, "FDArray"): - fdArray = topDict.FDArray = FDArrayIndex() - fdArray.strings = None - fdArray.GlobalSubrs = topDict.GlobalSubrs - topDict.GlobalSubrs.fdArray = fdArray - charStrings = topDict.CharStrings - if charStrings.charStringsAreIndexed: - charStrings.charStringsIndex.fdArray = fdArray - else: - charStrings.fdArray = fdArray - fontDict = FontDict() - fontDict.setCFF2(True) - fdArray.append(fontDict) - fontDict.Private = privateDict - privateOpOrder = buildOrder(privateDictOperators2) - if privateDict is not None: - for entry in privateDictOperators: - key = entry[1] - if key not in privateOpOrder: - if key in privateDict.rawDict: - # print "Removing private dict", key - del privateDict.rawDict[key] - if hasattr(privateDict, key): - delattr(privateDict, key) - # print "Removing privateDict attr", key - else: - # clean up the PrivateDicts in the fdArray - fdArray = topDict.FDArray - privateOpOrder = buildOrder(privateDictOperators2) - for fontDict in fdArray: - fontDict.setCFF2(True) - for key in list(fontDict.rawDict.keys()): - if key not in fontDict.order: - del fontDict.rawDict[key] - if hasattr(fontDict, key): - delattr(fontDict, key) - - privateDict = fontDict.Private - for entry in privateDictOperators: - key = entry[1] - if key not in privateOpOrder: - if key in list(privateDict.rawDict.keys()): - # print "Removing private dict", key - del privateDict.rawDict[key] - if hasattr(privateDict, key): - delattr(privateDict, key) - # print "Removing privateDict attr", key - - # Now delete up the deprecated topDict operators from CFF 1.0 - for entry in topDictOperators: - key = entry[1] - # We seem to need to keep the charset operator for now, - # or we fail to compile with some fonts, like AdditionFont.otf. - # I don't know which kind of CFF font those are. But keeping - # charset seems to work. It will be removed when we save and - # read the font again. - # - # AdditionFont.otf has . - if key == "charset": - continue - if key not in opOrder: - if key in topDict.rawDict: - del topDict.rawDict[key] - if hasattr(topDict, key): - delattr(topDict, key) - - # TODO(behdad): What does the following comment even mean? Both CFF and CFF2 - # use the same T2Charstring class. I *think* what it means is that the CharStrings - # were loaded for CFF1, and we need to reload them for CFF2 to set varstore, etc - # on them. At least that's what I understand. It's probably safe to remove this - # and just set vstore where needed. - # - # See comment above about charset as well. - - # At this point, the Subrs and Charstrings are all still T2Charstring class - # easiest to fix this by compiling, then decompiling again - file = BytesIO() - cff.compile(file, otFont, isCFF2=True) - file.seek(0) - cff.decompile(file, otFont, isCFF2=True) - - -def convertCFFToCFF2(font): - cff = font["CFF "].cff - del font["CFF "] - _convertCFFToCFF2(cff, font) - table = font["CFF2"] = newTable("CFF2") - table.cff = cff - - -def main(args=None): - """Convert CFF OTF font to CFF2 OTF font""" - if args is None: - import sys - - args = sys.argv[1:] - - import argparse - - parser = argparse.ArgumentParser( - "fonttools cffLib.CFFToCFF2", - description="Upgrade a CFF font to CFF2.", - ) - parser.add_argument( - "input", metavar="INPUT.ttf", help="Input OTF file with CFF table." - ) - parser.add_argument( - "-o", - "--output", - metavar="OUTPUT.ttf", - default=None, - help="Output instance OTF file (default: INPUT-CFF2.ttf).", - ) - parser.add_argument( - "--no-recalc-timestamp", - dest="recalc_timestamp", - action="store_false", - help="Don't set the output font's timestamp to the current time.", - ) - loggingGroup = parser.add_mutually_exclusive_group(required=False) - loggingGroup.add_argument( - "-v", "--verbose", action="store_true", help="Run more verbosely." - ) - loggingGroup.add_argument( - "-q", "--quiet", action="store_true", help="Turn verbosity off." - ) - options = parser.parse_args(args) - - from fontTools import configLogger - - configLogger( - level=("DEBUG" if options.verbose else "ERROR" if options.quiet else "INFO") - ) - - import os - - infile = options.input - if not os.path.isfile(infile): - parser.error("No such file '{}'".format(infile)) - - outfile = ( - makeOutputFileName(infile, overWrite=True, suffix="-CFF2") - if not options.output - else options.output - ) - - font = TTFont(infile, recalcTimestamp=options.recalc_timestamp, recalcBBoxes=False) - - convertCFFToCFF2(font) - - log.info( - "Saving %s", - outfile, - ) - font.save(outfile) - - -if __name__ == "__main__": - import sys - - sys.exit(main(sys.argv[1:])) diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/__init__.py b/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/__init__.py deleted file mode 100644 index 4ad724a2..00000000 --- a/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/__init__.py +++ /dev/null @@ -1,3694 +0,0 @@ -"""cffLib: read/write Adobe CFF fonts - -OpenType fonts with PostScript outlines embed a completely independent -font file in Adobe's *Compact Font Format*. So dealing with OpenType fonts -requires also dealing with CFF. This module allows you to read and write -fonts written in the CFF format. - -In 2016, OpenType 1.8 introduced the `CFF2 `_ -format which, along with other changes, extended the CFF format to deal with -the demands of variable fonts. This module parses both original CFF and CFF2. - -""" - -from fontTools.misc import sstruct -from fontTools.misc import psCharStrings -from fontTools.misc.arrayTools import unionRect, intRect -from fontTools.misc.textTools import ( - bytechr, - byteord, - bytesjoin, - tobytes, - tostr, - safeEval, -) -from fontTools.ttLib import TTFont -from fontTools.ttLib.tables.otBase import OTTableWriter -from fontTools.ttLib.tables.otBase import OTTableReader -from fontTools.ttLib.tables import otTables as ot -from io import BytesIO -import struct -import logging -import re - -# mute cffLib debug messages when running ttx in verbose mode -DEBUG = logging.DEBUG - 1 -log = logging.getLogger(__name__) - -cffHeaderFormat = """ - major: B - minor: B - hdrSize: B -""" - -maxStackLimit = 513 -# maxstack operator has been deprecated. max stack is now always 513. - - -class CFFFontSet(object): - """A CFF font "file" can contain more than one font, although this is - extremely rare (and not allowed within OpenType fonts). - - This class is the entry point for parsing a CFF table. To actually - manipulate the data inside the CFF font, you will want to access the - ``CFFFontSet``'s :class:`TopDict` object. To do this, a ``CFFFontSet`` - object can either be treated as a dictionary (with appropriate - ``keys()`` and ``values()`` methods) mapping font names to :class:`TopDict` - objects, or as a list. - - .. code:: python - - from fontTools import ttLib - tt = ttLib.TTFont("Tests/cffLib/data/LinLibertine_RBI.otf") - tt["CFF "].cff - # - tt["CFF "].cff[0] # Here's your actual font data - # - - """ - - def decompile(self, file, otFont, isCFF2=None): - """Parse a binary CFF file into an internal representation. ``file`` - should be a file handle object. ``otFont`` is the top-level - :py:class:`fontTools.ttLib.ttFont.TTFont` object containing this CFF file. - - If ``isCFF2`` is passed and set to ``True`` or ``False``, then the - library makes an assertion that the CFF header is of the appropriate - version. - """ - - self.otFont = otFont - sstruct.unpack(cffHeaderFormat, file.read(3), self) - if isCFF2 is not None: - # called from ttLib: assert 'major' as read from file matches the - # expected version - expected_major = 2 if isCFF2 else 1 - if self.major != expected_major: - raise ValueError( - "Invalid CFF 'major' version: expected %d, found %d" - % (expected_major, self.major) - ) - else: - # use 'major' version from file to determine if isCFF2 - assert self.major in (1, 2), "Unknown CFF format" - isCFF2 = self.major == 2 - if not isCFF2: - self.offSize = struct.unpack("B", file.read(1))[0] - file.seek(self.hdrSize) - self.fontNames = list(tostr(s) for s in Index(file, isCFF2=isCFF2)) - self.topDictIndex = TopDictIndex(file, isCFF2=isCFF2) - self.strings = IndexedStrings(file) - else: # isCFF2 - self.topDictSize = struct.unpack(">H", file.read(2))[0] - file.seek(self.hdrSize) - self.fontNames = ["CFF2Font"] - cff2GetGlyphOrder = otFont.getGlyphOrder - # in CFF2, offsetSize is the size of the TopDict data. - self.topDictIndex = TopDictIndex( - file, cff2GetGlyphOrder, self.topDictSize, isCFF2=isCFF2 - ) - self.strings = None - self.GlobalSubrs = GlobalSubrsIndex(file, isCFF2=isCFF2) - self.topDictIndex.strings = self.strings - self.topDictIndex.GlobalSubrs = self.GlobalSubrs - - def __len__(self): - return len(self.fontNames) - - def keys(self): - return list(self.fontNames) - - def values(self): - return self.topDictIndex - - def __getitem__(self, nameOrIndex): - """Return TopDict instance identified by name (str) or index (int - or any object that implements `__index__`). - """ - if hasattr(nameOrIndex, "__index__"): - index = nameOrIndex.__index__() - elif isinstance(nameOrIndex, str): - name = nameOrIndex - try: - index = self.fontNames.index(name) - except ValueError: - raise KeyError(nameOrIndex) - else: - raise TypeError(nameOrIndex) - return self.topDictIndex[index] - - def compile(self, file, otFont, isCFF2=None): - """Write the object back into binary representation onto the given file. - ``file`` should be a file handle object. ``otFont`` is the top-level - :py:class:`fontTools.ttLib.ttFont.TTFont` object containing this CFF file. - - If ``isCFF2`` is passed and set to ``True`` or ``False``, then the - library makes an assertion that the CFF header is of the appropriate - version. - """ - self.otFont = otFont - if isCFF2 is not None: - # called from ttLib: assert 'major' value matches expected version - expected_major = 2 if isCFF2 else 1 - if self.major != expected_major: - raise ValueError( - "Invalid CFF 'major' version: expected %d, found %d" - % (expected_major, self.major) - ) - else: - # use current 'major' value to determine output format - assert self.major in (1, 2), "Unknown CFF format" - isCFF2 = self.major == 2 - - if otFont.recalcBBoxes and not isCFF2: - for topDict in self.topDictIndex: - topDict.recalcFontBBox() - - if not isCFF2: - strings = IndexedStrings() - else: - strings = None - writer = CFFWriter(isCFF2) - topCompiler = self.topDictIndex.getCompiler(strings, self, isCFF2=isCFF2) - if isCFF2: - self.hdrSize = 5 - writer.add(sstruct.pack(cffHeaderFormat, self)) - # Note: topDictSize will most likely change in CFFWriter.toFile(). - self.topDictSize = topCompiler.getDataLength() - writer.add(struct.pack(">H", self.topDictSize)) - else: - self.hdrSize = 4 - self.offSize = 4 # will most likely change in CFFWriter.toFile(). - writer.add(sstruct.pack(cffHeaderFormat, self)) - writer.add(struct.pack("B", self.offSize)) - if not isCFF2: - fontNames = Index() - for name in self.fontNames: - fontNames.append(name) - writer.add(fontNames.getCompiler(strings, self, isCFF2=isCFF2)) - writer.add(topCompiler) - if not isCFF2: - writer.add(strings.getCompiler()) - writer.add(self.GlobalSubrs.getCompiler(strings, self, isCFF2=isCFF2)) - - for topDict in self.topDictIndex: - if not hasattr(topDict, "charset") or topDict.charset is None: - charset = otFont.getGlyphOrder() - topDict.charset = charset - children = topCompiler.getChildren(strings) - for child in children: - writer.add(child) - - writer.toFile(file) - - def toXML(self, xmlWriter): - """Write the object into XML representation onto the given - :class:`fontTools.misc.xmlWriter.XMLWriter`. - - .. code:: python - - writer = xmlWriter.XMLWriter(sys.stdout) - tt["CFF "].cff.toXML(writer) - - """ - - xmlWriter.simpletag("major", value=self.major) - xmlWriter.newline() - xmlWriter.simpletag("minor", value=self.minor) - xmlWriter.newline() - for fontName in self.fontNames: - xmlWriter.begintag("CFFFont", name=tostr(fontName)) - xmlWriter.newline() - font = self[fontName] - font.toXML(xmlWriter) - xmlWriter.endtag("CFFFont") - xmlWriter.newline() - xmlWriter.newline() - xmlWriter.begintag("GlobalSubrs") - xmlWriter.newline() - self.GlobalSubrs.toXML(xmlWriter) - xmlWriter.endtag("GlobalSubrs") - xmlWriter.newline() - - def fromXML(self, name, attrs, content, otFont=None): - """Reads data from the XML element into the ``CFFFontSet`` object.""" - self.otFont = otFont - - # set defaults. These will be replaced if there are entries for them - # in the XML file. - if not hasattr(self, "major"): - self.major = 1 - if not hasattr(self, "minor"): - self.minor = 0 - - if name == "CFFFont": - if self.major == 1: - if not hasattr(self, "offSize"): - # this will be recalculated when the cff is compiled. - self.offSize = 4 - if not hasattr(self, "hdrSize"): - self.hdrSize = 4 - if not hasattr(self, "GlobalSubrs"): - self.GlobalSubrs = GlobalSubrsIndex() - if not hasattr(self, "fontNames"): - self.fontNames = [] - self.topDictIndex = TopDictIndex() - fontName = attrs["name"] - self.fontNames.append(fontName) - topDict = TopDict(GlobalSubrs=self.GlobalSubrs) - topDict.charset = None # gets filled in later - elif self.major == 2: - if not hasattr(self, "hdrSize"): - self.hdrSize = 5 - if not hasattr(self, "GlobalSubrs"): - self.GlobalSubrs = GlobalSubrsIndex() - if not hasattr(self, "fontNames"): - self.fontNames = ["CFF2Font"] - cff2GetGlyphOrder = self.otFont.getGlyphOrder - topDict = TopDict( - GlobalSubrs=self.GlobalSubrs, cff2GetGlyphOrder=cff2GetGlyphOrder - ) - self.topDictIndex = TopDictIndex(None, cff2GetGlyphOrder) - self.topDictIndex.append(topDict) - for element in content: - if isinstance(element, str): - continue - name, attrs, content = element - topDict.fromXML(name, attrs, content) - - if hasattr(topDict, "VarStore") and topDict.FDArray[0].vstore is None: - fdArray = topDict.FDArray - for fontDict in fdArray: - if hasattr(fontDict, "Private"): - fontDict.Private.vstore = topDict.VarStore - - elif name == "GlobalSubrs": - subrCharStringClass = psCharStrings.T2CharString - if not hasattr(self, "GlobalSubrs"): - self.GlobalSubrs = GlobalSubrsIndex() - for element in content: - if isinstance(element, str): - continue - name, attrs, content = element - subr = subrCharStringClass() - subr.fromXML(name, attrs, content) - self.GlobalSubrs.append(subr) - elif name == "major": - self.major = int(attrs["value"]) - elif name == "minor": - self.minor = int(attrs["value"]) - - def convertCFFToCFF2(self, otFont): - from .CFFToCFF2 import _convertCFFToCFF2 - - _convertCFFToCFF2(self, otFont) - - def convertCFF2ToCFF(self, otFont): - from .CFF2ToCFF import _convertCFF2ToCFF - - _convertCFF2ToCFF(self, otFont) - - def desubroutinize(self): - from .transforms import desubroutinize - - desubroutinize(self) - - def remove_hints(self): - from .transforms import remove_hints - - remove_hints(self) - - def remove_unused_subroutines(self): - from .transforms import remove_unused_subroutines - - remove_unused_subroutines(self) - - -class CFFWriter(object): - """Helper class for serializing CFF data to binary. Used by - :meth:`CFFFontSet.compile`.""" - - def __init__(self, isCFF2): - self.data = [] - self.isCFF2 = isCFF2 - - def add(self, table): - self.data.append(table) - - def toFile(self, file): - lastPosList = None - count = 1 - while True: - log.log(DEBUG, "CFFWriter.toFile() iteration: %d", count) - count = count + 1 - pos = 0 - posList = [pos] - for item in self.data: - if hasattr(item, "getDataLength"): - endPos = pos + item.getDataLength() - if isinstance(item, TopDictIndexCompiler) and item.isCFF2: - self.topDictSize = item.getDataLength() - else: - endPos = pos + len(item) - if hasattr(item, "setPos"): - item.setPos(pos, endPos) - pos = endPos - posList.append(pos) - if posList == lastPosList: - break - lastPosList = posList - log.log(DEBUG, "CFFWriter.toFile() writing to file.") - begin = file.tell() - if self.isCFF2: - self.data[1] = struct.pack(">H", self.topDictSize) - else: - self.offSize = calcOffSize(lastPosList[-1]) - self.data[1] = struct.pack("B", self.offSize) - posList = [0] - for item in self.data: - if hasattr(item, "toFile"): - item.toFile(file) - else: - file.write(item) - posList.append(file.tell() - begin) - assert posList == lastPosList - - -def calcOffSize(largestOffset): - if largestOffset < 0x100: - offSize = 1 - elif largestOffset < 0x10000: - offSize = 2 - elif largestOffset < 0x1000000: - offSize = 3 - else: - offSize = 4 - return offSize - - -class IndexCompiler(object): - """Base class for writing CFF `INDEX data `_ - to binary.""" - - def __init__(self, items, strings, parent, isCFF2=None): - if isCFF2 is None and hasattr(parent, "isCFF2"): - isCFF2 = parent.isCFF2 - assert isCFF2 is not None - self.isCFF2 = isCFF2 - self.items = self.getItems(items, strings) - self.parent = parent - - def getItems(self, items, strings): - return items - - def getOffsets(self): - # An empty INDEX contains only the count field. - if self.items: - pos = 1 - offsets = [pos] - for item in self.items: - if hasattr(item, "getDataLength"): - pos = pos + item.getDataLength() - else: - pos = pos + len(item) - offsets.append(pos) - else: - offsets = [] - return offsets - - def getDataLength(self): - if self.isCFF2: - countSize = 4 - else: - countSize = 2 - - if self.items: - lastOffset = self.getOffsets()[-1] - offSize = calcOffSize(lastOffset) - dataLength = ( - countSize - + 1 # count - + (len(self.items) + 1) * offSize # offSize - + lastOffset # the offsets - - 1 # size of object data - ) - else: - # count. For empty INDEX tables, this is the only entry. - dataLength = countSize - - return dataLength - - def toFile(self, file): - offsets = self.getOffsets() - if self.isCFF2: - writeCard32(file, len(self.items)) - else: - writeCard16(file, len(self.items)) - # An empty INDEX contains only the count field. - if self.items: - offSize = calcOffSize(offsets[-1]) - writeCard8(file, offSize) - offSize = -offSize - pack = struct.pack - for offset in offsets: - binOffset = pack(">l", offset)[offSize:] - assert len(binOffset) == -offSize - file.write(binOffset) - for item in self.items: - if hasattr(item, "toFile"): - item.toFile(file) - else: - data = tobytes(item, encoding="latin1") - file.write(data) - - -class IndexedStringsCompiler(IndexCompiler): - def getItems(self, items, strings): - return items.strings - - -class TopDictIndexCompiler(IndexCompiler): - """Helper class for writing the TopDict to binary.""" - - def getItems(self, items, strings): - out = [] - for item in items: - out.append(item.getCompiler(strings, self)) - return out - - def getChildren(self, strings): - children = [] - for topDict in self.items: - children.extend(topDict.getChildren(strings)) - return children - - def getOffsets(self): - if self.isCFF2: - offsets = [0, self.items[0].getDataLength()] - return offsets - else: - return super(TopDictIndexCompiler, self).getOffsets() - - def getDataLength(self): - if self.isCFF2: - dataLength = self.items[0].getDataLength() - return dataLength - else: - return super(TopDictIndexCompiler, self).getDataLength() - - def toFile(self, file): - if self.isCFF2: - self.items[0].toFile(file) - else: - super(TopDictIndexCompiler, self).toFile(file) - - -class FDArrayIndexCompiler(IndexCompiler): - """Helper class for writing the - `Font DICT INDEX `_ - to binary.""" - - def getItems(self, items, strings): - out = [] - for item in items: - out.append(item.getCompiler(strings, self)) - return out - - def getChildren(self, strings): - children = [] - for fontDict in self.items: - children.extend(fontDict.getChildren(strings)) - return children - - def toFile(self, file): - offsets = self.getOffsets() - if self.isCFF2: - writeCard32(file, len(self.items)) - else: - writeCard16(file, len(self.items)) - offSize = calcOffSize(offsets[-1]) - writeCard8(file, offSize) - offSize = -offSize - pack = struct.pack - for offset in offsets: - binOffset = pack(">l", offset)[offSize:] - assert len(binOffset) == -offSize - file.write(binOffset) - for item in self.items: - if hasattr(item, "toFile"): - item.toFile(file) - else: - file.write(item) - - def setPos(self, pos, endPos): - self.parent.rawDict["FDArray"] = pos - - -class GlobalSubrsCompiler(IndexCompiler): - """Helper class for writing the `global subroutine INDEX `_ - to binary.""" - - def getItems(self, items, strings): - out = [] - for cs in items: - cs.compile(self.isCFF2) - out.append(cs.bytecode) - return out - - -class SubrsCompiler(GlobalSubrsCompiler): - """Helper class for writing the `local subroutine INDEX `_ - to binary.""" - - def setPos(self, pos, endPos): - offset = pos - self.parent.pos - self.parent.rawDict["Subrs"] = offset - - -class CharStringsCompiler(GlobalSubrsCompiler): - """Helper class for writing the `CharStrings INDEX `_ - to binary.""" - - def getItems(self, items, strings): - out = [] - for cs in items: - cs.compile(self.isCFF2) - out.append(cs.bytecode) - return out - - def setPos(self, pos, endPos): - self.parent.rawDict["CharStrings"] = pos - - -class Index(object): - """This class represents what the CFF spec calls an INDEX (an array of - variable-sized objects). `Index` items can be addressed and set using - Python list indexing.""" - - compilerClass = IndexCompiler - - def __init__(self, file=None, isCFF2=None): - self.items = [] - self.offsets = offsets = [] - name = self.__class__.__name__ - if file is None: - return - self._isCFF2 = isCFF2 - log.log(DEBUG, "loading %s at %s", name, file.tell()) - self.file = file - if isCFF2: - count = readCard32(file) - else: - count = readCard16(file) - if count == 0: - return - self.items = [None] * count - offSize = readCard8(file) - log.log(DEBUG, " index count: %s offSize: %s", count, offSize) - assert offSize <= 4, "offSize too large: %s" % offSize - pad = b"\0" * (4 - offSize) - for index in range(count + 1): - chunk = file.read(offSize) - chunk = pad + chunk - (offset,) = struct.unpack(">L", chunk) - offsets.append(int(offset)) - self.offsetBase = file.tell() - 1 - file.seek(self.offsetBase + offsets[-1]) # pretend we've read the whole lot - log.log(DEBUG, " end of %s at %s", name, file.tell()) - - def __len__(self): - return len(self.items) - - def __getitem__(self, index): - item = self.items[index] - if item is not None: - return item - offset = self.offsets[index] + self.offsetBase - size = self.offsets[index + 1] - self.offsets[index] - file = self.file - file.seek(offset) - data = file.read(size) - assert len(data) == size - item = self.produceItem(index, data, file, offset) - self.items[index] = item - return item - - def __setitem__(self, index, item): - self.items[index] = item - - def produceItem(self, index, data, file, offset): - return data - - def append(self, item): - """Add an item to an INDEX.""" - self.items.append(item) - - def getCompiler(self, strings, parent, isCFF2=None): - return self.compilerClass(self, strings, parent, isCFF2=isCFF2) - - def clear(self): - """Empty the INDEX.""" - del self.items[:] - - -class GlobalSubrsIndex(Index): - """This index contains all the global subroutines in the font. A global - subroutine is a set of ``CharString`` data which is accessible to any - glyph in the font, and are used to store repeated instructions - for - example, components may be encoded as global subroutines, but so could - hinting instructions. - - Remember that when interpreting a ``callgsubr`` instruction (or indeed - a ``callsubr`` instruction) that you will need to add the "subroutine - number bias" to number given: - - .. code:: python - - tt = ttLib.TTFont("Almendra-Bold.otf") - u = tt["CFF "].cff[0].CharStrings["udieresis"] - u.decompile() - - u.toXML(XMLWriter(sys.stdout)) - # - # -64 callgsubr <-- Subroutine which implements the dieresis mark - # - - tt["CFF "].cff[0].GlobalSubrs[-64] # <-- WRONG - # - - tt["CFF "].cff[0].GlobalSubrs[-64 + 107] # <-- RIGHT - # - - ("The bias applied depends on the number of subrs (gsubrs). If the number of - subrs (gsubrs) is less than 1240, the bias is 107. Otherwise if it is less - than 33900, it is 1131; otherwise it is 32768.", - `Subroutine Operators `) - """ - - compilerClass = GlobalSubrsCompiler - subrClass = psCharStrings.T2CharString - charStringClass = psCharStrings.T2CharString - - def __init__( - self, - file=None, - globalSubrs=None, - private=None, - fdSelect=None, - fdArray=None, - isCFF2=None, - ): - super(GlobalSubrsIndex, self).__init__(file, isCFF2=isCFF2) - self.globalSubrs = globalSubrs - self.private = private - if fdSelect: - self.fdSelect = fdSelect - if fdArray: - self.fdArray = fdArray - - def produceItem(self, index, data, file, offset): - if self.private is not None: - private = self.private - elif hasattr(self, "fdArray") and self.fdArray is not None: - if hasattr(self, "fdSelect") and self.fdSelect is not None: - fdIndex = self.fdSelect[index] - else: - fdIndex = 0 - private = self.fdArray[fdIndex].Private - else: - private = None - return self.subrClass(data, private=private, globalSubrs=self.globalSubrs) - - def toXML(self, xmlWriter): - """Write the subroutines index into XML representation onto the given - :class:`fontTools.misc.xmlWriter.XMLWriter`. - - .. code:: python - - writer = xmlWriter.XMLWriter(sys.stdout) - tt["CFF "].cff[0].GlobalSubrs.toXML(writer) - - """ - xmlWriter.comment( - "The 'index' attribute is only for humans; " "it is ignored when parsed." - ) - xmlWriter.newline() - for i in range(len(self)): - subr = self[i] - if subr.needsDecompilation(): - xmlWriter.begintag("CharString", index=i, raw=1) - else: - xmlWriter.begintag("CharString", index=i) - xmlWriter.newline() - subr.toXML(xmlWriter) - xmlWriter.endtag("CharString") - xmlWriter.newline() - - def fromXML(self, name, attrs, content): - if name != "CharString": - return - subr = self.subrClass() - subr.fromXML(name, attrs, content) - self.append(subr) - - def getItemAndSelector(self, index): - sel = None - if hasattr(self, "fdSelect"): - sel = self.fdSelect[index] - return self[index], sel - - -class SubrsIndex(GlobalSubrsIndex): - """This index contains a glyph's local subroutines. A local subroutine is a - private set of ``CharString`` data which is accessible only to the glyph to - which the index is attached.""" - - compilerClass = SubrsCompiler - - -class TopDictIndex(Index): - """This index represents the array of ``TopDict`` structures in the font - (again, usually only one entry is present). Hence the following calls are - equivalent: - - .. code:: python - - tt["CFF "].cff[0] - # - tt["CFF "].cff.topDictIndex[0] - # - - """ - - compilerClass = TopDictIndexCompiler - - def __init__(self, file=None, cff2GetGlyphOrder=None, topSize=0, isCFF2=None): - self.cff2GetGlyphOrder = cff2GetGlyphOrder - if file is not None and isCFF2: - self._isCFF2 = isCFF2 - self.items = [] - name = self.__class__.__name__ - log.log(DEBUG, "loading %s at %s", name, file.tell()) - self.file = file - count = 1 - self.items = [None] * count - self.offsets = [0, topSize] - self.offsetBase = file.tell() - # pretend we've read the whole lot - file.seek(self.offsetBase + topSize) - log.log(DEBUG, " end of %s at %s", name, file.tell()) - else: - super(TopDictIndex, self).__init__(file, isCFF2=isCFF2) - - def produceItem(self, index, data, file, offset): - top = TopDict( - self.strings, - file, - offset, - self.GlobalSubrs, - self.cff2GetGlyphOrder, - isCFF2=self._isCFF2, - ) - top.decompile(data) - return top - - def toXML(self, xmlWriter): - for i in range(len(self)): - xmlWriter.begintag("FontDict", index=i) - xmlWriter.newline() - self[i].toXML(xmlWriter) - xmlWriter.endtag("FontDict") - xmlWriter.newline() - - -class FDArrayIndex(Index): - compilerClass = FDArrayIndexCompiler - - def toXML(self, xmlWriter): - for i in range(len(self)): - xmlWriter.begintag("FontDict", index=i) - xmlWriter.newline() - self[i].toXML(xmlWriter) - xmlWriter.endtag("FontDict") - xmlWriter.newline() - - def produceItem(self, index, data, file, offset): - fontDict = FontDict( - self.strings, - file, - offset, - self.GlobalSubrs, - isCFF2=self._isCFF2, - vstore=self.vstore, - ) - fontDict.decompile(data) - return fontDict - - def fromXML(self, name, attrs, content): - if name != "FontDict": - return - fontDict = FontDict() - for element in content: - if isinstance(element, str): - continue - name, attrs, content = element - fontDict.fromXML(name, attrs, content) - self.append(fontDict) - - -class VarStoreData(object): - def __init__(self, file=None, otVarStore=None): - self.file = file - self.data = None - self.otVarStore = otVarStore - self.font = TTFont() # dummy font for the decompile function. - - def decompile(self): - if self.file: - # read data in from file. Assume position is correct. - length = readCard16(self.file) - # https://github.com/fonttools/fonttools/issues/3673 - if length == 65535: - self.data = self.file.read() - else: - self.data = self.file.read(length) - globalState = {} - reader = OTTableReader(self.data, globalState) - self.otVarStore = ot.VarStore() - self.otVarStore.decompile(reader, self.font) - self.data = None - return self - - def compile(self): - writer = OTTableWriter() - self.otVarStore.compile(writer, self.font) - # Note that this omits the initial Card16 length from the CFF2 - # VarStore data block - self.data = writer.getAllData() - - def writeXML(self, xmlWriter, name): - self.otVarStore.toXML(xmlWriter, self.font) - - def xmlRead(self, name, attrs, content, parent): - self.otVarStore = ot.VarStore() - for element in content: - if isinstance(element, tuple): - name, attrs, content = element - self.otVarStore.fromXML(name, attrs, content, self.font) - else: - pass - return None - - def __len__(self): - return len(self.data) - - def getNumRegions(self, vsIndex): - if vsIndex is None: - vsIndex = 0 - varData = self.otVarStore.VarData[vsIndex] - numRegions = varData.VarRegionCount - return numRegions - - -class FDSelect(object): - def __init__(self, file=None, numGlyphs=None, format=None): - if file: - # read data in from file - self.format = readCard8(file) - if self.format == 0: - from array import array - - self.gidArray = array("B", file.read(numGlyphs)).tolist() - elif self.format == 3: - gidArray = [None] * numGlyphs - nRanges = readCard16(file) - fd = None - prev = None - for i in range(nRanges): - first = readCard16(file) - if prev is not None: - for glyphID in range(prev, first): - gidArray[glyphID] = fd - prev = first - fd = readCard8(file) - if prev is not None: - first = readCard16(file) - for glyphID in range(prev, first): - gidArray[glyphID] = fd - self.gidArray = gidArray - elif self.format == 4: - gidArray = [None] * numGlyphs - nRanges = readCard32(file) - fd = None - prev = None - for i in range(nRanges): - first = readCard32(file) - if prev is not None: - for glyphID in range(prev, first): - gidArray[glyphID] = fd - prev = first - fd = readCard16(file) - if prev is not None: - first = readCard32(file) - for glyphID in range(prev, first): - gidArray[glyphID] = fd - self.gidArray = gidArray - else: - assert False, "unsupported FDSelect format: %s" % format - else: - # reading from XML. Make empty gidArray, and leave format as passed in. - # format is None will result in the smallest representation being used. - self.format = format - self.gidArray = [] - - def __len__(self): - return len(self.gidArray) - - def __getitem__(self, index): - return self.gidArray[index] - - def __setitem__(self, index, fdSelectValue): - self.gidArray[index] = fdSelectValue - - def append(self, fdSelectValue): - self.gidArray.append(fdSelectValue) - - -class CharStrings(object): - """The ``CharStrings`` in the font represent the instructions for drawing - each glyph. This object presents a dictionary interface to the font's - CharStrings, indexed by glyph name: - - .. code:: python - - tt["CFF "].cff[0].CharStrings["a"] - # - - See :class:`fontTools.misc.psCharStrings.T1CharString` and - :class:`fontTools.misc.psCharStrings.T2CharString` for how to decompile, - compile and interpret the glyph drawing instructions in the returned objects. - - """ - - def __init__( - self, - file, - charset, - globalSubrs, - private, - fdSelect, - fdArray, - isCFF2=None, - varStore=None, - ): - self.globalSubrs = globalSubrs - self.varStore = varStore - if file is not None: - self.charStringsIndex = SubrsIndex( - file, globalSubrs, private, fdSelect, fdArray, isCFF2=isCFF2 - ) - self.charStrings = charStrings = {} - for i in range(len(charset)): - charStrings[charset[i]] = i - # read from OTF file: charStrings.values() are indices into - # charStringsIndex. - self.charStringsAreIndexed = 1 - else: - self.charStrings = {} - # read from ttx file: charStrings.values() are actual charstrings - self.charStringsAreIndexed = 0 - self.private = private - if fdSelect is not None: - self.fdSelect = fdSelect - if fdArray is not None: - self.fdArray = fdArray - - def keys(self): - return list(self.charStrings.keys()) - - def values(self): - if self.charStringsAreIndexed: - return self.charStringsIndex - else: - return list(self.charStrings.values()) - - def has_key(self, name): - return name in self.charStrings - - __contains__ = has_key - - def __len__(self): - return len(self.charStrings) - - def __getitem__(self, name): - charString = self.charStrings[name] - if self.charStringsAreIndexed: - charString = self.charStringsIndex[charString] - return charString - - def __setitem__(self, name, charString): - if self.charStringsAreIndexed: - index = self.charStrings[name] - self.charStringsIndex[index] = charString - else: - self.charStrings[name] = charString - - def getItemAndSelector(self, name): - if self.charStringsAreIndexed: - index = self.charStrings[name] - return self.charStringsIndex.getItemAndSelector(index) - else: - if hasattr(self, "fdArray"): - if hasattr(self, "fdSelect"): - sel = self.charStrings[name].fdSelectIndex - else: - sel = 0 - else: - sel = None - return self.charStrings[name], sel - - def toXML(self, xmlWriter): - names = sorted(self.keys()) - for name in names: - charStr, fdSelectIndex = self.getItemAndSelector(name) - if charStr.needsDecompilation(): - raw = [("raw", 1)] - else: - raw = [] - if fdSelectIndex is None: - xmlWriter.begintag("CharString", [("name", name)] + raw) - else: - xmlWriter.begintag( - "CharString", - [("name", name), ("fdSelectIndex", fdSelectIndex)] + raw, - ) - xmlWriter.newline() - charStr.toXML(xmlWriter) - xmlWriter.endtag("CharString") - xmlWriter.newline() - - def fromXML(self, name, attrs, content): - for element in content: - if isinstance(element, str): - continue - name, attrs, content = element - if name != "CharString": - continue - fdID = -1 - if hasattr(self, "fdArray"): - try: - fdID = safeEval(attrs["fdSelectIndex"]) - except KeyError: - fdID = 0 - private = self.fdArray[fdID].Private - else: - private = self.private - - glyphName = attrs["name"] - charStringClass = psCharStrings.T2CharString - charString = charStringClass(private=private, globalSubrs=self.globalSubrs) - charString.fromXML(name, attrs, content) - if fdID >= 0: - charString.fdSelectIndex = fdID - self[glyphName] = charString - - -def readCard8(file): - return byteord(file.read(1)) - - -def readCard16(file): - (value,) = struct.unpack(">H", file.read(2)) - return value - - -def readCard32(file): - (value,) = struct.unpack(">L", file.read(4)) - return value - - -def writeCard8(file, value): - file.write(bytechr(value)) - - -def writeCard16(file, value): - file.write(struct.pack(">H", value)) - - -def writeCard32(file, value): - file.write(struct.pack(">L", value)) - - -def packCard8(value): - return bytechr(value) - - -def packCard16(value): - return struct.pack(">H", value) - - -def packCard32(value): - return struct.pack(">L", value) - - -def buildOperatorDict(table): - d = {} - for op, name, arg, default, conv in table: - d[op] = (name, arg) - return d - - -def buildOpcodeDict(table): - d = {} - for op, name, arg, default, conv in table: - if isinstance(op, tuple): - op = bytechr(op[0]) + bytechr(op[1]) - else: - op = bytechr(op) - d[name] = (op, arg) - return d - - -def buildOrder(table): - l = [] - for op, name, arg, default, conv in table: - l.append(name) - return l - - -def buildDefaults(table): - d = {} - for op, name, arg, default, conv in table: - if default is not None: - d[name] = default - return d - - -def buildConverters(table): - d = {} - for op, name, arg, default, conv in table: - d[name] = conv - return d - - -class SimpleConverter(object): - def read(self, parent, value): - if not hasattr(parent, "file"): - return self._read(parent, value) - file = parent.file - pos = file.tell() - try: - return self._read(parent, value) - finally: - file.seek(pos) - - def _read(self, parent, value): - return value - - def write(self, parent, value): - return value - - def xmlWrite(self, xmlWriter, name, value): - xmlWriter.simpletag(name, value=value) - xmlWriter.newline() - - def xmlRead(self, name, attrs, content, parent): - return attrs["value"] - - -class ASCIIConverter(SimpleConverter): - def _read(self, parent, value): - return tostr(value, encoding="ascii") - - def write(self, parent, value): - return tobytes(value, encoding="ascii") - - def xmlWrite(self, xmlWriter, name, value): - xmlWriter.simpletag(name, value=tostr(value, encoding="ascii")) - xmlWriter.newline() - - def xmlRead(self, name, attrs, content, parent): - return tobytes(attrs["value"], encoding=("ascii")) - - -class Latin1Converter(SimpleConverter): - def _read(self, parent, value): - return tostr(value, encoding="latin1") - - def write(self, parent, value): - return tobytes(value, encoding="latin1") - - def xmlWrite(self, xmlWriter, name, value): - value = tostr(value, encoding="latin1") - if name in ["Notice", "Copyright"]: - value = re.sub(r"[\r\n]\s+", " ", value) - xmlWriter.simpletag(name, value=value) - xmlWriter.newline() - - def xmlRead(self, name, attrs, content, parent): - return tobytes(attrs["value"], encoding=("latin1")) - - -def parseNum(s): - try: - value = int(s) - except: - value = float(s) - return value - - -def parseBlendList(s): - valueList = [] - for element in s: - if isinstance(element, str): - continue - name, attrs, content = element - blendList = attrs["value"].split() - blendList = [eval(val) for val in blendList] - valueList.append(blendList) - if len(valueList) == 1: - valueList = valueList[0] - return valueList - - -class NumberConverter(SimpleConverter): - def xmlWrite(self, xmlWriter, name, value): - if isinstance(value, list): - xmlWriter.begintag(name) - xmlWriter.newline() - xmlWriter.indent() - blendValue = " ".join([str(val) for val in value]) - xmlWriter.simpletag(kBlendDictOpName, value=blendValue) - xmlWriter.newline() - xmlWriter.dedent() - xmlWriter.endtag(name) - xmlWriter.newline() - else: - xmlWriter.simpletag(name, value=value) - xmlWriter.newline() - - def xmlRead(self, name, attrs, content, parent): - valueString = attrs.get("value", None) - if valueString is None: - value = parseBlendList(content) - else: - value = parseNum(attrs["value"]) - return value - - -class ArrayConverter(SimpleConverter): - def xmlWrite(self, xmlWriter, name, value): - if value and isinstance(value[0], list): - xmlWriter.begintag(name) - xmlWriter.newline() - xmlWriter.indent() - for valueList in value: - blendValue = " ".join([str(val) for val in valueList]) - xmlWriter.simpletag(kBlendDictOpName, value=blendValue) - xmlWriter.newline() - xmlWriter.dedent() - xmlWriter.endtag(name) - xmlWriter.newline() - else: - value = " ".join([str(val) for val in value]) - xmlWriter.simpletag(name, value=value) - xmlWriter.newline() - - def xmlRead(self, name, attrs, content, parent): - valueString = attrs.get("value", None) - if valueString is None: - valueList = parseBlendList(content) - else: - values = valueString.split() - valueList = [parseNum(value) for value in values] - return valueList - - -class TableConverter(SimpleConverter): - def xmlWrite(self, xmlWriter, name, value): - xmlWriter.begintag(name) - xmlWriter.newline() - value.toXML(xmlWriter) - xmlWriter.endtag(name) - xmlWriter.newline() - - def xmlRead(self, name, attrs, content, parent): - ob = self.getClass()() - for element in content: - if isinstance(element, str): - continue - name, attrs, content = element - ob.fromXML(name, attrs, content) - return ob - - -class PrivateDictConverter(TableConverter): - def getClass(self): - return PrivateDict - - def _read(self, parent, value): - size, offset = value - file = parent.file - isCFF2 = parent._isCFF2 - try: - vstore = parent.vstore - except AttributeError: - vstore = None - priv = PrivateDict(parent.strings, file, offset, isCFF2=isCFF2, vstore=vstore) - file.seek(offset) - data = file.read(size) - assert len(data) == size - priv.decompile(data) - return priv - - def write(self, parent, value): - return (0, 0) # dummy value - - -class SubrsConverter(TableConverter): - def getClass(self): - return SubrsIndex - - def _read(self, parent, value): - file = parent.file - isCFF2 = parent._isCFF2 - file.seek(parent.offset + value) # Offset(self) - return SubrsIndex(file, isCFF2=isCFF2) - - def write(self, parent, value): - return 0 # dummy value - - -class CharStringsConverter(TableConverter): - def _read(self, parent, value): - file = parent.file - isCFF2 = parent._isCFF2 - charset = parent.charset - varStore = getattr(parent, "VarStore", None) - globalSubrs = parent.GlobalSubrs - if hasattr(parent, "FDArray"): - fdArray = parent.FDArray - if hasattr(parent, "FDSelect"): - fdSelect = parent.FDSelect - else: - fdSelect = None - private = None - else: - fdSelect, fdArray = None, None - private = parent.Private - file.seek(value) # Offset(0) - charStrings = CharStrings( - file, - charset, - globalSubrs, - private, - fdSelect, - fdArray, - isCFF2=isCFF2, - varStore=varStore, - ) - return charStrings - - def write(self, parent, value): - return 0 # dummy value - - def xmlRead(self, name, attrs, content, parent): - if hasattr(parent, "FDArray"): - # if it is a CID-keyed font, then the private Dict is extracted from the - # parent.FDArray - fdArray = parent.FDArray - if hasattr(parent, "FDSelect"): - fdSelect = parent.FDSelect - else: - fdSelect = None - private = None - else: - # if it is a name-keyed font, then the private dict is in the top dict, - # and - # there is no fdArray. - private, fdSelect, fdArray = parent.Private, None, None - charStrings = CharStrings( - None, - None, - parent.GlobalSubrs, - private, - fdSelect, - fdArray, - varStore=getattr(parent, "VarStore", None), - ) - charStrings.fromXML(name, attrs, content) - return charStrings - - -class CharsetConverter(SimpleConverter): - def _read(self, parent, value): - isCID = hasattr(parent, "ROS") - if value > 2: - numGlyphs = parent.numGlyphs - file = parent.file - file.seek(value) - log.log(DEBUG, "loading charset at %s", value) - format = readCard8(file) - if format == 0: - charset = parseCharset0(numGlyphs, file, parent.strings, isCID) - elif format == 1 or format == 2: - charset = parseCharset(numGlyphs, file, parent.strings, isCID, format) - else: - raise NotImplementedError - assert len(charset) == numGlyphs - log.log(DEBUG, " charset end at %s", file.tell()) - # make sure glyph names are unique - allNames = {} - newCharset = [] - for glyphName in charset: - if glyphName in allNames: - # make up a new glyphName that's unique - n = allNames[glyphName] - names = set(allNames) | set(charset) - while (glyphName + "." + str(n)) in names: - n += 1 - allNames[glyphName] = n + 1 - glyphName = glyphName + "." + str(n) - allNames[glyphName] = 1 - newCharset.append(glyphName) - charset = newCharset - else: # offset == 0 -> no charset data. - if isCID or "CharStrings" not in parent.rawDict: - # We get here only when processing fontDicts from the FDArray of - # CFF-CID fonts. Only the real topDict references the charset. - assert value == 0 - charset = None - elif value == 0: - charset = cffISOAdobeStrings - elif value == 1: - charset = cffIExpertStrings - elif value == 2: - charset = cffExpertSubsetStrings - if charset and (len(charset) != parent.numGlyphs): - charset = charset[: parent.numGlyphs] - return charset - - def write(self, parent, value): - return 0 # dummy value - - def xmlWrite(self, xmlWriter, name, value): - # XXX only write charset when not in OT/TTX context, where we - # dump charset as a separate "GlyphOrder" table. - # # xmlWriter.simpletag("charset") - xmlWriter.comment("charset is dumped separately as the 'GlyphOrder' element") - xmlWriter.newline() - - def xmlRead(self, name, attrs, content, parent): - pass - - -class CharsetCompiler(object): - def __init__(self, strings, charset, parent): - assert charset[0] == ".notdef" - isCID = hasattr(parent.dictObj, "ROS") - data0 = packCharset0(charset, isCID, strings) - data = packCharset(charset, isCID, strings) - if len(data) < len(data0): - self.data = data - else: - self.data = data0 - self.parent = parent - - def setPos(self, pos, endPos): - self.parent.rawDict["charset"] = pos - - def getDataLength(self): - return len(self.data) - - def toFile(self, file): - file.write(self.data) - - -def getStdCharSet(charset): - # check to see if we can use a predefined charset value. - predefinedCharSetVal = None - predefinedCharSets = [ - (cffISOAdobeStringCount, cffISOAdobeStrings, 0), - (cffExpertStringCount, cffIExpertStrings, 1), - (cffExpertSubsetStringCount, cffExpertSubsetStrings, 2), - ] - lcs = len(charset) - for cnt, pcs, csv in predefinedCharSets: - if predefinedCharSetVal is not None: - break - if lcs > cnt: - continue - predefinedCharSetVal = csv - for i in range(lcs): - if charset[i] != pcs[i]: - predefinedCharSetVal = None - break - return predefinedCharSetVal - - -def getCIDfromName(name, strings): - return int(name[3:]) - - -def getSIDfromName(name, strings): - return strings.getSID(name) - - -def packCharset0(charset, isCID, strings): - fmt = 0 - data = [packCard8(fmt)] - if isCID: - getNameID = getCIDfromName - else: - getNameID = getSIDfromName - - for name in charset[1:]: - data.append(packCard16(getNameID(name, strings))) - return bytesjoin(data) - - -def packCharset(charset, isCID, strings): - fmt = 1 - ranges = [] - first = None - end = 0 - if isCID: - getNameID = getCIDfromName - else: - getNameID = getSIDfromName - - for name in charset[1:]: - SID = getNameID(name, strings) - if first is None: - first = SID - elif end + 1 != SID: - nLeft = end - first - if nLeft > 255: - fmt = 2 - ranges.append((first, nLeft)) - first = SID - end = SID - if end: - nLeft = end - first - if nLeft > 255: - fmt = 2 - ranges.append((first, nLeft)) - - data = [packCard8(fmt)] - if fmt == 1: - nLeftFunc = packCard8 - else: - nLeftFunc = packCard16 - for first, nLeft in ranges: - data.append(packCard16(first) + nLeftFunc(nLeft)) - return bytesjoin(data) - - -def parseCharset0(numGlyphs, file, strings, isCID): - charset = [".notdef"] - if isCID: - for i in range(numGlyphs - 1): - CID = readCard16(file) - charset.append("cid" + str(CID).zfill(5)) - else: - for i in range(numGlyphs - 1): - SID = readCard16(file) - charset.append(strings[SID]) - return charset - - -def parseCharset(numGlyphs, file, strings, isCID, fmt): - charset = [".notdef"] - count = 1 - if fmt == 1: - nLeftFunc = readCard8 - else: - nLeftFunc = readCard16 - while count < numGlyphs: - first = readCard16(file) - nLeft = nLeftFunc(file) - if isCID: - for CID in range(first, first + nLeft + 1): - charset.append("cid" + str(CID).zfill(5)) - else: - for SID in range(first, first + nLeft + 1): - charset.append(strings[SID]) - count = count + nLeft + 1 - return charset - - -class EncodingCompiler(object): - def __init__(self, strings, encoding, parent): - assert not isinstance(encoding, str) - data0 = packEncoding0(parent.dictObj.charset, encoding, parent.strings) - data1 = packEncoding1(parent.dictObj.charset, encoding, parent.strings) - if len(data0) < len(data1): - self.data = data0 - else: - self.data = data1 - self.parent = parent - - def setPos(self, pos, endPos): - self.parent.rawDict["Encoding"] = pos - - def getDataLength(self): - return len(self.data) - - def toFile(self, file): - file.write(self.data) - - -class EncodingConverter(SimpleConverter): - def _read(self, parent, value): - if value == 0: - return "StandardEncoding" - elif value == 1: - return "ExpertEncoding" - # custom encoding at offset `value` - assert value > 1 - file = parent.file - file.seek(value) - log.log(DEBUG, "loading Encoding at %s", value) - fmt = readCard8(file) - haveSupplement = bool(fmt & 0x80) - fmt = fmt & 0x7F - - if fmt == 0: - encoding = parseEncoding0(parent.charset, file) - elif fmt == 1: - encoding = parseEncoding1(parent.charset, file) - else: - raise ValueError(f"Unknown Encoding format: {fmt}") - - if haveSupplement: - parseEncodingSupplement(file, encoding, parent.strings) - - return encoding - - def write(self, parent, value): - if value == "StandardEncoding": - return 0 - elif value == "ExpertEncoding": - return 1 - return 0 # dummy value - - def xmlWrite(self, xmlWriter, name, value): - if value in ("StandardEncoding", "ExpertEncoding"): - xmlWriter.simpletag(name, name=value) - xmlWriter.newline() - return - xmlWriter.begintag(name) - xmlWriter.newline() - for code in range(len(value)): - glyphName = value[code] - if glyphName != ".notdef": - xmlWriter.simpletag("map", code=hex(code), name=glyphName) - xmlWriter.newline() - xmlWriter.endtag(name) - xmlWriter.newline() - - def xmlRead(self, name, attrs, content, parent): - if "name" in attrs: - return attrs["name"] - encoding = [".notdef"] * 256 - for element in content: - if isinstance(element, str): - continue - name, attrs, content = element - code = safeEval(attrs["code"]) - glyphName = attrs["name"] - encoding[code] = glyphName - return encoding - - -def readSID(file): - """Read a String ID (SID) — 2-byte unsigned integer.""" - data = file.read(2) - if len(data) != 2: - raise EOFError("Unexpected end of file while reading SID") - return struct.unpack(">H", data)[0] # big-endian uint16 - - -def parseEncodingSupplement(file, encoding, strings): - """ - Parse the CFF Encoding supplement data: - - nSups: number of supplementary mappings - - each mapping: (code, SID) pair - and apply them to the `encoding` list in place. - """ - nSups = readCard8(file) - for _ in range(nSups): - code = readCard8(file) - sid = readSID(file) - name = strings[sid] - encoding[code] = name - - -def parseEncoding0(charset, file): - """ - Format 0: simple list of codes. - After reading the base table, optionally parse the supplement. - """ - nCodes = readCard8(file) - encoding = [".notdef"] * 256 - for glyphID in range(1, nCodes + 1): - code = readCard8(file) - if code != 0: - encoding[code] = charset[glyphID] - - return encoding - - -def parseEncoding1(charset, file): - """ - Format 1: range-based encoding. - After reading the base ranges, optionally parse the supplement. - """ - nRanges = readCard8(file) - encoding = [".notdef"] * 256 - glyphID = 1 - for _ in range(nRanges): - code = readCard8(file) - nLeft = readCard8(file) - for _ in range(nLeft + 1): - encoding[code] = charset[glyphID] - code += 1 - glyphID += 1 - - return encoding - - -def packEncoding0(charset, encoding, strings): - fmt = 0 - m = {} - for code in range(len(encoding)): - name = encoding[code] - if name != ".notdef": - m[name] = code - codes = [] - for name in charset[1:]: - code = m.get(name) - codes.append(code) - - while codes and codes[-1] is None: - codes.pop() - - data = [packCard8(fmt), packCard8(len(codes))] - for code in codes: - if code is None: - code = 0 - data.append(packCard8(code)) - return bytesjoin(data) - - -def packEncoding1(charset, encoding, strings): - fmt = 1 - m = {} - for code in range(len(encoding)): - name = encoding[code] - if name != ".notdef": - m[name] = code - ranges = [] - first = None - end = 0 - for name in charset[1:]: - code = m.get(name, -1) - if first is None: - first = code - elif end + 1 != code: - nLeft = end - first - ranges.append((first, nLeft)) - first = code - end = code - nLeft = end - first - ranges.append((first, nLeft)) - - # remove unencoded glyphs at the end. - while ranges and ranges[-1][0] == -1: - ranges.pop() - - data = [packCard8(fmt), packCard8(len(ranges))] - for first, nLeft in ranges: - if first == -1: # unencoded - first = 0 - data.append(packCard8(first) + packCard8(nLeft)) - return bytesjoin(data) - - -class FDArrayConverter(TableConverter): - def _read(self, parent, value): - try: - vstore = parent.VarStore - except AttributeError: - vstore = None - file = parent.file - isCFF2 = parent._isCFF2 - file.seek(value) - fdArray = FDArrayIndex(file, isCFF2=isCFF2) - fdArray.vstore = vstore - fdArray.strings = parent.strings - fdArray.GlobalSubrs = parent.GlobalSubrs - return fdArray - - def write(self, parent, value): - return 0 # dummy value - - def xmlRead(self, name, attrs, content, parent): - fdArray = FDArrayIndex() - for element in content: - if isinstance(element, str): - continue - name, attrs, content = element - fdArray.fromXML(name, attrs, content) - return fdArray - - -class FDSelectConverter(SimpleConverter): - def _read(self, parent, value): - file = parent.file - file.seek(value) - fdSelect = FDSelect(file, parent.numGlyphs) - return fdSelect - - def write(self, parent, value): - return 0 # dummy value - - # The FDSelect glyph data is written out to XML in the charstring keys, - # so we write out only the format selector - def xmlWrite(self, xmlWriter, name, value): - xmlWriter.simpletag(name, [("format", value.format)]) - xmlWriter.newline() - - def xmlRead(self, name, attrs, content, parent): - fmt = safeEval(attrs["format"]) - file = None - numGlyphs = None - fdSelect = FDSelect(file, numGlyphs, fmt) - return fdSelect - - -class VarStoreConverter(SimpleConverter): - def _read(self, parent, value): - file = parent.file - file.seek(value) - varStore = VarStoreData(file) - varStore.decompile() - return varStore - - def write(self, parent, value): - return 0 # dummy value - - def xmlWrite(self, xmlWriter, name, value): - value.writeXML(xmlWriter, name) - - def xmlRead(self, name, attrs, content, parent): - varStore = VarStoreData() - varStore.xmlRead(name, attrs, content, parent) - return varStore - - -def packFDSelect0(fdSelectArray): - fmt = 0 - data = [packCard8(fmt)] - for index in fdSelectArray: - data.append(packCard8(index)) - return bytesjoin(data) - - -def packFDSelect3(fdSelectArray): - fmt = 3 - fdRanges = [] - lenArray = len(fdSelectArray) - lastFDIndex = -1 - for i in range(lenArray): - fdIndex = fdSelectArray[i] - if lastFDIndex != fdIndex: - fdRanges.append([i, fdIndex]) - lastFDIndex = fdIndex - sentinelGID = i + 1 - - data = [packCard8(fmt)] - data.append(packCard16(len(fdRanges))) - for fdRange in fdRanges: - data.append(packCard16(fdRange[0])) - data.append(packCard8(fdRange[1])) - data.append(packCard16(sentinelGID)) - return bytesjoin(data) - - -def packFDSelect4(fdSelectArray): - fmt = 4 - fdRanges = [] - lenArray = len(fdSelectArray) - lastFDIndex = -1 - for i in range(lenArray): - fdIndex = fdSelectArray[i] - if lastFDIndex != fdIndex: - fdRanges.append([i, fdIndex]) - lastFDIndex = fdIndex - sentinelGID = i + 1 - - data = [packCard8(fmt)] - data.append(packCard32(len(fdRanges))) - for fdRange in fdRanges: - data.append(packCard32(fdRange[0])) - data.append(packCard16(fdRange[1])) - data.append(packCard32(sentinelGID)) - return bytesjoin(data) - - -class FDSelectCompiler(object): - def __init__(self, fdSelect, parent): - fmt = fdSelect.format - fdSelectArray = fdSelect.gidArray - if fmt == 0: - self.data = packFDSelect0(fdSelectArray) - elif fmt == 3: - self.data = packFDSelect3(fdSelectArray) - elif fmt == 4: - self.data = packFDSelect4(fdSelectArray) - else: - # choose smaller of the two formats - data0 = packFDSelect0(fdSelectArray) - data3 = packFDSelect3(fdSelectArray) - if len(data0) < len(data3): - self.data = data0 - fdSelect.format = 0 - else: - self.data = data3 - fdSelect.format = 3 - - self.parent = parent - - def setPos(self, pos, endPos): - self.parent.rawDict["FDSelect"] = pos - - def getDataLength(self): - return len(self.data) - - def toFile(self, file): - file.write(self.data) - - -class VarStoreCompiler(object): - def __init__(self, varStoreData, parent): - self.parent = parent - if not varStoreData.data: - varStoreData.compile() - varStoreDataLen = min(0xFFFF, len(varStoreData.data)) - data = [packCard16(varStoreDataLen), varStoreData.data] - self.data = bytesjoin(data) - - def setPos(self, pos, endPos): - self.parent.rawDict["VarStore"] = pos - - def getDataLength(self): - return len(self.data) - - def toFile(self, file): - file.write(self.data) - - -class ROSConverter(SimpleConverter): - def xmlWrite(self, xmlWriter, name, value): - registry, order, supplement = value - xmlWriter.simpletag( - name, - [ - ("Registry", tostr(registry)), - ("Order", tostr(order)), - ("Supplement", supplement), - ], - ) - xmlWriter.newline() - - def xmlRead(self, name, attrs, content, parent): - return (attrs["Registry"], attrs["Order"], safeEval(attrs["Supplement"])) - - -topDictOperators = [ - # opcode name argument type default converter - (25, "maxstack", "number", None, None), - ((12, 30), "ROS", ("SID", "SID", "number"), None, ROSConverter()), - ((12, 20), "SyntheticBase", "number", None, None), - (0, "version", "SID", None, None), - (1, "Notice", "SID", None, Latin1Converter()), - ((12, 0), "Copyright", "SID", None, Latin1Converter()), - (2, "FullName", "SID", None, Latin1Converter()), - ((12, 38), "FontName", "SID", None, Latin1Converter()), - (3, "FamilyName", "SID", None, Latin1Converter()), - (4, "Weight", "SID", None, None), - ((12, 1), "isFixedPitch", "number", 0, None), - ((12, 2), "ItalicAngle", "number", 0, None), - ((12, 3), "UnderlinePosition", "number", -100, None), - ((12, 4), "UnderlineThickness", "number", 50, None), - ((12, 5), "PaintType", "number", 0, None), - ((12, 6), "CharstringType", "number", 2, None), - ((12, 7), "FontMatrix", "array", [0.001, 0, 0, 0.001, 0, 0], None), - (13, "UniqueID", "number", None, None), - (5, "FontBBox", "array", [0, 0, 0, 0], None), - ((12, 8), "StrokeWidth", "number", 0, None), - (14, "XUID", "array", None, None), - ((12, 21), "PostScript", "SID", None, None), - ((12, 22), "BaseFontName", "SID", None, None), - ((12, 23), "BaseFontBlend", "delta", None, None), - ((12, 31), "CIDFontVersion", "number", 0, None), - ((12, 32), "CIDFontRevision", "number", 0, None), - ((12, 33), "CIDFontType", "number", 0, None), - ((12, 34), "CIDCount", "number", 8720, None), - (15, "charset", "number", None, CharsetConverter()), - ((12, 35), "UIDBase", "number", None, None), - (16, "Encoding", "number", 0, EncodingConverter()), - (18, "Private", ("number", "number"), None, PrivateDictConverter()), - ((12, 37), "FDSelect", "number", None, FDSelectConverter()), - ((12, 36), "FDArray", "number", None, FDArrayConverter()), - (17, "CharStrings", "number", None, CharStringsConverter()), - (24, "VarStore", "number", None, VarStoreConverter()), -] - -topDictOperators2 = [ - # opcode name argument type default converter - (25, "maxstack", "number", None, None), - ((12, 7), "FontMatrix", "array", [0.001, 0, 0, 0.001, 0, 0], None), - ((12, 37), "FDSelect", "number", None, FDSelectConverter()), - ((12, 36), "FDArray", "number", None, FDArrayConverter()), - (17, "CharStrings", "number", None, CharStringsConverter()), - (24, "VarStore", "number", None, VarStoreConverter()), -] - -# Note! FDSelect and FDArray must both preceed CharStrings in the output XML build order, -# in order for the font to compile back from xml. - -kBlendDictOpName = "blend" -blendOp = 23 - -privateDictOperators = [ - # opcode name argument type default converter - (22, "vsindex", "number", None, None), - ( - blendOp, - kBlendDictOpName, - "blendList", - None, - None, - ), # This is for reading to/from XML: it not written to CFF. - (6, "BlueValues", "delta", None, None), - (7, "OtherBlues", "delta", None, None), - (8, "FamilyBlues", "delta", None, None), - (9, "FamilyOtherBlues", "delta", None, None), - ((12, 9), "BlueScale", "number", 0.039625, None), - ((12, 10), "BlueShift", "number", 7, None), - ((12, 11), "BlueFuzz", "number", 1, None), - (10, "StdHW", "number", None, None), - (11, "StdVW", "number", None, None), - ((12, 12), "StemSnapH", "delta", None, None), - ((12, 13), "StemSnapV", "delta", None, None), - ((12, 14), "ForceBold", "number", 0, None), - ((12, 15), "ForceBoldThreshold", "number", None, None), # deprecated - ((12, 16), "lenIV", "number", None, None), # deprecated - ((12, 17), "LanguageGroup", "number", 0, None), - ((12, 18), "ExpansionFactor", "number", 0.06, None), - ((12, 19), "initialRandomSeed", "number", 0, None), - (20, "defaultWidthX", "number", 0, None), - (21, "nominalWidthX", "number", 0, None), - (19, "Subrs", "number", None, SubrsConverter()), -] - -privateDictOperators2 = [ - # opcode name argument type default converter - (22, "vsindex", "number", None, None), - ( - blendOp, - kBlendDictOpName, - "blendList", - None, - None, - ), # This is for reading to/from XML: it not written to CFF. - (6, "BlueValues", "delta", None, None), - (7, "OtherBlues", "delta", None, None), - (8, "FamilyBlues", "delta", None, None), - (9, "FamilyOtherBlues", "delta", None, None), - ((12, 9), "BlueScale", "number", 0.039625, None), - ((12, 10), "BlueShift", "number", 7, None), - ((12, 11), "BlueFuzz", "number", 1, None), - (10, "StdHW", "number", None, None), - (11, "StdVW", "number", None, None), - ((12, 12), "StemSnapH", "delta", None, None), - ((12, 13), "StemSnapV", "delta", None, None), - ((12, 17), "LanguageGroup", "number", 0, None), - ((12, 18), "ExpansionFactor", "number", 0.06, None), - (19, "Subrs", "number", None, SubrsConverter()), -] - - -def addConverters(table): - for i in range(len(table)): - op, name, arg, default, conv = table[i] - if conv is not None: - continue - if arg in ("delta", "array"): - conv = ArrayConverter() - elif arg == "number": - conv = NumberConverter() - elif arg == "SID": - conv = ASCIIConverter() - elif arg == "blendList": - conv = None - else: - assert False - table[i] = op, name, arg, default, conv - - -addConverters(privateDictOperators) -addConverters(topDictOperators) - - -class TopDictDecompiler(psCharStrings.DictDecompiler): - operators = buildOperatorDict(topDictOperators) - - -class PrivateDictDecompiler(psCharStrings.DictDecompiler): - operators = buildOperatorDict(privateDictOperators) - - -class DictCompiler(object): - maxBlendStack = 0 - - def __init__(self, dictObj, strings, parent, isCFF2=None): - if strings: - assert isinstance(strings, IndexedStrings) - if isCFF2 is None and hasattr(parent, "isCFF2"): - isCFF2 = parent.isCFF2 - assert isCFF2 is not None - self.isCFF2 = isCFF2 - self.dictObj = dictObj - self.strings = strings - self.parent = parent - rawDict = {} - for name in dictObj.order: - value = getattr(dictObj, name, None) - if value is None: - continue - conv = dictObj.converters[name] - value = conv.write(dictObj, value) - if value == dictObj.defaults.get(name): - continue - rawDict[name] = value - self.rawDict = rawDict - - def setPos(self, pos, endPos): - pass - - def getDataLength(self): - return len(self.compile("getDataLength")) - - def compile(self, reason): - log.log(DEBUG, "-- compiling %s for %s", self.__class__.__name__, reason) - rawDict = self.rawDict - data = [] - for name in self.dictObj.order: - value = rawDict.get(name) - if value is None: - continue - op, argType = self.opcodes[name] - if isinstance(argType, tuple): - l = len(argType) - assert len(value) == l, "value doesn't match arg type" - for i in range(l): - arg = argType[i] - v = value[i] - arghandler = getattr(self, "arg_" + arg) - data.append(arghandler(v)) - else: - arghandler = getattr(self, "arg_" + argType) - data.append(arghandler(value)) - data.append(op) - data = bytesjoin(data) - return data - - def toFile(self, file): - data = self.compile("toFile") - file.write(data) - - def arg_number(self, num): - if isinstance(num, list): - data = [encodeNumber(val) for val in num] - data.append(encodeNumber(1)) - data.append(bytechr(blendOp)) - datum = bytesjoin(data) - else: - datum = encodeNumber(num) - return datum - - def arg_SID(self, s): - return psCharStrings.encodeIntCFF(self.strings.getSID(s)) - - def arg_array(self, value): - data = [] - for num in value: - data.append(self.arg_number(num)) - return bytesjoin(data) - - def arg_delta(self, value): - if not value: - return b"" - val0 = value[0] - if isinstance(val0, list): - data = self.arg_delta_blend(value) - else: - out = [] - last = 0 - for v in value: - out.append(v - last) - last = v - data = [] - for num in out: - data.append(encodeNumber(num)) - return bytesjoin(data) - - def arg_delta_blend(self, value): - """A delta list with blend lists has to be *all* blend lists. - - The value is a list is arranged as follows:: - - [ - [V0, d0..dn] - [V1, d0..dn] - ... - [Vm, d0..dn] - ] - - ``V`` is the absolute coordinate value from the default font, and ``d0-dn`` - are the delta values from the *n* regions. Each ``V`` is an absolute - coordinate from the default font. - - We want to return a list:: - - [ - [v0, v1..vm] - [d0..dn] - ... - [d0..dn] - numBlends - blendOp - ] - - where each ``v`` is relative to the previous default font value. - """ - numMasters = len(value[0]) - numBlends = len(value) - numStack = (numBlends * numMasters) + 1 - if numStack > self.maxBlendStack: - # Figure out the max number of value we can blend - # and divide this list up into chunks of that size. - - numBlendValues = int((self.maxBlendStack - 1) / numMasters) - out = [] - while True: - numVal = min(len(value), numBlendValues) - if numVal == 0: - break - valList = value[0:numVal] - out1 = self.arg_delta_blend(valList) - out.extend(out1) - value = value[numVal:] - else: - firstList = [0] * numBlends - deltaList = [None] * numBlends - i = 0 - prevVal = 0 - while i < numBlends: - # For PrivateDict BlueValues, the default font - # values are absolute, not relative. - # Must convert these back to relative coordinates - # before writing to CFF2. - defaultValue = value[i][0] - firstList[i] = defaultValue - prevVal - prevVal = defaultValue - deltaList[i] = value[i][1:] - i += 1 - - relValueList = firstList - for blendList in deltaList: - relValueList.extend(blendList) - out = [encodeNumber(val) for val in relValueList] - out.append(encodeNumber(numBlends)) - out.append(bytechr(blendOp)) - return out - - -def encodeNumber(num): - if isinstance(num, float): - return psCharStrings.encodeFloat(num) - else: - return psCharStrings.encodeIntCFF(num) - - -class TopDictCompiler(DictCompiler): - opcodes = buildOpcodeDict(topDictOperators) - - def getChildren(self, strings): - isCFF2 = self.isCFF2 - children = [] - if self.dictObj.cff2GetGlyphOrder is None: - if hasattr(self.dictObj, "charset") and self.dictObj.charset: - if hasattr(self.dictObj, "ROS"): # aka isCID - charsetCode = None - else: - charsetCode = getStdCharSet(self.dictObj.charset) - if charsetCode is None: - children.append( - CharsetCompiler(strings, self.dictObj.charset, self) - ) - else: - self.rawDict["charset"] = charsetCode - if hasattr(self.dictObj, "Encoding") and self.dictObj.Encoding: - encoding = self.dictObj.Encoding - if not isinstance(encoding, str): - children.append(EncodingCompiler(strings, encoding, self)) - else: - if hasattr(self.dictObj, "VarStore"): - varStoreData = self.dictObj.VarStore - varStoreComp = VarStoreCompiler(varStoreData, self) - children.append(varStoreComp) - if hasattr(self.dictObj, "FDSelect"): - # I have not yet supported merging a ttx CFF-CID font, as there are - # interesting issues about merging the FDArrays. Here I assume that - # either the font was read from XML, and the FDSelect indices are all - # in the charstring data, or the FDSelect array is already fully defined. - fdSelect = self.dictObj.FDSelect - # probably read in from XML; assume fdIndex in CharString data - if len(fdSelect) == 0: - charStrings = self.dictObj.CharStrings - for name in self.dictObj.charset: - fdSelect.append(charStrings[name].fdSelectIndex) - fdSelectComp = FDSelectCompiler(fdSelect, self) - children.append(fdSelectComp) - if hasattr(self.dictObj, "CharStrings"): - items = [] - charStrings = self.dictObj.CharStrings - for name in self.dictObj.charset: - items.append(charStrings[name]) - charStringsComp = CharStringsCompiler(items, strings, self, isCFF2=isCFF2) - children.append(charStringsComp) - if hasattr(self.dictObj, "FDArray"): - # I have not yet supported merging a ttx CFF-CID font, as there are - # interesting issues about merging the FDArrays. Here I assume that the - # FDArray info is correct and complete. - fdArrayIndexComp = self.dictObj.FDArray.getCompiler(strings, self) - children.append(fdArrayIndexComp) - children.extend(fdArrayIndexComp.getChildren(strings)) - if hasattr(self.dictObj, "Private"): - privComp = self.dictObj.Private.getCompiler(strings, self) - children.append(privComp) - children.extend(privComp.getChildren(strings)) - return children - - -class FontDictCompiler(DictCompiler): - opcodes = buildOpcodeDict(topDictOperators) - - def __init__(self, dictObj, strings, parent, isCFF2=None): - super(FontDictCompiler, self).__init__(dictObj, strings, parent, isCFF2=isCFF2) - # - # We now take some effort to detect if there were any key/value pairs - # supplied that were ignored in the FontDict context, and issue a warning - # for those cases. - # - ignoredNames = [] - dictObj = self.dictObj - for name in sorted(set(dictObj.converters) - set(dictObj.order)): - if name in dictObj.rawDict: - # The font was directly read from binary. In this - # case, we want to report *all* "useless" key/value - # pairs that are in the font, not just the ones that - # are different from the default. - ignoredNames.append(name) - else: - # The font was probably read from a TTX file. We only - # warn about keys whos value is not the default. The - # ones that have the default value will not be written - # to binary anyway. - default = dictObj.defaults.get(name) - if default is not None: - conv = dictObj.converters[name] - default = conv.read(dictObj, default) - if getattr(dictObj, name, None) != default: - ignoredNames.append(name) - if ignoredNames: - log.warning( - "Some CFF FDArray/FontDict keys were ignored upon compile: " - + " ".join(sorted(ignoredNames)) - ) - - def getChildren(self, strings): - children = [] - if hasattr(self.dictObj, "Private"): - privComp = self.dictObj.Private.getCompiler(strings, self) - children.append(privComp) - children.extend(privComp.getChildren(strings)) - return children - - -class PrivateDictCompiler(DictCompiler): - maxBlendStack = maxStackLimit - opcodes = buildOpcodeDict(privateDictOperators) - - def setPos(self, pos, endPos): - size = endPos - pos - self.parent.rawDict["Private"] = size, pos - self.pos = pos - - def getChildren(self, strings): - children = [] - if hasattr(self.dictObj, "Subrs"): - children.append(self.dictObj.Subrs.getCompiler(strings, self)) - return children - - -class BaseDict(object): - def __init__(self, strings=None, file=None, offset=None, isCFF2=None): - assert (isCFF2 is None) == (file is None) - self.rawDict = {} - self.skipNames = [] - self.strings = strings - if file is None: - return - self._isCFF2 = isCFF2 - self.file = file - if offset is not None: - log.log(DEBUG, "loading %s at %s", self.__class__.__name__, offset) - self.offset = offset - - def decompile(self, data): - log.log(DEBUG, " length %s is %d", self.__class__.__name__, len(data)) - dec = self.decompilerClass(self.strings, self) - dec.decompile(data) - self.rawDict = dec.getDict() - self.postDecompile() - - def postDecompile(self): - pass - - def getCompiler(self, strings, parent, isCFF2=None): - return self.compilerClass(self, strings, parent, isCFF2=isCFF2) - - def __getattr__(self, name): - if name[:2] == name[-2:] == "__": - # to make deepcopy() and pickle.load() work, we need to signal with - # AttributeError that dunder methods like '__deepcopy__' or '__getstate__' - # aren't implemented. For more details, see: - # https://github.com/fonttools/fonttools/pull/1488 - raise AttributeError(name) - value = self.rawDict.get(name, None) - if value is None: - value = self.defaults.get(name) - if value is None: - raise AttributeError(name) - conv = self.converters[name] - value = conv.read(self, value) - setattr(self, name, value) - return value - - def toXML(self, xmlWriter): - for name in self.order: - if name in self.skipNames: - continue - value = getattr(self, name, None) - # XXX For "charset" we never skip calling xmlWrite even if the - # value is None, so we always write the following XML comment: - # - # - # - # Charset is None when 'CFF ' table is imported from XML into an - # empty TTFont(). By writing this comment all the time, we obtain - # the same XML output whether roundtripping XML-to-XML or - # dumping binary-to-XML - if value is None and name != "charset": - continue - conv = self.converters[name] - conv.xmlWrite(xmlWriter, name, value) - ignoredNames = set(self.rawDict) - set(self.order) - if ignoredNames: - xmlWriter.comment( - "some keys were ignored: %s" % " ".join(sorted(ignoredNames)) - ) - xmlWriter.newline() - - def fromXML(self, name, attrs, content): - conv = self.converters[name] - value = conv.xmlRead(name, attrs, content, self) - setattr(self, name, value) - - -class TopDict(BaseDict): - """The ``TopDict`` represents the top-level dictionary holding font - information. CFF2 tables contain a restricted set of top-level entries - as described `here `_, - but CFF tables may contain a wider range of information. This information - can be accessed through attributes or through the dictionary returned - through the ``rawDict`` property: - - .. code:: python - - font = tt["CFF "].cff[0] - font.FamilyName - # 'Linux Libertine O' - font.rawDict["FamilyName"] - # 'Linux Libertine O' - - More information is available in the CFF file's private dictionary, accessed - via the ``Private`` property: - - .. code:: python - - tt["CFF "].cff[0].Private.BlueValues - # [-15, 0, 515, 515, 666, 666] - - """ - - defaults = buildDefaults(topDictOperators) - converters = buildConverters(topDictOperators) - compilerClass = TopDictCompiler - order = buildOrder(topDictOperators) - decompilerClass = TopDictDecompiler - - def __init__( - self, - strings=None, - file=None, - offset=None, - GlobalSubrs=None, - cff2GetGlyphOrder=None, - isCFF2=None, - ): - super(TopDict, self).__init__(strings, file, offset, isCFF2=isCFF2) - self.cff2GetGlyphOrder = cff2GetGlyphOrder - self.GlobalSubrs = GlobalSubrs - if isCFF2: - self.defaults = buildDefaults(topDictOperators2) - self.charset = cff2GetGlyphOrder() - self.order = buildOrder(topDictOperators2) - else: - self.defaults = buildDefaults(topDictOperators) - self.order = buildOrder(topDictOperators) - - def getGlyphOrder(self): - """Returns a list of glyph names in the CFF font.""" - return self.charset - - def postDecompile(self): - offset = self.rawDict.get("CharStrings") - if offset is None: - return - # get the number of glyphs beforehand. - self.file.seek(offset) - if self._isCFF2: - self.numGlyphs = readCard32(self.file) - else: - self.numGlyphs = readCard16(self.file) - - def toXML(self, xmlWriter): - if hasattr(self, "CharStrings"): - self.decompileAllCharStrings() - if hasattr(self, "ROS"): - self.skipNames = ["Encoding"] - if not hasattr(self, "ROS") or not hasattr(self, "CharStrings"): - # these values have default values, but I only want them to show up - # in CID fonts. - self.skipNames = [ - "CIDFontVersion", - "CIDFontRevision", - "CIDFontType", - "CIDCount", - ] - BaseDict.toXML(self, xmlWriter) - - def decompileAllCharStrings(self): - # Make sure that all the Private Dicts have been instantiated. - for i, charString in enumerate(self.CharStrings.values()): - try: - charString.decompile() - except: - log.error("Error in charstring %s", i) - raise - - def recalcFontBBox(self): - fontBBox = None - for charString in self.CharStrings.values(): - bounds = charString.calcBounds(self.CharStrings) - if bounds is not None: - if fontBBox is not None: - fontBBox = unionRect(fontBBox, bounds) - else: - fontBBox = bounds - - if fontBBox is None: - self.FontBBox = self.defaults["FontBBox"][:] - else: - self.FontBBox = list(intRect(fontBBox)) - - -class FontDict(BaseDict): - # - # Since fonttools used to pass a lot of fields that are not relevant in the FDArray - # FontDict, there are 'ttx' files in the wild that contain all these. These got in - # the ttx files because fonttools writes explicit values for all the TopDict default - # values. These are not actually illegal in the context of an FDArray FontDict - you - # can legally, per spec, put any arbitrary key/value pair in a FontDict - but are - # useless since current major company CFF interpreters ignore anything but the set - # listed in this file. So, we just silently skip them. An exception is Weight: this - # is not used by any interpreter, but some foundries have asked that this be - # supported in FDArray FontDicts just to preserve information about the design when - # the font is being inspected. - # - # On top of that, there are fonts out there that contain such useless FontDict values. - # - # By subclassing TopDict, we *allow* all key/values from TopDict, both when reading - # from binary or when reading from XML, but by overriding `order` with a limited - # list of names, we ensure that only the useful names ever get exported to XML and - # ever get compiled into the binary font. - # - # We override compilerClass so we can warn about "useless" key/value pairs, either - # from the original binary font or from TTX input. - # - # See: - # - https://github.com/fonttools/fonttools/issues/740 - # - https://github.com/fonttools/fonttools/issues/601 - # - https://github.com/adobe-type-tools/afdko/issues/137 - # - defaults = {} - converters = buildConverters(topDictOperators) - compilerClass = FontDictCompiler - orderCFF = ["FontName", "FontMatrix", "Weight", "Private"] - orderCFF2 = ["Private"] - decompilerClass = TopDictDecompiler - - def __init__( - self, - strings=None, - file=None, - offset=None, - GlobalSubrs=None, - isCFF2=None, - vstore=None, - ): - super(FontDict, self).__init__(strings, file, offset, isCFF2=isCFF2) - self.vstore = vstore - self.setCFF2(isCFF2) - - def setCFF2(self, isCFF2): - # isCFF2 may be None. - if isCFF2: - self.order = self.orderCFF2 - self._isCFF2 = True - else: - self.order = self.orderCFF - self._isCFF2 = False - - -class PrivateDict(BaseDict): - defaults = buildDefaults(privateDictOperators) - converters = buildConverters(privateDictOperators) - order = buildOrder(privateDictOperators) - decompilerClass = PrivateDictDecompiler - compilerClass = PrivateDictCompiler - - def __init__(self, strings=None, file=None, offset=None, isCFF2=None, vstore=None): - super(PrivateDict, self).__init__(strings, file, offset, isCFF2=isCFF2) - self.vstore = vstore - if isCFF2: - self.defaults = buildDefaults(privateDictOperators2) - self.order = buildOrder(privateDictOperators2) - # Provide dummy values. This avoids needing to provide - # an isCFF2 state in a lot of places. - self.nominalWidthX = self.defaultWidthX = None - self._isCFF2 = True - else: - self.defaults = buildDefaults(privateDictOperators) - self.order = buildOrder(privateDictOperators) - self._isCFF2 = False - - @property - def in_cff2(self): - return self._isCFF2 - - def getNumRegions(self, vi=None): # called from misc/psCharStrings.py - # if getNumRegions is being called, we can assume that VarStore exists. - if vi is None: - if hasattr(self, "vsindex"): - vi = self.vsindex - else: - vi = 0 - numRegions = self.vstore.getNumRegions(vi) - return numRegions - - -class IndexedStrings(object): - """SID -> string mapping.""" - - def __init__(self, file=None): - if file is None: - strings = [] - else: - strings = [tostr(s, encoding="latin1") for s in Index(file, isCFF2=False)] - self.strings = strings - - def getCompiler(self): - return IndexedStringsCompiler(self, None, self, isCFF2=False) - - def __len__(self): - return len(self.strings) - - def __getitem__(self, SID): - if SID < cffStandardStringCount: - return cffStandardStrings[SID] - else: - return self.strings[SID - cffStandardStringCount] - - def getSID(self, s): - if not hasattr(self, "stringMapping"): - self.buildStringMapping() - s = tostr(s, encoding="latin1") - if s in cffStandardStringMapping: - SID = cffStandardStringMapping[s] - elif s in self.stringMapping: - SID = self.stringMapping[s] - else: - SID = len(self.strings) + cffStandardStringCount - self.strings.append(s) - self.stringMapping[s] = SID - return SID - - def getStrings(self): - return self.strings - - def buildStringMapping(self): - self.stringMapping = {} - for index in range(len(self.strings)): - self.stringMapping[self.strings[index]] = index + cffStandardStringCount - - -# The 391 Standard Strings as used in the CFF format. -# from Adobe Technical None #5176, version 1.0, 18 March 1998 - -cffStandardStrings = [ - ".notdef", - "space", - "exclam", - "quotedbl", - "numbersign", - "dollar", - "percent", - "ampersand", - "quoteright", - "parenleft", - "parenright", - "asterisk", - "plus", - "comma", - "hyphen", - "period", - "slash", - "zero", - "one", - "two", - "three", - "four", - "five", - "six", - "seven", - "eight", - "nine", - "colon", - "semicolon", - "less", - "equal", - "greater", - "question", - "at", - "A", - "B", - "C", - "D", - "E", - "F", - "G", - "H", - "I", - "J", - "K", - "L", - "M", - "N", - "O", - "P", - "Q", - "R", - "S", - "T", - "U", - "V", - "W", - "X", - "Y", - "Z", - "bracketleft", - "backslash", - "bracketright", - "asciicircum", - "underscore", - "quoteleft", - "a", - "b", - "c", - "d", - "e", - "f", - "g", - "h", - "i", - "j", - "k", - "l", - "m", - "n", - "o", - "p", - "q", - "r", - "s", - "t", - "u", - "v", - "w", - "x", - "y", - "z", - "braceleft", - "bar", - "braceright", - "asciitilde", - "exclamdown", - "cent", - "sterling", - "fraction", - "yen", - "florin", - "section", - "currency", - "quotesingle", - "quotedblleft", - "guillemotleft", - "guilsinglleft", - "guilsinglright", - "fi", - "fl", - "endash", - "dagger", - "daggerdbl", - "periodcentered", - "paragraph", - "bullet", - "quotesinglbase", - "quotedblbase", - "quotedblright", - "guillemotright", - "ellipsis", - "perthousand", - "questiondown", - "grave", - "acute", - "circumflex", - "tilde", - "macron", - "breve", - "dotaccent", - "dieresis", - "ring", - "cedilla", - "hungarumlaut", - "ogonek", - "caron", - "emdash", - "AE", - "ordfeminine", - "Lslash", - "Oslash", - "OE", - "ordmasculine", - "ae", - "dotlessi", - "lslash", - "oslash", - "oe", - "germandbls", - "onesuperior", - "logicalnot", - "mu", - "trademark", - "Eth", - "onehalf", - "plusminus", - "Thorn", - "onequarter", - "divide", - "brokenbar", - "degree", - "thorn", - "threequarters", - "twosuperior", - "registered", - "minus", - "eth", - "multiply", - "threesuperior", - "copyright", - "Aacute", - "Acircumflex", - "Adieresis", - "Agrave", - "Aring", - "Atilde", - "Ccedilla", - "Eacute", - "Ecircumflex", - "Edieresis", - "Egrave", - "Iacute", - "Icircumflex", - "Idieresis", - "Igrave", - "Ntilde", - "Oacute", - "Ocircumflex", - "Odieresis", - "Ograve", - "Otilde", - "Scaron", - "Uacute", - "Ucircumflex", - "Udieresis", - "Ugrave", - "Yacute", - "Ydieresis", - "Zcaron", - "aacute", - "acircumflex", - "adieresis", - "agrave", - "aring", - "atilde", - "ccedilla", - "eacute", - "ecircumflex", - "edieresis", - "egrave", - "iacute", - "icircumflex", - "idieresis", - "igrave", - "ntilde", - "oacute", - "ocircumflex", - "odieresis", - "ograve", - "otilde", - "scaron", - "uacute", - "ucircumflex", - "udieresis", - "ugrave", - "yacute", - "ydieresis", - "zcaron", - "exclamsmall", - "Hungarumlautsmall", - "dollaroldstyle", - "dollarsuperior", - "ampersandsmall", - "Acutesmall", - "parenleftsuperior", - "parenrightsuperior", - "twodotenleader", - "onedotenleader", - "zerooldstyle", - "oneoldstyle", - "twooldstyle", - "threeoldstyle", - "fouroldstyle", - "fiveoldstyle", - "sixoldstyle", - "sevenoldstyle", - "eightoldstyle", - "nineoldstyle", - "commasuperior", - "threequartersemdash", - "periodsuperior", - "questionsmall", - "asuperior", - "bsuperior", - "centsuperior", - "dsuperior", - "esuperior", - "isuperior", - "lsuperior", - "msuperior", - "nsuperior", - "osuperior", - "rsuperior", - "ssuperior", - "tsuperior", - "ff", - "ffi", - "ffl", - "parenleftinferior", - "parenrightinferior", - "Circumflexsmall", - "hyphensuperior", - "Gravesmall", - "Asmall", - "Bsmall", - "Csmall", - "Dsmall", - "Esmall", - "Fsmall", - "Gsmall", - "Hsmall", - "Ismall", - "Jsmall", - "Ksmall", - "Lsmall", - "Msmall", - "Nsmall", - "Osmall", - "Psmall", - "Qsmall", - "Rsmall", - "Ssmall", - "Tsmall", - "Usmall", - "Vsmall", - "Wsmall", - "Xsmall", - "Ysmall", - "Zsmall", - "colonmonetary", - "onefitted", - "rupiah", - "Tildesmall", - "exclamdownsmall", - "centoldstyle", - "Lslashsmall", - "Scaronsmall", - "Zcaronsmall", - "Dieresissmall", - "Brevesmall", - "Caronsmall", - "Dotaccentsmall", - "Macronsmall", - "figuredash", - "hypheninferior", - "Ogoneksmall", - "Ringsmall", - "Cedillasmall", - "questiondownsmall", - "oneeighth", - "threeeighths", - "fiveeighths", - "seveneighths", - "onethird", - "twothirds", - "zerosuperior", - "foursuperior", - "fivesuperior", - "sixsuperior", - "sevensuperior", - "eightsuperior", - "ninesuperior", - "zeroinferior", - "oneinferior", - "twoinferior", - "threeinferior", - "fourinferior", - "fiveinferior", - "sixinferior", - "seveninferior", - "eightinferior", - "nineinferior", - "centinferior", - "dollarinferior", - "periodinferior", - "commainferior", - "Agravesmall", - "Aacutesmall", - "Acircumflexsmall", - "Atildesmall", - "Adieresissmall", - "Aringsmall", - "AEsmall", - "Ccedillasmall", - "Egravesmall", - "Eacutesmall", - "Ecircumflexsmall", - "Edieresissmall", - "Igravesmall", - "Iacutesmall", - "Icircumflexsmall", - "Idieresissmall", - "Ethsmall", - "Ntildesmall", - "Ogravesmall", - "Oacutesmall", - "Ocircumflexsmall", - "Otildesmall", - "Odieresissmall", - "OEsmall", - "Oslashsmall", - "Ugravesmall", - "Uacutesmall", - "Ucircumflexsmall", - "Udieresissmall", - "Yacutesmall", - "Thornsmall", - "Ydieresissmall", - "001.000", - "001.001", - "001.002", - "001.003", - "Black", - "Bold", - "Book", - "Light", - "Medium", - "Regular", - "Roman", - "Semibold", -] - -cffStandardStringCount = 391 -assert len(cffStandardStrings) == cffStandardStringCount -# build reverse mapping -cffStandardStringMapping = {} -for _i in range(cffStandardStringCount): - cffStandardStringMapping[cffStandardStrings[_i]] = _i - -cffISOAdobeStrings = [ - ".notdef", - "space", - "exclam", - "quotedbl", - "numbersign", - "dollar", - "percent", - "ampersand", - "quoteright", - "parenleft", - "parenright", - "asterisk", - "plus", - "comma", - "hyphen", - "period", - "slash", - "zero", - "one", - "two", - "three", - "four", - "five", - "six", - "seven", - "eight", - "nine", - "colon", - "semicolon", - "less", - "equal", - "greater", - "question", - "at", - "A", - "B", - "C", - "D", - "E", - "F", - "G", - "H", - "I", - "J", - "K", - "L", - "M", - "N", - "O", - "P", - "Q", - "R", - "S", - "T", - "U", - "V", - "W", - "X", - "Y", - "Z", - "bracketleft", - "backslash", - "bracketright", - "asciicircum", - "underscore", - "quoteleft", - "a", - "b", - "c", - "d", - "e", - "f", - "g", - "h", - "i", - "j", - "k", - "l", - "m", - "n", - "o", - "p", - "q", - "r", - "s", - "t", - "u", - "v", - "w", - "x", - "y", - "z", - "braceleft", - "bar", - "braceright", - "asciitilde", - "exclamdown", - "cent", - "sterling", - "fraction", - "yen", - "florin", - "section", - "currency", - "quotesingle", - "quotedblleft", - "guillemotleft", - "guilsinglleft", - "guilsinglright", - "fi", - "fl", - "endash", - "dagger", - "daggerdbl", - "periodcentered", - "paragraph", - "bullet", - "quotesinglbase", - "quotedblbase", - "quotedblright", - "guillemotright", - "ellipsis", - "perthousand", - "questiondown", - "grave", - "acute", - "circumflex", - "tilde", - "macron", - "breve", - "dotaccent", - "dieresis", - "ring", - "cedilla", - "hungarumlaut", - "ogonek", - "caron", - "emdash", - "AE", - "ordfeminine", - "Lslash", - "Oslash", - "OE", - "ordmasculine", - "ae", - "dotlessi", - "lslash", - "oslash", - "oe", - "germandbls", - "onesuperior", - "logicalnot", - "mu", - "trademark", - "Eth", - "onehalf", - "plusminus", - "Thorn", - "onequarter", - "divide", - "brokenbar", - "degree", - "thorn", - "threequarters", - "twosuperior", - "registered", - "minus", - "eth", - "multiply", - "threesuperior", - "copyright", - "Aacute", - "Acircumflex", - "Adieresis", - "Agrave", - "Aring", - "Atilde", - "Ccedilla", - "Eacute", - "Ecircumflex", - "Edieresis", - "Egrave", - "Iacute", - "Icircumflex", - "Idieresis", - "Igrave", - "Ntilde", - "Oacute", - "Ocircumflex", - "Odieresis", - "Ograve", - "Otilde", - "Scaron", - "Uacute", - "Ucircumflex", - "Udieresis", - "Ugrave", - "Yacute", - "Ydieresis", - "Zcaron", - "aacute", - "acircumflex", - "adieresis", - "agrave", - "aring", - "atilde", - "ccedilla", - "eacute", - "ecircumflex", - "edieresis", - "egrave", - "iacute", - "icircumflex", - "idieresis", - "igrave", - "ntilde", - "oacute", - "ocircumflex", - "odieresis", - "ograve", - "otilde", - "scaron", - "uacute", - "ucircumflex", - "udieresis", - "ugrave", - "yacute", - "ydieresis", - "zcaron", -] - -cffISOAdobeStringCount = 229 -assert len(cffISOAdobeStrings) == cffISOAdobeStringCount - -cffIExpertStrings = [ - ".notdef", - "space", - "exclamsmall", - "Hungarumlautsmall", - "dollaroldstyle", - "dollarsuperior", - "ampersandsmall", - "Acutesmall", - "parenleftsuperior", - "parenrightsuperior", - "twodotenleader", - "onedotenleader", - "comma", - "hyphen", - "period", - "fraction", - "zerooldstyle", - "oneoldstyle", - "twooldstyle", - "threeoldstyle", - "fouroldstyle", - "fiveoldstyle", - "sixoldstyle", - "sevenoldstyle", - "eightoldstyle", - "nineoldstyle", - "colon", - "semicolon", - "commasuperior", - "threequartersemdash", - "periodsuperior", - "questionsmall", - "asuperior", - "bsuperior", - "centsuperior", - "dsuperior", - "esuperior", - "isuperior", - "lsuperior", - "msuperior", - "nsuperior", - "osuperior", - "rsuperior", - "ssuperior", - "tsuperior", - "ff", - "fi", - "fl", - "ffi", - "ffl", - "parenleftinferior", - "parenrightinferior", - "Circumflexsmall", - "hyphensuperior", - "Gravesmall", - "Asmall", - "Bsmall", - "Csmall", - "Dsmall", - "Esmall", - "Fsmall", - "Gsmall", - "Hsmall", - "Ismall", - "Jsmall", - "Ksmall", - "Lsmall", - "Msmall", - "Nsmall", - "Osmall", - "Psmall", - "Qsmall", - "Rsmall", - "Ssmall", - "Tsmall", - "Usmall", - "Vsmall", - "Wsmall", - "Xsmall", - "Ysmall", - "Zsmall", - "colonmonetary", - "onefitted", - "rupiah", - "Tildesmall", - "exclamdownsmall", - "centoldstyle", - "Lslashsmall", - "Scaronsmall", - "Zcaronsmall", - "Dieresissmall", - "Brevesmall", - "Caronsmall", - "Dotaccentsmall", - "Macronsmall", - "figuredash", - "hypheninferior", - "Ogoneksmall", - "Ringsmall", - "Cedillasmall", - "onequarter", - "onehalf", - "threequarters", - "questiondownsmall", - "oneeighth", - "threeeighths", - "fiveeighths", - "seveneighths", - "onethird", - "twothirds", - "zerosuperior", - "onesuperior", - "twosuperior", - "threesuperior", - "foursuperior", - "fivesuperior", - "sixsuperior", - "sevensuperior", - "eightsuperior", - "ninesuperior", - "zeroinferior", - "oneinferior", - "twoinferior", - "threeinferior", - "fourinferior", - "fiveinferior", - "sixinferior", - "seveninferior", - "eightinferior", - "nineinferior", - "centinferior", - "dollarinferior", - "periodinferior", - "commainferior", - "Agravesmall", - "Aacutesmall", - "Acircumflexsmall", - "Atildesmall", - "Adieresissmall", - "Aringsmall", - "AEsmall", - "Ccedillasmall", - "Egravesmall", - "Eacutesmall", - "Ecircumflexsmall", - "Edieresissmall", - "Igravesmall", - "Iacutesmall", - "Icircumflexsmall", - "Idieresissmall", - "Ethsmall", - "Ntildesmall", - "Ogravesmall", - "Oacutesmall", - "Ocircumflexsmall", - "Otildesmall", - "Odieresissmall", - "OEsmall", - "Oslashsmall", - "Ugravesmall", - "Uacutesmall", - "Ucircumflexsmall", - "Udieresissmall", - "Yacutesmall", - "Thornsmall", - "Ydieresissmall", -] - -cffExpertStringCount = 166 -assert len(cffIExpertStrings) == cffExpertStringCount - -cffExpertSubsetStrings = [ - ".notdef", - "space", - "dollaroldstyle", - "dollarsuperior", - "parenleftsuperior", - "parenrightsuperior", - "twodotenleader", - "onedotenleader", - "comma", - "hyphen", - "period", - "fraction", - "zerooldstyle", - "oneoldstyle", - "twooldstyle", - "threeoldstyle", - "fouroldstyle", - "fiveoldstyle", - "sixoldstyle", - "sevenoldstyle", - "eightoldstyle", - "nineoldstyle", - "colon", - "semicolon", - "commasuperior", - "threequartersemdash", - "periodsuperior", - "asuperior", - "bsuperior", - "centsuperior", - "dsuperior", - "esuperior", - "isuperior", - "lsuperior", - "msuperior", - "nsuperior", - "osuperior", - "rsuperior", - "ssuperior", - "tsuperior", - "ff", - "fi", - "fl", - "ffi", - "ffl", - "parenleftinferior", - "parenrightinferior", - "hyphensuperior", - "colonmonetary", - "onefitted", - "rupiah", - "centoldstyle", - "figuredash", - "hypheninferior", - "onequarter", - "onehalf", - "threequarters", - "oneeighth", - "threeeighths", - "fiveeighths", - "seveneighths", - "onethird", - "twothirds", - "zerosuperior", - "onesuperior", - "twosuperior", - "threesuperior", - "foursuperior", - "fivesuperior", - "sixsuperior", - "sevensuperior", - "eightsuperior", - "ninesuperior", - "zeroinferior", - "oneinferior", - "twoinferior", - "threeinferior", - "fourinferior", - "fiveinferior", - "sixinferior", - "seveninferior", - "eightinferior", - "nineinferior", - "centinferior", - "dollarinferior", - "periodinferior", - "commainferior", -] - -cffExpertSubsetStringCount = 87 -assert len(cffExpertSubsetStrings) == cffExpertSubsetStringCount diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/CFF2ToCFF.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/CFF2ToCFF.cpython-312.pyc deleted file mode 100644 index 90c620dc..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/CFF2ToCFF.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/CFFToCFF2.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/CFFToCFF2.cpython-312.pyc deleted file mode 100644 index aa3ae007..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/CFFToCFF2.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/__init__.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 20f9a102..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/specializer.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/specializer.cpython-312.pyc deleted file mode 100644 index 928cb279..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/specializer.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/transforms.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/transforms.cpython-312.pyc deleted file mode 100644 index 57ce6ecc..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/transforms.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/width.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/width.cpython-312.pyc deleted file mode 100644 index 5577fb15..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/__pycache__/width.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/specializer.py b/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/specializer.py deleted file mode 100644 index 974060c4..00000000 --- a/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/specializer.py +++ /dev/null @@ -1,927 +0,0 @@ -# -*- coding: utf-8 -*- - -"""T2CharString operator specializer and generalizer. - -PostScript glyph drawing operations can be expressed in multiple different -ways. For example, as well as the ``lineto`` operator, there is also a -``hlineto`` operator which draws a horizontal line, removing the need to -specify a ``dx`` coordinate, and a ``vlineto`` operator which draws a -vertical line, removing the need to specify a ``dy`` coordinate. As well -as decompiling :class:`fontTools.misc.psCharStrings.T2CharString` objects -into lists of operations, this module allows for conversion between general -and specific forms of the operation. - -""" - -from fontTools.cffLib import maxStackLimit - - -def stringToProgram(string): - if isinstance(string, str): - string = string.split() - program = [] - for token in string: - try: - token = int(token) - except ValueError: - try: - token = float(token) - except ValueError: - pass - program.append(token) - return program - - -def programToString(program): - return " ".join(str(x) for x in program) - - -def programToCommands(program, getNumRegions=None): - """Takes a T2CharString program list and returns list of commands. - Each command is a two-tuple of commandname,arg-list. The commandname might - be empty string if no commandname shall be emitted (used for glyph width, - hintmask/cntrmask argument, as well as stray arguments at the end of the - program (🤷). - 'getNumRegions' may be None, or a callable object. It must return the - number of regions. 'getNumRegions' takes a single argument, vsindex. It - returns the numRegions for the vsindex. - The Charstring may or may not start with a width value. If the first - non-blend operator has an odd number of arguments, then the first argument is - a width, and is popped off. This is complicated with blend operators, as - there may be more than one before the first hint or moveto operator, and each - one reduces several arguments to just one list argument. We have to sum the - number of arguments that are not part of the blend arguments, and all the - 'numBlends' values. We could instead have said that by definition, if there - is a blend operator, there is no width value, since CFF2 Charstrings don't - have width values. I discussed this with Behdad, and we are allowing for an - initial width value in this case because developers may assemble a CFF2 - charstring from CFF Charstrings, which could have width values. - """ - - seenWidthOp = False - vsIndex = 0 - lenBlendStack = 0 - lastBlendIndex = 0 - commands = [] - stack = [] - it = iter(program) - - for token in it: - if not isinstance(token, str): - stack.append(token) - continue - - if token == "blend": - assert getNumRegions is not None - numSourceFonts = 1 + getNumRegions(vsIndex) - # replace the blend op args on the stack with a single list - # containing all the blend op args. - numBlends = stack[-1] - numBlendArgs = numBlends * numSourceFonts + 1 - # replace first blend op by a list of the blend ops. - stack[-numBlendArgs:] = [stack[-numBlendArgs:]] - lenStack = len(stack) - lenBlendStack += numBlends + lenStack - 1 - lastBlendIndex = lenStack - # if a blend op exists, this is or will be a CFF2 charstring. - continue - - elif token == "vsindex": - vsIndex = stack[-1] - assert type(vsIndex) is int - - elif (not seenWidthOp) and token in { - "hstem", - "hstemhm", - "vstem", - "vstemhm", - "cntrmask", - "hintmask", - "hmoveto", - "vmoveto", - "rmoveto", - "endchar", - }: - seenWidthOp = True - parity = token in {"hmoveto", "vmoveto"} - if lenBlendStack: - # lenBlendStack has the number of args represented by the last blend - # arg and all the preceding args. We need to now add the number of - # args following the last blend arg. - numArgs = lenBlendStack + len(stack[lastBlendIndex:]) - else: - numArgs = len(stack) - if numArgs and (numArgs % 2) ^ parity: - width = stack.pop(0) - commands.append(("", [width])) - - if token in {"hintmask", "cntrmask"}: - if stack: - commands.append(("", stack)) - commands.append((token, [])) - commands.append(("", [next(it)])) - else: - commands.append((token, stack)) - stack = [] - if stack: - commands.append(("", stack)) - return commands - - -def _flattenBlendArgs(args): - token_list = [] - for arg in args: - if isinstance(arg, list): - token_list.extend(arg) - token_list.append("blend") - else: - token_list.append(arg) - return token_list - - -def commandsToProgram(commands): - """Takes a commands list as returned by programToCommands() and converts - it back to a T2CharString program list.""" - program = [] - for op, args in commands: - if any(isinstance(arg, list) for arg in args): - args = _flattenBlendArgs(args) - program.extend(args) - if op: - program.append(op) - return program - - -def _everyN(el, n): - """Group the list el into groups of size n""" - l = len(el) - if l % n != 0: - raise ValueError(el) - for i in range(0, l, n): - yield el[i : i + n] - - -class _GeneralizerDecombinerCommandsMap(object): - @staticmethod - def rmoveto(args): - if len(args) != 2: - raise ValueError(args) - yield ("rmoveto", args) - - @staticmethod - def hmoveto(args): - if len(args) != 1: - raise ValueError(args) - yield ("rmoveto", [args[0], 0]) - - @staticmethod - def vmoveto(args): - if len(args) != 1: - raise ValueError(args) - yield ("rmoveto", [0, args[0]]) - - @staticmethod - def rlineto(args): - if not args: - raise ValueError(args) - for args in _everyN(args, 2): - yield ("rlineto", args) - - @staticmethod - def hlineto(args): - if not args: - raise ValueError(args) - it = iter(args) - try: - while True: - yield ("rlineto", [next(it), 0]) - yield ("rlineto", [0, next(it)]) - except StopIteration: - pass - - @staticmethod - def vlineto(args): - if not args: - raise ValueError(args) - it = iter(args) - try: - while True: - yield ("rlineto", [0, next(it)]) - yield ("rlineto", [next(it), 0]) - except StopIteration: - pass - - @staticmethod - def rrcurveto(args): - if not args: - raise ValueError(args) - for args in _everyN(args, 6): - yield ("rrcurveto", args) - - @staticmethod - def hhcurveto(args): - l = len(args) - if l < 4 or l % 4 > 1: - raise ValueError(args) - if l % 2 == 1: - yield ("rrcurveto", [args[1], args[0], args[2], args[3], args[4], 0]) - args = args[5:] - for args in _everyN(args, 4): - yield ("rrcurveto", [args[0], 0, args[1], args[2], args[3], 0]) - - @staticmethod - def vvcurveto(args): - l = len(args) - if l < 4 or l % 4 > 1: - raise ValueError(args) - if l % 2 == 1: - yield ("rrcurveto", [args[0], args[1], args[2], args[3], 0, args[4]]) - args = args[5:] - for args in _everyN(args, 4): - yield ("rrcurveto", [0, args[0], args[1], args[2], 0, args[3]]) - - @staticmethod - def hvcurveto(args): - l = len(args) - if l < 4 or l % 8 not in {0, 1, 4, 5}: - raise ValueError(args) - last_args = None - if l % 2 == 1: - lastStraight = l % 8 == 5 - args, last_args = args[:-5], args[-5:] - it = _everyN(args, 4) - try: - while True: - args = next(it) - yield ("rrcurveto", [args[0], 0, args[1], args[2], 0, args[3]]) - args = next(it) - yield ("rrcurveto", [0, args[0], args[1], args[2], args[3], 0]) - except StopIteration: - pass - if last_args: - args = last_args - if lastStraight: - yield ("rrcurveto", [args[0], 0, args[1], args[2], args[4], args[3]]) - else: - yield ("rrcurveto", [0, args[0], args[1], args[2], args[3], args[4]]) - - @staticmethod - def vhcurveto(args): - l = len(args) - if l < 4 or l % 8 not in {0, 1, 4, 5}: - raise ValueError(args) - last_args = None - if l % 2 == 1: - lastStraight = l % 8 == 5 - args, last_args = args[:-5], args[-5:] - it = _everyN(args, 4) - try: - while True: - args = next(it) - yield ("rrcurveto", [0, args[0], args[1], args[2], args[3], 0]) - args = next(it) - yield ("rrcurveto", [args[0], 0, args[1], args[2], 0, args[3]]) - except StopIteration: - pass - if last_args: - args = last_args - if lastStraight: - yield ("rrcurveto", [0, args[0], args[1], args[2], args[3], args[4]]) - else: - yield ("rrcurveto", [args[0], 0, args[1], args[2], args[4], args[3]]) - - @staticmethod - def rcurveline(args): - l = len(args) - if l < 8 or l % 6 != 2: - raise ValueError(args) - args, last_args = args[:-2], args[-2:] - for args in _everyN(args, 6): - yield ("rrcurveto", args) - yield ("rlineto", last_args) - - @staticmethod - def rlinecurve(args): - l = len(args) - if l < 8 or l % 2 != 0: - raise ValueError(args) - args, last_args = args[:-6], args[-6:] - for args in _everyN(args, 2): - yield ("rlineto", args) - yield ("rrcurveto", last_args) - - -def _convertBlendOpToArgs(blendList): - # args is list of blend op args. Since we are supporting - # recursive blend op calls, some of these args may also - # be a list of blend op args, and need to be converted before - # we convert the current list. - if any([isinstance(arg, list) for arg in blendList]): - args = [ - i - for e in blendList - for i in (_convertBlendOpToArgs(e) if isinstance(e, list) else [e]) - ] - else: - args = blendList - - # We now know that blendList contains a blend op argument list, even if - # some of the args are lists that each contain a blend op argument list. - # Convert from: - # [default font arg sequence x0,...,xn] + [delta tuple for x0] + ... + [delta tuple for xn] - # to: - # [ [x0] + [delta tuple for x0], - # ..., - # [xn] + [delta tuple for xn] ] - numBlends = args[-1] - # Can't use args.pop() when the args are being used in a nested list - # comprehension. See calling context - args = args[:-1] - - l = len(args) - numRegions = l // numBlends - 1 - if not (numBlends * (numRegions + 1) == l): - raise ValueError(blendList) - - defaultArgs = [[arg] for arg in args[:numBlends]] - deltaArgs = args[numBlends:] - numDeltaValues = len(deltaArgs) - deltaList = [ - deltaArgs[i : i + numRegions] for i in range(0, numDeltaValues, numRegions) - ] - blend_args = [a + b + [1] for a, b in zip(defaultArgs, deltaList)] - return blend_args - - -def generalizeCommands(commands, ignoreErrors=False): - result = [] - mapping = _GeneralizerDecombinerCommandsMap - for op, args in commands: - # First, generalize any blend args in the arg list. - if any([isinstance(arg, list) for arg in args]): - try: - args = [ - n - for arg in args - for n in ( - _convertBlendOpToArgs(arg) if isinstance(arg, list) else [arg] - ) - ] - except ValueError: - if ignoreErrors: - # Store op as data, such that consumers of commands do not have to - # deal with incorrect number of arguments. - result.append(("", args)) - result.append(("", [op])) - else: - raise - - func = getattr(mapping, op, None) - if func is None: - result.append((op, args)) - continue - try: - for command in func(args): - result.append(command) - except ValueError: - if ignoreErrors: - # Store op as data, such that consumers of commands do not have to - # deal with incorrect number of arguments. - result.append(("", args)) - result.append(("", [op])) - else: - raise - return result - - -def generalizeProgram(program, getNumRegions=None, **kwargs): - return commandsToProgram( - generalizeCommands(programToCommands(program, getNumRegions), **kwargs) - ) - - -def _categorizeVector(v): - """ - Takes X,Y vector v and returns one of r, h, v, or 0 depending on which - of X and/or Y are zero, plus tuple of nonzero ones. If both are zero, - it returns a single zero still. - - >>> _categorizeVector((0,0)) - ('0', (0,)) - >>> _categorizeVector((1,0)) - ('h', (1,)) - >>> _categorizeVector((0,2)) - ('v', (2,)) - >>> _categorizeVector((1,2)) - ('r', (1, 2)) - """ - if not v[0]: - if not v[1]: - return "0", v[:1] - else: - return "v", v[1:] - else: - if not v[1]: - return "h", v[:1] - else: - return "r", v - - -def _mergeCategories(a, b): - if a == "0": - return b - if b == "0": - return a - if a == b: - return a - return None - - -def _negateCategory(a): - if a == "h": - return "v" - if a == "v": - return "h" - assert a in "0r" - return a - - -def _convertToBlendCmds(args): - # return a list of blend commands, and - # the remaining non-blended args, if any. - num_args = len(args) - stack_use = 0 - new_args = [] - i = 0 - while i < num_args: - arg = args[i] - i += 1 - if not isinstance(arg, list): - new_args.append(arg) - stack_use += 1 - else: - prev_stack_use = stack_use - # The arg is a tuple of blend values. - # These are each (master 0,delta 1..delta n, 1) - # Combine as many successive tuples as we can, - # up to the max stack limit. - num_sources = len(arg) - 1 - blendlist = [arg] - stack_use += 1 + num_sources # 1 for the num_blends arg - - # if we are here, max stack is the CFF2 max stack. - # I use the CFF2 max stack limit here rather than - # the 'maxstack' chosen by the client, as the default - # maxstack may have been used unintentionally. For all - # the other operators, this just produces a little less - # optimization, but here it puts a hard (and low) limit - # on the number of source fonts that can be used. - # - # Make sure the stack depth does not exceed (maxstack - 1), so - # that subroutinizer can insert subroutine calls at any point. - while ( - (i < num_args) - and isinstance(args[i], list) - and stack_use + num_sources < maxStackLimit - ): - blendlist.append(args[i]) - i += 1 - stack_use += num_sources - # blendList now contains as many single blend tuples as can be - # combined without exceeding the CFF2 stack limit. - num_blends = len(blendlist) - # append the 'num_blends' default font values - blend_args = [] - for arg in blendlist: - blend_args.append(arg[0]) - for arg in blendlist: - assert arg[-1] == 1 - blend_args.extend(arg[1:-1]) - blend_args.append(num_blends) - new_args.append(blend_args) - stack_use = prev_stack_use + num_blends - - return new_args - - -def _addArgs(a, b): - if isinstance(b, list): - if isinstance(a, list): - if len(a) != len(b) or a[-1] != b[-1]: - raise ValueError() - return [_addArgs(va, vb) for va, vb in zip(a[:-1], b[:-1])] + [a[-1]] - else: - a, b = b, a - if isinstance(a, list): - assert a[-1] == 1 - return [_addArgs(a[0], b)] + a[1:] - return a + b - - -def _argsStackUse(args): - stackLen = 0 - maxLen = 0 - for arg in args: - if type(arg) is list: - # Blended arg - maxLen = max(maxLen, stackLen + _argsStackUse(arg)) - stackLen += arg[-1] - else: - stackLen += 1 - return max(stackLen, maxLen) - - -def specializeCommands( - commands, - ignoreErrors=False, - generalizeFirst=True, - preserveTopology=False, - maxstack=48, -): - # We perform several rounds of optimizations. They are carefully ordered and are: - # - # 0. Generalize commands. - # This ensures that they are in our expected simple form, with each line/curve only - # having arguments for one segment, and using the generic form (rlineto/rrcurveto). - # If caller is sure the input is in this form, they can turn off generalization to - # save time. - # - # 1. Combine successive rmoveto operations. - # - # 2. Specialize rmoveto/rlineto/rrcurveto operators into horizontal/vertical variants. - # We specialize into some, made-up, variants as well, which simplifies following - # passes. - # - # 3. Merge or delete redundant operations, to the extent requested. - # OpenType spec declares point numbers in CFF undefined. As such, we happily - # change topology. If client relies on point numbers (in GPOS anchors, or for - # hinting purposes(what?)) they can turn this off. - # - # 4. Peephole optimization to revert back some of the h/v variants back into their - # original "relative" operator (rline/rrcurveto) if that saves a byte. - # - # 5. Combine adjacent operators when possible, minding not to go over max stack size. - # - # 6. Resolve any remaining made-up operators into real operators. - # - # I have convinced myself that this produces optimal bytecode (except for, possibly - # one byte each time maxstack size prohibits combining.) YMMV, but you'd be wrong. :-) - # A dynamic-programming approach can do the same but would be significantly slower. - # - # 7. For any args which are blend lists, convert them to a blend command. - - # 0. Generalize commands. - if generalizeFirst: - commands = generalizeCommands(commands, ignoreErrors=ignoreErrors) - else: - commands = list(commands) # Make copy since we modify in-place later. - - # 1. Combine successive rmoveto operations. - for i in range(len(commands) - 1, 0, -1): - if "rmoveto" == commands[i][0] == commands[i - 1][0]: - v1, v2 = commands[i - 1][1], commands[i][1] - commands[i - 1] = ( - "rmoveto", - [_addArgs(v1[0], v2[0]), _addArgs(v1[1], v2[1])], - ) - del commands[i] - - # 2. Specialize rmoveto/rlineto/rrcurveto operators into horizontal/vertical variants. - # - # We, in fact, specialize into more, made-up, variants that special-case when both - # X and Y components are zero. This simplifies the following optimization passes. - # This case is rare, but OCD does not let me skip it. - # - # After this round, we will have four variants that use the following mnemonics: - # - # - 'r' for relative, ie. non-zero X and non-zero Y, - # - 'h' for horizontal, ie. zero X and non-zero Y, - # - 'v' for vertical, ie. non-zero X and zero Y, - # - '0' for zeros, ie. zero X and zero Y. - # - # The '0' pseudo-operators are not part of the spec, but help simplify the following - # optimization rounds. We resolve them at the end. So, after this, we will have four - # moveto and four lineto variants: - # - # - 0moveto, 0lineto - # - hmoveto, hlineto - # - vmoveto, vlineto - # - rmoveto, rlineto - # - # and sixteen curveto variants. For example, a '0hcurveto' operator means a curve - # dx0,dy0,dx1,dy1,dx2,dy2,dx3,dy3 where dx0, dx1, and dy3 are zero but not dx3. - # An 'rvcurveto' means dx3 is zero but not dx0,dy0,dy3. - # - # There are nine different variants of curves without the '0'. Those nine map exactly - # to the existing curve variants in the spec: rrcurveto, and the four variants hhcurveto, - # vvcurveto, hvcurveto, and vhcurveto each cover two cases, one with an odd number of - # arguments and one without. Eg. an hhcurveto with an extra argument (odd number of - # arguments) is in fact an rhcurveto. The operators in the spec are designed such that - # all four of rhcurveto, rvcurveto, hrcurveto, and vrcurveto are encodable for one curve. - # - # Of the curve types with '0', the 00curveto is equivalent to a lineto variant. The rest - # of the curve types with a 0 need to be encoded as a h or v variant. Ie. a '0' can be - # thought of a "don't care" and can be used as either an 'h' or a 'v'. As such, we always - # encode a number 0 as argument when we use a '0' variant. Later on, we can just substitute - # the '0' with either 'h' or 'v' and it works. - # - # When we get to curve splines however, things become more complicated... XXX finish this. - # There's one more complexity with splines. If one side of the spline is not horizontal or - # vertical (or zero), ie. if it's 'r', then it limits which spline types we can encode. - # Only hhcurveto and vvcurveto operators can encode a spline starting with 'r', and - # only hvcurveto and vhcurveto operators can encode a spline ending with 'r'. - # This limits our merge opportunities later. - # - for i in range(len(commands)): - op, args = commands[i] - - if op in {"rmoveto", "rlineto"}: - c, args = _categorizeVector(args) - commands[i] = c + op[1:], args - continue - - if op == "rrcurveto": - c1, args1 = _categorizeVector(args[:2]) - c2, args2 = _categorizeVector(args[-2:]) - commands[i] = c1 + c2 + "curveto", args1 + args[2:4] + args2 - continue - - # 3. Merge or delete redundant operations, to the extent requested. - # - # TODO - # A 0moveto that comes before all other path operations can be removed. - # though I find conflicting evidence for this. - # - # TODO - # "If hstem and vstem hints are both declared at the beginning of a - # CharString, and this sequence is followed directly by the hintmask or - # cntrmask operators, then the vstem hint operator (or, if applicable, - # the vstemhm operator) need not be included." - # - # "The sequence and form of a CFF2 CharString program may be represented as: - # {hs* vs* cm* hm* mt subpath}? {mt subpath}*" - # - # https://www.microsoft.com/typography/otspec/cff2charstr.htm#section3.1 - # - # For Type2 CharStrings the sequence is: - # w? {hs* vs* cm* hm* mt subpath}? {mt subpath}* endchar" - - # Some other redundancies change topology (point numbers). - if not preserveTopology: - for i in range(len(commands) - 1, -1, -1): - op, args = commands[i] - - # A 00curveto is demoted to a (specialized) lineto. - if op == "00curveto": - assert len(args) == 4 - c, args = _categorizeVector(args[1:3]) - op = c + "lineto" - commands[i] = op, args - # and then... - - # A 0lineto can be deleted. - if op == "0lineto": - del commands[i] - continue - - # Merge adjacent hlineto's and vlineto's. - # In CFF2 charstrings from variable fonts, each - # arg item may be a list of blendable values, one from - # each source font. - if i and op in {"hlineto", "vlineto"} and (op == commands[i - 1][0]): - _, other_args = commands[i - 1] - assert len(args) == 1 and len(other_args) == 1 - try: - new_args = [_addArgs(args[0], other_args[0])] - except ValueError: - continue - commands[i - 1] = (op, new_args) - del commands[i] - continue - - # 4. Peephole optimization to revert back some of the h/v variants back into their - # original "relative" operator (rline/rrcurveto) if that saves a byte. - for i in range(1, len(commands) - 1): - op, args = commands[i] - prv, nxt = commands[i - 1][0], commands[i + 1][0] - - if op in {"0lineto", "hlineto", "vlineto"} and prv == nxt == "rlineto": - assert len(args) == 1 - args = [0, args[0]] if op[0] == "v" else [args[0], 0] - commands[i] = ("rlineto", args) - continue - - if op[2:] == "curveto" and len(args) == 5 and prv == nxt == "rrcurveto": - assert (op[0] == "r") ^ (op[1] == "r") - if op[0] == "v": - pos = 0 - elif op[0] != "r": - pos = 1 - elif op[1] == "v": - pos = 4 - else: - pos = 5 - # Insert, while maintaining the type of args (can be tuple or list). - args = args[:pos] + type(args)((0,)) + args[pos:] - commands[i] = ("rrcurveto", args) - continue - - # 5. Combine adjacent operators when possible, minding not to go over max stack size. - stackUse = _argsStackUse(commands[-1][1]) if commands else 0 - for i in range(len(commands) - 1, 0, -1): - op1, args1 = commands[i - 1] - op2, args2 = commands[i] - new_op = None - - # Merge logic... - if {op1, op2} <= {"rlineto", "rrcurveto"}: - if op1 == op2: - new_op = op1 - else: - l = len(args2) - if op2 == "rrcurveto" and l == 6: - new_op = "rlinecurve" - elif l == 2: - new_op = "rcurveline" - - elif (op1, op2) in {("rlineto", "rlinecurve"), ("rrcurveto", "rcurveline")}: - new_op = op2 - - elif {op1, op2} == {"vlineto", "hlineto"}: - new_op = op1 - - elif "curveto" == op1[2:] == op2[2:]: - d0, d1 = op1[:2] - d2, d3 = op2[:2] - - if d1 == "r" or d2 == "r" or d0 == d3 == "r": - continue - - d = _mergeCategories(d1, d2) - if d is None: - continue - if d0 == "r": - d = _mergeCategories(d, d3) - if d is None: - continue - new_op = "r" + d + "curveto" - elif d3 == "r": - d0 = _mergeCategories(d0, _negateCategory(d)) - if d0 is None: - continue - new_op = d0 + "r" + "curveto" - else: - d0 = _mergeCategories(d0, d3) - if d0 is None: - continue - new_op = d0 + d + "curveto" - - # Make sure the stack depth does not exceed (maxstack - 1), so - # that subroutinizer can insert subroutine calls at any point. - args1StackUse = _argsStackUse(args1) - combinedStackUse = max(args1StackUse, len(args1) + stackUse) - if new_op and combinedStackUse < maxstack: - commands[i - 1] = (new_op, args1 + args2) - del commands[i] - stackUse = combinedStackUse - else: - stackUse = args1StackUse - - # 6. Resolve any remaining made-up operators into real operators. - for i in range(len(commands)): - op, args = commands[i] - - if op in {"0moveto", "0lineto"}: - commands[i] = "h" + op[1:], args - continue - - if op[2:] == "curveto" and op[:2] not in {"rr", "hh", "vv", "vh", "hv"}: - l = len(args) - - op0, op1 = op[:2] - if (op0 == "r") ^ (op1 == "r"): - assert l % 2 == 1 - if op0 == "0": - op0 = "h" - if op1 == "0": - op1 = "h" - if op0 == "r": - op0 = op1 - if op1 == "r": - op1 = _negateCategory(op0) - assert {op0, op1} <= {"h", "v"}, (op0, op1) - - if l % 2: - if op0 != op1: # vhcurveto / hvcurveto - if (op0 == "h") ^ (l % 8 == 1): - # Swap last two args order - args = args[:-2] + args[-1:] + args[-2:-1] - else: # hhcurveto / vvcurveto - if op0 == "h": # hhcurveto - # Swap first two args order - args = args[1:2] + args[:1] + args[2:] - - commands[i] = op0 + op1 + "curveto", args - continue - - # 7. For any series of args which are blend lists, convert the series to a single blend arg. - for i in range(len(commands)): - op, args = commands[i] - if any(isinstance(arg, list) for arg in args): - commands[i] = op, _convertToBlendCmds(args) - - return commands - - -def specializeProgram(program, getNumRegions=None, **kwargs): - return commandsToProgram( - specializeCommands(programToCommands(program, getNumRegions), **kwargs) - ) - - -if __name__ == "__main__": - import sys - - if len(sys.argv) == 1: - import doctest - - sys.exit(doctest.testmod().failed) - - import argparse - - parser = argparse.ArgumentParser( - "fonttools cffLib.specializer", - description="CFF CharString generalizer/specializer", - ) - parser.add_argument("program", metavar="command", nargs="*", help="Commands.") - parser.add_argument( - "--num-regions", - metavar="NumRegions", - nargs="*", - default=None, - help="Number of variable-font regions for blend opertaions.", - ) - parser.add_argument( - "--font", - metavar="FONTFILE", - default=None, - help="CFF2 font to specialize.", - ) - parser.add_argument( - "-o", - "--output-file", - type=str, - help="Output font file name.", - ) - - options = parser.parse_args(sys.argv[1:]) - - if options.program: - getNumRegions = ( - None - if options.num_regions is None - else lambda vsIndex: int( - options.num_regions[0 if vsIndex is None else vsIndex] - ) - ) - - program = stringToProgram(options.program) - print("Program:") - print(programToString(program)) - commands = programToCommands(program, getNumRegions) - print("Commands:") - print(commands) - program2 = commandsToProgram(commands) - print("Program from commands:") - print(programToString(program2)) - assert program == program2 - print("Generalized program:") - print(programToString(generalizeProgram(program, getNumRegions))) - print("Specialized program:") - print(programToString(specializeProgram(program, getNumRegions))) - - if options.font: - from fontTools.ttLib import TTFont - - font = TTFont(options.font) - cff2 = font["CFF2"].cff.topDictIndex[0] - charstrings = cff2.CharStrings - for glyphName in charstrings.keys(): - charstring = charstrings[glyphName] - charstring.decompile() - getNumRegions = charstring.private.getNumRegions - charstring.program = specializeProgram( - charstring.program, getNumRegions, maxstack=maxStackLimit - ) - - if options.output_file is None: - from fontTools.misc.cliTools import makeOutputFileName - - outfile = makeOutputFileName( - options.font, overWrite=True, suffix=".specialized" - ) - else: - outfile = options.output_file - if outfile: - print("Saving", outfile) - font.save(outfile) diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/transforms.py b/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/transforms.py deleted file mode 100644 index b9b7c86c..00000000 --- a/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/transforms.py +++ /dev/null @@ -1,495 +0,0 @@ -from fontTools.misc.psCharStrings import ( - SimpleT2Decompiler, - T2WidthExtractor, - calcSubrBias, -) - - -def _uniq_sort(l): - return sorted(set(l)) - - -class StopHintCountEvent(Exception): - pass - - -class _DesubroutinizingT2Decompiler(SimpleT2Decompiler): - stop_hintcount_ops = ( - "op_hintmask", - "op_cntrmask", - "op_rmoveto", - "op_hmoveto", - "op_vmoveto", - ) - - def __init__(self, localSubrs, globalSubrs, private=None): - SimpleT2Decompiler.__init__(self, localSubrs, globalSubrs, private) - - def execute(self, charString): - self.need_hintcount = True # until proven otherwise - for op_name in self.stop_hintcount_ops: - setattr(self, op_name, self.stop_hint_count) - - if hasattr(charString, "_desubroutinized"): - # If a charstring has already been desubroutinized, we will still - # need to execute it if we need to count hints in order to - # compute the byte length for mask arguments, and haven't finished - # counting hints pairs. - if self.need_hintcount and self.callingStack: - try: - SimpleT2Decompiler.execute(self, charString) - except StopHintCountEvent: - del self.callingStack[-1] - return - - charString._patches = [] - SimpleT2Decompiler.execute(self, charString) - desubroutinized = charString.program[:] - for idx, expansion in reversed(charString._patches): - assert idx >= 2 - assert desubroutinized[idx - 1] in [ - "callsubr", - "callgsubr", - ], desubroutinized[idx - 1] - assert type(desubroutinized[idx - 2]) == int - if expansion[-1] == "return": - expansion = expansion[:-1] - desubroutinized[idx - 2 : idx] = expansion - if not self.private.in_cff2: - if "endchar" in desubroutinized: - # Cut off after first endchar - desubroutinized = desubroutinized[ - : desubroutinized.index("endchar") + 1 - ] - - charString._desubroutinized = desubroutinized - del charString._patches - - def op_callsubr(self, index): - subr = self.localSubrs[self.operandStack[-1] + self.localBias] - SimpleT2Decompiler.op_callsubr(self, index) - self.processSubr(index, subr) - - def op_callgsubr(self, index): - subr = self.globalSubrs[self.operandStack[-1] + self.globalBias] - SimpleT2Decompiler.op_callgsubr(self, index) - self.processSubr(index, subr) - - def stop_hint_count(self, *args): - self.need_hintcount = False - for op_name in self.stop_hintcount_ops: - setattr(self, op_name, None) - cs = self.callingStack[-1] - if hasattr(cs, "_desubroutinized"): - raise StopHintCountEvent() - - def op_hintmask(self, index): - SimpleT2Decompiler.op_hintmask(self, index) - if self.need_hintcount: - self.stop_hint_count() - - def processSubr(self, index, subr): - cs = self.callingStack[-1] - if not hasattr(cs, "_desubroutinized"): - cs._patches.append((index, subr._desubroutinized)) - - -def desubroutinizeCharString(cs): - """Desubroutinize a charstring in-place.""" - cs.decompile() - subrs = getattr(cs.private, "Subrs", []) - decompiler = _DesubroutinizingT2Decompiler(subrs, cs.globalSubrs, cs.private) - decompiler.execute(cs) - cs.program = cs._desubroutinized - del cs._desubroutinized - - -def desubroutinize(cff): - for fontName in cff.fontNames: - font = cff[fontName] - cs = font.CharStrings - for c in cs.values(): - desubroutinizeCharString(c) - # Delete all the local subrs - if hasattr(font, "FDArray"): - for fd in font.FDArray: - pd = fd.Private - if hasattr(pd, "Subrs"): - del pd.Subrs - if "Subrs" in pd.rawDict: - del pd.rawDict["Subrs"] - else: - pd = font.Private - if hasattr(pd, "Subrs"): - del pd.Subrs - if "Subrs" in pd.rawDict: - del pd.rawDict["Subrs"] - # as well as the global subrs - cff.GlobalSubrs.clear() - - -class _MarkingT2Decompiler(SimpleT2Decompiler): - def __init__(self, localSubrs, globalSubrs, private): - SimpleT2Decompiler.__init__(self, localSubrs, globalSubrs, private) - for subrs in [localSubrs, globalSubrs]: - if subrs and not hasattr(subrs, "_used"): - subrs._used = set() - - def op_callsubr(self, index): - self.localSubrs._used.add(self.operandStack[-1] + self.localBias) - SimpleT2Decompiler.op_callsubr(self, index) - - def op_callgsubr(self, index): - self.globalSubrs._used.add(self.operandStack[-1] + self.globalBias) - SimpleT2Decompiler.op_callgsubr(self, index) - - -class _DehintingT2Decompiler(T2WidthExtractor): - class Hints(object): - def __init__(self): - # Whether calling this charstring produces any hint stems - # Note that if a charstring starts with hintmask, it will - # have has_hint set to True, because it *might* produce an - # implicit vstem if called under certain conditions. - self.has_hint = False - # Index to start at to drop all hints - self.last_hint = 0 - # Index up to which we know more hints are possible. - # Only relevant if status is 0 or 1. - self.last_checked = 0 - # The status means: - # 0: after dropping hints, this charstring is empty - # 1: after dropping hints, there may be more hints - # continuing after this, or there might be - # other things. Not clear yet. - # 2: no more hints possible after this charstring - self.status = 0 - # Has hintmask instructions; not recursive - self.has_hintmask = False - # List of indices of calls to empty subroutines to remove. - self.deletions = [] - - pass - - def __init__( - self, css, localSubrs, globalSubrs, nominalWidthX, defaultWidthX, private=None - ): - self._css = css - T2WidthExtractor.__init__( - self, localSubrs, globalSubrs, nominalWidthX, defaultWidthX - ) - self.private = private - - def execute(self, charString): - old_hints = charString._hints if hasattr(charString, "_hints") else None - charString._hints = self.Hints() - - T2WidthExtractor.execute(self, charString) - - hints = charString._hints - - if hints.has_hint or hints.has_hintmask: - self._css.add(charString) - - if hints.status != 2: - # Check from last_check, make sure we didn't have any operators. - for i in range(hints.last_checked, len(charString.program) - 1): - if isinstance(charString.program[i], str): - hints.status = 2 - break - else: - hints.status = 1 # There's *something* here - hints.last_checked = len(charString.program) - - if old_hints: - assert hints.__dict__ == old_hints.__dict__ - - def op_callsubr(self, index): - subr = self.localSubrs[self.operandStack[-1] + self.localBias] - T2WidthExtractor.op_callsubr(self, index) - self.processSubr(index, subr) - - def op_callgsubr(self, index): - subr = self.globalSubrs[self.operandStack[-1] + self.globalBias] - T2WidthExtractor.op_callgsubr(self, index) - self.processSubr(index, subr) - - def op_hstem(self, index): - T2WidthExtractor.op_hstem(self, index) - self.processHint(index) - - def op_vstem(self, index): - T2WidthExtractor.op_vstem(self, index) - self.processHint(index) - - def op_hstemhm(self, index): - T2WidthExtractor.op_hstemhm(self, index) - self.processHint(index) - - def op_vstemhm(self, index): - T2WidthExtractor.op_vstemhm(self, index) - self.processHint(index) - - def op_hintmask(self, index): - rv = T2WidthExtractor.op_hintmask(self, index) - self.processHintmask(index) - return rv - - def op_cntrmask(self, index): - rv = T2WidthExtractor.op_cntrmask(self, index) - self.processHintmask(index) - return rv - - def processHintmask(self, index): - cs = self.callingStack[-1] - hints = cs._hints - hints.has_hintmask = True - if hints.status != 2: - # Check from last_check, see if we may be an implicit vstem - for i in range(hints.last_checked, index - 1): - if isinstance(cs.program[i], str): - hints.status = 2 - break - else: - # We are an implicit vstem - hints.has_hint = True - hints.last_hint = index + 1 - hints.status = 0 - hints.last_checked = index + 1 - - def processHint(self, index): - cs = self.callingStack[-1] - hints = cs._hints - hints.has_hint = True - hints.last_hint = index - hints.last_checked = index - - def processSubr(self, index, subr): - cs = self.callingStack[-1] - hints = cs._hints - subr_hints = subr._hints - - # Check from last_check, make sure we didn't have - # any operators. - if hints.status != 2: - for i in range(hints.last_checked, index - 1): - if isinstance(cs.program[i], str): - hints.status = 2 - break - hints.last_checked = index - - if hints.status != 2: - if subr_hints.has_hint: - hints.has_hint = True - - # Decide where to chop off from - if subr_hints.status == 0: - hints.last_hint = index - else: - hints.last_hint = index - 2 # Leave the subr call in - - elif subr_hints.status == 0: - hints.deletions.append(index) - - hints.status = max(hints.status, subr_hints.status) - - -def _cs_subset_subroutines(charstring, subrs, gsubrs): - p = charstring.program - for i in range(1, len(p)): - if p[i] == "callsubr": - assert isinstance(p[i - 1], int) - p[i - 1] = subrs._used.index(p[i - 1] + subrs._old_bias) - subrs._new_bias - elif p[i] == "callgsubr": - assert isinstance(p[i - 1], int) - p[i - 1] = ( - gsubrs._used.index(p[i - 1] + gsubrs._old_bias) - gsubrs._new_bias - ) - - -def _cs_drop_hints(charstring): - hints = charstring._hints - - if hints.deletions: - p = charstring.program - for idx in reversed(hints.deletions): - del p[idx - 2 : idx] - - if hints.has_hint: - assert not hints.deletions or hints.last_hint <= hints.deletions[0] - charstring.program = charstring.program[hints.last_hint :] - if not charstring.program: - # TODO CFF2 no need for endchar. - charstring.program.append("endchar") - if hasattr(charstring, "width"): - # Insert width back if needed - if charstring.width != charstring.private.defaultWidthX: - # For CFF2 charstrings, this should never happen - assert ( - charstring.private.defaultWidthX is not None - ), "CFF2 CharStrings must not have an initial width value" - charstring.program.insert( - 0, charstring.width - charstring.private.nominalWidthX - ) - - if hints.has_hintmask: - i = 0 - p = charstring.program - while i < len(p): - if p[i] in ["hintmask", "cntrmask"]: - assert i + 1 <= len(p) - del p[i : i + 2] - continue - i += 1 - - assert len(charstring.program) - - del charstring._hints - - -def remove_hints(cff, *, removeUnusedSubrs: bool = True): - for fontname in cff.keys(): - font = cff[fontname] - cs = font.CharStrings - # This can be tricky, but doesn't have to. What we do is: - # - # - Run all used glyph charstrings and recurse into subroutines, - # - For each charstring (including subroutines), if it has any - # of the hint stem operators, we mark it as such. - # Upon returning, for each charstring we note all the - # subroutine calls it makes that (recursively) contain a stem, - # - Dropping hinting then consists of the following two ops: - # * Drop the piece of the program in each charstring before the - # last call to a stem op or a stem-calling subroutine, - # * Drop all hintmask operations. - # - It's trickier... A hintmask right after hints and a few numbers - # will act as an implicit vstemhm. As such, we track whether - # we have seen any non-hint operators so far and do the right - # thing, recursively... Good luck understanding that :( - css = set() - for c in cs.values(): - c.decompile() - subrs = getattr(c.private, "Subrs", []) - decompiler = _DehintingT2Decompiler( - css, - subrs, - c.globalSubrs, - c.private.nominalWidthX, - c.private.defaultWidthX, - c.private, - ) - decompiler.execute(c) - c.width = decompiler.width - for charstring in css: - _cs_drop_hints(charstring) - del css - - # Drop font-wide hinting values - all_privs = [] - if hasattr(font, "FDArray"): - all_privs.extend(fd.Private for fd in font.FDArray) - else: - all_privs.append(font.Private) - for priv in all_privs: - for k in [ - "BlueValues", - "OtherBlues", - "FamilyBlues", - "FamilyOtherBlues", - "BlueScale", - "BlueShift", - "BlueFuzz", - "StemSnapH", - "StemSnapV", - "StdHW", - "StdVW", - "ForceBold", - "LanguageGroup", - "ExpansionFactor", - ]: - if hasattr(priv, k): - setattr(priv, k, None) - if removeUnusedSubrs: - remove_unused_subroutines(cff) - - -def _pd_delete_empty_subrs(private_dict): - if hasattr(private_dict, "Subrs") and not private_dict.Subrs: - if "Subrs" in private_dict.rawDict: - del private_dict.rawDict["Subrs"] - del private_dict.Subrs - - -def remove_unused_subroutines(cff): - for fontname in cff.keys(): - font = cff[fontname] - cs = font.CharStrings - # Renumber subroutines to remove unused ones - - # Mark all used subroutines - for c in cs.values(): - subrs = getattr(c.private, "Subrs", []) - decompiler = _MarkingT2Decompiler(subrs, c.globalSubrs, c.private) - decompiler.execute(c) - - all_subrs = [font.GlobalSubrs] - if hasattr(font, "FDArray"): - all_subrs.extend( - fd.Private.Subrs - for fd in font.FDArray - if hasattr(fd.Private, "Subrs") and fd.Private.Subrs - ) - elif hasattr(font.Private, "Subrs") and font.Private.Subrs: - all_subrs.append(font.Private.Subrs) - - subrs = set(subrs) # Remove duplicates - - # Prepare - for subrs in all_subrs: - if not hasattr(subrs, "_used"): - subrs._used = set() - subrs._used = _uniq_sort(subrs._used) - subrs._old_bias = calcSubrBias(subrs) - subrs._new_bias = calcSubrBias(subrs._used) - - # Renumber glyph charstrings - for c in cs.values(): - subrs = getattr(c.private, "Subrs", None) - _cs_subset_subroutines(c, subrs, font.GlobalSubrs) - - # Renumber subroutines themselves - for subrs in all_subrs: - if subrs == font.GlobalSubrs: - if not hasattr(font, "FDArray") and hasattr(font.Private, "Subrs"): - local_subrs = font.Private.Subrs - elif ( - hasattr(font, "FDArray") - and len(font.FDArray) == 1 - and hasattr(font.FDArray[0].Private, "Subrs") - ): - # Technically we shouldn't do this. But I've run into fonts that do it. - local_subrs = font.FDArray[0].Private.Subrs - else: - local_subrs = None - else: - local_subrs = subrs - - subrs.items = [subrs.items[i] for i in subrs._used] - if hasattr(subrs, "file"): - del subrs.file - if hasattr(subrs, "offsets"): - del subrs.offsets - - for subr in subrs.items: - _cs_subset_subroutines(subr, local_subrs, font.GlobalSubrs) - - # Delete local SubrsIndex if empty - if hasattr(font, "FDArray"): - for fd in font.FDArray: - _pd_delete_empty_subrs(fd.Private) - else: - _pd_delete_empty_subrs(font.Private) - - # Cleanup - for subrs in all_subrs: - del subrs._used, subrs._old_bias, subrs._new_bias diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/width.py b/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/width.py deleted file mode 100644 index 78ff27e4..00000000 --- a/.venv-docs/lib/python3.12/site-packages/fontTools/cffLib/width.py +++ /dev/null @@ -1,210 +0,0 @@ -# -*- coding: utf-8 -*- - -"""T2CharString glyph width optimizer. - -CFF glyphs whose width equals the CFF Private dictionary's ``defaultWidthX`` -value do not need to specify their width in their charstring, saving bytes. -This module determines the optimum ``defaultWidthX`` and ``nominalWidthX`` -values for a font, when provided with a list of glyph widths.""" - -from fontTools.ttLib import TTFont -from collections import defaultdict -from operator import add -from functools import reduce - - -__all__ = ["optimizeWidths", "main"] - - -class missingdict(dict): - def __init__(self, missing_func): - self.missing_func = missing_func - - def __missing__(self, v): - return self.missing_func(v) - - -def cumSum(f, op=add, start=0, decreasing=False): - keys = sorted(f.keys()) - minx, maxx = keys[0], keys[-1] - - total = reduce(op, f.values(), start) - - if decreasing: - missing = lambda x: start if x > maxx else total - domain = range(maxx, minx - 1, -1) - else: - missing = lambda x: start if x < minx else total - domain = range(minx, maxx + 1) - - out = missingdict(missing) - - v = start - for x in domain: - v = op(v, f[x]) - out[x] = v - - return out - - -def byteCost(widths, default, nominal): - if not hasattr(widths, "items"): - d = defaultdict(int) - for w in widths: - d[w] += 1 - widths = d - - cost = 0 - for w, freq in widths.items(): - if w == default: - continue - diff = abs(w - nominal) - if diff <= 107: - cost += freq - elif diff <= 1131: - cost += freq * 2 - else: - cost += freq * 5 - return cost - - -def optimizeWidthsBruteforce(widths): - """Bruteforce version. Veeeeeeeeeeeeeeeeery slow. Only works for smallests of fonts.""" - - d = defaultdict(int) - for w in widths: - d[w] += 1 - - # Maximum number of bytes using default can possibly save - maxDefaultAdvantage = 5 * max(d.values()) - - minw, maxw = min(widths), max(widths) - domain = list(range(minw, maxw + 1)) - - bestCostWithoutDefault = min(byteCost(widths, None, nominal) for nominal in domain) - - bestCost = len(widths) * 5 + 1 - for nominal in domain: - if byteCost(widths, None, nominal) > bestCost + maxDefaultAdvantage: - continue - for default in domain: - cost = byteCost(widths, default, nominal) - if cost < bestCost: - bestCost = cost - bestDefault = default - bestNominal = nominal - - return bestDefault, bestNominal - - -def optimizeWidths(widths): - """Given a list of glyph widths, or dictionary mapping glyph width to number of - glyphs having that, returns a tuple of best CFF default and nominal glyph widths. - - This algorithm is linear in UPEM+numGlyphs.""" - - if not hasattr(widths, "items"): - d = defaultdict(int) - for w in widths: - d[w] += 1 - widths = d - - keys = sorted(widths.keys()) - minw, maxw = keys[0], keys[-1] - domain = list(range(minw, maxw + 1)) - - # Cumulative sum/max forward/backward. - cumFrqU = cumSum(widths, op=add) - cumMaxU = cumSum(widths, op=max) - cumFrqD = cumSum(widths, op=add, decreasing=True) - cumMaxD = cumSum(widths, op=max, decreasing=True) - - # Cost per nominal choice, without default consideration. - nomnCostU = missingdict( - lambda x: cumFrqU[x] + cumFrqU[x - 108] + cumFrqU[x - 1132] * 3 - ) - nomnCostD = missingdict( - lambda x: cumFrqD[x] + cumFrqD[x + 108] + cumFrqD[x + 1132] * 3 - ) - nomnCost = missingdict(lambda x: nomnCostU[x] + nomnCostD[x] - widths[x]) - - # Cost-saving per nominal choice, by best default choice. - dfltCostU = missingdict( - lambda x: max(cumMaxU[x], cumMaxU[x - 108] * 2, cumMaxU[x - 1132] * 5) - ) - dfltCostD = missingdict( - lambda x: max(cumMaxD[x], cumMaxD[x + 108] * 2, cumMaxD[x + 1132] * 5) - ) - dfltCost = missingdict(lambda x: max(dfltCostU[x], dfltCostD[x])) - - # Combined cost per nominal choice. - bestCost = missingdict(lambda x: nomnCost[x] - dfltCost[x]) - - # Best nominal. - nominal = min(domain, key=lambda x: bestCost[x]) - - # Work back the best default. - bestC = bestCost[nominal] - dfltC = nomnCost[nominal] - bestCost[nominal] - ends = [] - if dfltC == dfltCostU[nominal]: - starts = [nominal, nominal - 108, nominal - 1132] - for start in starts: - while cumMaxU[start] and cumMaxU[start] == cumMaxU[start - 1]: - start -= 1 - ends.append(start) - else: - starts = [nominal, nominal + 108, nominal + 1132] - for start in starts: - while cumMaxD[start] and cumMaxD[start] == cumMaxD[start + 1]: - start += 1 - ends.append(start) - default = min(ends, key=lambda default: byteCost(widths, default, nominal)) - - return default, nominal - - -def main(args=None): - """Calculate optimum defaultWidthX/nominalWidthX values""" - - import argparse - - parser = argparse.ArgumentParser( - "fonttools cffLib.width", - description=main.__doc__, - ) - parser.add_argument( - "inputs", metavar="FILE", type=str, nargs="+", help="Input TTF files" - ) - parser.add_argument( - "-b", - "--brute-force", - dest="brute", - action="store_true", - help="Use brute-force approach (VERY slow)", - ) - - args = parser.parse_args(args) - - for fontfile in args.inputs: - font = TTFont(fontfile) - hmtx = font["hmtx"] - widths = [m[0] for m in hmtx.metrics.values()] - if args.brute: - default, nominal = optimizeWidthsBruteforce(widths) - else: - default, nominal = optimizeWidths(widths) - print( - "glyphs=%d default=%d nominal=%d byteCost=%d" - % (len(widths), default, nominal, byteCost(widths, default, nominal)) - ) - - -if __name__ == "__main__": - import sys - - if len(sys.argv) == 1: - import doctest - - sys.exit(doctest.testmod().failed) - main() diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/__init__.py b/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/__pycache__/__init__.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index b80d5086..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/__pycache__/builder.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/__pycache__/builder.cpython-312.pyc deleted file mode 100644 index b1e6aa28..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/__pycache__/builder.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/__pycache__/errors.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/__pycache__/errors.cpython-312.pyc deleted file mode 100644 index c949fdac..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/__pycache__/errors.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/__pycache__/geometry.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/__pycache__/geometry.cpython-312.pyc deleted file mode 100644 index 9c7caa0a..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/__pycache__/geometry.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/__pycache__/table_builder.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/__pycache__/table_builder.cpython-312.pyc deleted file mode 100644 index c2dc9353..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/__pycache__/table_builder.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/__pycache__/unbuilder.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/__pycache__/unbuilder.cpython-312.pyc deleted file mode 100644 index 2ecdcc76..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/__pycache__/unbuilder.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/builder.py b/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/builder.py deleted file mode 100644 index 6e45e7a8..00000000 --- a/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/builder.py +++ /dev/null @@ -1,664 +0,0 @@ -""" -colorLib.builder: Build COLR/CPAL tables from scratch - -""" - -import collections -import copy -import enum -from functools import partial -from math import ceil, log -from typing import ( - Any, - Dict, - Generator, - Iterable, - List, - Mapping, - Optional, - Sequence, - Tuple, - Type, - TypeVar, - Union, -) -from fontTools.misc.arrayTools import intRect -from fontTools.misc.fixedTools import fixedToFloat -from fontTools.misc.treeTools import build_n_ary_tree -from fontTools.ttLib.tables import C_O_L_R_ -from fontTools.ttLib.tables import C_P_A_L_ -from fontTools.ttLib.tables import _n_a_m_e -from fontTools.ttLib.tables import otTables as ot -from fontTools.ttLib.tables.otTables import ExtendMode, CompositeMode -from .errors import ColorLibError -from .geometry import round_start_circle_stable_containment -from .table_builder import BuildCallback, TableBuilder - - -# TODO move type aliases to colorLib.types? -T = TypeVar("T") -_Kwargs = Mapping[str, Any] -_PaintInput = Union[int, _Kwargs, ot.Paint, Tuple[str, "_PaintInput"]] -_PaintInputList = Sequence[_PaintInput] -_ColorGlyphsDict = Dict[str, Union[_PaintInputList, _PaintInput]] -_ColorGlyphsV0Dict = Dict[str, Sequence[Tuple[str, int]]] -_ClipBoxInput = Union[ - Tuple[int, int, int, int, int], # format 1, variable - Tuple[int, int, int, int], # format 0, non-variable - ot.ClipBox, -] - - -MAX_PAINT_COLR_LAYER_COUNT = 255 -_DEFAULT_ALPHA = 1.0 -_MAX_REUSE_LEN = 32 - - -def _beforeBuildPaintRadialGradient(paint, source): - x0 = source["x0"] - y0 = source["y0"] - r0 = source["r0"] - x1 = source["x1"] - y1 = source["y1"] - r1 = source["r1"] - - # TODO apparently no builder_test confirms this works (?) - - # avoid abrupt change after rounding when c0 is near c1's perimeter - c = round_start_circle_stable_containment((x0, y0), r0, (x1, y1), r1) - x0, y0 = c.centre - r0 = c.radius - - # update source to ensure paint is built with corrected values - source["x0"] = x0 - source["y0"] = y0 - source["r0"] = r0 - source["x1"] = x1 - source["y1"] = y1 - source["r1"] = r1 - - return paint, source - - -def _defaultColorStop(): - colorStop = ot.ColorStop() - colorStop.Alpha = _DEFAULT_ALPHA - return colorStop - - -def _defaultVarColorStop(): - colorStop = ot.VarColorStop() - colorStop.Alpha = _DEFAULT_ALPHA - return colorStop - - -def _defaultColorLine(): - colorLine = ot.ColorLine() - colorLine.Extend = ExtendMode.PAD - return colorLine - - -def _defaultVarColorLine(): - colorLine = ot.VarColorLine() - colorLine.Extend = ExtendMode.PAD - return colorLine - - -def _defaultPaintSolid(): - paint = ot.Paint() - paint.Alpha = _DEFAULT_ALPHA - return paint - - -def _buildPaintCallbacks(): - return { - ( - BuildCallback.BEFORE_BUILD, - ot.Paint, - ot.PaintFormat.PaintRadialGradient, - ): _beforeBuildPaintRadialGradient, - ( - BuildCallback.BEFORE_BUILD, - ot.Paint, - ot.PaintFormat.PaintVarRadialGradient, - ): _beforeBuildPaintRadialGradient, - (BuildCallback.CREATE_DEFAULT, ot.ColorStop): _defaultColorStop, - (BuildCallback.CREATE_DEFAULT, ot.VarColorStop): _defaultVarColorStop, - (BuildCallback.CREATE_DEFAULT, ot.ColorLine): _defaultColorLine, - (BuildCallback.CREATE_DEFAULT, ot.VarColorLine): _defaultVarColorLine, - ( - BuildCallback.CREATE_DEFAULT, - ot.Paint, - ot.PaintFormat.PaintSolid, - ): _defaultPaintSolid, - ( - BuildCallback.CREATE_DEFAULT, - ot.Paint, - ot.PaintFormat.PaintVarSolid, - ): _defaultPaintSolid, - } - - -def populateCOLRv0( - table: ot.COLR, - colorGlyphsV0: _ColorGlyphsV0Dict, - glyphMap: Optional[Mapping[str, int]] = None, -): - """Build v0 color layers and add to existing COLR table. - - Args: - table: a raw ``otTables.COLR()`` object (not ttLib's ``table_C_O_L_R_``). - colorGlyphsV0: map of base glyph names to lists of (layer glyph names, - color palette index) tuples. Can be empty. - glyphMap: a map from glyph names to glyph indices, as returned from - ``TTFont.getReverseGlyphMap()``, to optionally sort base records by GID. - """ - if glyphMap is not None: - colorGlyphItems = sorted( - colorGlyphsV0.items(), key=lambda item: glyphMap[item[0]] - ) - else: - colorGlyphItems = colorGlyphsV0.items() - baseGlyphRecords = [] - layerRecords = [] - for baseGlyph, layers in colorGlyphItems: - baseRec = ot.BaseGlyphRecord() - baseRec.BaseGlyph = baseGlyph - baseRec.FirstLayerIndex = len(layerRecords) - baseRec.NumLayers = len(layers) - baseGlyphRecords.append(baseRec) - - for layerGlyph, paletteIndex in layers: - layerRec = ot.LayerRecord() - layerRec.LayerGlyph = layerGlyph - layerRec.PaletteIndex = paletteIndex - layerRecords.append(layerRec) - - table.BaseGlyphRecordArray = table.LayerRecordArray = None - if baseGlyphRecords: - table.BaseGlyphRecordArray = ot.BaseGlyphRecordArray() - table.BaseGlyphRecordArray.BaseGlyphRecord = baseGlyphRecords - if layerRecords: - table.LayerRecordArray = ot.LayerRecordArray() - table.LayerRecordArray.LayerRecord = layerRecords - table.BaseGlyphRecordCount = len(baseGlyphRecords) - table.LayerRecordCount = len(layerRecords) - - -def buildCOLR( - colorGlyphs: _ColorGlyphsDict, - version: Optional[int] = None, - *, - glyphMap: Optional[Mapping[str, int]] = None, - varStore: Optional[ot.VarStore] = None, - varIndexMap: Optional[ot.DeltaSetIndexMap] = None, - clipBoxes: Optional[Dict[str, _ClipBoxInput]] = None, - allowLayerReuse: bool = True, -) -> C_O_L_R_.table_C_O_L_R_: - """Build COLR table from color layers mapping. - - Args: - - colorGlyphs: map of base glyph name to, either list of (layer glyph name, - color palette index) tuples for COLRv0; or a single ``Paint`` (dict) or - list of ``Paint`` for COLRv1. - version: the version of COLR table. If None, the version is determined - by the presence of COLRv1 paints or variation data (varStore), which - require version 1; otherwise, if all base glyphs use only simple color - layers, version 0 is used. - glyphMap: a map from glyph names to glyph indices, as returned from - TTFont.getReverseGlyphMap(), to optionally sort base records by GID. - varStore: Optional ItemVarationStore for deltas associated with v1 layer. - varIndexMap: Optional DeltaSetIndexMap for deltas associated with v1 layer. - clipBoxes: Optional map of base glyph name to clip box 4- or 5-tuples: - (xMin, yMin, xMax, yMax) or (xMin, yMin, xMax, yMax, varIndexBase). - - Returns: - A new COLR table. - """ - self = C_O_L_R_.table_C_O_L_R_() - - if varStore is not None and version == 0: - raise ValueError("Can't add VarStore to COLRv0") - - if version in (None, 0) and not varStore: - # split color glyphs into v0 and v1 and encode separately - colorGlyphsV0, colorGlyphsV1 = _split_color_glyphs_by_version(colorGlyphs) - if version == 0 and colorGlyphsV1: - raise ValueError("Can't encode COLRv1 glyphs in COLRv0") - else: - # unless explicitly requested for v1 or have variations, in which case - # we encode all color glyph as v1 - colorGlyphsV0, colorGlyphsV1 = {}, colorGlyphs - - colr = ot.COLR() - - populateCOLRv0(colr, colorGlyphsV0, glyphMap) - - colr.LayerList, colr.BaseGlyphList = buildColrV1( - colorGlyphsV1, - glyphMap, - allowLayerReuse=allowLayerReuse, - ) - - if version is None: - version = 1 if (varStore or colorGlyphsV1) else 0 - elif version not in (0, 1): - raise NotImplementedError(version) - self.version = colr.Version = version - - if version == 0: - self.ColorLayers = self._decompileColorLayersV0(colr) - else: - colr.ClipList = buildClipList(clipBoxes) if clipBoxes else None - colr.VarIndexMap = varIndexMap - colr.VarStore = varStore - self.table = colr - - return self - - -def buildClipList(clipBoxes: Dict[str, _ClipBoxInput]) -> ot.ClipList: - clipList = ot.ClipList() - clipList.Format = 1 - clipList.clips = {name: buildClipBox(box) for name, box in clipBoxes.items()} - return clipList - - -def buildClipBox(clipBox: _ClipBoxInput) -> ot.ClipBox: - if isinstance(clipBox, ot.ClipBox): - return clipBox - n = len(clipBox) - clip = ot.ClipBox() - if n not in (4, 5): - raise ValueError(f"Invalid ClipBox: expected 4 or 5 values, found {n}") - clip.xMin, clip.yMin, clip.xMax, clip.yMax = intRect(clipBox[:4]) - clip.Format = int(n == 5) + 1 - if n == 5: - clip.VarIndexBase = int(clipBox[4]) - return clip - - -class ColorPaletteType(enum.IntFlag): - USABLE_WITH_LIGHT_BACKGROUND = 0x0001 - USABLE_WITH_DARK_BACKGROUND = 0x0002 - - @classmethod - def _missing_(cls, value): - # enforce reserved bits - if isinstance(value, int) and (value < 0 or value & 0xFFFC != 0): - raise ValueError(f"{value} is not a valid {cls.__name__}") - return super()._missing_(value) - - -# None, 'abc' or {'en': 'abc', 'de': 'xyz'} -_OptionalLocalizedString = Union[None, str, Dict[str, str]] - - -def buildPaletteLabels( - labels: Iterable[_OptionalLocalizedString], nameTable: _n_a_m_e.table__n_a_m_e -) -> List[Optional[int]]: - return [ - ( - nameTable.addMultilingualName(l, mac=False) - if isinstance(l, dict) - else ( - C_P_A_L_.table_C_P_A_L_.NO_NAME_ID - if l is None - else nameTable.addMultilingualName({"en": l}, mac=False) - ) - ) - for l in labels - ] - - -def buildCPAL( - palettes: Sequence[Sequence[Tuple[float, float, float, float]]], - paletteTypes: Optional[Sequence[ColorPaletteType]] = None, - paletteLabels: Optional[Sequence[_OptionalLocalizedString]] = None, - paletteEntryLabels: Optional[Sequence[_OptionalLocalizedString]] = None, - nameTable: Optional[_n_a_m_e.table__n_a_m_e] = None, -) -> C_P_A_L_.table_C_P_A_L_: - """Build CPAL table from list of color palettes. - - Args: - palettes: list of lists of colors encoded as tuples of (R, G, B, A) floats - in the range [0..1]. - paletteTypes: optional list of ColorPaletteType, one for each palette. - paletteLabels: optional list of palette labels. Each lable can be either: - None (no label), a string (for for default English labels), or a - localized string (as a dict keyed with BCP47 language codes). - paletteEntryLabels: optional list of palette entry labels, one for each - palette entry (see paletteLabels). - nameTable: optional name table where to store palette and palette entry - labels. Required if either paletteLabels or paletteEntryLabels is set. - - Return: - A new CPAL v0 or v1 table, if custom palette types or labels are specified. - """ - if len({len(p) for p in palettes}) != 1: - raise ColorLibError("color palettes have different lengths") - - if (paletteLabels or paletteEntryLabels) and not nameTable: - raise TypeError( - "nameTable is required if palette or palette entries have labels" - ) - - cpal = C_P_A_L_.table_C_P_A_L_() - cpal.numPaletteEntries = len(palettes[0]) - - cpal.palettes = [] - for i, palette in enumerate(palettes): - colors = [] - for j, color in enumerate(palette): - if not isinstance(color, tuple) or len(color) != 4: - raise ColorLibError( - f"In palette[{i}][{j}]: expected (R, G, B, A) tuple, got {color!r}" - ) - if any(v > 1 or v < 0 for v in color): - raise ColorLibError( - f"palette[{i}][{j}] has invalid out-of-range [0..1] color: {color!r}" - ) - # input colors are RGBA, CPAL encodes them as BGRA - red, green, blue, alpha = color - colors.append( - C_P_A_L_.Color(*(round(v * 255) for v in (blue, green, red, alpha))) - ) - cpal.palettes.append(colors) - - if any(v is not None for v in (paletteTypes, paletteLabels, paletteEntryLabels)): - cpal.version = 1 - - if paletteTypes is not None: - if len(paletteTypes) != len(palettes): - raise ColorLibError( - f"Expected {len(palettes)} paletteTypes, got {len(paletteTypes)}" - ) - cpal.paletteTypes = [ColorPaletteType(t).value for t in paletteTypes] - else: - cpal.paletteTypes = [C_P_A_L_.table_C_P_A_L_.DEFAULT_PALETTE_TYPE] * len( - palettes - ) - - if paletteLabels is not None: - if len(paletteLabels) != len(palettes): - raise ColorLibError( - f"Expected {len(palettes)} paletteLabels, got {len(paletteLabels)}" - ) - cpal.paletteLabels = buildPaletteLabels(paletteLabels, nameTable) - else: - cpal.paletteLabels = [C_P_A_L_.table_C_P_A_L_.NO_NAME_ID] * len(palettes) - - if paletteEntryLabels is not None: - if len(paletteEntryLabels) != cpal.numPaletteEntries: - raise ColorLibError( - f"Expected {cpal.numPaletteEntries} paletteEntryLabels, " - f"got {len(paletteEntryLabels)}" - ) - cpal.paletteEntryLabels = buildPaletteLabels(paletteEntryLabels, nameTable) - else: - cpal.paletteEntryLabels = [ - C_P_A_L_.table_C_P_A_L_.NO_NAME_ID - ] * cpal.numPaletteEntries - else: - cpal.version = 0 - - return cpal - - -# COLR v1 tables -# See draft proposal at: https://github.com/googlefonts/colr-gradients-spec - - -def _is_colrv0_layer(layer: Any) -> bool: - # Consider as COLRv0 layer any sequence of length 2 (be it tuple or list) in which - # the first element is a str (the layerGlyph) and the second element is an int - # (CPAL paletteIndex). - # https://github.com/googlefonts/ufo2ft/issues/426 - try: - layerGlyph, paletteIndex = layer - except (TypeError, ValueError): - return False - else: - return isinstance(layerGlyph, str) and isinstance(paletteIndex, int) - - -def _split_color_glyphs_by_version( - colorGlyphs: _ColorGlyphsDict, -) -> Tuple[_ColorGlyphsV0Dict, _ColorGlyphsDict]: - colorGlyphsV0 = {} - colorGlyphsV1 = {} - for baseGlyph, layers in colorGlyphs.items(): - if all(_is_colrv0_layer(l) for l in layers): - colorGlyphsV0[baseGlyph] = layers - else: - colorGlyphsV1[baseGlyph] = layers - - # sanity check - assert set(colorGlyphs) == (set(colorGlyphsV0) | set(colorGlyphsV1)) - - return colorGlyphsV0, colorGlyphsV1 - - -def _reuse_ranges(num_layers: int) -> Generator[Tuple[int, int], None, None]: - # TODO feels like something itertools might have already - for lbound in range(num_layers): - # Reuse of very large #s of layers is relatively unlikely - # +2: we want sequences of at least 2 - # otData handles single-record duplication - for ubound in range( - lbound + 2, min(num_layers + 1, lbound + 2 + _MAX_REUSE_LEN) - ): - yield (lbound, ubound) - - -class LayerReuseCache: - reusePool: Mapping[Tuple[Any, ...], int] - tuples: Mapping[int, Tuple[Any, ...]] - keepAlive: List[ot.Paint] # we need id to remain valid - - def __init__(self): - self.reusePool = {} - self.tuples = {} - self.keepAlive = [] - - def _paint_tuple(self, paint: ot.Paint): - # start simple, who even cares about cyclic graphs or interesting field types - def _tuple_safe(value): - if isinstance(value, enum.Enum): - return value - elif hasattr(value, "__dict__"): - return tuple( - (k, _tuple_safe(v)) for k, v in sorted(value.__dict__.items()) - ) - elif isinstance(value, collections.abc.MutableSequence): - return tuple(_tuple_safe(e) for e in value) - return value - - # Cache the tuples for individual Paint instead of the whole sequence - # because the seq could be a transient slice - result = self.tuples.get(id(paint), None) - if result is None: - result = _tuple_safe(paint) - self.tuples[id(paint)] = result - self.keepAlive.append(paint) - return result - - def _as_tuple(self, paints: Sequence[ot.Paint]) -> Tuple[Any, ...]: - return tuple(self._paint_tuple(p) for p in paints) - - def try_reuse(self, layers: List[ot.Paint]) -> List[ot.Paint]: - found_reuse = True - while found_reuse: - found_reuse = False - - ranges = sorted( - _reuse_ranges(len(layers)), - key=lambda t: (t[1] - t[0], t[1], t[0]), - reverse=True, - ) - for lbound, ubound in ranges: - reuse_lbound = self.reusePool.get( - self._as_tuple(layers[lbound:ubound]), -1 - ) - if reuse_lbound == -1: - continue - new_slice = ot.Paint() - new_slice.Format = int(ot.PaintFormat.PaintColrLayers) - new_slice.NumLayers = ubound - lbound - new_slice.FirstLayerIndex = reuse_lbound - layers = layers[:lbound] + [new_slice] + layers[ubound:] - found_reuse = True - break - return layers - - def add(self, layers: List[ot.Paint], first_layer_index: int): - for lbound, ubound in _reuse_ranges(len(layers)): - self.reusePool[self._as_tuple(layers[lbound:ubound])] = ( - lbound + first_layer_index - ) - - -class LayerListBuilder: - layers: List[ot.Paint] - cache: LayerReuseCache - allowLayerReuse: bool - - def __init__(self, *, allowLayerReuse=True): - self.layers = [] - if allowLayerReuse: - self.cache = LayerReuseCache() - else: - self.cache = None - - # We need to intercept construction of PaintColrLayers - callbacks = _buildPaintCallbacks() - callbacks[ - ( - BuildCallback.BEFORE_BUILD, - ot.Paint, - ot.PaintFormat.PaintColrLayers, - ) - ] = self._beforeBuildPaintColrLayers - self.tableBuilder = TableBuilder(callbacks) - - # COLR layers is unusual in that it modifies shared state - # so we need a callback into an object - def _beforeBuildPaintColrLayers(self, dest, source): - # Sketchy gymnastics: a sequence input will have dropped it's layers - # into NumLayers; get it back - if isinstance(source.get("NumLayers", None), collections.abc.Sequence): - layers = source["NumLayers"] - else: - layers = source["Layers"] - - # Convert maps seqs or whatever into typed objects - layers = [self.buildPaint(l) for l in layers] - - # No reason to have a colr layers with just one entry - if len(layers) == 1: - return layers[0], {} - - if self.cache is not None: - # Look for reuse, with preference to longer sequences - # This may make the layer list smaller - layers = self.cache.try_reuse(layers) - - # The layer list is now final; if it's too big we need to tree it - is_tree = len(layers) > MAX_PAINT_COLR_LAYER_COUNT - layers = build_n_ary_tree(layers, n=MAX_PAINT_COLR_LAYER_COUNT) - - # We now have a tree of sequences with Paint leaves. - # Convert the sequences into PaintColrLayers. - def listToColrLayers(layer): - if isinstance(layer, collections.abc.Sequence): - return self.buildPaint( - { - "Format": ot.PaintFormat.PaintColrLayers, - "Layers": [listToColrLayers(l) for l in layer], - } - ) - return layer - - layers = [listToColrLayers(l) for l in layers] - - # No reason to have a colr layers with just one entry - if len(layers) == 1: - return layers[0], {} - - paint = ot.Paint() - paint.Format = int(ot.PaintFormat.PaintColrLayers) - paint.NumLayers = len(layers) - paint.FirstLayerIndex = len(self.layers) - self.layers.extend(layers) - - # Register our parts for reuse provided we aren't a tree - # If we are a tree the leaves registered for reuse and that will suffice - if self.cache is not None and not is_tree: - self.cache.add(layers, paint.FirstLayerIndex) - - # we've fully built dest; empty source prevents generalized build from kicking in - return paint, {} - - def buildPaint(self, paint: _PaintInput) -> ot.Paint: - return self.tableBuilder.build(ot.Paint, paint) - - def build(self) -> Optional[ot.LayerList]: - if not self.layers: - return None - layers = ot.LayerList() - layers.LayerCount = len(self.layers) - layers.Paint = self.layers - return layers - - -def buildBaseGlyphPaintRecord( - baseGlyph: str, layerBuilder: LayerListBuilder, paint: _PaintInput -) -> ot.BaseGlyphList: - self = ot.BaseGlyphPaintRecord() - self.BaseGlyph = baseGlyph - self.Paint = layerBuilder.buildPaint(paint) - return self - - -def _format_glyph_errors(errors: Mapping[str, Exception]) -> str: - lines = [] - for baseGlyph, error in sorted(errors.items()): - lines.append(f" {baseGlyph} => {type(error).__name__}: {error}") - return "\n".join(lines) - - -def buildColrV1( - colorGlyphs: _ColorGlyphsDict, - glyphMap: Optional[Mapping[str, int]] = None, - *, - allowLayerReuse: bool = True, -) -> Tuple[Optional[ot.LayerList], ot.BaseGlyphList]: - if glyphMap is not None: - colorGlyphItems = sorted( - colorGlyphs.items(), key=lambda item: glyphMap[item[0]] - ) - else: - colorGlyphItems = colorGlyphs.items() - - errors = {} - baseGlyphs = [] - layerBuilder = LayerListBuilder(allowLayerReuse=allowLayerReuse) - for baseGlyph, paint in colorGlyphItems: - try: - baseGlyphs.append(buildBaseGlyphPaintRecord(baseGlyph, layerBuilder, paint)) - - except (ColorLibError, OverflowError, ValueError, TypeError) as e: - errors[baseGlyph] = e - - if errors: - failed_glyphs = _format_glyph_errors(errors) - exc = ColorLibError(f"Failed to build BaseGlyphList:\n{failed_glyphs}") - exc.errors = errors - raise exc from next(iter(errors.values())) - - layers = layerBuilder.build() - glyphs = ot.BaseGlyphList() - glyphs.BaseGlyphCount = len(baseGlyphs) - glyphs.BaseGlyphPaintRecord = baseGlyphs - return (layers, glyphs) diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/errors.py b/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/errors.py deleted file mode 100644 index 18cbebba..00000000 --- a/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/errors.py +++ /dev/null @@ -1,2 +0,0 @@ -class ColorLibError(Exception): - pass diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/geometry.py b/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/geometry.py deleted file mode 100644 index 1ce161bf..00000000 --- a/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/geometry.py +++ /dev/null @@ -1,143 +0,0 @@ -"""Helpers for manipulating 2D points and vectors in COLR table.""" - -from math import copysign, cos, hypot, isclose, pi -from fontTools.misc.roundTools import otRound - - -def _vector_between(origin, target): - return (target[0] - origin[0], target[1] - origin[1]) - - -def _round_point(pt): - return (otRound(pt[0]), otRound(pt[1])) - - -def _unit_vector(vec): - length = hypot(*vec) - if length == 0: - return None - return (vec[0] / length, vec[1] / length) - - -_CIRCLE_INSIDE_TOLERANCE = 1e-4 - - -# The unit vector's X and Y components are respectively -# U = (cos(α), sin(α)) -# where α is the angle between the unit vector and the positive x axis. -_UNIT_VECTOR_THRESHOLD = cos(3 / 8 * pi) # == sin(1/8 * pi) == 0.38268343236508984 - - -def _rounding_offset(direction): - # Return 2-tuple of -/+ 1.0 or 0.0 approximately based on the direction vector. - # We divide the unit circle in 8 equal slices oriented towards the cardinal - # (N, E, S, W) and intermediate (NE, SE, SW, NW) directions. To each slice we - # map one of the possible cases: -1, 0, +1 for either X and Y coordinate. - # E.g. Return (+1.0, -1.0) if unit vector is oriented towards SE, or - # (-1.0, 0.0) if it's pointing West, etc. - uv = _unit_vector(direction) - if not uv: - return (0, 0) - - result = [] - for uv_component in uv: - if -_UNIT_VECTOR_THRESHOLD <= uv_component < _UNIT_VECTOR_THRESHOLD: - # unit vector component near 0: direction almost orthogonal to the - # direction of the current axis, thus keep coordinate unchanged - result.append(0) - else: - # nudge coord by +/- 1.0 in direction of unit vector - result.append(copysign(1.0, uv_component)) - return tuple(result) - - -class Circle: - def __init__(self, centre, radius): - self.centre = centre - self.radius = radius - - def __repr__(self): - return f"Circle(centre={self.centre}, radius={self.radius})" - - def round(self): - return Circle(_round_point(self.centre), otRound(self.radius)) - - def inside(self, outer_circle, tolerance=_CIRCLE_INSIDE_TOLERANCE): - dist = self.radius + hypot(*_vector_between(self.centre, outer_circle.centre)) - return ( - isclose(outer_circle.radius, dist, rel_tol=_CIRCLE_INSIDE_TOLERANCE) - or outer_circle.radius > dist - ) - - def concentric(self, other): - return self.centre == other.centre - - def move(self, dx, dy): - self.centre = (self.centre[0] + dx, self.centre[1] + dy) - - -def round_start_circle_stable_containment(c0, r0, c1, r1): - """Round start circle so that it stays inside/outside end circle after rounding. - - The rounding of circle coordinates to integers may cause an abrupt change - if the start circle c0 is so close to the end circle c1's perimiter that - it ends up falling outside (or inside) as a result of the rounding. - To keep the gradient unchanged, we nudge it in the right direction. - - See: - https://github.com/googlefonts/colr-gradients-spec/issues/204 - https://github.com/googlefonts/picosvg/issues/158 - """ - start, end = Circle(c0, r0), Circle(c1, r1) - - inside_before_round = start.inside(end) - - round_start = start.round() - round_end = end.round() - inside_after_round = round_start.inside(round_end) - - if inside_before_round == inside_after_round: - return round_start - elif inside_after_round: - # start was outside before rounding: we need to push start away from end - direction = _vector_between(round_end.centre, round_start.centre) - radius_delta = +1.0 - else: - # start was inside before rounding: we need to push start towards end - direction = _vector_between(round_start.centre, round_end.centre) - radius_delta = -1.0 - dx, dy = _rounding_offset(direction) - - # At most 2 iterations ought to be enough to converge. Before the loop, we - # know the start circle didn't keep containment after normal rounding; thus - # we continue adjusting by -/+ 1.0 until containment is restored. - # Normal rounding can at most move each coordinates -/+0.5; in the worst case - # both the start and end circle's centres and radii will be rounded in opposite - # directions, e.g. when they move along a 45 degree diagonal: - # c0 = (1.5, 1.5) ===> (2.0, 2.0) - # r0 = 0.5 ===> 1.0 - # c1 = (0.499, 0.499) ===> (0.0, 0.0) - # r1 = 2.499 ===> 2.0 - # In this example, the relative distance between the circles, calculated - # as r1 - (r0 + distance(c0, c1)) is initially 0.57437 (c0 is inside c1), and - # -1.82842 after rounding (c0 is now outside c1). Nudging c0 by -1.0 on both - # x and y axes moves it towards c1 by hypot(-1.0, -1.0) = 1.41421. Two of these - # moves cover twice that distance, which is enough to restore containment. - max_attempts = 2 - for _ in range(max_attempts): - if round_start.concentric(round_end): - # can't move c0 towards c1 (they are the same), so we change the radius - round_start.radius += radius_delta - assert round_start.radius >= 0 - else: - round_start.move(dx, dy) - if inside_before_round == round_start.inside(round_end): - break - else: # likely a bug - raise AssertionError( - f"Rounding circle {start} " - f"{'inside' if inside_before_round else 'outside'} " - f"{end} failed after {max_attempts} attempts!" - ) - - return round_start diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/table_builder.py b/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/table_builder.py deleted file mode 100644 index f1e182c4..00000000 --- a/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/table_builder.py +++ /dev/null @@ -1,223 +0,0 @@ -""" -colorLib.table_builder: Generic helper for filling in BaseTable derivatives from tuples and maps and such. - -""" - -import collections -import enum -from fontTools.ttLib.tables.otBase import ( - BaseTable, - FormatSwitchingBaseTable, - UInt8FormatSwitchingBaseTable, -) -from fontTools.ttLib.tables.otConverters import ( - ComputedInt, - SimpleValue, - Struct, - Short, - UInt8, - UShort, - IntValue, - FloatValue, - OptionalValue, -) -from fontTools.misc.roundTools import otRound - - -class BuildCallback(enum.Enum): - """Keyed on (BEFORE_BUILD, class[, Format if available]). - Receives (dest, source). - Should return (dest, source), which can be new objects. - """ - - BEFORE_BUILD = enum.auto() - - """Keyed on (AFTER_BUILD, class[, Format if available]). - Receives (dest). - Should return dest, which can be a new object. - """ - AFTER_BUILD = enum.auto() - - """Keyed on (CREATE_DEFAULT, class[, Format if available]). - Receives no arguments. - Should return a new instance of class. - """ - CREATE_DEFAULT = enum.auto() - - -def _assignable(convertersByName): - return {k: v for k, v in convertersByName.items() if not isinstance(v, ComputedInt)} - - -def _isNonStrSequence(value): - return isinstance(value, collections.abc.Sequence) and not isinstance(value, str) - - -def _split_format(cls, source): - if _isNonStrSequence(source): - assert len(source) > 0, f"{cls} needs at least format from {source}" - fmt, remainder = source[0], source[1:] - elif isinstance(source, collections.abc.Mapping): - assert "Format" in source, f"{cls} needs at least Format from {source}" - remainder = source.copy() - fmt = remainder.pop("Format") - else: - raise ValueError(f"Not sure how to populate {cls} from {source}") - - assert isinstance( - fmt, collections.abc.Hashable - ), f"{cls} Format is not hashable: {fmt!r}" - assert fmt in cls.convertersByName, f"{cls} invalid Format: {fmt!r}" - - return fmt, remainder - - -class TableBuilder: - """ - Helps to populate things derived from BaseTable from maps, tuples, etc. - - A table of lifecycle callbacks may be provided to add logic beyond what is possible - based on otData info for the target class. See BuildCallbacks. - """ - - def __init__(self, callbackTable=None): - if callbackTable is None: - callbackTable = {} - self._callbackTable = callbackTable - - def _convert(self, dest, field, converter, value): - enumClass = getattr(converter, "enumClass", None) - - if enumClass: - if isinstance(value, enumClass): - pass - elif isinstance(value, str): - try: - value = getattr(enumClass, value.upper()) - except AttributeError: - raise ValueError(f"{value} is not a valid {enumClass}") - else: - value = enumClass(value) - - elif isinstance(converter, IntValue): - value = otRound(value) - elif isinstance(converter, FloatValue): - value = float(value) - - elif isinstance(converter, Struct): - if converter.repeat: - if _isNonStrSequence(value): - value = [self.build(converter.tableClass, v) for v in value] - else: - value = [self.build(converter.tableClass, value)] - setattr(dest, converter.repeat, len(value)) - else: - value = self.build(converter.tableClass, value) - elif callable(converter): - value = converter(value) - - setattr(dest, field, value) - - def build(self, cls, source): - assert issubclass(cls, BaseTable) - - if isinstance(source, cls): - return source - - callbackKey = (cls,) - fmt = None - if issubclass(cls, FormatSwitchingBaseTable): - fmt, source = _split_format(cls, source) - callbackKey = (cls, fmt) - - dest = self._callbackTable.get( - (BuildCallback.CREATE_DEFAULT,) + callbackKey, lambda: cls() - )() - assert isinstance(dest, cls) - - convByName = _assignable(cls.convertersByName) - skippedFields = set() - - # For format switchers we need to resolve converters based on format - if issubclass(cls, FormatSwitchingBaseTable): - dest.Format = fmt - convByName = _assignable(convByName[dest.Format]) - skippedFields.add("Format") - - # Convert sequence => mapping so before thunk only has to handle one format - if _isNonStrSequence(source): - # Sequence (typically list or tuple) assumed to match fields in declaration order - assert len(source) <= len( - convByName - ), f"Sequence of {len(source)} too long for {cls}; expected <= {len(convByName)} values" - source = dict(zip(convByName.keys(), source)) - - dest, source = self._callbackTable.get( - (BuildCallback.BEFORE_BUILD,) + callbackKey, lambda d, s: (d, s) - )(dest, source) - - if isinstance(source, collections.abc.Mapping): - for field, value in source.items(): - if field in skippedFields: - continue - converter = convByName.get(field, None) - if not converter: - raise ValueError( - f"Unrecognized field {field} for {cls}; expected one of {sorted(convByName.keys())}" - ) - self._convert(dest, field, converter, value) - else: - # let's try as a 1-tuple - dest = self.build(cls, (source,)) - - for field, conv in convByName.items(): - if not hasattr(dest, field) and isinstance(conv, OptionalValue): - setattr(dest, field, conv.DEFAULT) - - dest = self._callbackTable.get( - (BuildCallback.AFTER_BUILD,) + callbackKey, lambda d: d - )(dest) - - return dest - - -class TableUnbuilder: - def __init__(self, callbackTable=None): - if callbackTable is None: - callbackTable = {} - self._callbackTable = callbackTable - - def unbuild(self, table): - assert isinstance(table, BaseTable) - - source = {} - - callbackKey = (type(table),) - if isinstance(table, FormatSwitchingBaseTable): - source["Format"] = int(table.Format) - callbackKey += (table.Format,) - - for converter in table.getConverters(): - if isinstance(converter, ComputedInt): - continue - value = getattr(table, converter.name) - - enumClass = getattr(converter, "enumClass", None) - if enumClass: - source[converter.name] = value.name.lower() - elif isinstance(converter, Struct): - if converter.repeat: - source[converter.name] = [self.unbuild(v) for v in value] - else: - source[converter.name] = self.unbuild(value) - elif isinstance(converter, SimpleValue): - # "simple" values (e.g. int, float, str) need no further un-building - source[converter.name] = value - else: - raise NotImplementedError( - "Don't know how unbuild {value!r} with {converter!r}" - ) - - source = self._callbackTable.get(callbackKey, lambda s: s)(source) - - return source diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/unbuilder.py b/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/unbuilder.py deleted file mode 100644 index ac243550..00000000 --- a/.venv-docs/lib/python3.12/site-packages/fontTools/colorLib/unbuilder.py +++ /dev/null @@ -1,81 +0,0 @@ -from fontTools.ttLib.tables import otTables as ot -from .table_builder import TableUnbuilder - - -def unbuildColrV1(layerList, baseGlyphList): - layers = [] - if layerList: - layers = layerList.Paint - unbuilder = LayerListUnbuilder(layers) - return { - rec.BaseGlyph: unbuilder.unbuildPaint(rec.Paint) - for rec in baseGlyphList.BaseGlyphPaintRecord - } - - -def _flatten_layers(lst): - for paint in lst: - if paint["Format"] == ot.PaintFormat.PaintColrLayers: - yield from _flatten_layers(paint["Layers"]) - else: - yield paint - - -class LayerListUnbuilder: - def __init__(self, layers): - self.layers = layers - - callbacks = { - ( - ot.Paint, - ot.PaintFormat.PaintColrLayers, - ): self._unbuildPaintColrLayers, - } - self.tableUnbuilder = TableUnbuilder(callbacks) - - def unbuildPaint(self, paint): - assert isinstance(paint, ot.Paint) - return self.tableUnbuilder.unbuild(paint) - - def _unbuildPaintColrLayers(self, source): - assert source["Format"] == ot.PaintFormat.PaintColrLayers - - layers = list( - _flatten_layers( - [ - self.unbuildPaint(childPaint) - for childPaint in self.layers[ - source["FirstLayerIndex"] : source["FirstLayerIndex"] - + source["NumLayers"] - ] - ] - ) - ) - - if len(layers) == 1: - return layers[0] - - return {"Format": source["Format"], "Layers": layers} - - -if __name__ == "__main__": - from pprint import pprint - import sys - from fontTools.ttLib import TTFont - - try: - fontfile = sys.argv[1] - except IndexError: - sys.exit("usage: fonttools colorLib.unbuilder FONTFILE") - - font = TTFont(fontfile) - colr = font["COLR"] - if colr.version < 1: - sys.exit(f"error: No COLR table version=1 found in {fontfile}") - - colorGlyphs = unbuildColrV1( - colr.table.LayerList, - colr.table.BaseGlyphList, - ) - - pprint(colorGlyphs) diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/config/__init__.py b/.venv-docs/lib/python3.12/site-packages/fontTools/config/__init__.py deleted file mode 100644 index ff0328a3..00000000 --- a/.venv-docs/lib/python3.12/site-packages/fontTools/config/__init__.py +++ /dev/null @@ -1,90 +0,0 @@ -""" -Define all configuration options that can affect the working of fontTools -modules. E.g. optimization levels of varLib IUP, otlLib GPOS compression level, -etc. If this file gets too big, split it into smaller files per-module. - -An instance of the Config class can be attached to a TTFont object, so that -the various modules can access their configuration options from it. -""" - -from textwrap import dedent - -from fontTools.misc.configTools import * - - -class Config(AbstractConfig): - options = Options() - - -OPTIONS = Config.options - - -Config.register_option( - name="fontTools.otlLib.optimize.gpos:COMPRESSION_LEVEL", - help=dedent( - """\ - GPOS Lookup type 2 (PairPos) compression level: - 0 = do not attempt to compact PairPos lookups; - 1 to 8 = create at most 1 to 8 new subtables for each existing - subtable, provided that it would yield a 50%% file size saving; - 9 = create as many new subtables as needed to yield a file size saving. - Default: 0. - - This compaction aims to save file size, by splitting large class - kerning subtables (Format 2) that contain many zero values into - smaller and denser subtables. It's a trade-off between the overhead - of several subtables versus the sparseness of one big subtable. - - See the pull request: https://github.com/fonttools/fonttools/pull/2326 - """ - ), - default=0, - parse=int, - validate=lambda v: v in range(10), -) - -Config.register_option( - name="fontTools.ttLib.tables.otBase:USE_HARFBUZZ_REPACKER", - help=dedent( - """\ - FontTools tries to use the HarfBuzz Repacker to serialize GPOS/GSUB tables - if the uharfbuzz python bindings are importable, otherwise falls back to its - slower, less efficient serializer. Set to False to always use the latter. - Set to True to explicitly request the HarfBuzz Repacker (will raise an - error if uharfbuzz cannot be imported). - """ - ), - default=None, - parse=Option.parse_optional_bool, - validate=Option.validate_optional_bool, -) - -Config.register_option( - name="fontTools.otlLib.builder:WRITE_GPOS7", - help=dedent( - """\ - macOS before 13.2 didn’t support GPOS LookupType 7 (non-chaining - ContextPos lookups), so FontTools.otlLib.builder disables a file size - optimisation that would use LookupType 7 instead of 8 when there is no - chaining (no prefix or suffix). Set to True to enable the optimization. - """ - ), - default=False, - parse=Option.parse_optional_bool, - validate=Option.validate_optional_bool, -) - -Config.register_option( - name="fontTools.ttLib:OPTIMIZE_FONT_SPEED", - help=dedent( - """\ - Enable optimizations that prioritize speed over file size. This - mainly affects how glyf table and gvar / VARC tables are compiled. - The produced fonts will be larger, but rendering performance will - be improved with HarfBuzz and other text layout engines. - """ - ), - default=False, - parse=Option.parse_optional_bool, - validate=Option.validate_optional_bool, -) diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/config/__pycache__/__init__.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/config/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 79ca003f..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/config/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/__init__.py b/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/__init__.py deleted file mode 100644 index 4ae6356e..00000000 --- a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2016 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from .cu2qu import * diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/__main__.py b/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/__main__.py deleted file mode 100644 index 5205ffee..00000000 --- a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/__main__.py +++ /dev/null @@ -1,6 +0,0 @@ -import sys -from .cli import _main as main - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/__pycache__/__init__.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 636db241..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/__pycache__/__init__.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/__pycache__/__main__.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/__pycache__/__main__.cpython-312.pyc deleted file mode 100644 index 8528015a..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/__pycache__/__main__.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/__pycache__/benchmark.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/__pycache__/benchmark.cpython-312.pyc deleted file mode 100644 index 2b8ec66f..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/__pycache__/benchmark.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/__pycache__/cli.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/__pycache__/cli.cpython-312.pyc deleted file mode 100644 index 9dc6ffb9..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/__pycache__/cli.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/__pycache__/cu2qu.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/__pycache__/cu2qu.cpython-312.pyc deleted file mode 100644 index 6209d379..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/__pycache__/cu2qu.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/__pycache__/errors.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/__pycache__/errors.cpython-312.pyc deleted file mode 100644 index d6f381a8..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/__pycache__/errors.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/__pycache__/ufo.cpython-312.pyc b/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/__pycache__/ufo.cpython-312.pyc deleted file mode 100644 index 96ce291b..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/__pycache__/ufo.cpython-312.pyc and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/benchmark.py b/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/benchmark.py deleted file mode 100644 index 007f75d8..00000000 --- a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/benchmark.py +++ /dev/null @@ -1,54 +0,0 @@ -"""Benchmark the cu2qu algorithm performance.""" - -from .cu2qu import * -import random -import timeit - -MAX_ERR = 0.05 - - -def generate_curve(): - return [ - tuple(float(random.randint(0, 2048)) for coord in range(2)) - for point in range(4) - ] - - -def setup_curve_to_quadratic(): - return generate_curve(), MAX_ERR - - -def setup_curves_to_quadratic(): - num_curves = 3 - return ([generate_curve() for curve in range(num_curves)], [MAX_ERR] * num_curves) - - -def run_benchmark(module, function, setup_suffix="", repeat=5, number=1000): - setup_func = "setup_" + function - if setup_suffix: - print("%s with %s:" % (function, setup_suffix), end="") - setup_func += "_" + setup_suffix - else: - print("%s:" % function, end="") - - def wrapper(function, setup_func): - function = globals()[function] - setup_func = globals()[setup_func] - - def wrapped(): - return function(*setup_func()) - - return wrapped - - results = timeit.repeat(wrapper(function, setup_func), repeat=repeat, number=number) - print("\t%5.1fus" % (min(results) * 1000000.0 / number)) - - -def main(): - run_benchmark("cu2qu", "curve_to_quadratic") - run_benchmark("cu2qu", "curves_to_quadratic") - - -if __name__ == "__main__": - random.seed(1) - main() diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/cli.py b/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/cli.py deleted file mode 100644 index ddc64502..00000000 --- a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/cli.py +++ /dev/null @@ -1,198 +0,0 @@ -import os -import argparse -import logging -import shutil -import multiprocessing as mp -from contextlib import closing -from functools import partial - -import fontTools -from .ufo import font_to_quadratic, fonts_to_quadratic - -ufo_module = None -try: - import ufoLib2 as ufo_module -except ImportError: - try: - import defcon as ufo_module - except ImportError as e: - pass - - -logger = logging.getLogger("fontTools.cu2qu") - - -def _cpu_count(): - try: - return mp.cpu_count() - except NotImplementedError: # pragma: no cover - return 1 - - -def open_ufo(path): - if hasattr(ufo_module.Font, "open"): # ufoLib2 - return ufo_module.Font.open(path) - return ufo_module.Font(path) # defcon - - -def _font_to_quadratic(input_path, output_path=None, **kwargs): - ufo = open_ufo(input_path) - logger.info("Converting curves for %s", input_path) - if font_to_quadratic(ufo, **kwargs): - logger.info("Saving %s", output_path) - if output_path: - ufo.save(output_path) - else: - ufo.save() # save in-place - elif output_path: - _copytree(input_path, output_path) - - -def _samepath(path1, path2): - # TODO on python3+, there's os.path.samefile - path1 = os.path.normcase(os.path.abspath(os.path.realpath(path1))) - path2 = os.path.normcase(os.path.abspath(os.path.realpath(path2))) - return path1 == path2 - - -def _copytree(input_path, output_path): - if _samepath(input_path, output_path): - logger.debug("input and output paths are the same file; skipped copy") - return - if os.path.exists(output_path): - shutil.rmtree(output_path) - shutil.copytree(input_path, output_path) - - -def _main(args=None): - """Convert a UFO font from cubic to quadratic curves""" - parser = argparse.ArgumentParser(prog="cu2qu") - parser.add_argument("--version", action="version", version=fontTools.__version__) - parser.add_argument( - "infiles", - nargs="+", - metavar="INPUT", - help="one or more input UFO source file(s).", - ) - parser.add_argument("-v", "--verbose", action="count", default=0) - parser.add_argument( - "-e", - "--conversion-error", - type=float, - metavar="ERROR", - default=None, - help="maxiumum approximation error measured in EM (default: 0.001)", - ) - parser.add_argument( - "-m", - "--mixed", - default=False, - action="store_true", - help="whether to used mixed quadratic and cubic curves", - ) - parser.add_argument( - "--keep-direction", - dest="reverse_direction", - action="store_false", - help="do not reverse the contour direction", - ) - - mode_parser = parser.add_mutually_exclusive_group() - mode_parser.add_argument( - "-i", - "--interpolatable", - action="store_true", - help="whether curve conversion should keep interpolation compatibility", - ) - mode_parser.add_argument( - "-j", - "--jobs", - type=int, - nargs="?", - default=1, - const=_cpu_count(), - metavar="N", - help="Convert using N multiple processes (default: %(default)s)", - ) - - output_parser = parser.add_mutually_exclusive_group() - output_parser.add_argument( - "-o", - "--output-file", - default=None, - metavar="OUTPUT", - help=( - "output filename for the converted UFO. By default fonts are " - "modified in place. This only works with a single input." - ), - ) - output_parser.add_argument( - "-d", - "--output-dir", - default=None, - metavar="DIRECTORY", - help="output directory where to save converted UFOs", - ) - - options = parser.parse_args(args) - - if ufo_module is None: - parser.error("Either ufoLib2 or defcon are required to run this script.") - - if not options.verbose: - level = "WARNING" - elif options.verbose == 1: - level = "INFO" - else: - level = "DEBUG" - logging.basicConfig(level=level) - - if len(options.infiles) > 1 and options.output_file: - parser.error("-o/--output-file can't be used with multile inputs") - - if options.output_dir: - output_dir = options.output_dir - if not os.path.exists(output_dir): - os.mkdir(output_dir) - elif not os.path.isdir(output_dir): - parser.error("'%s' is not a directory" % output_dir) - output_paths = [ - os.path.join(output_dir, os.path.basename(p)) for p in options.infiles - ] - elif options.output_file: - output_paths = [options.output_file] - else: - # save in-place - output_paths = [None] * len(options.infiles) - - kwargs = dict( - dump_stats=options.verbose > 0, - max_err_em=options.conversion_error, - reverse_direction=options.reverse_direction, - all_quadratic=False if options.mixed else True, - ) - - if options.interpolatable: - logger.info("Converting curves compatibly") - ufos = [open_ufo(infile) for infile in options.infiles] - if fonts_to_quadratic(ufos, **kwargs): - for ufo, output_path in zip(ufos, output_paths): - logger.info("Saving %s", output_path) - if output_path: - ufo.save(output_path) - else: - ufo.save() - else: - for input_path, output_path in zip(options.infiles, output_paths): - if output_path: - _copytree(input_path, output_path) - else: - jobs = min(len(options.infiles), options.jobs) if options.jobs > 1 else 1 - if jobs > 1: - func = partial(_font_to_quadratic, **kwargs) - logger.info("Running %d parallel processes", jobs) - with closing(mp.Pool(jobs)) as pool: - pool.starmap(func, zip(options.infiles, output_paths)) - else: - for input_path, output_path in zip(options.infiles, output_paths): - _font_to_quadratic(input_path, output_path, **kwargs) diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/cu2qu.c b/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/cu2qu.c deleted file mode 100644 index 0b26ad1b..00000000 --- a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/cu2qu.c +++ /dev/null @@ -1,15773 +0,0 @@ -/* Generated by Cython 3.1.4 */ - -/* BEGIN: Cython Metadata -{ - "distutils": { - "define_macros": [ - [ - "CYTHON_TRACE_NOGIL", - "1" - ] - ], - "name": "fontTools.cu2qu.cu2qu", - "sources": [ - "Lib/fontTools/cu2qu/cu2qu.py" - ] - }, - "module_name": "fontTools.cu2qu.cu2qu" -} -END: Cython Metadata */ - -#ifndef PY_SSIZE_T_CLEAN -#define PY_SSIZE_T_CLEAN -#endif /* PY_SSIZE_T_CLEAN */ -/* InitLimitedAPI */ -#if defined(Py_LIMITED_API) && !defined(CYTHON_LIMITED_API) - #define CYTHON_LIMITED_API 1 -#endif - -#include "Python.h" -#ifndef Py_PYTHON_H - #error Python headers needed to compile C extensions, please install development version of Python. -#elif PY_VERSION_HEX < 0x03080000 - #error Cython requires Python 3.8+. -#else -#define __PYX_ABI_VERSION "3_1_4" -#define CYTHON_HEX_VERSION 0x030104F0 -#define CYTHON_FUTURE_DIVISION 1 -/* CModulePreamble */ -#include -#ifndef offsetof - #define offsetof(type, member) ( (size_t) & ((type*)0) -> member ) -#endif -#if !defined(_WIN32) && !defined(WIN32) && !defined(MS_WINDOWS) - #ifndef __stdcall - #define __stdcall - #endif - #ifndef __cdecl - #define __cdecl - #endif - #ifndef __fastcall - #define __fastcall - #endif -#endif -#ifndef DL_IMPORT - #define DL_IMPORT(t) t -#endif -#ifndef DL_EXPORT - #define DL_EXPORT(t) t -#endif -#define __PYX_COMMA , -#ifndef HAVE_LONG_LONG - #define HAVE_LONG_LONG -#endif -#ifndef PY_LONG_LONG - #define PY_LONG_LONG LONG_LONG -#endif -#ifndef Py_HUGE_VAL - #define Py_HUGE_VAL HUGE_VAL -#endif -#define __PYX_LIMITED_VERSION_HEX PY_VERSION_HEX -#if defined(GRAALVM_PYTHON) - /* For very preliminary testing purposes. Most variables are set the same as PyPy. - The existence of this section does not imply that anything works or is even tested */ - #define CYTHON_COMPILING_IN_PYPY 0 - #define CYTHON_COMPILING_IN_CPYTHON 0 - #define CYTHON_COMPILING_IN_LIMITED_API 0 - #define CYTHON_COMPILING_IN_GRAAL 1 - #define CYTHON_COMPILING_IN_CPYTHON_FREETHREADING 0 - #undef CYTHON_USE_TYPE_SLOTS - #define CYTHON_USE_TYPE_SLOTS 0 - #undef CYTHON_USE_TYPE_SPECS - #define CYTHON_USE_TYPE_SPECS 0 - #undef CYTHON_USE_PYTYPE_LOOKUP - #define CYTHON_USE_PYTYPE_LOOKUP 0 - #undef CYTHON_USE_PYLIST_INTERNALS - #define CYTHON_USE_PYLIST_INTERNALS 0 - #undef CYTHON_USE_UNICODE_INTERNALS - #define CYTHON_USE_UNICODE_INTERNALS 0 - #undef CYTHON_USE_UNICODE_WRITER - #define CYTHON_USE_UNICODE_WRITER 0 - #undef CYTHON_USE_PYLONG_INTERNALS - #define CYTHON_USE_PYLONG_INTERNALS 0 - #undef CYTHON_AVOID_BORROWED_REFS - #define CYTHON_AVOID_BORROWED_REFS 1 - #undef CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS - #define CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS 1 - #undef CYTHON_ASSUME_SAFE_MACROS - #define CYTHON_ASSUME_SAFE_MACROS 0 - #undef CYTHON_ASSUME_SAFE_SIZE - #define CYTHON_ASSUME_SAFE_SIZE 0 - #undef CYTHON_UNPACK_METHODS - #define CYTHON_UNPACK_METHODS 0 - #undef CYTHON_FAST_THREAD_STATE - #define CYTHON_FAST_THREAD_STATE 0 - #undef CYTHON_FAST_GIL - #define CYTHON_FAST_GIL 0 - #undef CYTHON_METH_FASTCALL - #define CYTHON_METH_FASTCALL 0 - #undef CYTHON_FAST_PYCALL - #define CYTHON_FAST_PYCALL 0 - #ifndef CYTHON_PEP487_INIT_SUBCLASS - #define CYTHON_PEP487_INIT_SUBCLASS 1 - #endif - #undef CYTHON_PEP489_MULTI_PHASE_INIT - #define CYTHON_PEP489_MULTI_PHASE_INIT 1 - #undef CYTHON_USE_MODULE_STATE - #define CYTHON_USE_MODULE_STATE 0 - #undef CYTHON_USE_SYS_MONITORING - #define CYTHON_USE_SYS_MONITORING 0 - #undef CYTHON_USE_TP_FINALIZE - #define CYTHON_USE_TP_FINALIZE 0 - #undef CYTHON_USE_AM_SEND - #define CYTHON_USE_AM_SEND 0 - #undef CYTHON_USE_DICT_VERSIONS - #define CYTHON_USE_DICT_VERSIONS 0 - #undef CYTHON_USE_EXC_INFO_STACK - #define CYTHON_USE_EXC_INFO_STACK 1 - #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC - #define CYTHON_UPDATE_DESCRIPTOR_DOC 0 - #endif - #undef CYTHON_USE_FREELISTS - #define CYTHON_USE_FREELISTS 0 -#elif defined(PYPY_VERSION) - #define CYTHON_COMPILING_IN_PYPY 1 - #define CYTHON_COMPILING_IN_CPYTHON 0 - #define CYTHON_COMPILING_IN_LIMITED_API 0 - #define CYTHON_COMPILING_IN_GRAAL 0 - #define CYTHON_COMPILING_IN_CPYTHON_FREETHREADING 0 - #undef CYTHON_USE_TYPE_SLOTS - #define CYTHON_USE_TYPE_SLOTS 1 - #ifndef CYTHON_USE_TYPE_SPECS - #define CYTHON_USE_TYPE_SPECS 0 - #endif - #undef CYTHON_USE_PYTYPE_LOOKUP - #define CYTHON_USE_PYTYPE_LOOKUP 0 - #undef CYTHON_USE_PYLIST_INTERNALS - #define CYTHON_USE_PYLIST_INTERNALS 0 - #undef CYTHON_USE_UNICODE_INTERNALS - #define CYTHON_USE_UNICODE_INTERNALS 0 - #undef CYTHON_USE_UNICODE_WRITER - #define CYTHON_USE_UNICODE_WRITER 0 - #undef CYTHON_USE_PYLONG_INTERNALS - #define CYTHON_USE_PYLONG_INTERNALS 0 - #undef CYTHON_AVOID_BORROWED_REFS - #define CYTHON_AVOID_BORROWED_REFS 1 - #undef CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS - #define CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS 1 - #undef CYTHON_ASSUME_SAFE_MACROS - #define CYTHON_ASSUME_SAFE_MACROS 0 - #ifndef CYTHON_ASSUME_SAFE_SIZE - #define CYTHON_ASSUME_SAFE_SIZE 1 - #endif - #undef CYTHON_UNPACK_METHODS - #define CYTHON_UNPACK_METHODS 0 - #undef CYTHON_FAST_THREAD_STATE - #define CYTHON_FAST_THREAD_STATE 0 - #undef CYTHON_FAST_GIL - #define CYTHON_FAST_GIL 0 - #undef CYTHON_METH_FASTCALL - #define CYTHON_METH_FASTCALL 0 - #undef CYTHON_FAST_PYCALL - #define CYTHON_FAST_PYCALL 0 - #ifndef CYTHON_PEP487_INIT_SUBCLASS - #define CYTHON_PEP487_INIT_SUBCLASS 1 - #endif - #if PY_VERSION_HEX < 0x03090000 - #undef CYTHON_PEP489_MULTI_PHASE_INIT - #define CYTHON_PEP489_MULTI_PHASE_INIT 0 - #elif !defined(CYTHON_PEP489_MULTI_PHASE_INIT) - #define CYTHON_PEP489_MULTI_PHASE_INIT 1 - #endif - #undef CYTHON_USE_MODULE_STATE - #define CYTHON_USE_MODULE_STATE 0 - #undef CYTHON_USE_SYS_MONITORING - #define CYTHON_USE_SYS_MONITORING 0 - #ifndef CYTHON_USE_TP_FINALIZE - #define CYTHON_USE_TP_FINALIZE (PYPY_VERSION_NUM >= 0x07030C00) - #endif - #undef CYTHON_USE_AM_SEND - #define CYTHON_USE_AM_SEND 0 - #undef CYTHON_USE_DICT_VERSIONS - #define CYTHON_USE_DICT_VERSIONS 0 - #undef CYTHON_USE_EXC_INFO_STACK - #define CYTHON_USE_EXC_INFO_STACK 0 - #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC - #define CYTHON_UPDATE_DESCRIPTOR_DOC (PYPY_VERSION_NUM >= 0x07031100) - #endif - #undef CYTHON_USE_FREELISTS - #define CYTHON_USE_FREELISTS 0 -#elif defined(CYTHON_LIMITED_API) - #ifdef Py_LIMITED_API - #undef __PYX_LIMITED_VERSION_HEX - #define __PYX_LIMITED_VERSION_HEX Py_LIMITED_API - #endif - #define CYTHON_COMPILING_IN_PYPY 0 - #define CYTHON_COMPILING_IN_CPYTHON 0 - #define CYTHON_COMPILING_IN_LIMITED_API 1 - #define CYTHON_COMPILING_IN_GRAAL 0 - #define CYTHON_COMPILING_IN_CPYTHON_FREETHREADING 0 - #undef CYTHON_CLINE_IN_TRACEBACK - #define CYTHON_CLINE_IN_TRACEBACK 0 - #undef CYTHON_USE_TYPE_SLOTS - #define CYTHON_USE_TYPE_SLOTS 0 - #undef CYTHON_USE_TYPE_SPECS - #define CYTHON_USE_TYPE_SPECS 1 - #undef CYTHON_USE_PYTYPE_LOOKUP - #define CYTHON_USE_PYTYPE_LOOKUP 0 - #undef CYTHON_USE_PYLIST_INTERNALS - #define CYTHON_USE_PYLIST_INTERNALS 0 - #undef CYTHON_USE_UNICODE_INTERNALS - #define CYTHON_USE_UNICODE_INTERNALS 0 - #ifndef CYTHON_USE_UNICODE_WRITER - #define CYTHON_USE_UNICODE_WRITER 0 - #endif - #undef CYTHON_USE_PYLONG_INTERNALS - #define CYTHON_USE_PYLONG_INTERNALS 0 - #ifndef CYTHON_AVOID_BORROWED_REFS - #define CYTHON_AVOID_BORROWED_REFS 0 - #endif - #ifndef CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS - #define CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS 0 - #endif - #undef CYTHON_ASSUME_SAFE_MACROS - #define CYTHON_ASSUME_SAFE_MACROS 0 - #undef CYTHON_ASSUME_SAFE_SIZE - #define CYTHON_ASSUME_SAFE_SIZE 0 - #undef CYTHON_UNPACK_METHODS - #define CYTHON_UNPACK_METHODS 0 - #undef CYTHON_FAST_THREAD_STATE - #define CYTHON_FAST_THREAD_STATE 0 - #undef CYTHON_FAST_GIL - #define CYTHON_FAST_GIL 0 - #undef CYTHON_METH_FASTCALL - #define CYTHON_METH_FASTCALL (__PYX_LIMITED_VERSION_HEX >= 0x030C0000) - #undef CYTHON_FAST_PYCALL - #define CYTHON_FAST_PYCALL 0 - #ifndef CYTHON_PEP487_INIT_SUBCLASS - #define CYTHON_PEP487_INIT_SUBCLASS 1 - #endif - #ifndef CYTHON_PEP489_MULTI_PHASE_INIT - #define CYTHON_PEP489_MULTI_PHASE_INIT 1 - #endif - #ifndef CYTHON_USE_MODULE_STATE - #define CYTHON_USE_MODULE_STATE 0 - #endif - #undef CYTHON_USE_SYS_MONITORING - #define CYTHON_USE_SYS_MONITORING 0 - #ifndef CYTHON_USE_TP_FINALIZE - #define CYTHON_USE_TP_FINALIZE 0 - #endif - #ifndef CYTHON_USE_AM_SEND - #define CYTHON_USE_AM_SEND (__PYX_LIMITED_VERSION_HEX >= 0x030A0000) - #endif - #undef CYTHON_USE_DICT_VERSIONS - #define CYTHON_USE_DICT_VERSIONS 0 - #undef CYTHON_USE_EXC_INFO_STACK - #define CYTHON_USE_EXC_INFO_STACK 0 - #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC - #define CYTHON_UPDATE_DESCRIPTOR_DOC 0 - #endif - #undef CYTHON_USE_FREELISTS - #define CYTHON_USE_FREELISTS 0 -#else - #define CYTHON_COMPILING_IN_PYPY 0 - #define CYTHON_COMPILING_IN_CPYTHON 1 - #define CYTHON_COMPILING_IN_LIMITED_API 0 - #define CYTHON_COMPILING_IN_GRAAL 0 - #ifdef Py_GIL_DISABLED - #define CYTHON_COMPILING_IN_CPYTHON_FREETHREADING 1 - #else - #define CYTHON_COMPILING_IN_CPYTHON_FREETHREADING 0 - #endif - #if PY_VERSION_HEX < 0x030A0000 - #undef CYTHON_USE_TYPE_SLOTS - #define CYTHON_USE_TYPE_SLOTS 1 - #elif !defined(CYTHON_USE_TYPE_SLOTS) - #define CYTHON_USE_TYPE_SLOTS 1 - #endif - #ifndef CYTHON_USE_TYPE_SPECS - #define CYTHON_USE_TYPE_SPECS 0 - #endif - #ifndef CYTHON_USE_PYTYPE_LOOKUP - #define CYTHON_USE_PYTYPE_LOOKUP 1 - #endif - #ifndef CYTHON_USE_PYLONG_INTERNALS - #define CYTHON_USE_PYLONG_INTERNALS 1 - #endif - #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING - #undef CYTHON_USE_PYLIST_INTERNALS - #define CYTHON_USE_PYLIST_INTERNALS 0 - #elif !defined(CYTHON_USE_PYLIST_INTERNALS) - #define CYTHON_USE_PYLIST_INTERNALS 1 - #endif - #ifndef CYTHON_USE_UNICODE_INTERNALS - #define CYTHON_USE_UNICODE_INTERNALS 1 - #endif - #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING || PY_VERSION_HEX >= 0x030B00A2 - #undef CYTHON_USE_UNICODE_WRITER - #define CYTHON_USE_UNICODE_WRITER 0 - #elif !defined(CYTHON_USE_UNICODE_WRITER) - #define CYTHON_USE_UNICODE_WRITER 1 - #endif - #ifndef CYTHON_AVOID_BORROWED_REFS - #define CYTHON_AVOID_BORROWED_REFS 0 - #endif - #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING - #undef CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS - #define CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS 1 - #elif !defined(CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS) - #define CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS 0 - #endif - #ifndef CYTHON_ASSUME_SAFE_MACROS - #define CYTHON_ASSUME_SAFE_MACROS 1 - #endif - #ifndef CYTHON_ASSUME_SAFE_SIZE - #define CYTHON_ASSUME_SAFE_SIZE 1 - #endif - #ifndef CYTHON_UNPACK_METHODS - #define CYTHON_UNPACK_METHODS 1 - #endif - #ifndef CYTHON_FAST_THREAD_STATE - #define CYTHON_FAST_THREAD_STATE 1 - #endif - #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING - #undef CYTHON_FAST_GIL - #define CYTHON_FAST_GIL 0 - #elif !defined(CYTHON_FAST_GIL) - #define CYTHON_FAST_GIL (PY_VERSION_HEX < 0x030C00A6) - #endif - #ifndef CYTHON_METH_FASTCALL - #define CYTHON_METH_FASTCALL 1 - #endif - #ifndef CYTHON_FAST_PYCALL - #define CYTHON_FAST_PYCALL 1 - #endif - #ifndef CYTHON_PEP487_INIT_SUBCLASS - #define CYTHON_PEP487_INIT_SUBCLASS 1 - #endif - #ifndef CYTHON_PEP489_MULTI_PHASE_INIT - #define CYTHON_PEP489_MULTI_PHASE_INIT 1 - #endif - #ifndef CYTHON_USE_MODULE_STATE - #define CYTHON_USE_MODULE_STATE 0 - #endif - #ifndef CYTHON_USE_SYS_MONITORING - #define CYTHON_USE_SYS_MONITORING (PY_VERSION_HEX >= 0x030d00B1) - #endif - #ifndef CYTHON_USE_TP_FINALIZE - #define CYTHON_USE_TP_FINALIZE 1 - #endif - #ifndef CYTHON_USE_AM_SEND - #define CYTHON_USE_AM_SEND 1 - #endif - #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING - #undef CYTHON_USE_DICT_VERSIONS - #define CYTHON_USE_DICT_VERSIONS 0 - #elif !defined(CYTHON_USE_DICT_VERSIONS) - #define CYTHON_USE_DICT_VERSIONS (PY_VERSION_HEX < 0x030C00A5 && !CYTHON_USE_MODULE_STATE) - #endif - #ifndef CYTHON_USE_EXC_INFO_STACK - #define CYTHON_USE_EXC_INFO_STACK 1 - #endif - #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC - #define CYTHON_UPDATE_DESCRIPTOR_DOC 1 - #endif - #ifndef CYTHON_USE_FREELISTS - #define CYTHON_USE_FREELISTS (!CYTHON_COMPILING_IN_CPYTHON_FREETHREADING) - #endif -#endif -#ifndef CYTHON_FAST_PYCCALL -#define CYTHON_FAST_PYCCALL CYTHON_FAST_PYCALL -#endif -#ifndef CYTHON_VECTORCALL -#if CYTHON_COMPILING_IN_LIMITED_API -#define CYTHON_VECTORCALL (__PYX_LIMITED_VERSION_HEX >= 0x030C0000) -#else -#define CYTHON_VECTORCALL (CYTHON_FAST_PYCCALL && PY_VERSION_HEX >= 0x030800B1) -#endif -#endif -#define CYTHON_BACKPORT_VECTORCALL (CYTHON_METH_FASTCALL && PY_VERSION_HEX < 0x030800B1) -#if CYTHON_USE_PYLONG_INTERNALS - #undef SHIFT - #undef BASE - #undef MASK - #ifdef SIZEOF_VOID_P - enum { __pyx_check_sizeof_voidp = 1 / (int)(SIZEOF_VOID_P == sizeof(void*)) }; - #endif -#endif -#ifndef CYTHON_LOCK_AND_GIL_DEADLOCK_AVOIDANCE_TIME - #define CYTHON_LOCK_AND_GIL_DEADLOCK_AVOIDANCE_TIME 100 -#endif -#ifndef __has_attribute - #define __has_attribute(x) 0 -#endif -#ifndef __has_cpp_attribute - #define __has_cpp_attribute(x) 0 -#endif -#ifndef CYTHON_RESTRICT - #if defined(__GNUC__) - #define CYTHON_RESTRICT __restrict__ - #elif defined(_MSC_VER) && _MSC_VER >= 1400 - #define CYTHON_RESTRICT __restrict - #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L - #define CYTHON_RESTRICT restrict - #else - #define CYTHON_RESTRICT - #endif -#endif -#ifndef CYTHON_UNUSED - #if defined(__cplusplus) - /* for clang __has_cpp_attribute(maybe_unused) is true even before C++17 - * but leads to warnings with -pedantic, since it is a C++17 feature */ - #if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L) - #if __has_cpp_attribute(maybe_unused) - #define CYTHON_UNUSED [[maybe_unused]] - #endif - #endif - #endif -#endif -#ifndef CYTHON_UNUSED -# if defined(__GNUC__) -# if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) -# define CYTHON_UNUSED __attribute__ ((__unused__)) -# else -# define CYTHON_UNUSED -# endif -# elif defined(__ICC) || (defined(__INTEL_COMPILER) && !defined(_MSC_VER)) -# define CYTHON_UNUSED __attribute__ ((__unused__)) -# else -# define CYTHON_UNUSED -# endif -#endif -#ifndef CYTHON_UNUSED_VAR -# if defined(__cplusplus) - template void CYTHON_UNUSED_VAR( const T& ) { } -# else -# define CYTHON_UNUSED_VAR(x) (void)(x) -# endif -#endif -#ifndef CYTHON_MAYBE_UNUSED_VAR - #define CYTHON_MAYBE_UNUSED_VAR(x) CYTHON_UNUSED_VAR(x) -#endif -#ifndef CYTHON_NCP_UNUSED -# if CYTHON_COMPILING_IN_CPYTHON && !CYTHON_COMPILING_IN_CPYTHON_FREETHREADING -# define CYTHON_NCP_UNUSED -# else -# define CYTHON_NCP_UNUSED CYTHON_UNUSED -# endif -#endif -#ifndef CYTHON_USE_CPP_STD_MOVE - #if defined(__cplusplus) && (\ - __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1600)) - #define CYTHON_USE_CPP_STD_MOVE 1 - #else - #define CYTHON_USE_CPP_STD_MOVE 0 - #endif -#endif -#define __Pyx_void_to_None(void_result) ((void)(void_result), Py_INCREF(Py_None), Py_None) -#ifdef _MSC_VER - #ifndef _MSC_STDINT_H_ - #if _MSC_VER < 1300 - typedef unsigned char uint8_t; - typedef unsigned short uint16_t; - typedef unsigned int uint32_t; - #else - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; - #endif - #endif - #if _MSC_VER < 1300 - #ifdef _WIN64 - typedef unsigned long long __pyx_uintptr_t; - #else - typedef unsigned int __pyx_uintptr_t; - #endif - #else - #ifdef _WIN64 - typedef unsigned __int64 __pyx_uintptr_t; - #else - typedef unsigned __int32 __pyx_uintptr_t; - #endif - #endif -#else - #include - typedef uintptr_t __pyx_uintptr_t; -#endif -#ifndef CYTHON_FALLTHROUGH - #if defined(__cplusplus) - /* for clang __has_cpp_attribute(fallthrough) is true even before C++17 - * but leads to warnings with -pedantic, since it is a C++17 feature */ - #if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L) - #if __has_cpp_attribute(fallthrough) - #define CYTHON_FALLTHROUGH [[fallthrough]] - #endif - #endif - #ifndef CYTHON_FALLTHROUGH - #if __has_cpp_attribute(clang::fallthrough) - #define CYTHON_FALLTHROUGH [[clang::fallthrough]] - #elif __has_cpp_attribute(gnu::fallthrough) - #define CYTHON_FALLTHROUGH [[gnu::fallthrough]] - #endif - #endif - #endif - #ifndef CYTHON_FALLTHROUGH - #if __has_attribute(fallthrough) - #define CYTHON_FALLTHROUGH __attribute__((fallthrough)) - #else - #define CYTHON_FALLTHROUGH - #endif - #endif - #if defined(__clang__) && defined(__apple_build_version__) - #if __apple_build_version__ < 7000000 - #undef CYTHON_FALLTHROUGH - #define CYTHON_FALLTHROUGH - #endif - #endif -#endif -#ifndef Py_UNREACHABLE - #define Py_UNREACHABLE() assert(0); abort() -#endif -#ifdef __cplusplus - template - struct __PYX_IS_UNSIGNED_IMPL {static const bool value = T(0) < T(-1);}; - #define __PYX_IS_UNSIGNED(type) (__PYX_IS_UNSIGNED_IMPL::value) -#else - #define __PYX_IS_UNSIGNED(type) (((type)-1) > 0) -#endif -#if CYTHON_COMPILING_IN_PYPY == 1 - #define __PYX_NEED_TP_PRINT_SLOT (PY_VERSION_HEX >= 0x030800b4 && PY_VERSION_HEX < 0x030A0000) -#else - #define __PYX_NEED_TP_PRINT_SLOT (PY_VERSION_HEX >= 0x030800b4 && PY_VERSION_HEX < 0x03090000) -#endif -#define __PYX_REINTERPRET_FUNCION(func_pointer, other_pointer) ((func_pointer)(void(*)(void))(other_pointer)) - -/* CInitCode */ -#ifndef CYTHON_INLINE - #if defined(__clang__) - #define CYTHON_INLINE __inline__ __attribute__ ((__unused__)) - #elif defined(__GNUC__) - #define CYTHON_INLINE __inline__ - #elif defined(_MSC_VER) - #define CYTHON_INLINE __inline - #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L - #define CYTHON_INLINE inline - #else - #define CYTHON_INLINE - #endif -#endif - -/* PythonCompatibility */ -#define __PYX_BUILD_PY_SSIZE_T "n" -#define CYTHON_FORMAT_SSIZE_T "z" -#define __Pyx_BUILTIN_MODULE_NAME "builtins" -#define __Pyx_DefaultClassType PyType_Type -#if CYTHON_COMPILING_IN_LIMITED_API - #ifndef CO_OPTIMIZED - static int CO_OPTIMIZED; - #endif - #ifndef CO_NEWLOCALS - static int CO_NEWLOCALS; - #endif - #ifndef CO_VARARGS - static int CO_VARARGS; - #endif - #ifndef CO_VARKEYWORDS - static int CO_VARKEYWORDS; - #endif - #ifndef CO_ASYNC_GENERATOR - static int CO_ASYNC_GENERATOR; - #endif - #ifndef CO_GENERATOR - static int CO_GENERATOR; - #endif - #ifndef CO_COROUTINE - static int CO_COROUTINE; - #endif -#else - #ifndef CO_COROUTINE - #define CO_COROUTINE 0x80 - #endif - #ifndef CO_ASYNC_GENERATOR - #define CO_ASYNC_GENERATOR 0x200 - #endif -#endif -static int __Pyx_init_co_variables(void); -#if PY_VERSION_HEX >= 0x030900A4 || defined(Py_IS_TYPE) - #define __Pyx_IS_TYPE(ob, type) Py_IS_TYPE(ob, type) -#else - #define __Pyx_IS_TYPE(ob, type) (((const PyObject*)ob)->ob_type == (type)) -#endif -#if PY_VERSION_HEX >= 0x030A00B1 || defined(Py_Is) - #define __Pyx_Py_Is(x, y) Py_Is(x, y) -#else - #define __Pyx_Py_Is(x, y) ((x) == (y)) -#endif -#if PY_VERSION_HEX >= 0x030A00B1 || defined(Py_IsNone) - #define __Pyx_Py_IsNone(ob) Py_IsNone(ob) -#else - #define __Pyx_Py_IsNone(ob) __Pyx_Py_Is((ob), Py_None) -#endif -#if PY_VERSION_HEX >= 0x030A00B1 || defined(Py_IsTrue) - #define __Pyx_Py_IsTrue(ob) Py_IsTrue(ob) -#else - #define __Pyx_Py_IsTrue(ob) __Pyx_Py_Is((ob), Py_True) -#endif -#if PY_VERSION_HEX >= 0x030A00B1 || defined(Py_IsFalse) - #define __Pyx_Py_IsFalse(ob) Py_IsFalse(ob) -#else - #define __Pyx_Py_IsFalse(ob) __Pyx_Py_Is((ob), Py_False) -#endif -#define __Pyx_NoneAsNull(obj) (__Pyx_Py_IsNone(obj) ? NULL : (obj)) -#if PY_VERSION_HEX >= 0x030900F0 && !CYTHON_COMPILING_IN_PYPY - #define __Pyx_PyObject_GC_IsFinalized(o) PyObject_GC_IsFinalized(o) -#else - #define __Pyx_PyObject_GC_IsFinalized(o) _PyGC_FINALIZED(o) -#endif -#ifndef Py_TPFLAGS_CHECKTYPES - #define Py_TPFLAGS_CHECKTYPES 0 -#endif -#ifndef Py_TPFLAGS_HAVE_INDEX - #define Py_TPFLAGS_HAVE_INDEX 0 -#endif -#ifndef Py_TPFLAGS_HAVE_NEWBUFFER - #define Py_TPFLAGS_HAVE_NEWBUFFER 0 -#endif -#ifndef Py_TPFLAGS_HAVE_FINALIZE - #define Py_TPFLAGS_HAVE_FINALIZE 0 -#endif -#ifndef Py_TPFLAGS_SEQUENCE - #define Py_TPFLAGS_SEQUENCE 0 -#endif -#ifndef Py_TPFLAGS_MAPPING - #define Py_TPFLAGS_MAPPING 0 -#endif -#ifndef METH_STACKLESS - #define METH_STACKLESS 0 -#endif -#ifndef METH_FASTCALL - #ifndef METH_FASTCALL - #define METH_FASTCALL 0x80 - #endif - typedef PyObject *(*__Pyx_PyCFunctionFast) (PyObject *self, PyObject *const *args, Py_ssize_t nargs); - typedef PyObject *(*__Pyx_PyCFunctionFastWithKeywords) (PyObject *self, PyObject *const *args, - Py_ssize_t nargs, PyObject *kwnames); -#else - #if PY_VERSION_HEX >= 0x030d00A4 - # define __Pyx_PyCFunctionFast PyCFunctionFast - # define __Pyx_PyCFunctionFastWithKeywords PyCFunctionFastWithKeywords - #else - # define __Pyx_PyCFunctionFast _PyCFunctionFast - # define __Pyx_PyCFunctionFastWithKeywords _PyCFunctionFastWithKeywords - #endif -#endif -#if CYTHON_METH_FASTCALL - #define __Pyx_METH_FASTCALL METH_FASTCALL - #define __Pyx_PyCFunction_FastCall __Pyx_PyCFunctionFast - #define __Pyx_PyCFunction_FastCallWithKeywords __Pyx_PyCFunctionFastWithKeywords -#else - #define __Pyx_METH_FASTCALL METH_VARARGS - #define __Pyx_PyCFunction_FastCall PyCFunction - #define __Pyx_PyCFunction_FastCallWithKeywords PyCFunctionWithKeywords -#endif -#if CYTHON_VECTORCALL - #define __pyx_vectorcallfunc vectorcallfunc - #define __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET PY_VECTORCALL_ARGUMENTS_OFFSET - #define __Pyx_PyVectorcall_NARGS(n) PyVectorcall_NARGS((size_t)(n)) -#elif CYTHON_BACKPORT_VECTORCALL - typedef PyObject *(*__pyx_vectorcallfunc)(PyObject *callable, PyObject *const *args, - size_t nargsf, PyObject *kwnames); - #define __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET ((size_t)1 << (8 * sizeof(size_t) - 1)) - #define __Pyx_PyVectorcall_NARGS(n) ((Py_ssize_t)(((size_t)(n)) & ~__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET)) -#else - #define __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET 0 - #define __Pyx_PyVectorcall_NARGS(n) ((Py_ssize_t)(n)) -#endif -#if PY_VERSION_HEX >= 0x030900B1 -#define __Pyx_PyCFunction_CheckExact(func) PyCFunction_CheckExact(func) -#else -#define __Pyx_PyCFunction_CheckExact(func) PyCFunction_Check(func) -#endif -#define __Pyx_CyOrPyCFunction_Check(func) PyCFunction_Check(func) -#if CYTHON_COMPILING_IN_CPYTHON -#define __Pyx_CyOrPyCFunction_GET_FUNCTION(func) (((PyCFunctionObject*)(func))->m_ml->ml_meth) -#elif !CYTHON_COMPILING_IN_LIMITED_API -#define __Pyx_CyOrPyCFunction_GET_FUNCTION(func) PyCFunction_GET_FUNCTION(func) -#endif -#if CYTHON_COMPILING_IN_CPYTHON -#define __Pyx_CyOrPyCFunction_GET_FLAGS(func) (((PyCFunctionObject*)(func))->m_ml->ml_flags) -static CYTHON_INLINE PyObject* __Pyx_CyOrPyCFunction_GET_SELF(PyObject *func) { - return (__Pyx_CyOrPyCFunction_GET_FLAGS(func) & METH_STATIC) ? NULL : ((PyCFunctionObject*)func)->m_self; -} -#endif -static CYTHON_INLINE int __Pyx__IsSameCFunction(PyObject *func, void (*cfunc)(void)) { -#if CYTHON_COMPILING_IN_LIMITED_API - return PyCFunction_Check(func) && PyCFunction_GetFunction(func) == (PyCFunction) cfunc; -#else - return PyCFunction_Check(func) && PyCFunction_GET_FUNCTION(func) == (PyCFunction) cfunc; -#endif -} -#define __Pyx_IsSameCFunction(func, cfunc) __Pyx__IsSameCFunction(func, cfunc) -#if __PYX_LIMITED_VERSION_HEX < 0x03090000 - #define __Pyx_PyType_FromModuleAndSpec(m, s, b) ((void)m, PyType_FromSpecWithBases(s, b)) - typedef PyObject *(*__Pyx_PyCMethod)(PyObject *, PyTypeObject *, PyObject *const *, size_t, PyObject *); -#else - #define __Pyx_PyType_FromModuleAndSpec(m, s, b) PyType_FromModuleAndSpec(m, s, b) - #define __Pyx_PyCMethod PyCMethod -#endif -#ifndef METH_METHOD - #define METH_METHOD 0x200 -#endif -#if CYTHON_COMPILING_IN_PYPY && !defined(PyObject_Malloc) - #define PyObject_Malloc(s) PyMem_Malloc(s) - #define PyObject_Free(p) PyMem_Free(p) - #define PyObject_Realloc(p) PyMem_Realloc(p) -#endif -#if CYTHON_COMPILING_IN_LIMITED_API - #define __Pyx_PyFrame_SetLineNumber(frame, lineno) -#elif CYTHON_COMPILING_IN_GRAAL - #define __Pyx_PyCode_HasFreeVars(co) (PyCode_GetNumFree(co) > 0) - #define __Pyx_PyFrame_SetLineNumber(frame, lineno) _PyFrame_SetLineNumber((frame), (lineno)) -#else - #define __Pyx_PyCode_HasFreeVars(co) (PyCode_GetNumFree(co) > 0) - #define __Pyx_PyFrame_SetLineNumber(frame, lineno) (frame)->f_lineno = (lineno) -#endif -#if CYTHON_COMPILING_IN_LIMITED_API - #define __Pyx_PyThreadState_Current PyThreadState_Get() -#elif !CYTHON_FAST_THREAD_STATE - #define __Pyx_PyThreadState_Current PyThreadState_GET() -#elif PY_VERSION_HEX >= 0x030d00A1 - #define __Pyx_PyThreadState_Current PyThreadState_GetUnchecked() -#else - #define __Pyx_PyThreadState_Current _PyThreadState_UncheckedGet() -#endif -#if CYTHON_USE_MODULE_STATE -static CYTHON_INLINE void *__Pyx__PyModule_GetState(PyObject *op) -{ - void *result; - result = PyModule_GetState(op); - if (!result) - Py_FatalError("Couldn't find the module state"); - return result; -} -#define __Pyx_PyModule_GetState(o) (__pyx_mstatetype *)__Pyx__PyModule_GetState(o) -#else -#define __Pyx_PyModule_GetState(op) ((void)op,__pyx_mstate_global) -#endif -#define __Pyx_PyObject_GetSlot(obj, name, func_ctype) __Pyx_PyType_GetSlot(Py_TYPE((PyObject *) obj), name, func_ctype) -#define __Pyx_PyObject_TryGetSlot(obj, name, func_ctype) __Pyx_PyType_TryGetSlot(Py_TYPE(obj), name, func_ctype) -#define __Pyx_PyObject_GetSubSlot(obj, sub, name, func_ctype) __Pyx_PyType_GetSubSlot(Py_TYPE(obj), sub, name, func_ctype) -#define __Pyx_PyObject_TryGetSubSlot(obj, sub, name, func_ctype) __Pyx_PyType_TryGetSubSlot(Py_TYPE(obj), sub, name, func_ctype) -#if CYTHON_USE_TYPE_SLOTS - #define __Pyx_PyType_GetSlot(type, name, func_ctype) ((type)->name) - #define __Pyx_PyType_TryGetSlot(type, name, func_ctype) __Pyx_PyType_GetSlot(type, name, func_ctype) - #define __Pyx_PyType_GetSubSlot(type, sub, name, func_ctype) (((type)->sub) ? ((type)->sub->name) : NULL) - #define __Pyx_PyType_TryGetSubSlot(type, sub, name, func_ctype) __Pyx_PyType_GetSubSlot(type, sub, name, func_ctype) -#else - #define __Pyx_PyType_GetSlot(type, name, func_ctype) ((func_ctype) PyType_GetSlot((type), Py_##name)) - #define __Pyx_PyType_TryGetSlot(type, name, func_ctype)\ - ((__PYX_LIMITED_VERSION_HEX >= 0x030A0000 ||\ - (PyType_GetFlags(type) & Py_TPFLAGS_HEAPTYPE) || __Pyx_get_runtime_version() >= 0x030A0000) ?\ - __Pyx_PyType_GetSlot(type, name, func_ctype) : NULL) - #define __Pyx_PyType_GetSubSlot(obj, sub, name, func_ctype) __Pyx_PyType_GetSlot(obj, name, func_ctype) - #define __Pyx_PyType_TryGetSubSlot(obj, sub, name, func_ctype) __Pyx_PyType_TryGetSlot(obj, name, func_ctype) -#endif -#if CYTHON_COMPILING_IN_CPYTHON || defined(_PyDict_NewPresized) -#define __Pyx_PyDict_NewPresized(n) ((n <= 8) ? PyDict_New() : _PyDict_NewPresized(n)) -#else -#define __Pyx_PyDict_NewPresized(n) PyDict_New() -#endif -#define __Pyx_PyNumber_Divide(x,y) PyNumber_TrueDivide(x,y) -#define __Pyx_PyNumber_InPlaceDivide(x,y) PyNumber_InPlaceTrueDivide(x,y) -#if CYTHON_COMPILING_IN_CPYTHON && CYTHON_USE_UNICODE_INTERNALS -#define __Pyx_PyDict_GetItemStrWithError(dict, name) _PyDict_GetItem_KnownHash(dict, name, ((PyASCIIObject *) name)->hash) -static CYTHON_INLINE PyObject * __Pyx_PyDict_GetItemStr(PyObject *dict, PyObject *name) { - PyObject *res = __Pyx_PyDict_GetItemStrWithError(dict, name); - if (res == NULL) PyErr_Clear(); - return res; -} -#elif !CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07020000 -#define __Pyx_PyDict_GetItemStrWithError PyDict_GetItemWithError -#define __Pyx_PyDict_GetItemStr PyDict_GetItem -#else -static CYTHON_INLINE PyObject * __Pyx_PyDict_GetItemStrWithError(PyObject *dict, PyObject *name) { -#if CYTHON_COMPILING_IN_PYPY - return PyDict_GetItem(dict, name); -#else - PyDictEntry *ep; - PyDictObject *mp = (PyDictObject*) dict; - long hash = ((PyStringObject *) name)->ob_shash; - assert(hash != -1); - ep = (mp->ma_lookup)(mp, name, hash); - if (ep == NULL) { - return NULL; - } - return ep->me_value; -#endif -} -#define __Pyx_PyDict_GetItemStr PyDict_GetItem -#endif -#if CYTHON_USE_TYPE_SLOTS - #define __Pyx_PyType_GetFlags(tp) (((PyTypeObject *)tp)->tp_flags) - #define __Pyx_PyType_HasFeature(type, feature) ((__Pyx_PyType_GetFlags(type) & (feature)) != 0) -#else - #define __Pyx_PyType_GetFlags(tp) (PyType_GetFlags((PyTypeObject *)tp)) - #define __Pyx_PyType_HasFeature(type, feature) PyType_HasFeature(type, feature) -#endif -#define __Pyx_PyObject_GetIterNextFunc(iterator) __Pyx_PyObject_GetSlot(iterator, tp_iternext, iternextfunc) -#if CYTHON_USE_TYPE_SPECS && PY_VERSION_HEX >= 0x03080000 -#define __Pyx_PyHeapTypeObject_GC_Del(obj) {\ - PyTypeObject *type = Py_TYPE((PyObject*)obj);\ - assert(__Pyx_PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE));\ - PyObject_GC_Del(obj);\ - Py_DECREF(type);\ -} -#else -#define __Pyx_PyHeapTypeObject_GC_Del(obj) PyObject_GC_Del(obj) -#endif -#if CYTHON_COMPILING_IN_LIMITED_API - #define __Pyx_PyUnicode_READY(op) (0) - #define __Pyx_PyUnicode_READ_CHAR(u, i) PyUnicode_ReadChar(u, i) - #define __Pyx_PyUnicode_MAX_CHAR_VALUE(u) ((void)u, 1114111U) - #define __Pyx_PyUnicode_KIND(u) ((void)u, (0)) - #define __Pyx_PyUnicode_DATA(u) ((void*)u) - #define __Pyx_PyUnicode_READ(k, d, i) ((void)k, PyUnicode_ReadChar((PyObject*)(d), i)) - #define __Pyx_PyUnicode_IS_TRUE(u) (0 != PyUnicode_GetLength(u)) -#else - #if PY_VERSION_HEX >= 0x030C0000 - #define __Pyx_PyUnicode_READY(op) (0) - #else - #define __Pyx_PyUnicode_READY(op) (likely(PyUnicode_IS_READY(op)) ?\ - 0 : _PyUnicode_Ready((PyObject *)(op))) - #endif - #define __Pyx_PyUnicode_READ_CHAR(u, i) PyUnicode_READ_CHAR(u, i) - #define __Pyx_PyUnicode_MAX_CHAR_VALUE(u) PyUnicode_MAX_CHAR_VALUE(u) - #define __Pyx_PyUnicode_KIND(u) ((int)PyUnicode_KIND(u)) - #define __Pyx_PyUnicode_DATA(u) PyUnicode_DATA(u) - #define __Pyx_PyUnicode_READ(k, d, i) PyUnicode_READ(k, d, i) - #define __Pyx_PyUnicode_WRITE(k, d, i, ch) PyUnicode_WRITE(k, d, i, (Py_UCS4) ch) - #if PY_VERSION_HEX >= 0x030C0000 - #define __Pyx_PyUnicode_IS_TRUE(u) (0 != PyUnicode_GET_LENGTH(u)) - #else - #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x03090000 - #define __Pyx_PyUnicode_IS_TRUE(u) (0 != (likely(PyUnicode_IS_READY(u)) ? PyUnicode_GET_LENGTH(u) : ((PyCompactUnicodeObject *)(u))->wstr_length)) - #else - #define __Pyx_PyUnicode_IS_TRUE(u) (0 != (likely(PyUnicode_IS_READY(u)) ? PyUnicode_GET_LENGTH(u) : PyUnicode_GET_SIZE(u))) - #endif - #endif -#endif -#if CYTHON_COMPILING_IN_PYPY - #define __Pyx_PyUnicode_Concat(a, b) PyNumber_Add(a, b) - #define __Pyx_PyUnicode_ConcatSafe(a, b) PyNumber_Add(a, b) -#else - #define __Pyx_PyUnicode_Concat(a, b) PyUnicode_Concat(a, b) - #define __Pyx_PyUnicode_ConcatSafe(a, b) ((unlikely((a) == Py_None) || unlikely((b) == Py_None)) ?\ - PyNumber_Add(a, b) : __Pyx_PyUnicode_Concat(a, b)) -#endif -#if CYTHON_COMPILING_IN_PYPY - #if !defined(PyUnicode_DecodeUnicodeEscape) - #define PyUnicode_DecodeUnicodeEscape(s, size, errors) PyUnicode_Decode(s, size, "unicode_escape", errors) - #endif - #if !defined(PyUnicode_Contains) - #define PyUnicode_Contains(u, s) PySequence_Contains(u, s) - #endif - #if !defined(PyByteArray_Check) - #define PyByteArray_Check(obj) PyObject_TypeCheck(obj, &PyByteArray_Type) - #endif - #if !defined(PyObject_Format) - #define PyObject_Format(obj, fmt) PyObject_CallMethod(obj, "__format__", "O", fmt) - #endif -#endif -#define __Pyx_PyUnicode_FormatSafe(a, b) ((unlikely((a) == Py_None || (PyUnicode_Check(b) && !PyUnicode_CheckExact(b)))) ? PyNumber_Remainder(a, b) : PyUnicode_Format(a, b)) -#if CYTHON_COMPILING_IN_CPYTHON - #define __Pyx_PySequence_ListKeepNew(obj)\ - (likely(PyList_CheckExact(obj) && Py_REFCNT(obj) == 1) ? __Pyx_NewRef(obj) : PySequence_List(obj)) -#else - #define __Pyx_PySequence_ListKeepNew(obj) PySequence_List(obj) -#endif -#ifndef PySet_CheckExact - #define PySet_CheckExact(obj) __Pyx_IS_TYPE(obj, &PySet_Type) -#endif -#if PY_VERSION_HEX >= 0x030900A4 - #define __Pyx_SET_REFCNT(obj, refcnt) Py_SET_REFCNT(obj, refcnt) - #define __Pyx_SET_SIZE(obj, size) Py_SET_SIZE(obj, size) -#else - #define __Pyx_SET_REFCNT(obj, refcnt) Py_REFCNT(obj) = (refcnt) - #define __Pyx_SET_SIZE(obj, size) Py_SIZE(obj) = (size) -#endif -#if CYTHON_AVOID_BORROWED_REFS || CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS - #if __PYX_LIMITED_VERSION_HEX >= 0x030d0000 - #define __Pyx_PyList_GetItemRef(o, i) PyList_GetItemRef(o, i) - #elif CYTHON_COMPILING_IN_LIMITED_API || !CYTHON_ASSUME_SAFE_MACROS - #define __Pyx_PyList_GetItemRef(o, i) (likely((i) >= 0) ? PySequence_GetItem(o, i) : (PyErr_SetString(PyExc_IndexError, "list index out of range"), (PyObject*)NULL)) - #else - #define __Pyx_PyList_GetItemRef(o, i) PySequence_ITEM(o, i) - #endif -#elif CYTHON_COMPILING_IN_LIMITED_API || !CYTHON_ASSUME_SAFE_MACROS - #if __PYX_LIMITED_VERSION_HEX >= 0x030d0000 - #define __Pyx_PyList_GetItemRef(o, i) PyList_GetItemRef(o, i) - #else - #define __Pyx_PyList_GetItemRef(o, i) __Pyx_XNewRef(PyList_GetItem(o, i)) - #endif -#else - #define __Pyx_PyList_GetItemRef(o, i) __Pyx_NewRef(PyList_GET_ITEM(o, i)) -#endif -#if __PYX_LIMITED_VERSION_HEX >= 0x030d0000 -#define __Pyx_PyDict_GetItemRef(dict, key, result) PyDict_GetItemRef(dict, key, result) -#elif CYTHON_AVOID_BORROWED_REFS || CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS -static CYTHON_INLINE int __Pyx_PyDict_GetItemRef(PyObject *dict, PyObject *key, PyObject **result) { - *result = PyObject_GetItem(dict, key); - if (*result == NULL) { - if (PyErr_ExceptionMatches(PyExc_KeyError)) { - PyErr_Clear(); - return 0; - } - return -1; - } - return 1; -} -#else -static CYTHON_INLINE int __Pyx_PyDict_GetItemRef(PyObject *dict, PyObject *key, PyObject **result) { - *result = PyDict_GetItemWithError(dict, key); - if (*result == NULL) { - return PyErr_Occurred() ? -1 : 0; - } - Py_INCREF(*result); - return 1; -} -#endif -#if defined(CYTHON_DEBUG_VISIT_CONST) && CYTHON_DEBUG_VISIT_CONST - #define __Pyx_VISIT_CONST(obj) Py_VISIT(obj) -#else - #define __Pyx_VISIT_CONST(obj) -#endif -#if CYTHON_ASSUME_SAFE_MACROS - #define __Pyx_PySequence_ITEM(o, i) PySequence_ITEM(o, i) - #define __Pyx_PySequence_SIZE(seq) Py_SIZE(seq) - #define __Pyx_PyTuple_SET_ITEM(o, i, v) (PyTuple_SET_ITEM(o, i, v), (0)) - #define __Pyx_PyTuple_GET_ITEM(o, i) PyTuple_GET_ITEM(o, i) - #define __Pyx_PyList_SET_ITEM(o, i, v) (PyList_SET_ITEM(o, i, v), (0)) - #define __Pyx_PyList_GET_ITEM(o, i) PyList_GET_ITEM(o, i) -#else - #define __Pyx_PySequence_ITEM(o, i) PySequence_GetItem(o, i) - #define __Pyx_PySequence_SIZE(seq) PySequence_Size(seq) - #define __Pyx_PyTuple_SET_ITEM(o, i, v) PyTuple_SetItem(o, i, v) - #define __Pyx_PyTuple_GET_ITEM(o, i) PyTuple_GetItem(o, i) - #define __Pyx_PyList_SET_ITEM(o, i, v) PyList_SetItem(o, i, v) - #define __Pyx_PyList_GET_ITEM(o, i) PyList_GetItem(o, i) -#endif -#if CYTHON_ASSUME_SAFE_SIZE - #define __Pyx_PyTuple_GET_SIZE(o) PyTuple_GET_SIZE(o) - #define __Pyx_PyList_GET_SIZE(o) PyList_GET_SIZE(o) - #define __Pyx_PySet_GET_SIZE(o) PySet_GET_SIZE(o) - #define __Pyx_PyBytes_GET_SIZE(o) PyBytes_GET_SIZE(o) - #define __Pyx_PyByteArray_GET_SIZE(o) PyByteArray_GET_SIZE(o) - #define __Pyx_PyUnicode_GET_LENGTH(o) PyUnicode_GET_LENGTH(o) -#else - #define __Pyx_PyTuple_GET_SIZE(o) PyTuple_Size(o) - #define __Pyx_PyList_GET_SIZE(o) PyList_Size(o) - #define __Pyx_PySet_GET_SIZE(o) PySet_Size(o) - #define __Pyx_PyBytes_GET_SIZE(o) PyBytes_Size(o) - #define __Pyx_PyByteArray_GET_SIZE(o) PyByteArray_Size(o) - #define __Pyx_PyUnicode_GET_LENGTH(o) PyUnicode_GetLength(o) -#endif -#if __PYX_LIMITED_VERSION_HEX >= 0x030d0000 - #define __Pyx_PyImport_AddModuleRef(name) PyImport_AddModuleRef(name) -#else - static CYTHON_INLINE PyObject *__Pyx_PyImport_AddModuleRef(const char *name) { - PyObject *module = PyImport_AddModule(name); - Py_XINCREF(module); - return module; - } -#endif -#if CYTHON_COMPILING_IN_PYPY && !defined(PyUnicode_InternFromString) - #define PyUnicode_InternFromString(s) PyUnicode_FromString(s) -#endif -#define __Pyx_PyLong_FromHash_t PyLong_FromSsize_t -#define __Pyx_PyLong_AsHash_t __Pyx_PyIndex_AsSsize_t -#if __PYX_LIMITED_VERSION_HEX >= 0x030A0000 - #define __Pyx_PySendResult PySendResult -#else - typedef enum { - PYGEN_RETURN = 0, - PYGEN_ERROR = -1, - PYGEN_NEXT = 1, - } __Pyx_PySendResult; -#endif -#if CYTHON_COMPILING_IN_LIMITED_API || PY_VERSION_HEX < 0x030A00A3 - typedef __Pyx_PySendResult (*__Pyx_pyiter_sendfunc)(PyObject *iter, PyObject *value, PyObject **result); -#else - #define __Pyx_pyiter_sendfunc sendfunc -#endif -#if !CYTHON_USE_AM_SEND -#define __PYX_HAS_PY_AM_SEND 0 -#elif __PYX_LIMITED_VERSION_HEX >= 0x030A0000 -#define __PYX_HAS_PY_AM_SEND 1 -#else -#define __PYX_HAS_PY_AM_SEND 2 // our own backported implementation -#endif -#if __PYX_HAS_PY_AM_SEND < 2 - #define __Pyx_PyAsyncMethodsStruct PyAsyncMethods -#else - typedef struct { - unaryfunc am_await; - unaryfunc am_aiter; - unaryfunc am_anext; - __Pyx_pyiter_sendfunc am_send; - } __Pyx_PyAsyncMethodsStruct; - #define __Pyx_SlotTpAsAsync(s) ((PyAsyncMethods*)(s)) -#endif -#if CYTHON_USE_AM_SEND && PY_VERSION_HEX < 0x030A00F0 - #define __Pyx_TPFLAGS_HAVE_AM_SEND (1UL << 21) -#else - #define __Pyx_TPFLAGS_HAVE_AM_SEND (0) -#endif -#if PY_VERSION_HEX >= 0x03090000 -#define __Pyx_PyInterpreterState_Get() PyInterpreterState_Get() -#else -#define __Pyx_PyInterpreterState_Get() PyThreadState_Get()->interp -#endif -#if CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x030A0000 -#ifdef __cplusplus -extern "C" -#endif -PyAPI_FUNC(void *) PyMem_Calloc(size_t nelem, size_t elsize); -#endif -#if CYTHON_COMPILING_IN_LIMITED_API -static int __Pyx_init_co_variable(PyObject *inspect, const char* name, int *write_to) { - int value; - PyObject *py_value = PyObject_GetAttrString(inspect, name); - if (!py_value) return 0; - value = (int) PyLong_AsLong(py_value); - Py_DECREF(py_value); - *write_to = value; - return value != -1 || !PyErr_Occurred(); -} -static int __Pyx_init_co_variables(void) { - PyObject *inspect; - int result; - inspect = PyImport_ImportModule("inspect"); - result = -#if !defined(CO_OPTIMIZED) - __Pyx_init_co_variable(inspect, "CO_OPTIMIZED", &CO_OPTIMIZED) && -#endif -#if !defined(CO_NEWLOCALS) - __Pyx_init_co_variable(inspect, "CO_NEWLOCALS", &CO_NEWLOCALS) && -#endif -#if !defined(CO_VARARGS) - __Pyx_init_co_variable(inspect, "CO_VARARGS", &CO_VARARGS) && -#endif -#if !defined(CO_VARKEYWORDS) - __Pyx_init_co_variable(inspect, "CO_VARKEYWORDS", &CO_VARKEYWORDS) && -#endif -#if !defined(CO_ASYNC_GENERATOR) - __Pyx_init_co_variable(inspect, "CO_ASYNC_GENERATOR", &CO_ASYNC_GENERATOR) && -#endif -#if !defined(CO_GENERATOR) - __Pyx_init_co_variable(inspect, "CO_GENERATOR", &CO_GENERATOR) && -#endif -#if !defined(CO_COROUTINE) - __Pyx_init_co_variable(inspect, "CO_COROUTINE", &CO_COROUTINE) && -#endif - 1; - Py_DECREF(inspect); - return result ? 0 : -1; -} -#else -static int __Pyx_init_co_variables(void) { - return 0; // It's a limited API-only feature -} -#endif - -/* MathInitCode */ -#if defined(_WIN32) || defined(WIN32) || defined(MS_WINDOWS) - #ifndef _USE_MATH_DEFINES - #define _USE_MATH_DEFINES - #endif -#endif -#include -#ifdef NAN -#define __PYX_NAN() ((float) NAN) -#else -static CYTHON_INLINE float __PYX_NAN() { - float value; - memset(&value, 0xFF, sizeof(value)); - return value; -} -#endif -#if defined(__CYGWIN__) && defined(_LDBL_EQ_DBL) -#define __Pyx_truncl trunc -#else -#define __Pyx_truncl truncl -#endif - -#ifndef CYTHON_CLINE_IN_TRACEBACK_RUNTIME -#define CYTHON_CLINE_IN_TRACEBACK_RUNTIME 0 -#endif -#ifndef CYTHON_CLINE_IN_TRACEBACK -#define CYTHON_CLINE_IN_TRACEBACK CYTHON_CLINE_IN_TRACEBACK_RUNTIME -#endif -#if CYTHON_CLINE_IN_TRACEBACK -#define __PYX_MARK_ERR_POS(f_index, lineno) { __pyx_filename = __pyx_f[f_index]; (void) __pyx_filename; __pyx_lineno = lineno; (void) __pyx_lineno; __pyx_clineno = __LINE__; (void) __pyx_clineno; } -#else -#define __PYX_MARK_ERR_POS(f_index, lineno) { __pyx_filename = __pyx_f[f_index]; (void) __pyx_filename; __pyx_lineno = lineno; (void) __pyx_lineno; (void) __pyx_clineno; } -#endif -#define __PYX_ERR(f_index, lineno, Ln_error) \ - { __PYX_MARK_ERR_POS(f_index, lineno) goto Ln_error; } - -#ifdef CYTHON_EXTERN_C - #undef __PYX_EXTERN_C - #define __PYX_EXTERN_C CYTHON_EXTERN_C -#elif defined(__PYX_EXTERN_C) - #ifdef _MSC_VER - #pragma message ("Please do not define the '__PYX_EXTERN_C' macro externally. Use 'CYTHON_EXTERN_C' instead.") - #else - #warning Please do not define the '__PYX_EXTERN_C' macro externally. Use 'CYTHON_EXTERN_C' instead. - #endif -#else - #ifdef __cplusplus - #define __PYX_EXTERN_C extern "C" - #else - #define __PYX_EXTERN_C extern - #endif -#endif - -#define __PYX_HAVE__fontTools__cu2qu__cu2qu -#define __PYX_HAVE_API__fontTools__cu2qu__cu2qu -/* Early includes */ -#ifdef _OPENMP -#include -#endif /* _OPENMP */ - -#if defined(PYREX_WITHOUT_ASSERTIONS) && !defined(CYTHON_WITHOUT_ASSERTIONS) -#define CYTHON_WITHOUT_ASSERTIONS -#endif - -#define __PYX_DEFAULT_STRING_ENCODING_IS_ASCII 0 -#define __PYX_DEFAULT_STRING_ENCODING_IS_UTF8 0 -#define __PYX_DEFAULT_STRING_ENCODING "" -#define __Pyx_PyObject_FromString __Pyx_PyBytes_FromString -#define __Pyx_PyObject_FromStringAndSize __Pyx_PyBytes_FromStringAndSize -#define __Pyx_uchar_cast(c) ((unsigned char)c) -#define __Pyx_long_cast(x) ((long)x) -#define __Pyx_fits_Py_ssize_t(v, type, is_signed) (\ - (sizeof(type) < sizeof(Py_ssize_t)) ||\ - (sizeof(type) > sizeof(Py_ssize_t) &&\ - likely(v < (type)PY_SSIZE_T_MAX ||\ - v == (type)PY_SSIZE_T_MAX) &&\ - (!is_signed || likely(v > (type)PY_SSIZE_T_MIN ||\ - v == (type)PY_SSIZE_T_MIN))) ||\ - (sizeof(type) == sizeof(Py_ssize_t) &&\ - (is_signed || likely(v < (type)PY_SSIZE_T_MAX ||\ - v == (type)PY_SSIZE_T_MAX))) ) -static CYTHON_INLINE int __Pyx_is_valid_index(Py_ssize_t i, Py_ssize_t limit) { - return (size_t) i < (size_t) limit; -} -#if defined (__cplusplus) && __cplusplus >= 201103L - #include - #define __Pyx_sst_abs(value) std::abs(value) -#elif SIZEOF_INT >= SIZEOF_SIZE_T - #define __Pyx_sst_abs(value) abs(value) -#elif SIZEOF_LONG >= SIZEOF_SIZE_T - #define __Pyx_sst_abs(value) labs(value) -#elif defined (_MSC_VER) - #define __Pyx_sst_abs(value) ((Py_ssize_t)_abs64(value)) -#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L - #define __Pyx_sst_abs(value) llabs(value) -#elif defined (__GNUC__) - #define __Pyx_sst_abs(value) __builtin_llabs(value) -#else - #define __Pyx_sst_abs(value) ((value<0) ? -value : value) -#endif -static CYTHON_INLINE Py_ssize_t __Pyx_ssize_strlen(const char *s); -static CYTHON_INLINE const char* __Pyx_PyObject_AsString(PyObject*); -static CYTHON_INLINE const char* __Pyx_PyObject_AsStringAndSize(PyObject*, Py_ssize_t* length); -static CYTHON_INLINE PyObject* __Pyx_PyByteArray_FromString(const char*); -#define __Pyx_PyByteArray_FromStringAndSize(s, l) PyByteArray_FromStringAndSize((const char*)s, l) -#define __Pyx_PyBytes_FromString PyBytes_FromString -#define __Pyx_PyBytes_FromStringAndSize PyBytes_FromStringAndSize -static CYTHON_INLINE PyObject* __Pyx_PyUnicode_FromString(const char*); -#if CYTHON_ASSUME_SAFE_MACROS - #define __Pyx_PyBytes_AsWritableString(s) ((char*) PyBytes_AS_STRING(s)) - #define __Pyx_PyBytes_AsWritableSString(s) ((signed char*) PyBytes_AS_STRING(s)) - #define __Pyx_PyBytes_AsWritableUString(s) ((unsigned char*) PyBytes_AS_STRING(s)) - #define __Pyx_PyBytes_AsString(s) ((const char*) PyBytes_AS_STRING(s)) - #define __Pyx_PyBytes_AsSString(s) ((const signed char*) PyBytes_AS_STRING(s)) - #define __Pyx_PyBytes_AsUString(s) ((const unsigned char*) PyBytes_AS_STRING(s)) - #define __Pyx_PyByteArray_AsString(s) PyByteArray_AS_STRING(s) -#else - #define __Pyx_PyBytes_AsWritableString(s) ((char*) PyBytes_AsString(s)) - #define __Pyx_PyBytes_AsWritableSString(s) ((signed char*) PyBytes_AsString(s)) - #define __Pyx_PyBytes_AsWritableUString(s) ((unsigned char*) PyBytes_AsString(s)) - #define __Pyx_PyBytes_AsString(s) ((const char*) PyBytes_AsString(s)) - #define __Pyx_PyBytes_AsSString(s) ((const signed char*) PyBytes_AsString(s)) - #define __Pyx_PyBytes_AsUString(s) ((const unsigned char*) PyBytes_AsString(s)) - #define __Pyx_PyByteArray_AsString(s) PyByteArray_AsString(s) -#endif -#define __Pyx_PyObject_AsWritableString(s) ((char*)(__pyx_uintptr_t) __Pyx_PyObject_AsString(s)) -#define __Pyx_PyObject_AsWritableSString(s) ((signed char*)(__pyx_uintptr_t) __Pyx_PyObject_AsString(s)) -#define __Pyx_PyObject_AsWritableUString(s) ((unsigned char*)(__pyx_uintptr_t) __Pyx_PyObject_AsString(s)) -#define __Pyx_PyObject_AsSString(s) ((const signed char*) __Pyx_PyObject_AsString(s)) -#define __Pyx_PyObject_AsUString(s) ((const unsigned char*) __Pyx_PyObject_AsString(s)) -#define __Pyx_PyObject_FromCString(s) __Pyx_PyObject_FromString((const char*)s) -#define __Pyx_PyBytes_FromCString(s) __Pyx_PyBytes_FromString((const char*)s) -#define __Pyx_PyByteArray_FromCString(s) __Pyx_PyByteArray_FromString((const char*)s) -#define __Pyx_PyUnicode_FromCString(s) __Pyx_PyUnicode_FromString((const char*)s) -#define __Pyx_PyUnicode_FromOrdinal(o) PyUnicode_FromOrdinal((int)o) -#define __Pyx_PyUnicode_AsUnicode PyUnicode_AsUnicode -static CYTHON_INLINE PyObject *__Pyx_NewRef(PyObject *obj) { -#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030a0000 || defined(Py_NewRef) - return Py_NewRef(obj); -#else - Py_INCREF(obj); - return obj; -#endif -} -static CYTHON_INLINE PyObject *__Pyx_XNewRef(PyObject *obj) { -#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030a0000 || defined(Py_XNewRef) - return Py_XNewRef(obj); -#else - Py_XINCREF(obj); - return obj; -#endif -} -static CYTHON_INLINE PyObject *__Pyx_Owned_Py_None(int b); -static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b); -static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject*); -static CYTHON_INLINE int __Pyx_PyObject_IsTrueAndDecref(PyObject*); -static CYTHON_INLINE PyObject* __Pyx_PyNumber_Long(PyObject* x); -#define __Pyx_PySequence_Tuple(obj)\ - (likely(PyTuple_CheckExact(obj)) ? __Pyx_NewRef(obj) : PySequence_Tuple(obj)) -static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject*); -static CYTHON_INLINE PyObject * __Pyx_PyLong_FromSize_t(size_t); -static CYTHON_INLINE Py_hash_t __Pyx_PyIndex_AsHash_t(PyObject*); -#if CYTHON_ASSUME_SAFE_MACROS -#define __Pyx_PyFloat_AsDouble(x) (PyFloat_CheckExact(x) ? PyFloat_AS_DOUBLE(x) : PyFloat_AsDouble(x)) -#define __Pyx_PyFloat_AS_DOUBLE(x) PyFloat_AS_DOUBLE(x) -#else -#define __Pyx_PyFloat_AsDouble(x) PyFloat_AsDouble(x) -#define __Pyx_PyFloat_AS_DOUBLE(x) PyFloat_AsDouble(x) -#endif -#define __Pyx_PyFloat_AsFloat(x) ((float) __Pyx_PyFloat_AsDouble(x)) -#define __Pyx_PyNumber_Int(x) (PyLong_CheckExact(x) ? __Pyx_NewRef(x) : PyNumber_Long(x)) -#if CYTHON_USE_PYLONG_INTERNALS - #if PY_VERSION_HEX >= 0x030C00A7 - #ifndef _PyLong_SIGN_MASK - #define _PyLong_SIGN_MASK 3 - #endif - #ifndef _PyLong_NON_SIZE_BITS - #define _PyLong_NON_SIZE_BITS 3 - #endif - #define __Pyx_PyLong_Sign(x) (((PyLongObject*)x)->long_value.lv_tag & _PyLong_SIGN_MASK) - #define __Pyx_PyLong_IsNeg(x) ((__Pyx_PyLong_Sign(x) & 2) != 0) - #define __Pyx_PyLong_IsNonNeg(x) (!__Pyx_PyLong_IsNeg(x)) - #define __Pyx_PyLong_IsZero(x) (__Pyx_PyLong_Sign(x) & 1) - #define __Pyx_PyLong_IsPos(x) (__Pyx_PyLong_Sign(x) == 0) - #define __Pyx_PyLong_CompactValueUnsigned(x) (__Pyx_PyLong_Digits(x)[0]) - #define __Pyx_PyLong_DigitCount(x) ((Py_ssize_t) (((PyLongObject*)x)->long_value.lv_tag >> _PyLong_NON_SIZE_BITS)) - #define __Pyx_PyLong_SignedDigitCount(x)\ - ((1 - (Py_ssize_t) __Pyx_PyLong_Sign(x)) * __Pyx_PyLong_DigitCount(x)) - #if defined(PyUnstable_Long_IsCompact) && defined(PyUnstable_Long_CompactValue) - #define __Pyx_PyLong_IsCompact(x) PyUnstable_Long_IsCompact((PyLongObject*) x) - #define __Pyx_PyLong_CompactValue(x) PyUnstable_Long_CompactValue((PyLongObject*) x) - #else - #define __Pyx_PyLong_IsCompact(x) (((PyLongObject*)x)->long_value.lv_tag < (2 << _PyLong_NON_SIZE_BITS)) - #define __Pyx_PyLong_CompactValue(x) ((1 - (Py_ssize_t) __Pyx_PyLong_Sign(x)) * (Py_ssize_t) __Pyx_PyLong_Digits(x)[0]) - #endif - typedef Py_ssize_t __Pyx_compact_pylong; - typedef size_t __Pyx_compact_upylong; - #else - #define __Pyx_PyLong_IsNeg(x) (Py_SIZE(x) < 0) - #define __Pyx_PyLong_IsNonNeg(x) (Py_SIZE(x) >= 0) - #define __Pyx_PyLong_IsZero(x) (Py_SIZE(x) == 0) - #define __Pyx_PyLong_IsPos(x) (Py_SIZE(x) > 0) - #define __Pyx_PyLong_CompactValueUnsigned(x) ((Py_SIZE(x) == 0) ? 0 : __Pyx_PyLong_Digits(x)[0]) - #define __Pyx_PyLong_DigitCount(x) __Pyx_sst_abs(Py_SIZE(x)) - #define __Pyx_PyLong_SignedDigitCount(x) Py_SIZE(x) - #define __Pyx_PyLong_IsCompact(x) (Py_SIZE(x) == 0 || Py_SIZE(x) == 1 || Py_SIZE(x) == -1) - #define __Pyx_PyLong_CompactValue(x)\ - ((Py_SIZE(x) == 0) ? (sdigit) 0 : ((Py_SIZE(x) < 0) ? -(sdigit)__Pyx_PyLong_Digits(x)[0] : (sdigit)__Pyx_PyLong_Digits(x)[0])) - typedef sdigit __Pyx_compact_pylong; - typedef digit __Pyx_compact_upylong; - #endif - #if PY_VERSION_HEX >= 0x030C00A5 - #define __Pyx_PyLong_Digits(x) (((PyLongObject*)x)->long_value.ob_digit) - #else - #define __Pyx_PyLong_Digits(x) (((PyLongObject*)x)->ob_digit) - #endif -#endif -#if __PYX_DEFAULT_STRING_ENCODING_IS_UTF8 - #define __Pyx_PyUnicode_FromStringAndSize(c_str, size) PyUnicode_DecodeUTF8(c_str, size, NULL) -#elif __PYX_DEFAULT_STRING_ENCODING_IS_ASCII - #define __Pyx_PyUnicode_FromStringAndSize(c_str, size) PyUnicode_DecodeASCII(c_str, size, NULL) -#else - #define __Pyx_PyUnicode_FromStringAndSize(c_str, size) PyUnicode_Decode(c_str, size, __PYX_DEFAULT_STRING_ENCODING, NULL) -#endif - - -/* Test for GCC > 2.95 */ -#if defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && (__GNUC_MINOR__ > 95))) - #define likely(x) __builtin_expect(!!(x), 1) - #define unlikely(x) __builtin_expect(!!(x), 0) -#else /* !__GNUC__ or GCC < 2.95 */ - #define likely(x) (x) - #define unlikely(x) (x) -#endif /* __GNUC__ */ -/* PretendToInitialize */ -#ifdef __cplusplus -#if __cplusplus > 201103L -#include -#endif -template -static void __Pyx_pretend_to_initialize(T* ptr) { -#if __cplusplus > 201103L - if ((std::is_trivially_default_constructible::value)) -#endif - *ptr = T(); - (void)ptr; -} -#else -static CYTHON_INLINE void __Pyx_pretend_to_initialize(void* ptr) { (void)ptr; } -#endif - - -#if !CYTHON_USE_MODULE_STATE -static PyObject *__pyx_m = NULL; -#endif -static int __pyx_lineno; -static int __pyx_clineno = 0; -static const char * const __pyx_cfilenm = __FILE__; -static const char *__pyx_filename; - -/* Header.proto */ -#if !defined(CYTHON_CCOMPLEX) - #if defined(__cplusplus) - #define CYTHON_CCOMPLEX 1 - #elif (defined(_Complex_I) && !defined(_MSC_VER)) || ((defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) && !defined(__STDC_NO_COMPLEX__) && !defined(_MSC_VER)) - #define CYTHON_CCOMPLEX 1 - #else - #define CYTHON_CCOMPLEX 0 - #endif -#endif -#if CYTHON_CCOMPLEX - #ifdef __cplusplus - #include - #else - #include - #endif -#endif -#if CYTHON_CCOMPLEX && !defined(__cplusplus) && defined(__sun__) && defined(__GNUC__) - #undef _Complex_I - #define _Complex_I 1.0fj -#endif - -/* #### Code section: filename_table ### */ - -static const char* const __pyx_f[] = { - "Lib/fontTools/cu2qu/cu2qu.py", -}; -/* #### Code section: utility_code_proto_before_types ### */ -/* Atomics.proto */ -#include -#ifndef CYTHON_ATOMICS - #define CYTHON_ATOMICS 1 -#endif -#define __PYX_CYTHON_ATOMICS_ENABLED() CYTHON_ATOMICS -#define __PYX_GET_CYTHON_COMPILING_IN_CPYTHON_FREETHREADING() CYTHON_COMPILING_IN_CPYTHON_FREETHREADING -#define __pyx_atomic_int_type int -#define __pyx_nonatomic_int_type int -#if CYTHON_ATOMICS && (defined(__STDC_VERSION__) &&\ - (__STDC_VERSION__ >= 201112L) &&\ - !defined(__STDC_NO_ATOMICS__)) - #include -#elif CYTHON_ATOMICS && (defined(__cplusplus) && (\ - (__cplusplus >= 201103L) ||\ - (defined(_MSC_VER) && _MSC_VER >= 1700))) - #include -#endif -#if CYTHON_ATOMICS && (defined(__STDC_VERSION__) &&\ - (__STDC_VERSION__ >= 201112L) &&\ - !defined(__STDC_NO_ATOMICS__) &&\ - ATOMIC_INT_LOCK_FREE == 2) - #undef __pyx_atomic_int_type - #define __pyx_atomic_int_type atomic_int - #define __pyx_atomic_ptr_type atomic_uintptr_t - #define __pyx_nonatomic_ptr_type uintptr_t - #define __pyx_atomic_incr_relaxed(value) atomic_fetch_add_explicit(value, 1, memory_order_relaxed) - #define __pyx_atomic_incr_acq_rel(value) atomic_fetch_add_explicit(value, 1, memory_order_acq_rel) - #define __pyx_atomic_decr_acq_rel(value) atomic_fetch_sub_explicit(value, 1, memory_order_acq_rel) - #define __pyx_atomic_sub(value, arg) atomic_fetch_sub(value, arg) - #define __pyx_atomic_int_cmp_exchange(value, expected, desired) atomic_compare_exchange_strong(value, expected, desired) - #define __pyx_atomic_load(value) atomic_load(value) - #define __pyx_atomic_store(value, new_value) atomic_store(value, new_value) - #define __pyx_atomic_pointer_load_relaxed(value) atomic_load_explicit(value, memory_order_relaxed) - #define __pyx_atomic_pointer_load_acquire(value) atomic_load_explicit(value, memory_order_acquire) - #define __pyx_atomic_pointer_exchange(value, new_value) atomic_exchange(value, (__pyx_nonatomic_ptr_type)new_value) - #if defined(__PYX_DEBUG_ATOMICS) && defined(_MSC_VER) - #pragma message ("Using standard C atomics") - #elif defined(__PYX_DEBUG_ATOMICS) - #warning "Using standard C atomics" - #endif -#elif CYTHON_ATOMICS && (defined(__cplusplus) && (\ - (__cplusplus >= 201103L) ||\ -\ - (defined(_MSC_VER) && _MSC_VER >= 1700)) &&\ - ATOMIC_INT_LOCK_FREE == 2) - #undef __pyx_atomic_int_type - #define __pyx_atomic_int_type std::atomic_int - #define __pyx_atomic_ptr_type std::atomic_uintptr_t - #define __pyx_nonatomic_ptr_type uintptr_t - #define __pyx_atomic_incr_relaxed(value) std::atomic_fetch_add_explicit(value, 1, std::memory_order_relaxed) - #define __pyx_atomic_incr_acq_rel(value) std::atomic_fetch_add_explicit(value, 1, std::memory_order_acq_rel) - #define __pyx_atomic_decr_acq_rel(value) std::atomic_fetch_sub_explicit(value, 1, std::memory_order_acq_rel) - #define __pyx_atomic_sub(value, arg) std::atomic_fetch_sub(value, arg) - #define __pyx_atomic_int_cmp_exchange(value, expected, desired) std::atomic_compare_exchange_strong(value, expected, desired) - #define __pyx_atomic_load(value) std::atomic_load(value) - #define __pyx_atomic_store(value, new_value) std::atomic_store(value, new_value) - #define __pyx_atomic_pointer_load_relaxed(value) std::atomic_load_explicit(value, std::memory_order_relaxed) - #define __pyx_atomic_pointer_load_acquire(value) std::atomic_load_explicit(value, std::memory_order_acquire) - #define __pyx_atomic_pointer_exchange(value, new_value) std::atomic_exchange(value, (__pyx_nonatomic_ptr_type)new_value) - #if defined(__PYX_DEBUG_ATOMICS) && defined(_MSC_VER) - #pragma message ("Using standard C++ atomics") - #elif defined(__PYX_DEBUG_ATOMICS) - #warning "Using standard C++ atomics" - #endif -#elif CYTHON_ATOMICS && (__GNUC__ >= 5 || (__GNUC__ == 4 &&\ - (__GNUC_MINOR__ > 1 ||\ - (__GNUC_MINOR__ == 1 && __GNUC_PATCHLEVEL__ >= 2)))) - #define __pyx_atomic_ptr_type void* - #define __pyx_atomic_incr_relaxed(value) __sync_fetch_and_add(value, 1) - #define __pyx_atomic_incr_acq_rel(value) __sync_fetch_and_add(value, 1) - #define __pyx_atomic_decr_acq_rel(value) __sync_fetch_and_sub(value, 1) - #define __pyx_atomic_sub(value, arg) __sync_fetch_and_sub(value, arg) - static CYTHON_INLINE int __pyx_atomic_int_cmp_exchange(__pyx_atomic_int_type* value, __pyx_nonatomic_int_type* expected, __pyx_nonatomic_int_type desired) { - __pyx_nonatomic_int_type old = __sync_val_compare_and_swap(value, *expected, desired); - int result = old == *expected; - *expected = old; - return result; - } - #define __pyx_atomic_load(value) __sync_fetch_and_add(value, 0) - #define __pyx_atomic_store(value, new_value) __sync_lock_test_and_set(value, new_value) - #define __pyx_atomic_pointer_load_relaxed(value) __sync_fetch_and_add(value, 0) - #define __pyx_atomic_pointer_load_acquire(value) __sync_fetch_and_add(value, 0) - #define __pyx_atomic_pointer_exchange(value, new_value) __sync_lock_test_and_set(value, (__pyx_atomic_ptr_type)new_value) - #ifdef __PYX_DEBUG_ATOMICS - #warning "Using GNU atomics" - #endif -#elif CYTHON_ATOMICS && defined(_MSC_VER) - #include - #undef __pyx_atomic_int_type - #define __pyx_atomic_int_type long - #define __pyx_atomic_ptr_type void* - #undef __pyx_nonatomic_int_type - #define __pyx_nonatomic_int_type long - #pragma intrinsic (_InterlockedExchangeAdd, _InterlockedExchange, _InterlockedCompareExchange, _InterlockedCompareExchangePointer, _InterlockedExchangePointer) - #define __pyx_atomic_incr_relaxed(value) _InterlockedExchangeAdd(value, 1) - #define __pyx_atomic_incr_acq_rel(value) _InterlockedExchangeAdd(value, 1) - #define __pyx_atomic_decr_acq_rel(value) _InterlockedExchangeAdd(value, -1) - #define __pyx_atomic_sub(value, arg) _InterlockedExchangeAdd(value, -arg) - static CYTHON_INLINE int __pyx_atomic_int_cmp_exchange(__pyx_atomic_int_type* value, __pyx_nonatomic_int_type* expected, __pyx_nonatomic_int_type desired) { - __pyx_nonatomic_int_type old = _InterlockedCompareExchange(value, desired, *expected); - int result = old == *expected; - *expected = old; - return result; - } - #define __pyx_atomic_load(value) _InterlockedExchangeAdd(value, 0) - #define __pyx_atomic_store(value, new_value) _InterlockedExchange(value, new_value) - #define __pyx_atomic_pointer_load_relaxed(value) *(void * volatile *)value - #define __pyx_atomic_pointer_load_acquire(value) _InterlockedCompareExchangePointer(value, 0, 0) - #define __pyx_atomic_pointer_exchange(value, new_value) _InterlockedExchangePointer(value, (__pyx_atomic_ptr_type)new_value) - #ifdef __PYX_DEBUG_ATOMICS - #pragma message ("Using MSVC atomics") - #endif -#else - #undef CYTHON_ATOMICS - #define CYTHON_ATOMICS 0 - #ifdef __PYX_DEBUG_ATOMICS - #warning "Not using atomics" - #endif -#endif -#if CYTHON_ATOMICS - #define __pyx_add_acquisition_count(memview)\ - __pyx_atomic_incr_relaxed(__pyx_get_slice_count_pointer(memview)) - #define __pyx_sub_acquisition_count(memview)\ - __pyx_atomic_decr_acq_rel(__pyx_get_slice_count_pointer(memview)) -#else - #define __pyx_add_acquisition_count(memview)\ - __pyx_add_acquisition_count_locked(__pyx_get_slice_count_pointer(memview), memview->lock) - #define __pyx_sub_acquisition_count(memview)\ - __pyx_sub_acquisition_count_locked(__pyx_get_slice_count_pointer(memview), memview->lock) -#endif - -/* IncludeStructmemberH.proto */ -#include - -/* CriticalSections.proto */ -#if !CYTHON_COMPILING_IN_CPYTHON_FREETHREADING -#define __Pyx_PyCriticalSection void* -#define __Pyx_PyCriticalSection2 void* -#define __Pyx_PyCriticalSection_Begin1(cs, arg) (void)cs -#define __Pyx_PyCriticalSection_Begin2(cs, arg1, arg2) (void)cs -#define __Pyx_PyCriticalSection_End1(cs) -#define __Pyx_PyCriticalSection_End2(cs) -#else -#define __Pyx_PyCriticalSection PyCriticalSection -#define __Pyx_PyCriticalSection2 PyCriticalSection2 -#define __Pyx_PyCriticalSection_Begin1 PyCriticalSection_Begin -#define __Pyx_PyCriticalSection_Begin2 PyCriticalSection2_Begin -#define __Pyx_PyCriticalSection_End1 PyCriticalSection_End -#define __Pyx_PyCriticalSection_End2 PyCriticalSection2_End -#endif -#if PY_VERSION_HEX < 0x030d0000 || CYTHON_COMPILING_IN_LIMITED_API -#define __Pyx_BEGIN_CRITICAL_SECTION(o) { -#define __Pyx_END_CRITICAL_SECTION() } -#else -#define __Pyx_BEGIN_CRITICAL_SECTION Py_BEGIN_CRITICAL_SECTION -#define __Pyx_END_CRITICAL_SECTION Py_END_CRITICAL_SECTION -#endif - -/* #### Code section: numeric_typedefs ### */ -/* #### Code section: complex_type_declarations ### */ -/* Declarations.proto */ -#if CYTHON_CCOMPLEX && (1) && (!0 || __cplusplus) - #ifdef __cplusplus - typedef ::std::complex< double > __pyx_t_double_complex; - #else - typedef double _Complex __pyx_t_double_complex; - #endif -#else - typedef struct { double real, imag; } __pyx_t_double_complex; -#endif -static CYTHON_INLINE __pyx_t_double_complex __pyx_t_double_complex_from_parts(double, double); - -/* #### Code section: type_declarations ### */ - -/*--- Type declarations ---*/ -struct __pyx_obj_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen; - -/* "fontTools/cu2qu/cu2qu.py":150 - * - * - * @cython.locals( # <<<<<<<<<<<<<< - * p0=cython.complex, - * p1=cython.complex, -*/ -struct __pyx_obj_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen { - PyObject_HEAD - __pyx_t_double_complex __pyx_v_a; - __pyx_t_double_complex __pyx_v_a1; - __pyx_t_double_complex __pyx_v_b; - __pyx_t_double_complex __pyx_v_b1; - __pyx_t_double_complex __pyx_v_c; - __pyx_t_double_complex __pyx_v_c1; - __pyx_t_double_complex __pyx_v_d; - __pyx_t_double_complex __pyx_v_d1; - double __pyx_v_delta_2; - double __pyx_v_delta_3; - double __pyx_v_dt; - int __pyx_v_i; - int __pyx_v_n; - __pyx_t_double_complex __pyx_v_p0; - __pyx_t_double_complex __pyx_v_p1; - __pyx_t_double_complex __pyx_v_p2; - __pyx_t_double_complex __pyx_v_p3; - double __pyx_v_t1; - double __pyx_v_t1_2; - int __pyx_t_0; - int __pyx_t_1; - int __pyx_t_2; -}; - -/* #### Code section: utility_code_proto ### */ - -/* --- Runtime support code (head) --- */ -/* Refnanny.proto */ -#ifndef CYTHON_REFNANNY - #define CYTHON_REFNANNY 0 -#endif -#if CYTHON_REFNANNY - typedef struct { - void (*INCREF)(void*, PyObject*, Py_ssize_t); - void (*DECREF)(void*, PyObject*, Py_ssize_t); - void (*GOTREF)(void*, PyObject*, Py_ssize_t); - void (*GIVEREF)(void*, PyObject*, Py_ssize_t); - void* (*SetupContext)(const char*, Py_ssize_t, const char*); - void (*FinishContext)(void**); - } __Pyx_RefNannyAPIStruct; - static __Pyx_RefNannyAPIStruct *__Pyx_RefNanny = NULL; - static __Pyx_RefNannyAPIStruct *__Pyx_RefNannyImportAPI(const char *modname); - #define __Pyx_RefNannyDeclarations void *__pyx_refnanny = NULL; - #define __Pyx_RefNannySetupContext(name, acquire_gil)\ - if (acquire_gil) {\ - PyGILState_STATE __pyx_gilstate_save = PyGILState_Ensure();\ - __pyx_refnanny = __Pyx_RefNanny->SetupContext((name), (__LINE__), (__FILE__));\ - PyGILState_Release(__pyx_gilstate_save);\ - } else {\ - __pyx_refnanny = __Pyx_RefNanny->SetupContext((name), (__LINE__), (__FILE__));\ - } - #define __Pyx_RefNannyFinishContextNogil() {\ - PyGILState_STATE __pyx_gilstate_save = PyGILState_Ensure();\ - __Pyx_RefNannyFinishContext();\ - PyGILState_Release(__pyx_gilstate_save);\ - } - #define __Pyx_RefNannyFinishContextNogil() {\ - PyGILState_STATE __pyx_gilstate_save = PyGILState_Ensure();\ - __Pyx_RefNannyFinishContext();\ - PyGILState_Release(__pyx_gilstate_save);\ - } - #define __Pyx_RefNannyFinishContext()\ - __Pyx_RefNanny->FinishContext(&__pyx_refnanny) - #define __Pyx_INCREF(r) __Pyx_RefNanny->INCREF(__pyx_refnanny, (PyObject *)(r), (__LINE__)) - #define __Pyx_DECREF(r) __Pyx_RefNanny->DECREF(__pyx_refnanny, (PyObject *)(r), (__LINE__)) - #define __Pyx_GOTREF(r) __Pyx_RefNanny->GOTREF(__pyx_refnanny, (PyObject *)(r), (__LINE__)) - #define __Pyx_GIVEREF(r) __Pyx_RefNanny->GIVEREF(__pyx_refnanny, (PyObject *)(r), (__LINE__)) - #define __Pyx_XINCREF(r) do { if((r) == NULL); else {__Pyx_INCREF(r); }} while(0) - #define __Pyx_XDECREF(r) do { if((r) == NULL); else {__Pyx_DECREF(r); }} while(0) - #define __Pyx_XGOTREF(r) do { if((r) == NULL); else {__Pyx_GOTREF(r); }} while(0) - #define __Pyx_XGIVEREF(r) do { if((r) == NULL); else {__Pyx_GIVEREF(r);}} while(0) -#else - #define __Pyx_RefNannyDeclarations - #define __Pyx_RefNannySetupContext(name, acquire_gil) - #define __Pyx_RefNannyFinishContextNogil() - #define __Pyx_RefNannyFinishContext() - #define __Pyx_INCREF(r) Py_INCREF(r) - #define __Pyx_DECREF(r) Py_DECREF(r) - #define __Pyx_GOTREF(r) - #define __Pyx_GIVEREF(r) - #define __Pyx_XINCREF(r) Py_XINCREF(r) - #define __Pyx_XDECREF(r) Py_XDECREF(r) - #define __Pyx_XGOTREF(r) - #define __Pyx_XGIVEREF(r) -#endif -#define __Pyx_Py_XDECREF_SET(r, v) do {\ - PyObject *tmp = (PyObject *) r;\ - r = v; Py_XDECREF(tmp);\ - } while (0) -#define __Pyx_XDECREF_SET(r, v) do {\ - PyObject *tmp = (PyObject *) r;\ - r = v; __Pyx_XDECREF(tmp);\ - } while (0) -#define __Pyx_DECREF_SET(r, v) do {\ - PyObject *tmp = (PyObject *) r;\ - r = v; __Pyx_DECREF(tmp);\ - } while (0) -#define __Pyx_CLEAR(r) do { PyObject* tmp = ((PyObject*)(r)); r = NULL; __Pyx_DECREF(tmp);} while(0) -#define __Pyx_XCLEAR(r) do { if((r) != NULL) {PyObject* tmp = ((PyObject*)(r)); r = NULL; __Pyx_DECREF(tmp);}} while(0) - -/* PyErrExceptionMatches.proto */ -#if CYTHON_FAST_THREAD_STATE -#define __Pyx_PyErr_ExceptionMatches(err) __Pyx_PyErr_ExceptionMatchesInState(__pyx_tstate, err) -static CYTHON_INLINE int __Pyx_PyErr_ExceptionMatchesInState(PyThreadState* tstate, PyObject* err); -#else -#define __Pyx_PyErr_ExceptionMatches(err) PyErr_ExceptionMatches(err) -#endif - -/* PyThreadStateGet.proto */ -#if CYTHON_FAST_THREAD_STATE -#define __Pyx_PyThreadState_declare PyThreadState *__pyx_tstate; -#define __Pyx_PyThreadState_assign __pyx_tstate = __Pyx_PyThreadState_Current; -#if PY_VERSION_HEX >= 0x030C00A6 -#define __Pyx_PyErr_Occurred() (__pyx_tstate->current_exception != NULL) -#define __Pyx_PyErr_CurrentExceptionType() (__pyx_tstate->current_exception ? (PyObject*) Py_TYPE(__pyx_tstate->current_exception) : (PyObject*) NULL) -#else -#define __Pyx_PyErr_Occurred() (__pyx_tstate->curexc_type != NULL) -#define __Pyx_PyErr_CurrentExceptionType() (__pyx_tstate->curexc_type) -#endif -#else -#define __Pyx_PyThreadState_declare -#define __Pyx_PyThreadState_assign -#define __Pyx_PyErr_Occurred() (PyErr_Occurred() != NULL) -#define __Pyx_PyErr_CurrentExceptionType() PyErr_Occurred() -#endif - -/* PyErrFetchRestore.proto */ -#if CYTHON_FAST_THREAD_STATE -#define __Pyx_PyErr_Clear() __Pyx_ErrRestore(NULL, NULL, NULL) -#define __Pyx_ErrRestoreWithState(type, value, tb) __Pyx_ErrRestoreInState(PyThreadState_GET(), type, value, tb) -#define __Pyx_ErrFetchWithState(type, value, tb) __Pyx_ErrFetchInState(PyThreadState_GET(), type, value, tb) -#define __Pyx_ErrRestore(type, value, tb) __Pyx_ErrRestoreInState(__pyx_tstate, type, value, tb) -#define __Pyx_ErrFetch(type, value, tb) __Pyx_ErrFetchInState(__pyx_tstate, type, value, tb) -static CYTHON_INLINE void __Pyx_ErrRestoreInState(PyThreadState *tstate, PyObject *type, PyObject *value, PyObject *tb); -static CYTHON_INLINE void __Pyx_ErrFetchInState(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb); -#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030C00A6 -#define __Pyx_PyErr_SetNone(exc) (Py_INCREF(exc), __Pyx_ErrRestore((exc), NULL, NULL)) -#else -#define __Pyx_PyErr_SetNone(exc) PyErr_SetNone(exc) -#endif -#else -#define __Pyx_PyErr_Clear() PyErr_Clear() -#define __Pyx_PyErr_SetNone(exc) PyErr_SetNone(exc) -#define __Pyx_ErrRestoreWithState(type, value, tb) PyErr_Restore(type, value, tb) -#define __Pyx_ErrFetchWithState(type, value, tb) PyErr_Fetch(type, value, tb) -#define __Pyx_ErrRestoreInState(tstate, type, value, tb) PyErr_Restore(type, value, tb) -#define __Pyx_ErrFetchInState(tstate, type, value, tb) PyErr_Fetch(type, value, tb) -#define __Pyx_ErrRestore(type, value, tb) PyErr_Restore(type, value, tb) -#define __Pyx_ErrFetch(type, value, tb) PyErr_Fetch(type, value, tb) -#endif - -/* PyObjectGetAttrStr.proto */ -#if CYTHON_USE_TYPE_SLOTS -static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStr(PyObject* obj, PyObject* attr_name); -#else -#define __Pyx_PyObject_GetAttrStr(o,n) PyObject_GetAttr(o,n) -#endif - -/* PyObjectGetAttrStrNoError.proto */ -static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStrNoError(PyObject* obj, PyObject* attr_name); - -/* GetBuiltinName.proto */ -static PyObject *__Pyx_GetBuiltinName(PyObject *name); - -/* IncludeStdlibH.proto */ -#include - -/* PyFunctionFastCall.proto */ -#if CYTHON_FAST_PYCALL -#if !CYTHON_VECTORCALL -#define __Pyx_PyFunction_FastCall(func, args, nargs)\ - __Pyx_PyFunction_FastCallDict((func), (args), (nargs), NULL) -static PyObject *__Pyx_PyFunction_FastCallDict(PyObject *func, PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs); -#endif -#define __Pyx_BUILD_ASSERT_EXPR(cond)\ - (sizeof(char [1 - 2*!(cond)]) - 1) -#ifndef Py_MEMBER_SIZE -#define Py_MEMBER_SIZE(type, member) sizeof(((type *)0)->member) -#endif -#if !CYTHON_VECTORCALL -#if PY_VERSION_HEX >= 0x03080000 - #include "frameobject.h" - #define __Pxy_PyFrame_Initialize_Offsets() - #define __Pyx_PyFrame_GetLocalsplus(frame) ((frame)->f_localsplus) -#else - static size_t __pyx_pyframe_localsplus_offset = 0; - #include "frameobject.h" - #define __Pxy_PyFrame_Initialize_Offsets()\ - ((void)__Pyx_BUILD_ASSERT_EXPR(sizeof(PyFrameObject) == offsetof(PyFrameObject, f_localsplus) + Py_MEMBER_SIZE(PyFrameObject, f_localsplus)),\ - (void)(__pyx_pyframe_localsplus_offset = ((size_t)PyFrame_Type.tp_basicsize) - Py_MEMBER_SIZE(PyFrameObject, f_localsplus))) - #define __Pyx_PyFrame_GetLocalsplus(frame)\ - (assert(__pyx_pyframe_localsplus_offset), (PyObject **)(((char *)(frame)) + __pyx_pyframe_localsplus_offset)) -#endif -#endif -#endif - -/* PyObjectCall.proto */ -#if CYTHON_COMPILING_IN_CPYTHON -static CYTHON_INLINE PyObject* __Pyx_PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw); -#else -#define __Pyx_PyObject_Call(func, arg, kw) PyObject_Call(func, arg, kw) -#endif - -/* PyObjectCallMethO.proto */ -#if CYTHON_COMPILING_IN_CPYTHON -static CYTHON_INLINE PyObject* __Pyx_PyObject_CallMethO(PyObject *func, PyObject *arg); -#endif - -/* PyObjectFastCall.proto */ -#define __Pyx_PyObject_FastCall(func, args, nargs) __Pyx_PyObject_FastCallDict(func, args, (size_t)(nargs), NULL) -static CYTHON_INLINE PyObject* __Pyx_PyObject_FastCallDict(PyObject *func, PyObject * const*args, size_t nargs, PyObject *kwargs); - -/* PyLongCompare.proto */ -static CYTHON_INLINE int __Pyx_PyLong_BoolEqObjC(PyObject *op1, PyObject *op2, long intval, long inplace); - -/* RaiseTooManyValuesToUnpack.proto */ -static CYTHON_INLINE void __Pyx_RaiseTooManyValuesError(Py_ssize_t expected); - -/* RaiseNeedMoreValuesToUnpack.proto */ -static CYTHON_INLINE void __Pyx_RaiseNeedMoreValuesError(Py_ssize_t index); - -/* IterFinish.proto */ -static CYTHON_INLINE int __Pyx_IterFinish(void); - -/* UnpackItemEndCheck.proto */ -static int __Pyx_IternextUnpackEndCheck(PyObject *retval, Py_ssize_t expected); - -/* GetItemInt.proto */ -#define __Pyx_GetItemInt(o, i, type, is_signed, to_py_func, is_list, wraparound, boundscheck, has_gil)\ - (__Pyx_fits_Py_ssize_t(i, type, is_signed) ?\ - __Pyx_GetItemInt_Fast(o, (Py_ssize_t)i, is_list, wraparound, boundscheck) :\ - (is_list ? (PyErr_SetString(PyExc_IndexError, "list index out of range"), (PyObject*)NULL) :\ - __Pyx_GetItemInt_Generic(o, to_py_func(i)))) -#define __Pyx_GetItemInt_List(o, i, type, is_signed, to_py_func, is_list, wraparound, boundscheck, has_gil)\ - (__Pyx_fits_Py_ssize_t(i, type, is_signed) ?\ - __Pyx_GetItemInt_List_Fast(o, (Py_ssize_t)i, wraparound, boundscheck) :\ - (PyErr_SetString(PyExc_IndexError, "list index out of range"), (PyObject*)NULL)) -static CYTHON_INLINE PyObject *__Pyx_GetItemInt_List_Fast(PyObject *o, Py_ssize_t i, - int wraparound, int boundscheck); -#define __Pyx_GetItemInt_Tuple(o, i, type, is_signed, to_py_func, is_list, wraparound, boundscheck, has_gil)\ - (__Pyx_fits_Py_ssize_t(i, type, is_signed) ?\ - __Pyx_GetItemInt_Tuple_Fast(o, (Py_ssize_t)i, wraparound, boundscheck) :\ - (PyErr_SetString(PyExc_IndexError, "tuple index out of range"), (PyObject*)NULL)) -static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Tuple_Fast(PyObject *o, Py_ssize_t i, - int wraparound, int boundscheck); -static PyObject *__Pyx_GetItemInt_Generic(PyObject *o, PyObject* j); -static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Fast(PyObject *o, Py_ssize_t i, - int is_list, int wraparound, int boundscheck); - -/* PyDictVersioning.proto */ -#if CYTHON_USE_DICT_VERSIONS && CYTHON_USE_TYPE_SLOTS -#define __PYX_DICT_VERSION_INIT ((PY_UINT64_T) -1) -#define __PYX_GET_DICT_VERSION(dict) (((PyDictObject*)(dict))->ma_version_tag) -#define __PYX_UPDATE_DICT_CACHE(dict, value, cache_var, version_var)\ - (version_var) = __PYX_GET_DICT_VERSION(dict);\ - (cache_var) = (value); -#define __PYX_PY_DICT_LOOKUP_IF_MODIFIED(VAR, DICT, LOOKUP) {\ - static PY_UINT64_T __pyx_dict_version = 0;\ - static PyObject *__pyx_dict_cached_value = NULL;\ - if (likely(__PYX_GET_DICT_VERSION(DICT) == __pyx_dict_version)) {\ - (VAR) = __pyx_dict_cached_value;\ - } else {\ - (VAR) = __pyx_dict_cached_value = (LOOKUP);\ - __pyx_dict_version = __PYX_GET_DICT_VERSION(DICT);\ - }\ -} -static CYTHON_INLINE PY_UINT64_T __Pyx_get_tp_dict_version(PyObject *obj); -static CYTHON_INLINE PY_UINT64_T __Pyx_get_object_dict_version(PyObject *obj); -static CYTHON_INLINE int __Pyx_object_dict_version_matches(PyObject* obj, PY_UINT64_T tp_dict_version, PY_UINT64_T obj_dict_version); -#else -#define __PYX_GET_DICT_VERSION(dict) (0) -#define __PYX_UPDATE_DICT_CACHE(dict, value, cache_var, version_var) -#define __PYX_PY_DICT_LOOKUP_IF_MODIFIED(VAR, DICT, LOOKUP) (VAR) = (LOOKUP); -#endif - -/* GetModuleGlobalName.proto */ -#if CYTHON_USE_DICT_VERSIONS -#define __Pyx_GetModuleGlobalName(var, name) do {\ - static PY_UINT64_T __pyx_dict_version = 0;\ - static PyObject *__pyx_dict_cached_value = NULL;\ - (var) = (likely(__pyx_dict_version == __PYX_GET_DICT_VERSION(__pyx_mstate_global->__pyx_d))) ?\ - (likely(__pyx_dict_cached_value) ? __Pyx_NewRef(__pyx_dict_cached_value) : __Pyx_GetBuiltinName(name)) :\ - __Pyx__GetModuleGlobalName(name, &__pyx_dict_version, &__pyx_dict_cached_value);\ -} while(0) -#define __Pyx_GetModuleGlobalNameUncached(var, name) do {\ - PY_UINT64_T __pyx_dict_version;\ - PyObject *__pyx_dict_cached_value;\ - (var) = __Pyx__GetModuleGlobalName(name, &__pyx_dict_version, &__pyx_dict_cached_value);\ -} while(0) -static PyObject *__Pyx__GetModuleGlobalName(PyObject *name, PY_UINT64_T *dict_version, PyObject **dict_cached_value); -#else -#define __Pyx_GetModuleGlobalName(var, name) (var) = __Pyx__GetModuleGlobalName(name) -#define __Pyx_GetModuleGlobalNameUncached(var, name) (var) = __Pyx__GetModuleGlobalName(name) -static CYTHON_INLINE PyObject *__Pyx__GetModuleGlobalName(PyObject *name); -#endif - -/* TupleAndListFromArray.proto */ -#if CYTHON_COMPILING_IN_CPYTHON -static CYTHON_INLINE PyObject* __Pyx_PyList_FromArray(PyObject *const *src, Py_ssize_t n); -#endif -#if CYTHON_COMPILING_IN_CPYTHON || CYTHON_METH_FASTCALL -static CYTHON_INLINE PyObject* __Pyx_PyTuple_FromArray(PyObject *const *src, Py_ssize_t n); -#endif - -/* IncludeStringH.proto */ -#include - -/* BytesEquals.proto */ -static CYTHON_INLINE int __Pyx_PyBytes_Equals(PyObject* s1, PyObject* s2, int equals); - -/* UnicodeEquals.proto */ -static CYTHON_INLINE int __Pyx_PyUnicode_Equals(PyObject* s1, PyObject* s2, int equals); - -/* fastcall.proto */ -#if CYTHON_AVOID_BORROWED_REFS - #define __Pyx_ArgRef_VARARGS(args, i) __Pyx_PySequence_ITEM(args, i) -#elif CYTHON_ASSUME_SAFE_MACROS - #define __Pyx_ArgRef_VARARGS(args, i) __Pyx_NewRef(__Pyx_PyTuple_GET_ITEM(args, i)) -#else - #define __Pyx_ArgRef_VARARGS(args, i) __Pyx_XNewRef(PyTuple_GetItem(args, i)) -#endif -#define __Pyx_NumKwargs_VARARGS(kwds) PyDict_Size(kwds) -#define __Pyx_KwValues_VARARGS(args, nargs) NULL -#define __Pyx_GetKwValue_VARARGS(kw, kwvalues, s) __Pyx_PyDict_GetItemStrWithError(kw, s) -#define __Pyx_KwargsAsDict_VARARGS(kw, kwvalues) PyDict_Copy(kw) -#if CYTHON_METH_FASTCALL - #define __Pyx_ArgRef_FASTCALL(args, i) __Pyx_NewRef(args[i]) - #define __Pyx_NumKwargs_FASTCALL(kwds) __Pyx_PyTuple_GET_SIZE(kwds) - #define __Pyx_KwValues_FASTCALL(args, nargs) ((args) + (nargs)) - static CYTHON_INLINE PyObject * __Pyx_GetKwValue_FASTCALL(PyObject *kwnames, PyObject *const *kwvalues, PyObject *s); - #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030d0000 || CYTHON_COMPILING_IN_LIMITED_API - CYTHON_UNUSED static PyObject *__Pyx_KwargsAsDict_FASTCALL(PyObject *kwnames, PyObject *const *kwvalues); - #else - #define __Pyx_KwargsAsDict_FASTCALL(kw, kwvalues) _PyStack_AsDict(kwvalues, kw) - #endif -#else - #define __Pyx_ArgRef_FASTCALL __Pyx_ArgRef_VARARGS - #define __Pyx_NumKwargs_FASTCALL __Pyx_NumKwargs_VARARGS - #define __Pyx_KwValues_FASTCALL __Pyx_KwValues_VARARGS - #define __Pyx_GetKwValue_FASTCALL __Pyx_GetKwValue_VARARGS - #define __Pyx_KwargsAsDict_FASTCALL __Pyx_KwargsAsDict_VARARGS -#endif -#define __Pyx_ArgsSlice_VARARGS(args, start, stop) PyTuple_GetSlice(args, start, stop) -#if CYTHON_METH_FASTCALL || (CYTHON_COMPILING_IN_CPYTHON && CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS) -#define __Pyx_ArgsSlice_FASTCALL(args, start, stop) __Pyx_PyTuple_FromArray(args + start, stop - start) -#else -#define __Pyx_ArgsSlice_FASTCALL(args, start, stop) PyTuple_GetSlice(args, start, stop) -#endif - -/* RaiseDoubleKeywords.proto */ -static void __Pyx_RaiseDoubleKeywordsError(const char* func_name, PyObject* kw_name); - -/* ParseKeywords.proto */ -static CYTHON_INLINE int __Pyx_ParseKeywords( - PyObject *kwds, PyObject *const *kwvalues, PyObject ** const argnames[], - PyObject *kwds2, PyObject *values[], - Py_ssize_t num_pos_args, Py_ssize_t num_kwargs, - const char* function_name, - int ignore_unknown_kwargs -); - -/* CallCFunction.proto */ -#define __Pyx_CallCFunction(cfunc, self, args)\ - ((PyCFunction)(void(*)(void))(cfunc)->func)(self, args) -#define __Pyx_CallCFunctionWithKeywords(cfunc, self, args, kwargs)\ - ((PyCFunctionWithKeywords)(void(*)(void))(cfunc)->func)(self, args, kwargs) -#define __Pyx_CallCFunctionFast(cfunc, self, args, nargs)\ - ((__Pyx_PyCFunctionFast)(void(*)(void))(PyCFunction)(cfunc)->func)(self, args, nargs) -#define __Pyx_CallCFunctionFastWithKeywords(cfunc, self, args, nargs, kwnames)\ - ((__Pyx_PyCFunctionFastWithKeywords)(void(*)(void))(PyCFunction)(cfunc)->func)(self, args, nargs, kwnames) - -/* UnpackUnboundCMethod.proto */ -typedef struct { - PyObject *type; - PyObject **method_name; -#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING && CYTHON_ATOMICS - __pyx_atomic_int_type initialized; -#endif - PyCFunction func; - PyObject *method; - int flag; -} __Pyx_CachedCFunction; -#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING -static CYTHON_INLINE int __Pyx_CachedCFunction_GetAndSetInitializing(__Pyx_CachedCFunction *cfunc) { -#if !CYTHON_ATOMICS - return 1; -#else - __pyx_nonatomic_int_type expected = 0; - if (__pyx_atomic_int_cmp_exchange(&cfunc->initialized, &expected, 1)) { - return 0; - } - return expected; -#endif -} -static CYTHON_INLINE void __Pyx_CachedCFunction_SetFinishedInitializing(__Pyx_CachedCFunction *cfunc) { -#if CYTHON_ATOMICS - __pyx_atomic_store(&cfunc->initialized, 2); -#endif -} -#else -#define __Pyx_CachedCFunction_GetAndSetInitializing(cfunc) 2 -#define __Pyx_CachedCFunction_SetFinishedInitializing(cfunc) -#endif - -/* CallUnboundCMethod2.proto */ -CYTHON_UNUSED -static PyObject* __Pyx__CallUnboundCMethod2(__Pyx_CachedCFunction* cfunc, PyObject* self, PyObject* arg1, PyObject* arg2); -#if CYTHON_COMPILING_IN_CPYTHON -static CYTHON_INLINE PyObject *__Pyx_CallUnboundCMethod2(__Pyx_CachedCFunction *cfunc, PyObject *self, PyObject *arg1, PyObject *arg2); -#else -#define __Pyx_CallUnboundCMethod2(cfunc, self, arg1, arg2) __Pyx__CallUnboundCMethod2(cfunc, self, arg1, arg2) -#endif - -/* RaiseArgTupleInvalid.proto */ -static void __Pyx_RaiseArgtupleInvalid(const char* func_name, int exact, - Py_ssize_t num_min, Py_ssize_t num_max, Py_ssize_t num_found); - -/* GetException.proto */ -#if CYTHON_FAST_THREAD_STATE -#define __Pyx_GetException(type, value, tb) __Pyx__GetException(__pyx_tstate, type, value, tb) -static int __Pyx__GetException(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb); -#else -static int __Pyx_GetException(PyObject **type, PyObject **value, PyObject **tb); -#endif - -/* pep479.proto */ -static void __Pyx_Generator_Replace_StopIteration(int in_async_gen); - -/* GetTopmostException.proto */ -#if CYTHON_USE_EXC_INFO_STACK && CYTHON_FAST_THREAD_STATE -static _PyErr_StackItem * __Pyx_PyErr_GetTopmostException(PyThreadState *tstate); -#endif - -/* SaveResetException.proto */ -#if CYTHON_FAST_THREAD_STATE -#define __Pyx_ExceptionSave(type, value, tb) __Pyx__ExceptionSave(__pyx_tstate, type, value, tb) -static CYTHON_INLINE void __Pyx__ExceptionSave(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb); -#define __Pyx_ExceptionReset(type, value, tb) __Pyx__ExceptionReset(__pyx_tstate, type, value, tb) -static CYTHON_INLINE void __Pyx__ExceptionReset(PyThreadState *tstate, PyObject *type, PyObject *value, PyObject *tb); -#else -#define __Pyx_ExceptionSave(type, value, tb) PyErr_GetExcInfo(type, value, tb) -#define __Pyx_ExceptionReset(type, value, tb) PyErr_SetExcInfo(type, value, tb) -#endif - -/* IterNextPlain.proto */ -static CYTHON_INLINE PyObject *__Pyx_PyIter_Next_Plain(PyObject *iterator); -#if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x030A0000 -static PyObject *__Pyx_GetBuiltinNext_LimitedAPI(void); -#endif - -/* IterNext.proto */ -#define __Pyx_PyIter_Next(obj) __Pyx_PyIter_Next2(obj, NULL) -static CYTHON_INLINE PyObject *__Pyx_PyIter_Next2(PyObject *, PyObject *); - -/* ListAppend.proto */ -#if CYTHON_USE_PYLIST_INTERNALS && CYTHON_ASSUME_SAFE_MACROS -static CYTHON_INLINE int __Pyx_PyList_Append(PyObject* list, PyObject* x) { - PyListObject* L = (PyListObject*) list; - Py_ssize_t len = Py_SIZE(list); - if (likely(L->allocated > len) & likely(len > (L->allocated >> 1))) { - Py_INCREF(x); - #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030d0000 - L->ob_item[len] = x; - #else - PyList_SET_ITEM(list, len, x); - #endif - __Pyx_SET_SIZE(list, len + 1); - return 0; - } - return PyList_Append(list, x); -} -#else -#define __Pyx_PyList_Append(L,x) PyList_Append(L,x) -#endif - -/* ListCompAppend.proto */ -#if CYTHON_USE_PYLIST_INTERNALS && CYTHON_ASSUME_SAFE_MACROS -static CYTHON_INLINE int __Pyx_ListComp_Append(PyObject* list, PyObject* x) { - PyListObject* L = (PyListObject*) list; - Py_ssize_t len = Py_SIZE(list); - if (likely(L->allocated > len)) { - Py_INCREF(x); - #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030d0000 - L->ob_item[len] = x; - #else - PyList_SET_ITEM(list, len, x); - #endif - __Pyx_SET_SIZE(list, len + 1); - return 0; - } - return PyList_Append(list, x); -} -#else -#define __Pyx_ListComp_Append(L,x) PyList_Append(L,x) -#endif - -/* PyLongBinop.proto */ -#if !CYTHON_COMPILING_IN_PYPY -static CYTHON_INLINE PyObject* __Pyx_PyLong_AddObjC(PyObject *op1, PyObject *op2, long intval, int inplace, int zerodivision_check); -#else -#define __Pyx_PyLong_AddObjC(op1, op2, intval, inplace, zerodivision_check)\ - (inplace ? PyNumber_InPlaceAdd(op1, op2) : PyNumber_Add(op1, op2)) -#endif - -/* RaiseException.proto */ -static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb, PyObject *cause); - -/* AssertionsEnabled.proto */ -#if CYTHON_COMPILING_IN_LIMITED_API || (CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030C0000) - static int __pyx_assertions_enabled_flag; - #define __pyx_assertions_enabled() (__pyx_assertions_enabled_flag) - static int __Pyx_init_assertions_enabled(void) { - PyObject *builtins, *debug, *debug_str; - int flag; - builtins = PyEval_GetBuiltins(); - if (!builtins) goto bad; - debug_str = PyUnicode_FromStringAndSize("__debug__", 9); - if (!debug_str) goto bad; - debug = PyObject_GetItem(builtins, debug_str); - Py_DECREF(debug_str); - if (!debug) goto bad; - flag = PyObject_IsTrue(debug); - Py_DECREF(debug); - if (flag == -1) goto bad; - __pyx_assertions_enabled_flag = flag; - return 0; - bad: - __pyx_assertions_enabled_flag = 1; - return -1; - } -#else - #define __Pyx_init_assertions_enabled() (0) - #define __pyx_assertions_enabled() (!Py_OptimizeFlag) -#endif - -/* SetItemInt.proto */ -#define __Pyx_SetItemInt(o, i, v, type, is_signed, to_py_func, is_list, wraparound, boundscheck, has_gil)\ - (__Pyx_fits_Py_ssize_t(i, type, is_signed) ?\ - __Pyx_SetItemInt_Fast(o, (Py_ssize_t)i, v, is_list, wraparound, boundscheck) :\ - (is_list ? (PyErr_SetString(PyExc_IndexError, "list assignment index out of range"), -1) :\ - __Pyx_SetItemInt_Generic(o, to_py_func(i), v))) -static int __Pyx_SetItemInt_Generic(PyObject *o, PyObject *j, PyObject *v); -static CYTHON_INLINE int __Pyx_SetItemInt_Fast(PyObject *o, Py_ssize_t i, PyObject *v, - int is_list, int wraparound, int boundscheck); - -/* ModInt[long].proto */ -static CYTHON_INLINE long __Pyx_mod_long(long, long, int b_is_constant); - -/* LimitedApiGetTypeDict.proto */ -#if CYTHON_COMPILING_IN_LIMITED_API -static PyObject *__Pyx_GetTypeDict(PyTypeObject *tp); -#endif - -/* SetItemOnTypeDict.proto */ -static int __Pyx__SetItemOnTypeDict(PyTypeObject *tp, PyObject *k, PyObject *v); -#define __Pyx_SetItemOnTypeDict(tp, k, v) __Pyx__SetItemOnTypeDict((PyTypeObject*)tp, k, v) - -/* FixUpExtensionType.proto */ -static CYTHON_INLINE int __Pyx_fix_up_extension_type_from_spec(PyType_Spec *spec, PyTypeObject *type); - -/* PyObjectCallNoArg.proto */ -static CYTHON_INLINE PyObject* __Pyx_PyObject_CallNoArg(PyObject *func); - -/* PyObjectCallOneArg.proto */ -static CYTHON_INLINE PyObject* __Pyx_PyObject_CallOneArg(PyObject *func, PyObject *arg); - -/* PyObjectGetMethod.proto */ -static int __Pyx_PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method); - -/* PyObjectCallMethod0.proto */ -static PyObject* __Pyx_PyObject_CallMethod0(PyObject* obj, PyObject* method_name); - -/* ValidateBasesTuple.proto */ -#if CYTHON_COMPILING_IN_CPYTHON || CYTHON_COMPILING_IN_LIMITED_API || CYTHON_USE_TYPE_SPECS -static int __Pyx_validate_bases_tuple(const char *type_name, Py_ssize_t dictoffset, PyObject *bases); -#endif - -/* PyType_Ready.proto */ -CYTHON_UNUSED static int __Pyx_PyType_Ready(PyTypeObject *t); - -/* Import.proto */ -static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list, int level); - -/* ImportDottedModule.proto */ -static PyObject *__Pyx_ImportDottedModule(PyObject *name, PyObject *parts_tuple); -static PyObject *__Pyx_ImportDottedModule_WalkParts(PyObject *module, PyObject *name, PyObject *parts_tuple); - -/* ListPack.proto */ -static PyObject *__Pyx_PyList_Pack(Py_ssize_t n, ...); - -/* ImportFrom.proto */ -static PyObject* __Pyx_ImportFrom(PyObject* module, PyObject* name); - -/* pybytes_as_double.proto */ -static double __Pyx_SlowPyString_AsDouble(PyObject *obj); -static double __Pyx__PyBytes_AsDouble(PyObject *obj, const char* start, Py_ssize_t length); -static CYTHON_INLINE double __Pyx_PyBytes_AsDouble(PyObject *obj) { - char* as_c_string; - Py_ssize_t size; -#if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE - as_c_string = PyBytes_AS_STRING(obj); - size = PyBytes_GET_SIZE(obj); -#else - if (PyBytes_AsStringAndSize(obj, &as_c_string, &size) < 0) { - return (double)-1; - } -#endif - return __Pyx__PyBytes_AsDouble(obj, as_c_string, size); -} -static CYTHON_INLINE double __Pyx_PyByteArray_AsDouble(PyObject *obj) { - char* as_c_string; - Py_ssize_t size; -#if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE - as_c_string = PyByteArray_AS_STRING(obj); - size = PyByteArray_GET_SIZE(obj); -#else - as_c_string = PyByteArray_AsString(obj); - if (as_c_string == NULL) { - return (double)-1; - } - size = PyByteArray_Size(obj); -#endif - return __Pyx__PyBytes_AsDouble(obj, as_c_string, size); -} - -/* pyunicode_as_double.proto */ -#if !CYTHON_COMPILING_IN_PYPY && CYTHON_ASSUME_SAFE_MACROS -static const char* __Pyx__PyUnicode_AsDouble_Copy(const void* data, const int kind, char* buffer, Py_ssize_t start, Py_ssize_t end) { - int last_was_punctuation; - Py_ssize_t i; - last_was_punctuation = 1; - for (i=start; i <= end; i++) { - Py_UCS4 chr = PyUnicode_READ(kind, data, i); - int is_punctuation = (chr == '_') | (chr == '.'); - *buffer = (char)chr; - buffer += (chr != '_'); - if (unlikely(chr > 127)) goto parse_failure; - if (unlikely(last_was_punctuation & is_punctuation)) goto parse_failure; - last_was_punctuation = is_punctuation; - } - if (unlikely(last_was_punctuation)) goto parse_failure; - *buffer = '\0'; - return buffer; -parse_failure: - return NULL; -} -static double __Pyx__PyUnicode_AsDouble_inf_nan(const void* data, int kind, Py_ssize_t start, Py_ssize_t length) { - int matches = 1; - Py_UCS4 chr; - Py_UCS4 sign = PyUnicode_READ(kind, data, start); - int is_signed = (sign == '-') | (sign == '+'); - start += is_signed; - length -= is_signed; - switch (PyUnicode_READ(kind, data, start)) { - #ifdef Py_NAN - case 'n': - case 'N': - if (unlikely(length != 3)) goto parse_failure; - chr = PyUnicode_READ(kind, data, start+1); - matches &= (chr == 'a') | (chr == 'A'); - chr = PyUnicode_READ(kind, data, start+2); - matches &= (chr == 'n') | (chr == 'N'); - if (unlikely(!matches)) goto parse_failure; - return (sign == '-') ? -Py_NAN : Py_NAN; - #endif - case 'i': - case 'I': - if (unlikely(length < 3)) goto parse_failure; - chr = PyUnicode_READ(kind, data, start+1); - matches &= (chr == 'n') | (chr == 'N'); - chr = PyUnicode_READ(kind, data, start+2); - matches &= (chr == 'f') | (chr == 'F'); - if (likely(length == 3 && matches)) - return (sign == '-') ? -Py_HUGE_VAL : Py_HUGE_VAL; - if (unlikely(length != 8)) goto parse_failure; - chr = PyUnicode_READ(kind, data, start+3); - matches &= (chr == 'i') | (chr == 'I'); - chr = PyUnicode_READ(kind, data, start+4); - matches &= (chr == 'n') | (chr == 'N'); - chr = PyUnicode_READ(kind, data, start+5); - matches &= (chr == 'i') | (chr == 'I'); - chr = PyUnicode_READ(kind, data, start+6); - matches &= (chr == 't') | (chr == 'T'); - chr = PyUnicode_READ(kind, data, start+7); - matches &= (chr == 'y') | (chr == 'Y'); - if (unlikely(!matches)) goto parse_failure; - return (sign == '-') ? -Py_HUGE_VAL : Py_HUGE_VAL; - case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - break; - default: - goto parse_failure; - } - return 0.0; -parse_failure: - return -1.0; -} -static double __Pyx_PyUnicode_AsDouble_WithSpaces(PyObject *obj) { - double value; - const char *last; - char *end; - Py_ssize_t start, length = PyUnicode_GET_LENGTH(obj); - const int kind = PyUnicode_KIND(obj); - const void* data = PyUnicode_DATA(obj); - start = 0; - while (Py_UNICODE_ISSPACE(PyUnicode_READ(kind, data, start))) - start++; - while (start < length - 1 && Py_UNICODE_ISSPACE(PyUnicode_READ(kind, data, length - 1))) - length--; - length -= start; - if (unlikely(length <= 0)) goto fallback; - value = __Pyx__PyUnicode_AsDouble_inf_nan(data, kind, start, length); - if (unlikely(value == -1.0)) goto fallback; - if (value != 0.0) return value; - if (length < 40) { - char number[40]; - last = __Pyx__PyUnicode_AsDouble_Copy(data, kind, number, start, start + length); - if (unlikely(!last)) goto fallback; - value = PyOS_string_to_double(number, &end, NULL); - } else { - char *number = (char*) PyMem_Malloc((length + 1) * sizeof(char)); - if (unlikely(!number)) goto fallback; - last = __Pyx__PyUnicode_AsDouble_Copy(data, kind, number, start, start + length); - if (unlikely(!last)) { - PyMem_Free(number); - goto fallback; - } - value = PyOS_string_to_double(number, &end, NULL); - PyMem_Free(number); - } - if (likely(end == last) || (value == (double)-1 && PyErr_Occurred())) { - return value; - } -fallback: - return __Pyx_SlowPyString_AsDouble(obj); -} -#endif -static CYTHON_INLINE double __Pyx_PyUnicode_AsDouble(PyObject *obj) { -#if !CYTHON_COMPILING_IN_PYPY && CYTHON_ASSUME_SAFE_MACROS - if (unlikely(__Pyx_PyUnicode_READY(obj) == -1)) - return (double)-1; - if (likely(PyUnicode_IS_ASCII(obj))) { - const char *s; - Py_ssize_t length; - s = PyUnicode_AsUTF8AndSize(obj, &length); - return __Pyx__PyBytes_AsDouble(obj, s, length); - } - return __Pyx_PyUnicode_AsDouble_WithSpaces(obj); -#else - return __Pyx_SlowPyString_AsDouble(obj); -#endif -} - -/* FetchSharedCythonModule.proto */ -static PyObject *__Pyx_FetchSharedCythonABIModule(void); - -/* dict_setdefault.proto */ -static CYTHON_INLINE PyObject *__Pyx_PyDict_SetDefault(PyObject *d, PyObject *key, PyObject *default_value, int is_safe_type); - -/* FetchCommonType.proto */ -static PyTypeObject* __Pyx_FetchCommonTypeFromSpec(PyTypeObject *metaclass, PyObject *module, PyType_Spec *spec, PyObject *bases); - -/* CommonTypesMetaclass.proto */ -static int __pyx_CommonTypesMetaclass_init(PyObject *module); -#define __Pyx_CommonTypesMetaclass_USED - -/* CallTypeTraverse.proto */ -#if !CYTHON_USE_TYPE_SPECS || (!CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x03090000) -#define __Pyx_call_type_traverse(o, always_call, visit, arg) 0 -#else -static int __Pyx_call_type_traverse(PyObject *o, int always_call, visitproc visit, void *arg); -#endif - -/* PyMethodNew.proto */ -static PyObject *__Pyx_PyMethod_New(PyObject *func, PyObject *self, PyObject *typ); - -/* PyVectorcallFastCallDict.proto */ -#if CYTHON_METH_FASTCALL && (CYTHON_VECTORCALL || CYTHON_BACKPORT_VECTORCALL) -static CYTHON_INLINE PyObject *__Pyx_PyVectorcall_FastCallDict(PyObject *func, __pyx_vectorcallfunc vc, PyObject *const *args, size_t nargs, PyObject *kw); -#endif - -/* CythonFunctionShared.proto */ -#define __Pyx_CyFunction_USED -#define __Pyx_CYFUNCTION_STATICMETHOD 0x01 -#define __Pyx_CYFUNCTION_CLASSMETHOD 0x02 -#define __Pyx_CYFUNCTION_CCLASS 0x04 -#define __Pyx_CYFUNCTION_COROUTINE 0x08 -#define __Pyx_CyFunction_GetClosure(f)\ - (((__pyx_CyFunctionObject *) (f))->func_closure) -#if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API - #define __Pyx_CyFunction_GetClassObj(f)\ - (((__pyx_CyFunctionObject *) (f))->func_classobj) -#else - #define __Pyx_CyFunction_GetClassObj(f)\ - ((PyObject*) ((PyCMethodObject *) (f))->mm_class) -#endif -#define __Pyx_CyFunction_SetClassObj(f, classobj)\ - __Pyx__CyFunction_SetClassObj((__pyx_CyFunctionObject *) (f), (classobj)) -#define __Pyx_CyFunction_Defaults(type, f)\ - ((type *)(((__pyx_CyFunctionObject *) (f))->defaults)) -#define __Pyx_CyFunction_SetDefaultsGetter(f, g)\ - ((__pyx_CyFunctionObject *) (f))->defaults_getter = (g) -typedef struct { -#if CYTHON_COMPILING_IN_LIMITED_API - PyObject_HEAD - PyObject *func; -#elif PY_VERSION_HEX < 0x030900B1 - PyCFunctionObject func; -#else - PyCMethodObject func; -#endif -#if CYTHON_BACKPORT_VECTORCALL ||\ - (CYTHON_COMPILING_IN_LIMITED_API && CYTHON_METH_FASTCALL) - __pyx_vectorcallfunc func_vectorcall; -#endif -#if CYTHON_COMPILING_IN_LIMITED_API - PyObject *func_weakreflist; -#endif - PyObject *func_dict; - PyObject *func_name; - PyObject *func_qualname; - PyObject *func_doc; - PyObject *func_globals; - PyObject *func_code; - PyObject *func_closure; -#if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API - PyObject *func_classobj; -#endif - PyObject *defaults; - int flags; - PyObject *defaults_tuple; - PyObject *defaults_kwdict; - PyObject *(*defaults_getter)(PyObject *); - PyObject *func_annotations; - PyObject *func_is_coroutine; -} __pyx_CyFunctionObject; -#undef __Pyx_CyOrPyCFunction_Check -#define __Pyx_CyFunction_Check(obj) __Pyx_TypeCheck(obj, __pyx_mstate_global->__pyx_CyFunctionType) -#define __Pyx_CyOrPyCFunction_Check(obj) __Pyx_TypeCheck2(obj, __pyx_mstate_global->__pyx_CyFunctionType, &PyCFunction_Type) -#define __Pyx_CyFunction_CheckExact(obj) __Pyx_IS_TYPE(obj, __pyx_mstate_global->__pyx_CyFunctionType) -static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(PyObject *func, void (*cfunc)(void)); -#undef __Pyx_IsSameCFunction -#define __Pyx_IsSameCFunction(func, cfunc) __Pyx__IsSameCyOrCFunction(func, cfunc) -static PyObject *__Pyx_CyFunction_Init(__pyx_CyFunctionObject* op, PyMethodDef *ml, - int flags, PyObject* qualname, - PyObject *closure, - PyObject *module, PyObject *globals, - PyObject* code); -static CYTHON_INLINE void __Pyx__CyFunction_SetClassObj(__pyx_CyFunctionObject* f, PyObject* classobj); -static CYTHON_INLINE PyObject *__Pyx_CyFunction_InitDefaults(PyObject *func, - PyTypeObject *defaults_type); -static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsTuple(PyObject *m, - PyObject *tuple); -static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsKwDict(PyObject *m, - PyObject *dict); -static CYTHON_INLINE void __Pyx_CyFunction_SetAnnotationsDict(PyObject *m, - PyObject *dict); -static int __pyx_CyFunction_init(PyObject *module); -#if CYTHON_METH_FASTCALL -static PyObject * __Pyx_CyFunction_Vectorcall_NOARGS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); -static PyObject * __Pyx_CyFunction_Vectorcall_O(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); -static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); -static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS_METHOD(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); -#if CYTHON_BACKPORT_VECTORCALL || CYTHON_COMPILING_IN_LIMITED_API -#define __Pyx_CyFunction_func_vectorcall(f) (((__pyx_CyFunctionObject*)f)->func_vectorcall) -#else -#define __Pyx_CyFunction_func_vectorcall(f) (((PyCFunctionObject*)f)->vectorcall) -#endif -#endif - -/* CythonFunction.proto */ -static PyObject *__Pyx_CyFunction_New(PyMethodDef *ml, - int flags, PyObject* qualname, - PyObject *closure, - PyObject *module, PyObject *globals, - PyObject* code); - -/* CLineInTraceback.proto */ -#if CYTHON_CLINE_IN_TRACEBACK && CYTHON_CLINE_IN_TRACEBACK_RUNTIME -static int __Pyx_CLineForTraceback(PyThreadState *tstate, int c_line); -#else -#define __Pyx_CLineForTraceback(tstate, c_line) (((CYTHON_CLINE_IN_TRACEBACK)) ? c_line : 0) -#endif - -/* CodeObjectCache.proto */ -#if CYTHON_COMPILING_IN_LIMITED_API -typedef PyObject __Pyx_CachedCodeObjectType; -#else -typedef PyCodeObject __Pyx_CachedCodeObjectType; -#endif -typedef struct { - __Pyx_CachedCodeObjectType* code_object; - int code_line; -} __Pyx_CodeObjectCacheEntry; -struct __Pyx_CodeObjectCache { - int count; - int max_count; - __Pyx_CodeObjectCacheEntry* entries; - #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING - __pyx_atomic_int_type accessor_count; - #endif -}; -static int __pyx_bisect_code_objects(__Pyx_CodeObjectCacheEntry* entries, int count, int code_line); -static __Pyx_CachedCodeObjectType *__pyx_find_code_object(int code_line); -static void __pyx_insert_code_object(int code_line, __Pyx_CachedCodeObjectType* code_object); - -/* AddTraceback.proto */ -static void __Pyx_AddTraceback(const char *funcname, int c_line, - int py_line, const char *filename); - -/* RealImag.proto */ -#if CYTHON_CCOMPLEX - #ifdef __cplusplus - #define __Pyx_CREAL(z) ((z).real()) - #define __Pyx_CIMAG(z) ((z).imag()) - #else - #define __Pyx_CREAL(z) (__real__(z)) - #define __Pyx_CIMAG(z) (__imag__(z)) - #endif -#else - #define __Pyx_CREAL(z) ((z).real) - #define __Pyx_CIMAG(z) ((z).imag) -#endif -#if defined(__cplusplus) && CYTHON_CCOMPLEX\ - && (defined(_WIN32) || defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 5 || __GNUC__ == 4 && __GNUC_MINOR__ >= 4 )) || __cplusplus >= 201103) - #define __Pyx_SET_CREAL(z,x) ((z).real(x)) - #define __Pyx_SET_CIMAG(z,y) ((z).imag(y)) -#else - #define __Pyx_SET_CREAL(z,x) __Pyx_CREAL(z) = (x) - #define __Pyx_SET_CIMAG(z,y) __Pyx_CIMAG(z) = (y) -#endif - -/* Arithmetic.proto */ -#if CYTHON_CCOMPLEX && (1) && (!0 || __cplusplus) - #define __Pyx_c_eq_double(a, b) ((a)==(b)) - #define __Pyx_c_sum_double(a, b) ((a)+(b)) - #define __Pyx_c_diff_double(a, b) ((a)-(b)) - #define __Pyx_c_prod_double(a, b) ((a)*(b)) - #define __Pyx_c_quot_double(a, b) ((a)/(b)) - #define __Pyx_c_neg_double(a) (-(a)) - #ifdef __cplusplus - #define __Pyx_c_is_zero_double(z) ((z)==(double)0) - #define __Pyx_c_conj_double(z) (::std::conj(z)) - #if 1 - #define __Pyx_c_abs_double(z) (::std::abs(z)) - #define __Pyx_c_pow_double(a, b) (::std::pow(a, b)) - #endif - #else - #define __Pyx_c_is_zero_double(z) ((z)==0) - #define __Pyx_c_conj_double(z) (conj(z)) - #if 1 - #define __Pyx_c_abs_double(z) (cabs(z)) - #define __Pyx_c_pow_double(a, b) (cpow(a, b)) - #endif - #endif -#else - static CYTHON_INLINE int __Pyx_c_eq_double(__pyx_t_double_complex, __pyx_t_double_complex); - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_sum_double(__pyx_t_double_complex, __pyx_t_double_complex); - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_diff_double(__pyx_t_double_complex, __pyx_t_double_complex); - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_prod_double(__pyx_t_double_complex, __pyx_t_double_complex); - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_quot_double(__pyx_t_double_complex, __pyx_t_double_complex); - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_neg_double(__pyx_t_double_complex); - static CYTHON_INLINE int __Pyx_c_is_zero_double(__pyx_t_double_complex); - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_conj_double(__pyx_t_double_complex); - #if 1 - static CYTHON_INLINE double __Pyx_c_abs_double(__pyx_t_double_complex); - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_pow_double(__pyx_t_double_complex, __pyx_t_double_complex); - #endif -#endif - -/* FromPy.proto */ -static __pyx_t_double_complex __Pyx_PyComplex_As___pyx_t_double_complex(PyObject*); - -/* GCCDiagnostics.proto */ -#if !defined(__INTEL_COMPILER) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) -#define __Pyx_HAS_GCC_DIAGNOSTIC -#endif - -/* ToPy.proto */ -#define __pyx_PyComplex_FromComplex(z)\ - PyComplex_FromDoubles((double)__Pyx_CREAL(z),\ - (double)__Pyx_CIMAG(z)) - -/* CIntFromPy.proto */ -static CYTHON_INLINE int __Pyx_PyLong_As_int(PyObject *); - -/* PyObjectVectorCallKwBuilder.proto */ -CYTHON_UNUSED static int __Pyx_VectorcallBuilder_AddArg_Check(PyObject *key, PyObject *value, PyObject *builder, PyObject **args, int n); -#if CYTHON_VECTORCALL -#if PY_VERSION_HEX >= 0x03090000 -#define __Pyx_Object_Vectorcall_CallFromBuilder PyObject_Vectorcall -#else -#define __Pyx_Object_Vectorcall_CallFromBuilder _PyObject_Vectorcall -#endif -#define __Pyx_MakeVectorcallBuilderKwds(n) PyTuple_New(n) -static int __Pyx_VectorcallBuilder_AddArg(PyObject *key, PyObject *value, PyObject *builder, PyObject **args, int n); -static int __Pyx_VectorcallBuilder_AddArgStr(const char *key, PyObject *value, PyObject *builder, PyObject **args, int n); -#else -#define __Pyx_Object_Vectorcall_CallFromBuilder __Pyx_PyObject_FastCallDict -#define __Pyx_MakeVectorcallBuilderKwds(n) __Pyx_PyDict_NewPresized(n) -#define __Pyx_VectorcallBuilder_AddArg(key, value, builder, args, n) PyDict_SetItem(builder, key, value) -#define __Pyx_VectorcallBuilder_AddArgStr(key, value, builder, args, n) PyDict_SetItemString(builder, key, value) -#endif - -/* CIntToPy.proto */ -static CYTHON_INLINE PyObject* __Pyx_PyLong_From_long(long value); - -/* CIntToPy.proto */ -static CYTHON_INLINE PyObject* __Pyx_PyLong_From_int(int value); - -/* FormatTypeName.proto */ -#if CYTHON_COMPILING_IN_LIMITED_API -typedef PyObject *__Pyx_TypeName; -#define __Pyx_FMT_TYPENAME "%U" -#define __Pyx_DECREF_TypeName(obj) Py_XDECREF(obj) -#if __PYX_LIMITED_VERSION_HEX >= 0x030d0000 -#define __Pyx_PyType_GetFullyQualifiedName PyType_GetFullyQualifiedName -#else -static __Pyx_TypeName __Pyx_PyType_GetFullyQualifiedName(PyTypeObject* tp); -#endif -#else // !LIMITED_API -typedef const char *__Pyx_TypeName; -#define __Pyx_FMT_TYPENAME "%.200s" -#define __Pyx_PyType_GetFullyQualifiedName(tp) ((tp)->tp_name) -#define __Pyx_DECREF_TypeName(obj) -#endif - -/* CIntFromPy.proto */ -static CYTHON_INLINE long __Pyx_PyLong_As_long(PyObject *); - -/* FastTypeChecks.proto */ -#if CYTHON_COMPILING_IN_CPYTHON -#define __Pyx_TypeCheck(obj, type) __Pyx_IsSubtype(Py_TYPE(obj), (PyTypeObject *)type) -#define __Pyx_TypeCheck2(obj, type1, type2) __Pyx_IsAnySubtype2(Py_TYPE(obj), (PyTypeObject *)type1, (PyTypeObject *)type2) -static CYTHON_INLINE int __Pyx_IsSubtype(PyTypeObject *a, PyTypeObject *b); -static CYTHON_INLINE int __Pyx_IsAnySubtype2(PyTypeObject *cls, PyTypeObject *a, PyTypeObject *b); -static CYTHON_INLINE int __Pyx_PyErr_GivenExceptionMatches(PyObject *err, PyObject *type); -static CYTHON_INLINE int __Pyx_PyErr_GivenExceptionMatches2(PyObject *err, PyObject *type1, PyObject *type2); -#else -#define __Pyx_TypeCheck(obj, type) PyObject_TypeCheck(obj, (PyTypeObject *)type) -#define __Pyx_TypeCheck2(obj, type1, type2) (PyObject_TypeCheck(obj, (PyTypeObject *)type1) || PyObject_TypeCheck(obj, (PyTypeObject *)type2)) -#define __Pyx_PyErr_GivenExceptionMatches(err, type) PyErr_GivenExceptionMatches(err, type) -static CYTHON_INLINE int __Pyx_PyErr_GivenExceptionMatches2(PyObject *err, PyObject *type1, PyObject *type2) { - return PyErr_GivenExceptionMatches(err, type1) || PyErr_GivenExceptionMatches(err, type2); -} -#endif -#define __Pyx_PyErr_ExceptionMatches2(err1, err2) __Pyx_PyErr_GivenExceptionMatches2(__Pyx_PyErr_CurrentExceptionType(), err1, err2) -#define __Pyx_PyException_Check(obj) __Pyx_TypeCheck(obj, PyExc_Exception) -#ifdef PyExceptionInstance_Check - #define __Pyx_PyBaseException_Check(obj) PyExceptionInstance_Check(obj) -#else - #define __Pyx_PyBaseException_Check(obj) __Pyx_TypeCheck(obj, PyExc_BaseException) -#endif - -/* SwapException.proto */ -#if CYTHON_FAST_THREAD_STATE -#define __Pyx_ExceptionSwap(type, value, tb) __Pyx__ExceptionSwap(__pyx_tstate, type, value, tb) -static CYTHON_INLINE void __Pyx__ExceptionSwap(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb); -#else -static CYTHON_INLINE void __Pyx_ExceptionSwap(PyObject **type, PyObject **value, PyObject **tb); -#endif - -/* PyObjectCall2Args.proto */ -static CYTHON_INLINE PyObject* __Pyx_PyObject_Call2Args(PyObject* function, PyObject* arg1, PyObject* arg2); - -/* PyObjectCallMethod1.proto */ -static PyObject* __Pyx_PyObject_CallMethod1(PyObject* obj, PyObject* method_name, PyObject* arg); - -/* ReturnWithStopIteration.proto */ -static CYTHON_INLINE void __Pyx_ReturnWithStopIteration(PyObject* value, int async, int iternext); - -/* CoroutineBase.proto */ -struct __pyx_CoroutineObject; -typedef PyObject *(*__pyx_coroutine_body_t)(struct __pyx_CoroutineObject *, PyThreadState *, PyObject *); -#if CYTHON_USE_EXC_INFO_STACK -#define __Pyx_ExcInfoStruct _PyErr_StackItem -#else -typedef struct { - PyObject *exc_type; - PyObject *exc_value; - PyObject *exc_traceback; -} __Pyx_ExcInfoStruct; -#endif -typedef struct __pyx_CoroutineObject { - PyObject_HEAD - __pyx_coroutine_body_t body; - PyObject *closure; - __Pyx_ExcInfoStruct gi_exc_state; - PyObject *gi_weakreflist; - PyObject *classobj; - PyObject *yieldfrom; - __Pyx_pyiter_sendfunc yieldfrom_am_send; - PyObject *gi_name; - PyObject *gi_qualname; - PyObject *gi_modulename; - PyObject *gi_code; - PyObject *gi_frame; -#if CYTHON_USE_SYS_MONITORING && (CYTHON_PROFILE || CYTHON_TRACE) - PyMonitoringState __pyx_pymonitoring_state[__Pyx_MonitoringEventTypes_CyGen_count]; - uint64_t __pyx_pymonitoring_version; -#endif - int resume_label; - char is_running; -} __pyx_CoroutineObject; -static __pyx_CoroutineObject *__Pyx__Coroutine_New( - PyTypeObject *type, __pyx_coroutine_body_t body, PyObject *code, PyObject *closure, - PyObject *name, PyObject *qualname, PyObject *module_name); -static __pyx_CoroutineObject *__Pyx__Coroutine_NewInit( - __pyx_CoroutineObject *gen, __pyx_coroutine_body_t body, PyObject *code, PyObject *closure, - PyObject *name, PyObject *qualname, PyObject *module_name); -static CYTHON_INLINE void __Pyx_Coroutine_ExceptionClear(__Pyx_ExcInfoStruct *self); -static int __Pyx_Coroutine_clear(PyObject *self); -static __Pyx_PySendResult __Pyx_Coroutine_AmSend(PyObject *self, PyObject *value, PyObject **retval); -static PyObject *__Pyx_Coroutine_Send(PyObject *self, PyObject *value); -static __Pyx_PySendResult __Pyx_Coroutine_Close(PyObject *self, PyObject **retval); -static PyObject *__Pyx_Coroutine_Throw(PyObject *gen, PyObject *args); -#if CYTHON_USE_EXC_INFO_STACK -#define __Pyx_Coroutine_SwapException(self) -#define __Pyx_Coroutine_ResetAndClearException(self) __Pyx_Coroutine_ExceptionClear(&(self)->gi_exc_state) -#else -#define __Pyx_Coroutine_SwapException(self) {\ - __Pyx_ExceptionSwap(&(self)->gi_exc_state.exc_type, &(self)->gi_exc_state.exc_value, &(self)->gi_exc_state.exc_traceback);\ - __Pyx_Coroutine_ResetFrameBackpointer(&(self)->gi_exc_state);\ - } -#define __Pyx_Coroutine_ResetAndClearException(self) {\ - __Pyx_ExceptionReset((self)->gi_exc_state.exc_type, (self)->gi_exc_state.exc_value, (self)->gi_exc_state.exc_traceback);\ - (self)->gi_exc_state.exc_type = (self)->gi_exc_state.exc_value = (self)->gi_exc_state.exc_traceback = NULL;\ - } -#endif -#if CYTHON_FAST_THREAD_STATE -#define __Pyx_PyGen_FetchStopIterationValue(pvalue)\ - __Pyx_PyGen__FetchStopIterationValue(__pyx_tstate, pvalue) -#else -#define __Pyx_PyGen_FetchStopIterationValue(pvalue)\ - __Pyx_PyGen__FetchStopIterationValue(__Pyx_PyThreadState_Current, pvalue) -#endif -static int __Pyx_PyGen__FetchStopIterationValue(PyThreadState *tstate, PyObject **pvalue); -static CYTHON_INLINE void __Pyx_Coroutine_ResetFrameBackpointer(__Pyx_ExcInfoStruct *exc_state); -static char __Pyx_Coroutine_test_and_set_is_running(__pyx_CoroutineObject *gen); -static void __Pyx_Coroutine_unset_is_running(__pyx_CoroutineObject *gen); -static char __Pyx_Coroutine_get_is_running(__pyx_CoroutineObject *gen); -static PyObject *__Pyx_Coroutine_get_is_running_getter(PyObject *gen, void *closure); -#if __PYX_HAS_PY_AM_SEND == 2 -static void __Pyx_SetBackportTypeAmSend(PyTypeObject *type, __Pyx_PyAsyncMethodsStruct *static_amsend_methods, __Pyx_pyiter_sendfunc am_send); -#endif -static PyObject *__Pyx_Coroutine_fail_reduce_ex(PyObject *self, PyObject *arg); - -/* Generator.proto */ -#define __Pyx_Generator_USED -#define __Pyx_Generator_CheckExact(obj) __Pyx_IS_TYPE(obj, __pyx_mstate_global->__pyx_GeneratorType) -#define __Pyx_Generator_New(body, code, closure, name, qualname, module_name)\ - __Pyx__Coroutine_New(__pyx_mstate_global->__pyx_GeneratorType, body, code, closure, name, qualname, module_name) -static PyObject *__Pyx_Generator_Next(PyObject *self); -static int __pyx_Generator_init(PyObject *module); -static CYTHON_INLINE PyObject *__Pyx_Generator_GetInlinedResult(PyObject *self); - -/* GetRuntimeVersion.proto */ -static unsigned long __Pyx_get_runtime_version(void); - -/* CheckBinaryVersion.proto */ -static int __Pyx_check_binary_version(unsigned long ct_version, unsigned long rt_version, int allow_newer); - -/* MultiPhaseInitModuleState.proto */ -#if CYTHON_PEP489_MULTI_PHASE_INIT && CYTHON_USE_MODULE_STATE -static PyObject *__Pyx_State_FindModule(void*); -static int __Pyx_State_AddModule(PyObject* module, void*); -static int __Pyx_State_RemoveModule(void*); -#elif CYTHON_USE_MODULE_STATE -#define __Pyx_State_FindModule PyState_FindModule -#define __Pyx_State_AddModule PyState_AddModule -#define __Pyx_State_RemoveModule PyState_RemoveModule -#endif - -/* #### Code section: module_declarations ### */ -/* CythonABIVersion.proto */ -#if CYTHON_COMPILING_IN_LIMITED_API - #if CYTHON_METH_FASTCALL - #define __PYX_FASTCALL_ABI_SUFFIX "_fastcall" - #else - #define __PYX_FASTCALL_ABI_SUFFIX - #endif - #define __PYX_LIMITED_ABI_SUFFIX "limited" __PYX_FASTCALL_ABI_SUFFIX __PYX_AM_SEND_ABI_SUFFIX -#else - #define __PYX_LIMITED_ABI_SUFFIX -#endif -#if __PYX_HAS_PY_AM_SEND == 1 - #define __PYX_AM_SEND_ABI_SUFFIX -#elif __PYX_HAS_PY_AM_SEND == 2 - #define __PYX_AM_SEND_ABI_SUFFIX "amsendbackport" -#else - #define __PYX_AM_SEND_ABI_SUFFIX "noamsend" -#endif -#ifndef __PYX_MONITORING_ABI_SUFFIX - #define __PYX_MONITORING_ABI_SUFFIX -#endif -#if CYTHON_USE_TP_FINALIZE - #define __PYX_TP_FINALIZE_ABI_SUFFIX -#else - #define __PYX_TP_FINALIZE_ABI_SUFFIX "nofinalize" -#endif -#if CYTHON_USE_FREELISTS || !defined(__Pyx_AsyncGen_USED) - #define __PYX_FREELISTS_ABI_SUFFIX -#else - #define __PYX_FREELISTS_ABI_SUFFIX "nofreelists" -#endif -#define CYTHON_ABI __PYX_ABI_VERSION __PYX_LIMITED_ABI_SUFFIX __PYX_MONITORING_ABI_SUFFIX __PYX_TP_FINALIZE_ABI_SUFFIX __PYX_FREELISTS_ABI_SUFFIX __PYX_AM_SEND_ABI_SUFFIX -#define __PYX_ABI_MODULE_NAME "_cython_" CYTHON_ABI -#define __PYX_TYPE_MODULE_PREFIX __PYX_ABI_MODULE_NAME "." - - -/* Module declarations from "cython" */ - -/* Module declarations from "fontTools.cu2qu.cu2qu" */ -static CYTHON_INLINE double __pyx_f_9fontTools_5cu2qu_5cu2qu_dot(__pyx_t_double_complex, __pyx_t_double_complex); /*proto*/ -static PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu__complex_div_by_real(__pyx_t_double_complex, double); /*proto*/ -static CYTHON_INLINE PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu_calc_cubic_points(__pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex); /*proto*/ -static CYTHON_INLINE PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu_calc_cubic_parameters(__pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex); /*proto*/ -static CYTHON_INLINE PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_n_iter(__pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex, PyObject *); /*proto*/ -static CYTHON_INLINE PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_two(__pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex); /*proto*/ -static CYTHON_INLINE PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_three(__pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex); /*proto*/ -static CYTHON_INLINE __pyx_t_double_complex __pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_approx_control(double, __pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex); /*proto*/ -static CYTHON_INLINE __pyx_t_double_complex __pyx_f_9fontTools_5cu2qu_5cu2qu_calc_intersect(__pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex); /*proto*/ -static int __pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_farthest_fit_inside(__pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex, double); /*proto*/ -static CYTHON_INLINE PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_approx_quadratic(PyObject *, double); /*proto*/ -static PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_approx_spline(PyObject *, int, double, int); /*proto*/ -/* #### Code section: typeinfo ### */ -/* #### Code section: before_global_var ### */ -#define __Pyx_MODULE_NAME "fontTools.cu2qu.cu2qu" -extern int __pyx_module_is_main_fontTools__cu2qu__cu2qu; -int __pyx_module_is_main_fontTools__cu2qu__cu2qu = 0; - -/* Implementation of "fontTools.cu2qu.cu2qu" */ -/* #### Code section: global_var ### */ -static PyObject *__pyx_builtin_AttributeError; -static PyObject *__pyx_builtin_ImportError; -static PyObject *__pyx_builtin_range; -static PyObject *__pyx_builtin_ZeroDivisionError; -static PyObject *__pyx_builtin_AssertionError; -/* #### Code section: string_decls ### */ -static const char __pyx_k_[] = "."; -static const char __pyx_k_a[] = "a"; -static const char __pyx_k_b[] = "b"; -static const char __pyx_k_c[] = "c"; -static const char __pyx_k_d[] = "d"; -static const char __pyx_k_i[] = "i"; -static const char __pyx_k_l[] = "l"; -static const char __pyx_k_n[] = "n"; -static const char __pyx_k_p[] = "p"; -static const char __pyx_k_s[] = "s"; -static const char __pyx_k__2[] = "?"; -static const char __pyx_k__3[] = "\200\001"; -static const char __pyx_k_a1[] = "a1"; -static const char __pyx_k_b1[] = "b1"; -static const char __pyx_k_c1[] = "c1"; -static const char __pyx_k_d1[] = "d1"; -static const char __pyx_k_dt[] = "dt"; -static const char __pyx_k_gc[] = "gc"; -static const char __pyx_k_p0[] = "p0"; -static const char __pyx_k_p1[] = "p1"; -static const char __pyx_k_p2[] = "p2"; -static const char __pyx_k_p3[] = "p3"; -static const char __pyx_k_t1[] = "t1"; -static const char __pyx_k_NAN[] = "NAN"; -static const char __pyx_k_NaN[] = "NaN"; -static const char __pyx_k_all[] = "__all__"; -static const char __pyx_k_pop[] = "pop"; -static const char __pyx_k_func[] = "__func__"; -static const char __pyx_k_imag[] = "imag"; -static const char __pyx_k_main[] = "__main__"; -static const char __pyx_k_math[] = "math"; -static const char __pyx_k_name[] = "__name__"; -static const char __pyx_k_next[] = "next"; -static const char __pyx_k_real[] = "real"; -static const char __pyx_k_send[] = "send"; -static const char __pyx_k_spec[] = "__spec__"; -static const char __pyx_k_t1_2[] = "t1_2"; -static const char __pyx_k_test[] = "__test__"; -static const char __pyx_k_Error[] = "Error"; -static const char __pyx_k_MAX_N[] = "MAX_N"; -static const char __pyx_k_close[] = "close"; -static const char __pyx_k_curve[] = "curve"; -static const char __pyx_k_isnan[] = "isnan"; -static const char __pyx_k_range[] = "range"; -static const char __pyx_k_throw[] = "throw"; -static const char __pyx_k_value[] = "value"; -static const char __pyx_k_curves[] = "curves"; -static const char __pyx_k_enable[] = "enable"; -static const char __pyx_k_errors[] = "errors"; -static const char __pyx_k_last_i[] = "last_i"; -static const char __pyx_k_module[] = "__module__"; -static const char __pyx_k_spline[] = "spline"; -static const char __pyx_k_delta_2[] = "delta_2"; -static const char __pyx_k_delta_3[] = "delta_3"; -static const char __pyx_k_disable[] = "disable"; -static const char __pyx_k_max_err[] = "max_err"; -static const char __pyx_k_splines[] = "splines"; -static const char __pyx_k_COMPILED[] = "COMPILED"; -static const char __pyx_k_qualname[] = "__qualname__"; -static const char __pyx_k_set_name[] = "__set_name__"; -static const char __pyx_k_isenabled[] = "isenabled"; -static const char __pyx_k_Cu2QuError[] = "Cu2QuError"; -static const char __pyx_k_max_errors[] = "max_errors"; -static const char __pyx_k_ImportError[] = "ImportError"; -static const char __pyx_k_initializing[] = "_initializing"; -static const char __pyx_k_is_coroutine[] = "_is_coroutine"; -static const char __pyx_k_all_quadratic[] = "all_quadratic"; -static const char __pyx_k_AssertionError[] = "AssertionError"; -static const char __pyx_k_AttributeError[] = "AttributeError"; -static const char __pyx_k_ZeroDivisionError[] = "ZeroDivisionError"; -static const char __pyx_k_asyncio_coroutines[] = "asyncio.coroutines"; -static const char __pyx_k_cline_in_traceback[] = "cline_in_traceback"; -static const char __pyx_k_curve_to_quadratic[] = "curve_to_quadratic"; -static const char __pyx_k_ApproxNotFoundError[] = "ApproxNotFoundError"; -static const char __pyx_k_curves_to_quadratic[] = "curves_to_quadratic"; -static const char __pyx_k_fontTools_cu2qu_cu2qu[] = "fontTools.cu2qu.cu2qu"; -static const char __pyx_k_split_cubic_into_n_gen[] = "_split_cubic_into_n_gen"; -static const char __pyx_k_Lib_fontTools_cu2qu_cu2qu_py[] = "Lib/fontTools/cu2qu/cu2qu.py"; -static const char __pyx_k_curves_to_quadratic_line_503[] = "curves_to_quadratic (line 503)"; -static const char __pyx_k_AWBc_U_U_3fBa_AWCy_7_2QgQgT_a_Q[] = "\200\001\360\006\000()\360*\000\005\r\210A\210W\220B\220c\230\024\230U\240!\340\004\010\210\005\210U\220!\2203\220f\230B\230a\330\010\021\320\021$\240A\240W\250C\250y\270\001\330\010\013\2107\220'\230\021\340\014\023\2202\220Q\220g\230Q\230g\240T\250\025\250a\340\004\n\320\n\035\230Q\230a"; -static const char __pyx_k_J_Qawb_4uG4y_3a_3c_1A_avRq_T_AV[] = "\200\001\340,-\360J\001\000\005\016\210Q\210a\210w\220b\230\003\2304\230u\240G\2504\250y\270\001\330\004\013\2103\210a\210|\2303\230c\240\021\240!\340\004\010\210\003\2101\210A\330\004\016\210a\210v\220R\220q\330\004\r\210T\220\021\330\004\010\210\001\330\004\005\330\010\021\320\021$\240A\240V\2501\250D\260\003\260:\270Q\270d\300!\330\010\013\2107\220#\220Q\330\014\017\210r\220\023\220A\330\020\021\330\014\021\220\021\330\014\025\220Q\330\014\r\330\010\017\210q\220\005\220Q\330\010\r\210R\210r\220\023\220B\220a\330\010\013\2102\210S\220\001\340\014\023\2201\220B\220a\220w\230a\230w\240d\250%\250x\260t\270:\300Q\340\004\n\320\n\035\230Q\230a"; -static const char __pyx_k_Return_quadratic_Bezier_splines[] = "Return quadratic Bezier splines approximating the input cubic Beziers.\n\n Args:\n curves: A sequence of *n* curves, each curve being a sequence of four\n 2D tuples.\n max_errors: A sequence of *n* floats representing the maximum permissible\n deviation from each of the cubic Bezier curves.\n all_quadratic (bool): If True (default) returned values are a\n quadratic spline. If False, they are either a single quadratic\n curve or a single cubic curve.\n\n Example::\n\n >>> curves_to_quadratic( [\n ... [ (50,50), (100,100), (150,100), (200,50) ],\n ... [ (75,50), (120,100), (150,75), (200,60) ]\n ... ], [1,1] )\n [[(50.0, 50.0), (75.0, 75.0), (125.0, 91.66666666666666), (175.0, 75.0), (200.0, 50.0)], [(75.0, 50.0), (97.5, 75.0), (135.41666666666666, 82.08333333333333), (175.0, 67.5), (200.0, 60.0)]]\n\n The returned splines have \"implied oncurve points\" suitable for use in\n TrueType ``glif`` outlines - i.e. in the first spline returned above,\n the first quadratic segment runs from (50,50) to\n ( (75 + 125)/2 , (120 + 91.666..)/2 ) = (100, 83.333...).\n\n Returns:\n If all_quadratic is True, a list of splines, each spline being a list\n of 2D tuples.\n\n If all_quadratic is False, a list of curves, each curve being a quadratic\n (length 3), or cubic (length 4).\n\n Raises:\n fontTools.cu2qu.Errors.ApproxNotFoundError: if no suitable approximation\n can be found for all curves with the given parameters.\n "; -/* #### Code section: decls ### */ -static PyObject *__pyx_pf_9fontTools_5cu2qu_5cu2qu__split_cubic_into_n_gen(CYTHON_UNUSED PyObject *__pyx_self, __pyx_t_double_complex __pyx_v_p0, __pyx_t_double_complex __pyx_v_p1, __pyx_t_double_complex __pyx_v_p2, __pyx_t_double_complex __pyx_v_p3, int __pyx_v_n); /* proto */ -static PyObject *__pyx_pf_9fontTools_5cu2qu_5cu2qu_3curve_to_quadratic(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_curve, double __pyx_v_max_err, int __pyx_v_all_quadratic); /* proto */ -static PyObject *__pyx_pf_9fontTools_5cu2qu_5cu2qu_5curves_to_quadratic(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_curves, PyObject *__pyx_v_max_errors, int __pyx_v_all_quadratic); /* proto */ -static PyObject *__pyx_tp_new_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen(PyTypeObject *t, PyObject *a, PyObject *k); /*proto*/ -/* #### Code section: late_includes ### */ -/* #### Code section: module_state ### */ -/* SmallCodeConfig */ -#ifndef CYTHON_SMALL_CODE -#if defined(__clang__) - #define CYTHON_SMALL_CODE -#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) - #define CYTHON_SMALL_CODE __attribute__((cold)) -#else - #define CYTHON_SMALL_CODE -#endif -#endif - -typedef struct { - PyObject *__pyx_d; - PyObject *__pyx_b; - PyObject *__pyx_cython_runtime; - PyObject *__pyx_empty_tuple; - PyObject *__pyx_empty_bytes; - PyObject *__pyx_empty_unicode; - #ifdef __Pyx_CyFunction_USED - PyTypeObject *__pyx_CyFunctionType; - #endif - #ifdef __Pyx_FusedFunction_USED - PyTypeObject *__pyx_FusedFunctionType; - #endif - #ifdef __Pyx_Generator_USED - PyTypeObject *__pyx_GeneratorType; - #endif - #ifdef __Pyx_IterableCoroutine_USED - PyTypeObject *__pyx_IterableCoroutineType; - #endif - #ifdef __Pyx_Coroutine_USED - PyTypeObject *__pyx_CoroutineAwaitType; - #endif - #ifdef __Pyx_Coroutine_USED - PyTypeObject *__pyx_CoroutineType; - #endif - PyObject *__pyx_type_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen; - PyTypeObject *__pyx_ptype_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen; - __Pyx_CachedCFunction __pyx_umethod_PyDict_Type_pop; - PyObject *__pyx_codeobj_tab[3]; - PyObject *__pyx_string_tab[79]; - PyObject *__pyx_int_1; - PyObject *__pyx_int_2; - PyObject *__pyx_int_3; - PyObject *__pyx_int_4; - PyObject *__pyx_int_6; - PyObject *__pyx_int_100; -/* #### Code section: module_state_contents ### */ -/* IterNextPlain.module_state_decls */ -#if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x030A0000 -PyObject *__Pyx_GetBuiltinNext_LimitedAPI_cache; -#endif - - -#if CYTHON_USE_FREELISTS -struct __pyx_obj_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen *__pyx_freelist_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen[8]; -int __pyx_freecount_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen; -#endif -/* CommonTypesMetaclass.module_state_decls */ -PyTypeObject *__pyx_CommonTypesMetaclassType; - -/* CachedMethodType.module_state_decls */ -#if CYTHON_COMPILING_IN_LIMITED_API -PyObject *__Pyx_CachedMethodType; -#endif - -/* CodeObjectCache.module_state_decls */ -struct __Pyx_CodeObjectCache __pyx_code_cache; - -/* #### Code section: module_state_end ### */ -} __pyx_mstatetype; - -#if CYTHON_USE_MODULE_STATE -#ifdef __cplusplus -namespace { -extern struct PyModuleDef __pyx_moduledef; -} /* anonymous namespace */ -#else -static struct PyModuleDef __pyx_moduledef; -#endif - -#define __pyx_mstate_global (__Pyx_PyModule_GetState(__Pyx_State_FindModule(&__pyx_moduledef))) - -#define __pyx_m (__Pyx_State_FindModule(&__pyx_moduledef)) -#else -static __pyx_mstatetype __pyx_mstate_global_static = -#ifdef __cplusplus - {}; -#else - {0}; -#endif -static __pyx_mstatetype * const __pyx_mstate_global = &__pyx_mstate_global_static; -#endif -/* #### Code section: constant_name_defines ### */ -#define __pyx_kp_u_ __pyx_string_tab[0] -#define __pyx_n_u_ApproxNotFoundError __pyx_string_tab[1] -#define __pyx_n_u_AssertionError __pyx_string_tab[2] -#define __pyx_n_u_AttributeError __pyx_string_tab[3] -#define __pyx_n_u_COMPILED __pyx_string_tab[4] -#define __pyx_n_u_Cu2QuError __pyx_string_tab[5] -#define __pyx_n_u_Error __pyx_string_tab[6] -#define __pyx_n_u_ImportError __pyx_string_tab[7] -#define __pyx_kp_u_Lib_fontTools_cu2qu_cu2qu_py __pyx_string_tab[8] -#define __pyx_n_u_MAX_N __pyx_string_tab[9] -#define __pyx_n_u_NAN __pyx_string_tab[10] -#define __pyx_n_u_NaN __pyx_string_tab[11] -#define __pyx_kp_u_Return_quadratic_Bezier_splines __pyx_string_tab[12] -#define __pyx_n_u_ZeroDivisionError __pyx_string_tab[13] -#define __pyx_kp_u__2 __pyx_string_tab[14] -#define __pyx_n_u_a __pyx_string_tab[15] -#define __pyx_n_u_a1 __pyx_string_tab[16] -#define __pyx_n_u_all __pyx_string_tab[17] -#define __pyx_n_u_all_quadratic __pyx_string_tab[18] -#define __pyx_n_u_asyncio_coroutines __pyx_string_tab[19] -#define __pyx_n_u_b __pyx_string_tab[20] -#define __pyx_n_u_b1 __pyx_string_tab[21] -#define __pyx_n_u_c __pyx_string_tab[22] -#define __pyx_n_u_c1 __pyx_string_tab[23] -#define __pyx_n_u_cline_in_traceback __pyx_string_tab[24] -#define __pyx_n_u_close __pyx_string_tab[25] -#define __pyx_n_u_curve __pyx_string_tab[26] -#define __pyx_n_u_curve_to_quadratic __pyx_string_tab[27] -#define __pyx_n_u_curves __pyx_string_tab[28] -#define __pyx_n_u_curves_to_quadratic __pyx_string_tab[29] -#define __pyx_kp_u_curves_to_quadratic_line_503 __pyx_string_tab[30] -#define __pyx_n_u_d __pyx_string_tab[31] -#define __pyx_n_u_d1 __pyx_string_tab[32] -#define __pyx_n_u_delta_2 __pyx_string_tab[33] -#define __pyx_n_u_delta_3 __pyx_string_tab[34] -#define __pyx_kp_u_disable __pyx_string_tab[35] -#define __pyx_n_u_dt __pyx_string_tab[36] -#define __pyx_kp_u_enable __pyx_string_tab[37] -#define __pyx_n_u_errors __pyx_string_tab[38] -#define __pyx_n_u_fontTools_cu2qu_cu2qu __pyx_string_tab[39] -#define __pyx_n_u_func __pyx_string_tab[40] -#define __pyx_kp_u_gc __pyx_string_tab[41] -#define __pyx_n_u_i __pyx_string_tab[42] -#define __pyx_n_u_imag __pyx_string_tab[43] -#define __pyx_n_u_initializing __pyx_string_tab[44] -#define __pyx_n_u_is_coroutine __pyx_string_tab[45] -#define __pyx_kp_u_isenabled __pyx_string_tab[46] -#define __pyx_n_u_isnan __pyx_string_tab[47] -#define __pyx_n_u_l __pyx_string_tab[48] -#define __pyx_n_u_last_i __pyx_string_tab[49] -#define __pyx_n_u_main __pyx_string_tab[50] -#define __pyx_n_u_math __pyx_string_tab[51] -#define __pyx_n_u_max_err __pyx_string_tab[52] -#define __pyx_n_u_max_errors __pyx_string_tab[53] -#define __pyx_n_u_module __pyx_string_tab[54] -#define __pyx_n_u_n __pyx_string_tab[55] -#define __pyx_n_u_name __pyx_string_tab[56] -#define __pyx_n_u_next __pyx_string_tab[57] -#define __pyx_n_u_p __pyx_string_tab[58] -#define __pyx_n_u_p0 __pyx_string_tab[59] -#define __pyx_n_u_p1 __pyx_string_tab[60] -#define __pyx_n_u_p2 __pyx_string_tab[61] -#define __pyx_n_u_p3 __pyx_string_tab[62] -#define __pyx_n_u_pop __pyx_string_tab[63] -#define __pyx_n_u_qualname __pyx_string_tab[64] -#define __pyx_n_u_range __pyx_string_tab[65] -#define __pyx_n_u_real __pyx_string_tab[66] -#define __pyx_n_u_s __pyx_string_tab[67] -#define __pyx_n_u_send __pyx_string_tab[68] -#define __pyx_n_u_set_name __pyx_string_tab[69] -#define __pyx_n_u_spec __pyx_string_tab[70] -#define __pyx_n_u_spline __pyx_string_tab[71] -#define __pyx_n_u_splines __pyx_string_tab[72] -#define __pyx_n_u_split_cubic_into_n_gen __pyx_string_tab[73] -#define __pyx_n_u_t1 __pyx_string_tab[74] -#define __pyx_n_u_t1_2 __pyx_string_tab[75] -#define __pyx_n_u_test __pyx_string_tab[76] -#define __pyx_n_u_throw __pyx_string_tab[77] -#define __pyx_n_u_value __pyx_string_tab[78] -/* #### Code section: module_state_clear ### */ -#if CYTHON_USE_MODULE_STATE -static CYTHON_SMALL_CODE int __pyx_m_clear(PyObject *m) { - __pyx_mstatetype *clear_module_state = __Pyx_PyModule_GetState(m); - if (!clear_module_state) return 0; - Py_CLEAR(clear_module_state->__pyx_d); - Py_CLEAR(clear_module_state->__pyx_b); - Py_CLEAR(clear_module_state->__pyx_cython_runtime); - Py_CLEAR(clear_module_state->__pyx_empty_tuple); - Py_CLEAR(clear_module_state->__pyx_empty_bytes); - Py_CLEAR(clear_module_state->__pyx_empty_unicode); - #ifdef __Pyx_CyFunction_USED - Py_CLEAR(clear_module_state->__pyx_CyFunctionType); - #endif - #ifdef __Pyx_FusedFunction_USED - Py_CLEAR(clear_module_state->__pyx_FusedFunctionType); - #endif - #if CYTHON_PEP489_MULTI_PHASE_INIT - __Pyx_State_RemoveModule(NULL); - #endif - Py_CLEAR(clear_module_state->__pyx_ptype_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen); - Py_CLEAR(clear_module_state->__pyx_type_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen); - for (int i=0; i<3; ++i) { Py_CLEAR(clear_module_state->__pyx_codeobj_tab[i]); } - for (int i=0; i<79; ++i) { Py_CLEAR(clear_module_state->__pyx_string_tab[i]); } - Py_CLEAR(clear_module_state->__pyx_int_1); - Py_CLEAR(clear_module_state->__pyx_int_2); - Py_CLEAR(clear_module_state->__pyx_int_3); - Py_CLEAR(clear_module_state->__pyx_int_4); - Py_CLEAR(clear_module_state->__pyx_int_6); - Py_CLEAR(clear_module_state->__pyx_int_100); - return 0; -} -#endif -/* #### Code section: module_state_traverse ### */ -#if CYTHON_USE_MODULE_STATE -static CYTHON_SMALL_CODE int __pyx_m_traverse(PyObject *m, visitproc visit, void *arg) { - __pyx_mstatetype *traverse_module_state = __Pyx_PyModule_GetState(m); - if (!traverse_module_state) return 0; - Py_VISIT(traverse_module_state->__pyx_d); - Py_VISIT(traverse_module_state->__pyx_b); - Py_VISIT(traverse_module_state->__pyx_cython_runtime); - __Pyx_VISIT_CONST(traverse_module_state->__pyx_empty_tuple); - __Pyx_VISIT_CONST(traverse_module_state->__pyx_empty_bytes); - __Pyx_VISIT_CONST(traverse_module_state->__pyx_empty_unicode); - #ifdef __Pyx_CyFunction_USED - Py_VISIT(traverse_module_state->__pyx_CyFunctionType); - #endif - #ifdef __Pyx_FusedFunction_USED - Py_VISIT(traverse_module_state->__pyx_FusedFunctionType); - #endif - Py_VISIT(traverse_module_state->__pyx_ptype_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen); - Py_VISIT(traverse_module_state->__pyx_type_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen); - for (int i=0; i<3; ++i) { __Pyx_VISIT_CONST(traverse_module_state->__pyx_codeobj_tab[i]); } - for (int i=0; i<79; ++i) { __Pyx_VISIT_CONST(traverse_module_state->__pyx_string_tab[i]); } - __Pyx_VISIT_CONST(traverse_module_state->__pyx_int_1); - __Pyx_VISIT_CONST(traverse_module_state->__pyx_int_2); - __Pyx_VISIT_CONST(traverse_module_state->__pyx_int_3); - __Pyx_VISIT_CONST(traverse_module_state->__pyx_int_4); - __Pyx_VISIT_CONST(traverse_module_state->__pyx_int_6); - __Pyx_VISIT_CONST(traverse_module_state->__pyx_int_100); - return 0; -} -#endif -/* #### Code section: module_code ### */ - -/* "fontTools/cu2qu/cu2qu.py":37 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.returns(cython.double) -*/ - -static CYTHON_INLINE double __pyx_f_9fontTools_5cu2qu_5cu2qu_dot(__pyx_t_double_complex __pyx_v_v1, __pyx_t_double_complex __pyx_v_v2) { - double __pyx_v_result; - double __pyx_r; - double __pyx_t_1; - int __pyx_t_2; - - /* "fontTools/cu2qu/cu2qu.py":51 - * double: Dot product. - * """ - * result = (v1 * v2.conjugate()).real # <<<<<<<<<<<<<< - * # When vectors are perpendicular (i.e. dot product is 0), the above expression may - * # yield slightly different results when running in pure Python vs C/Cython, -*/ - __pyx_t_1 = __Pyx_CREAL(__Pyx_c_prod_double(__pyx_v_v1, __Pyx_c_conj_double(__pyx_v_v2))); - __pyx_v_result = __pyx_t_1; - - /* "fontTools/cu2qu/cu2qu.py":58 - * # implementation. Because we are using the result in a denominator and catching - * # ZeroDivisionError (see `calc_intersect`), it's best to normalize the result here. - * if abs(result) < 1e-15: # <<<<<<<<<<<<<< - * result = 0.0 - * return result -*/ - __pyx_t_1 = fabs(__pyx_v_result); - __pyx_t_2 = (__pyx_t_1 < 1e-15); - if (__pyx_t_2) { - - /* "fontTools/cu2qu/cu2qu.py":59 - * # ZeroDivisionError (see `calc_intersect`), it's best to normalize the result here. - * if abs(result) < 1e-15: - * result = 0.0 # <<<<<<<<<<<<<< - * return result - * -*/ - __pyx_v_result = 0.0; - - /* "fontTools/cu2qu/cu2qu.py":58 - * # implementation. Because we are using the result in a denominator and catching - * # ZeroDivisionError (see `calc_intersect`), it's best to normalize the result here. - * if abs(result) < 1e-15: # <<<<<<<<<<<<<< - * result = 0.0 - * return result -*/ - } - - /* "fontTools/cu2qu/cu2qu.py":60 - * if abs(result) < 1e-15: - * result = 0.0 - * return result # <<<<<<<<<<<<<< - * - * -*/ - __pyx_r = __pyx_v_result; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":37 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.returns(cython.double) -*/ - - /* function exit code */ - __pyx_L0:; - return __pyx_r; -} - -/* "fontTools/cu2qu/cu2qu.py":63 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.locals(z=cython.complex, den=cython.double) - * @cython.locals(zr=cython.double, zi=cython.double) -*/ - -static PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu__complex_div_by_real(__pyx_t_double_complex __pyx_v_z, double __pyx_v_den) { - double __pyx_v_zr; - double __pyx_v_zi; - PyObject *__pyx_r = NULL; - __Pyx_RefNannyDeclarations - double __pyx_t_1; - PyObject *__pyx_t_2 = NULL; - PyObject *__pyx_t_3 = NULL; - PyObject *__pyx_t_4 = NULL; - PyObject *__pyx_t_5 = NULL; - PyObject *__pyx_t_6 = NULL; - size_t __pyx_t_7; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("_complex_div_by_real", 0); - - /* "fontTools/cu2qu/cu2qu.py":75 - * https://github.com/fonttools/fonttools/issues/3928 - * """ - * zr = z.real # <<<<<<<<<<<<<< - * zi = z.imag - * return complex(zr / den, zi / den) -*/ - __pyx_t_1 = __Pyx_CREAL(__pyx_v_z); - __pyx_v_zr = __pyx_t_1; - - /* "fontTools/cu2qu/cu2qu.py":76 - * """ - * zr = z.real - * zi = z.imag # <<<<<<<<<<<<<< - * return complex(zr / den, zi / den) - * -*/ - __pyx_t_1 = __Pyx_CIMAG(__pyx_v_z); - __pyx_v_zi = __pyx_t_1; - - /* "fontTools/cu2qu/cu2qu.py":77 - * zr = z.real - * zi = z.imag - * return complex(zr / den, zi / den) # <<<<<<<<<<<<<< - * - * -*/ - __Pyx_XDECREF(__pyx_r); - __pyx_t_3 = NULL; - __Pyx_INCREF((PyObject *)(&PyComplex_Type)); - __pyx_t_4 = ((PyObject *)(&PyComplex_Type)); - if (unlikely(__pyx_v_den == 0)) { - PyErr_SetString(PyExc_ZeroDivisionError, "float division"); - __PYX_ERR(0, 77, __pyx_L1_error) - } - __pyx_t_5 = PyFloat_FromDouble((__pyx_v_zr / __pyx_v_den)); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 77, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - if (unlikely(__pyx_v_den == 0)) { - PyErr_SetString(PyExc_ZeroDivisionError, "float division"); - __PYX_ERR(0, 77, __pyx_L1_error) - } - __pyx_t_6 = PyFloat_FromDouble((__pyx_v_zi / __pyx_v_den)); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 77, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_6); - __pyx_t_7 = 1; - { - PyObject *__pyx_callargs[3] = {__pyx_t_3, __pyx_t_5, __pyx_t_6}; - __pyx_t_2 = __Pyx_PyObject_FastCall(__pyx_t_4, __pyx_callargs+__pyx_t_7, (3-__pyx_t_7) | (__pyx_t_7*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET)); - __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0; - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0; - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 77, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - } - __pyx_r = __pyx_t_2; - __pyx_t_2 = 0; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":63 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.locals(z=cython.complex, den=cython.double) - * @cython.locals(zr=cython.double, zi=cython.double) -*/ - - /* function exit code */ - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_2); - __Pyx_XDECREF(__pyx_t_3); - __Pyx_XDECREF(__pyx_t_4); - __Pyx_XDECREF(__pyx_t_5); - __Pyx_XDECREF(__pyx_t_6); - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu._complex_div_by_real", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = 0; - __pyx_L0:; - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* "fontTools/cu2qu/cu2qu.py":80 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex) -*/ - -static CYTHON_INLINE PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu_calc_cubic_points(__pyx_t_double_complex __pyx_v_a, __pyx_t_double_complex __pyx_v_b, __pyx_t_double_complex __pyx_v_c, __pyx_t_double_complex __pyx_v_d) { - __pyx_t_double_complex __pyx_v__1; - __pyx_t_double_complex __pyx_v__2; - __pyx_t_double_complex __pyx_v__3; - __pyx_t_double_complex __pyx_v__4; - PyObject *__pyx_r = NULL; - __Pyx_RefNannyDeclarations - PyObject *__pyx_t_1 = NULL; - PyObject *__pyx_t_2 = NULL; - PyObject *__pyx_t_3 = NULL; - __pyx_t_double_complex __pyx_t_4; - PyObject *__pyx_t_5 = NULL; - PyObject *__pyx_t_6 = NULL; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("calc_cubic_points", 0); - - /* "fontTools/cu2qu/cu2qu.py":87 - * ) - * def calc_cubic_points(a, b, c, d): - * _1 = d # <<<<<<<<<<<<<< - * _2 = _complex_div_by_real(c, 3.0) + d - * _3 = _complex_div_by_real(b + c, 3.0) + _2 -*/ - __pyx_v__1 = __pyx_v_d; - - /* "fontTools/cu2qu/cu2qu.py":88 - * def calc_cubic_points(a, b, c, d): - * _1 = d - * _2 = _complex_div_by_real(c, 3.0) + d # <<<<<<<<<<<<<< - * _3 = _complex_div_by_real(b + c, 3.0) + _2 - * _4 = a + d + c + b -*/ - __pyx_t_1 = __pyx_f_9fontTools_5cu2qu_5cu2qu__complex_div_by_real(__pyx_v_c, 3.0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 88, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_2 = __pyx_PyComplex_FromComplex(__pyx_v_d); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 88, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_3 = PyNumber_Add(__pyx_t_1, __pyx_t_2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 88, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_3); - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_4 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_3); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 88, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; - __pyx_v__2 = __pyx_t_4; - - /* "fontTools/cu2qu/cu2qu.py":89 - * _1 = d - * _2 = _complex_div_by_real(c, 3.0) + d - * _3 = _complex_div_by_real(b + c, 3.0) + _2 # <<<<<<<<<<<<<< - * _4 = a + d + c + b - * return _1, _2, _3, _4 -*/ - __pyx_t_3 = __pyx_f_9fontTools_5cu2qu_5cu2qu__complex_div_by_real(__Pyx_c_sum_double(__pyx_v_b, __pyx_v_c), 3.0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 89, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_3); - __pyx_t_2 = __pyx_PyComplex_FromComplex(__pyx_v__2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 89, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_1 = PyNumber_Add(__pyx_t_3, __pyx_t_2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 89, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_4 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_1); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 89, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __pyx_v__3 = __pyx_t_4; - - /* "fontTools/cu2qu/cu2qu.py":90 - * _2 = _complex_div_by_real(c, 3.0) + d - * _3 = _complex_div_by_real(b + c, 3.0) + _2 - * _4 = a + d + c + b # <<<<<<<<<<<<<< - * return _1, _2, _3, _4 - * -*/ - __pyx_v__4 = __Pyx_c_sum_double(__Pyx_c_sum_double(__Pyx_c_sum_double(__pyx_v_a, __pyx_v_d), __pyx_v_c), __pyx_v_b); - - /* "fontTools/cu2qu/cu2qu.py":91 - * _3 = _complex_div_by_real(b + c, 3.0) + _2 - * _4 = a + d + c + b - * return _1, _2, _3, _4 # <<<<<<<<<<<<<< - * - * -*/ - __Pyx_XDECREF(__pyx_r); - __pyx_t_1 = __pyx_PyComplex_FromComplex(__pyx_v__1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 91, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_2 = __pyx_PyComplex_FromComplex(__pyx_v__2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 91, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_3 = __pyx_PyComplex_FromComplex(__pyx_v__3); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 91, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_3); - __pyx_t_5 = __pyx_PyComplex_FromComplex(__pyx_v__4); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 91, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - __pyx_t_6 = PyTuple_New(4); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 91, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_6); - __Pyx_GIVEREF(__pyx_t_1); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 0, __pyx_t_1) != (0)) __PYX_ERR(0, 91, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_2); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 1, __pyx_t_2) != (0)) __PYX_ERR(0, 91, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_3); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 2, __pyx_t_3) != (0)) __PYX_ERR(0, 91, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_5); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 3, __pyx_t_5) != (0)) __PYX_ERR(0, 91, __pyx_L1_error); - __pyx_t_1 = 0; - __pyx_t_2 = 0; - __pyx_t_3 = 0; - __pyx_t_5 = 0; - __pyx_r = __pyx_t_6; - __pyx_t_6 = 0; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":80 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex) -*/ - - /* function exit code */ - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_1); - __Pyx_XDECREF(__pyx_t_2); - __Pyx_XDECREF(__pyx_t_3); - __Pyx_XDECREF(__pyx_t_5); - __Pyx_XDECREF(__pyx_t_6); - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu.calc_cubic_points", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = 0; - __pyx_L0:; - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* "fontTools/cu2qu/cu2qu.py":94 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.locals( -*/ - -static CYTHON_INLINE PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu_calc_cubic_parameters(__pyx_t_double_complex __pyx_v_p0, __pyx_t_double_complex __pyx_v_p1, __pyx_t_double_complex __pyx_v_p2, __pyx_t_double_complex __pyx_v_p3) { - __pyx_t_double_complex __pyx_v_a; - __pyx_t_double_complex __pyx_v_b; - __pyx_t_double_complex __pyx_v_c; - __pyx_t_double_complex __pyx_v_d; - PyObject *__pyx_r = NULL; - __Pyx_RefNannyDeclarations - PyObject *__pyx_t_1 = NULL; - PyObject *__pyx_t_2 = NULL; - PyObject *__pyx_t_3 = NULL; - PyObject *__pyx_t_4 = NULL; - PyObject *__pyx_t_5 = NULL; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("calc_cubic_parameters", 0); - - /* "fontTools/cu2qu/cu2qu.py":101 - * @cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex) - * def calc_cubic_parameters(p0, p1, p2, p3): - * c = (p1 - p0) * 3.0 # <<<<<<<<<<<<<< - * b = (p2 - p1) * 3.0 - c - * d = p0 -*/ - __pyx_v_c = __Pyx_c_prod_double(__Pyx_c_diff_double(__pyx_v_p1, __pyx_v_p0), __pyx_t_double_complex_from_parts(3.0, 0)); - - /* "fontTools/cu2qu/cu2qu.py":102 - * def calc_cubic_parameters(p0, p1, p2, p3): - * c = (p1 - p0) * 3.0 - * b = (p2 - p1) * 3.0 - c # <<<<<<<<<<<<<< - * d = p0 - * a = p3 - d - c - b -*/ - __pyx_v_b = __Pyx_c_diff_double(__Pyx_c_prod_double(__Pyx_c_diff_double(__pyx_v_p2, __pyx_v_p1), __pyx_t_double_complex_from_parts(3.0, 0)), __pyx_v_c); - - /* "fontTools/cu2qu/cu2qu.py":103 - * c = (p1 - p0) * 3.0 - * b = (p2 - p1) * 3.0 - c - * d = p0 # <<<<<<<<<<<<<< - * a = p3 - d - c - b - * return a, b, c, d -*/ - __pyx_v_d = __pyx_v_p0; - - /* "fontTools/cu2qu/cu2qu.py":104 - * b = (p2 - p1) * 3.0 - c - * d = p0 - * a = p3 - d - c - b # <<<<<<<<<<<<<< - * return a, b, c, d - * -*/ - __pyx_v_a = __Pyx_c_diff_double(__Pyx_c_diff_double(__Pyx_c_diff_double(__pyx_v_p3, __pyx_v_d), __pyx_v_c), __pyx_v_b); - - /* "fontTools/cu2qu/cu2qu.py":105 - * d = p0 - * a = p3 - d - c - b - * return a, b, c, d # <<<<<<<<<<<<<< - * - * -*/ - __Pyx_XDECREF(__pyx_r); - __pyx_t_1 = __pyx_PyComplex_FromComplex(__pyx_v_a); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 105, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_2 = __pyx_PyComplex_FromComplex(__pyx_v_b); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 105, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_3 = __pyx_PyComplex_FromComplex(__pyx_v_c); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 105, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_3); - __pyx_t_4 = __pyx_PyComplex_FromComplex(__pyx_v_d); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 105, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __pyx_t_5 = PyTuple_New(4); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 105, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - __Pyx_GIVEREF(__pyx_t_1); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 0, __pyx_t_1) != (0)) __PYX_ERR(0, 105, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_2); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 1, __pyx_t_2) != (0)) __PYX_ERR(0, 105, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_3); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 2, __pyx_t_3) != (0)) __PYX_ERR(0, 105, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_4); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 3, __pyx_t_4) != (0)) __PYX_ERR(0, 105, __pyx_L1_error); - __pyx_t_1 = 0; - __pyx_t_2 = 0; - __pyx_t_3 = 0; - __pyx_t_4 = 0; - __pyx_r = __pyx_t_5; - __pyx_t_5 = 0; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":94 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.locals( -*/ - - /* function exit code */ - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_1); - __Pyx_XDECREF(__pyx_t_2); - __Pyx_XDECREF(__pyx_t_3); - __Pyx_XDECREF(__pyx_t_4); - __Pyx_XDECREF(__pyx_t_5); - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu.calc_cubic_parameters", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = 0; - __pyx_L0:; - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* "fontTools/cu2qu/cu2qu.py":108 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.locals( -*/ - -static CYTHON_INLINE PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_n_iter(__pyx_t_double_complex __pyx_v_p0, __pyx_t_double_complex __pyx_v_p1, __pyx_t_double_complex __pyx_v_p2, __pyx_t_double_complex __pyx_v_p3, PyObject *__pyx_v_n) { - PyObject *__pyx_v_a = NULL; - PyObject *__pyx_v_b = NULL; - PyObject *__pyx_r = NULL; - __Pyx_RefNannyDeclarations - int __pyx_t_1; - PyObject *__pyx_t_2 = NULL; - PyObject *__pyx_t_3 = NULL; - PyObject *__pyx_t_4 = NULL; - PyObject *__pyx_t_5 = NULL; - PyObject *(*__pyx_t_6)(PyObject *); - __pyx_t_double_complex __pyx_t_7; - __pyx_t_double_complex __pyx_t_8; - __pyx_t_double_complex __pyx_t_9; - __pyx_t_double_complex __pyx_t_10; - PyObject *__pyx_t_11 = NULL; - PyObject *__pyx_t_12 = NULL; - PyObject *__pyx_t_13 = NULL; - size_t __pyx_t_14; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("split_cubic_into_n_iter", 0); - - /* "fontTools/cu2qu/cu2qu.py":130 - * """ - * # Hand-coded special-cases - * if n == 2: # <<<<<<<<<<<<<< - * return iter(split_cubic_into_two(p0, p1, p2, p3)) - * if n == 3: -*/ - __pyx_t_1 = (__Pyx_PyLong_BoolEqObjC(__pyx_v_n, __pyx_mstate_global->__pyx_int_2, 2, 0)); if (unlikely((__pyx_t_1 < 0))) __PYX_ERR(0, 130, __pyx_L1_error) - if (__pyx_t_1) { - - /* "fontTools/cu2qu/cu2qu.py":131 - * # Hand-coded special-cases - * if n == 2: - * return iter(split_cubic_into_two(p0, p1, p2, p3)) # <<<<<<<<<<<<<< - * if n == 3: - * return iter(split_cubic_into_three(p0, p1, p2, p3)) -*/ - __Pyx_XDECREF(__pyx_r); - __pyx_t_2 = __pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_two(__pyx_v_p0, __pyx_v_p1, __pyx_v_p2, __pyx_v_p3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 131, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_3 = PyObject_GetIter(__pyx_t_2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 131, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_3); - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_r = __pyx_t_3; - __pyx_t_3 = 0; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":130 - * """ - * # Hand-coded special-cases - * if n == 2: # <<<<<<<<<<<<<< - * return iter(split_cubic_into_two(p0, p1, p2, p3)) - * if n == 3: -*/ - } - - /* "fontTools/cu2qu/cu2qu.py":132 - * if n == 2: - * return iter(split_cubic_into_two(p0, p1, p2, p3)) - * if n == 3: # <<<<<<<<<<<<<< - * return iter(split_cubic_into_three(p0, p1, p2, p3)) - * if n == 4: -*/ - __pyx_t_1 = (__Pyx_PyLong_BoolEqObjC(__pyx_v_n, __pyx_mstate_global->__pyx_int_3, 3, 0)); if (unlikely((__pyx_t_1 < 0))) __PYX_ERR(0, 132, __pyx_L1_error) - if (__pyx_t_1) { - - /* "fontTools/cu2qu/cu2qu.py":133 - * return iter(split_cubic_into_two(p0, p1, p2, p3)) - * if n == 3: - * return iter(split_cubic_into_three(p0, p1, p2, p3)) # <<<<<<<<<<<<<< - * if n == 4: - * a, b = split_cubic_into_two(p0, p1, p2, p3) -*/ - __Pyx_XDECREF(__pyx_r); - __pyx_t_3 = __pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_three(__pyx_v_p0, __pyx_v_p1, __pyx_v_p2, __pyx_v_p3); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 133, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_3); - __pyx_t_2 = PyObject_GetIter(__pyx_t_3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 133, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; - __pyx_r = __pyx_t_2; - __pyx_t_2 = 0; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":132 - * if n == 2: - * return iter(split_cubic_into_two(p0, p1, p2, p3)) - * if n == 3: # <<<<<<<<<<<<<< - * return iter(split_cubic_into_three(p0, p1, p2, p3)) - * if n == 4: -*/ - } - - /* "fontTools/cu2qu/cu2qu.py":134 - * if n == 3: - * return iter(split_cubic_into_three(p0, p1, p2, p3)) - * if n == 4: # <<<<<<<<<<<<<< - * a, b = split_cubic_into_two(p0, p1, p2, p3) - * return iter( -*/ - __pyx_t_1 = (__Pyx_PyLong_BoolEqObjC(__pyx_v_n, __pyx_mstate_global->__pyx_int_4, 4, 0)); if (unlikely((__pyx_t_1 < 0))) __PYX_ERR(0, 134, __pyx_L1_error) - if (__pyx_t_1) { - - /* "fontTools/cu2qu/cu2qu.py":135 - * return iter(split_cubic_into_three(p0, p1, p2, p3)) - * if n == 4: - * a, b = split_cubic_into_two(p0, p1, p2, p3) # <<<<<<<<<<<<<< - * return iter( - * split_cubic_into_two(a[0], a[1], a[2], a[3]) -*/ - __pyx_t_2 = __pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_two(__pyx_v_p0, __pyx_v_p1, __pyx_v_p2, __pyx_v_p3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 135, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - if ((likely(PyTuple_CheckExact(__pyx_t_2))) || (PyList_CheckExact(__pyx_t_2))) { - PyObject* sequence = __pyx_t_2; - Py_ssize_t size = __Pyx_PySequence_SIZE(sequence); - if (unlikely(size != 2)) { - if (size > 2) __Pyx_RaiseTooManyValuesError(2); - else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size); - __PYX_ERR(0, 135, __pyx_L1_error) - } - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - if (likely(PyTuple_CheckExact(sequence))) { - __pyx_t_3 = PyTuple_GET_ITEM(sequence, 0); - __Pyx_INCREF(__pyx_t_3); - __pyx_t_4 = PyTuple_GET_ITEM(sequence, 1); - __Pyx_INCREF(__pyx_t_4); - } else { - __pyx_t_3 = __Pyx_PyList_GetItemRef(sequence, 0); - if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 135, __pyx_L1_error) - __Pyx_XGOTREF(__pyx_t_3); - __pyx_t_4 = __Pyx_PyList_GetItemRef(sequence, 1); - if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 135, __pyx_L1_error) - __Pyx_XGOTREF(__pyx_t_4); - } - #else - __pyx_t_3 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 135, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_3); - __pyx_t_4 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 135, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - #endif - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - } else { - Py_ssize_t index = -1; - __pyx_t_5 = PyObject_GetIter(__pyx_t_2); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 135, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_6 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_5); - index = 0; __pyx_t_3 = __pyx_t_6(__pyx_t_5); if (unlikely(!__pyx_t_3)) goto __pyx_L6_unpacking_failed; - __Pyx_GOTREF(__pyx_t_3); - index = 1; __pyx_t_4 = __pyx_t_6(__pyx_t_5); if (unlikely(!__pyx_t_4)) goto __pyx_L6_unpacking_failed; - __Pyx_GOTREF(__pyx_t_4); - if (__Pyx_IternextUnpackEndCheck(__pyx_t_6(__pyx_t_5), 2) < 0) __PYX_ERR(0, 135, __pyx_L1_error) - __pyx_t_6 = NULL; - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - goto __pyx_L7_unpacking_done; - __pyx_L6_unpacking_failed:; - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - __pyx_t_6 = NULL; - if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index); - __PYX_ERR(0, 135, __pyx_L1_error) - __pyx_L7_unpacking_done:; - } - __pyx_v_a = __pyx_t_3; - __pyx_t_3 = 0; - __pyx_v_b = __pyx_t_4; - __pyx_t_4 = 0; - - /* "fontTools/cu2qu/cu2qu.py":136 - * if n == 4: - * a, b = split_cubic_into_two(p0, p1, p2, p3) - * return iter( # <<<<<<<<<<<<<< - * split_cubic_into_two(a[0], a[1], a[2], a[3]) - * + split_cubic_into_two(b[0], b[1], b[2], b[3]) -*/ - __Pyx_XDECREF(__pyx_r); - - /* "fontTools/cu2qu/cu2qu.py":137 - * a, b = split_cubic_into_two(p0, p1, p2, p3) - * return iter( - * split_cubic_into_two(a[0], a[1], a[2], a[3]) # <<<<<<<<<<<<<< - * + split_cubic_into_two(b[0], b[1], b[2], b[3]) - * ) -*/ - __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_a, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 137, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_7 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_2); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 137, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_a, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 137, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_8 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_2); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 137, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_a, 2, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 137, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_9 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_2); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 137, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_a, 3, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 137, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_10 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_2); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 137, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_2 = __pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_two(__pyx_t_7, __pyx_t_8, __pyx_t_9, __pyx_t_10); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 137, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - - /* "fontTools/cu2qu/cu2qu.py":138 - * return iter( - * split_cubic_into_two(a[0], a[1], a[2], a[3]) - * + split_cubic_into_two(b[0], b[1], b[2], b[3]) # <<<<<<<<<<<<<< - * ) - * if n == 6: -*/ - __pyx_t_4 = __Pyx_GetItemInt(__pyx_v_b, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 138, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __pyx_t_10 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_4); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 138, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - __pyx_t_4 = __Pyx_GetItemInt(__pyx_v_b, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 138, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __pyx_t_9 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_4); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 138, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - __pyx_t_4 = __Pyx_GetItemInt(__pyx_v_b, 2, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 138, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __pyx_t_8 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_4); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 138, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - __pyx_t_4 = __Pyx_GetItemInt(__pyx_v_b, 3, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 138, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __pyx_t_7 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_4); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 138, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - __pyx_t_4 = __pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_two(__pyx_t_10, __pyx_t_9, __pyx_t_8, __pyx_t_7); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 138, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __pyx_t_3 = PyNumber_Add(__pyx_t_2, __pyx_t_4); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 138, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_3); - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - - /* "fontTools/cu2qu/cu2qu.py":136 - * if n == 4: - * a, b = split_cubic_into_two(p0, p1, p2, p3) - * return iter( # <<<<<<<<<<<<<< - * split_cubic_into_two(a[0], a[1], a[2], a[3]) - * + split_cubic_into_two(b[0], b[1], b[2], b[3]) -*/ - __pyx_t_4 = PyObject_GetIter(__pyx_t_3); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 136, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; - __pyx_r = __pyx_t_4; - __pyx_t_4 = 0; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":134 - * if n == 3: - * return iter(split_cubic_into_three(p0, p1, p2, p3)) - * if n == 4: # <<<<<<<<<<<<<< - * a, b = split_cubic_into_two(p0, p1, p2, p3) - * return iter( -*/ - } - - /* "fontTools/cu2qu/cu2qu.py":140 - * + split_cubic_into_two(b[0], b[1], b[2], b[3]) - * ) - * if n == 6: # <<<<<<<<<<<<<< - * a, b = split_cubic_into_two(p0, p1, p2, p3) - * return iter( -*/ - __pyx_t_1 = (__Pyx_PyLong_BoolEqObjC(__pyx_v_n, __pyx_mstate_global->__pyx_int_6, 6, 0)); if (unlikely((__pyx_t_1 < 0))) __PYX_ERR(0, 140, __pyx_L1_error) - if (__pyx_t_1) { - - /* "fontTools/cu2qu/cu2qu.py":141 - * ) - * if n == 6: - * a, b = split_cubic_into_two(p0, p1, p2, p3) # <<<<<<<<<<<<<< - * return iter( - * split_cubic_into_three(a[0], a[1], a[2], a[3]) -*/ - __pyx_t_4 = __pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_two(__pyx_v_p0, __pyx_v_p1, __pyx_v_p2, __pyx_v_p3); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 141, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - if ((likely(PyTuple_CheckExact(__pyx_t_4))) || (PyList_CheckExact(__pyx_t_4))) { - PyObject* sequence = __pyx_t_4; - Py_ssize_t size = __Pyx_PySequence_SIZE(sequence); - if (unlikely(size != 2)) { - if (size > 2) __Pyx_RaiseTooManyValuesError(2); - else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size); - __PYX_ERR(0, 141, __pyx_L1_error) - } - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - if (likely(PyTuple_CheckExact(sequence))) { - __pyx_t_3 = PyTuple_GET_ITEM(sequence, 0); - __Pyx_INCREF(__pyx_t_3); - __pyx_t_2 = PyTuple_GET_ITEM(sequence, 1); - __Pyx_INCREF(__pyx_t_2); - } else { - __pyx_t_3 = __Pyx_PyList_GetItemRef(sequence, 0); - if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 141, __pyx_L1_error) - __Pyx_XGOTREF(__pyx_t_3); - __pyx_t_2 = __Pyx_PyList_GetItemRef(sequence, 1); - if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 141, __pyx_L1_error) - __Pyx_XGOTREF(__pyx_t_2); - } - #else - __pyx_t_3 = __Pyx_PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 141, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_3); - __pyx_t_2 = __Pyx_PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 141, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - #endif - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - } else { - Py_ssize_t index = -1; - __pyx_t_5 = PyObject_GetIter(__pyx_t_4); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 141, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - __pyx_t_6 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_5); - index = 0; __pyx_t_3 = __pyx_t_6(__pyx_t_5); if (unlikely(!__pyx_t_3)) goto __pyx_L9_unpacking_failed; - __Pyx_GOTREF(__pyx_t_3); - index = 1; __pyx_t_2 = __pyx_t_6(__pyx_t_5); if (unlikely(!__pyx_t_2)) goto __pyx_L9_unpacking_failed; - __Pyx_GOTREF(__pyx_t_2); - if (__Pyx_IternextUnpackEndCheck(__pyx_t_6(__pyx_t_5), 2) < 0) __PYX_ERR(0, 141, __pyx_L1_error) - __pyx_t_6 = NULL; - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - goto __pyx_L10_unpacking_done; - __pyx_L9_unpacking_failed:; - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - __pyx_t_6 = NULL; - if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index); - __PYX_ERR(0, 141, __pyx_L1_error) - __pyx_L10_unpacking_done:; - } - __pyx_v_a = __pyx_t_3; - __pyx_t_3 = 0; - __pyx_v_b = __pyx_t_2; - __pyx_t_2 = 0; - - /* "fontTools/cu2qu/cu2qu.py":142 - * if n == 6: - * a, b = split_cubic_into_two(p0, p1, p2, p3) - * return iter( # <<<<<<<<<<<<<< - * split_cubic_into_three(a[0], a[1], a[2], a[3]) - * + split_cubic_into_three(b[0], b[1], b[2], b[3]) -*/ - __Pyx_XDECREF(__pyx_r); - - /* "fontTools/cu2qu/cu2qu.py":143 - * a, b = split_cubic_into_two(p0, p1, p2, p3) - * return iter( - * split_cubic_into_three(a[0], a[1], a[2], a[3]) # <<<<<<<<<<<<<< - * + split_cubic_into_three(b[0], b[1], b[2], b[3]) - * ) -*/ - __pyx_t_4 = __Pyx_GetItemInt(__pyx_v_a, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 143, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __pyx_t_7 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_4); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 143, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - __pyx_t_4 = __Pyx_GetItemInt(__pyx_v_a, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 143, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __pyx_t_8 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_4); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 143, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - __pyx_t_4 = __Pyx_GetItemInt(__pyx_v_a, 2, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 143, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __pyx_t_9 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_4); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 143, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - __pyx_t_4 = __Pyx_GetItemInt(__pyx_v_a, 3, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 143, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __pyx_t_10 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_4); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 143, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - __pyx_t_4 = __pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_three(__pyx_t_7, __pyx_t_8, __pyx_t_9, __pyx_t_10); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 143, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - - /* "fontTools/cu2qu/cu2qu.py":144 - * return iter( - * split_cubic_into_three(a[0], a[1], a[2], a[3]) - * + split_cubic_into_three(b[0], b[1], b[2], b[3]) # <<<<<<<<<<<<<< - * ) - * -*/ - __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_b, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 144, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_10 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_2); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 144, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_b, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 144, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_9 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_2); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 144, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_b, 2, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 144, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_8 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_2); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 144, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_b, 3, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 144, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_7 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_2); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 144, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_2 = __pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_three(__pyx_t_10, __pyx_t_9, __pyx_t_8, __pyx_t_7); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 144, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_3 = PyNumber_Add(__pyx_t_4, __pyx_t_2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 144, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_3); - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - - /* "fontTools/cu2qu/cu2qu.py":142 - * if n == 6: - * a, b = split_cubic_into_two(p0, p1, p2, p3) - * return iter( # <<<<<<<<<<<<<< - * split_cubic_into_three(a[0], a[1], a[2], a[3]) - * + split_cubic_into_three(b[0], b[1], b[2], b[3]) -*/ - __pyx_t_2 = PyObject_GetIter(__pyx_t_3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 142, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; - __pyx_r = __pyx_t_2; - __pyx_t_2 = 0; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":140 - * + split_cubic_into_two(b[0], b[1], b[2], b[3]) - * ) - * if n == 6: # <<<<<<<<<<<<<< - * a, b = split_cubic_into_two(p0, p1, p2, p3) - * return iter( -*/ - } - - /* "fontTools/cu2qu/cu2qu.py":147 - * ) - * - * return _split_cubic_into_n_gen(p0, p1, p2, p3, n) # <<<<<<<<<<<<<< - * - * -*/ - __Pyx_XDECREF(__pyx_r); - __pyx_t_3 = NULL; - __Pyx_GetModuleGlobalName(__pyx_t_4, __pyx_mstate_global->__pyx_n_u_split_cubic_into_n_gen); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 147, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __pyx_t_5 = __pyx_PyComplex_FromComplex(__pyx_v_p0); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 147, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - __pyx_t_11 = __pyx_PyComplex_FromComplex(__pyx_v_p1); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 147, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_11); - __pyx_t_12 = __pyx_PyComplex_FromComplex(__pyx_v_p2); if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 147, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_12); - __pyx_t_13 = __pyx_PyComplex_FromComplex(__pyx_v_p3); if (unlikely(!__pyx_t_13)) __PYX_ERR(0, 147, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_13); - __pyx_t_14 = 1; - #if CYTHON_UNPACK_METHODS - if (unlikely(PyMethod_Check(__pyx_t_4))) { - __pyx_t_3 = PyMethod_GET_SELF(__pyx_t_4); - assert(__pyx_t_3); - PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_4); - __Pyx_INCREF(__pyx_t_3); - __Pyx_INCREF(__pyx__function); - __Pyx_DECREF_SET(__pyx_t_4, __pyx__function); - __pyx_t_14 = 0; - } - #endif - { - PyObject *__pyx_callargs[6] = {__pyx_t_3, __pyx_t_5, __pyx_t_11, __pyx_t_12, __pyx_t_13, __pyx_v_n}; - __pyx_t_2 = __Pyx_PyObject_FastCall(__pyx_t_4, __pyx_callargs+__pyx_t_14, (6-__pyx_t_14) | (__pyx_t_14*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET)); - __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0; - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0; - __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0; - __Pyx_DECREF(__pyx_t_13); __pyx_t_13 = 0; - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 147, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - } - __pyx_r = __pyx_t_2; - __pyx_t_2 = 0; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":108 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.locals( -*/ - - /* function exit code */ - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_2); - __Pyx_XDECREF(__pyx_t_3); - __Pyx_XDECREF(__pyx_t_4); - __Pyx_XDECREF(__pyx_t_5); - __Pyx_XDECREF(__pyx_t_11); - __Pyx_XDECREF(__pyx_t_12); - __Pyx_XDECREF(__pyx_t_13); - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu.split_cubic_into_n_iter", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = 0; - __pyx_L0:; - __Pyx_XDECREF(__pyx_v_a); - __Pyx_XDECREF(__pyx_v_b); - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} -static PyObject *__pyx_gb_9fontTools_5cu2qu_5cu2qu_2generator(__pyx_CoroutineObject *__pyx_generator, CYTHON_UNUSED PyThreadState *__pyx_tstate, PyObject *__pyx_sent_value); /* proto */ - -/* "fontTools/cu2qu/cu2qu.py":150 - * - * - * @cython.locals( # <<<<<<<<<<<<<< - * p0=cython.complex, - * p1=cython.complex, -*/ - -/* Python wrapper */ -static PyObject *__pyx_pw_9fontTools_5cu2qu_5cu2qu_1_split_cubic_into_n_gen(PyObject *__pyx_self, -#if CYTHON_METH_FASTCALL -PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds -#else -PyObject *__pyx_args, PyObject *__pyx_kwds -#endif -); /*proto*/ -PyDoc_STRVAR(__pyx_doc_9fontTools_5cu2qu_5cu2qu__split_cubic_into_n_gen, "_split_cubic_into_n_gen(double complex p0, double complex p1, double complex p2, double complex p3, int n)"); -static PyMethodDef __pyx_mdef_9fontTools_5cu2qu_5cu2qu_1_split_cubic_into_n_gen = {"_split_cubic_into_n_gen", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_5cu2qu_5cu2qu_1_split_cubic_into_n_gen, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_5cu2qu_5cu2qu__split_cubic_into_n_gen}; -static PyObject *__pyx_pw_9fontTools_5cu2qu_5cu2qu_1_split_cubic_into_n_gen(PyObject *__pyx_self, -#if CYTHON_METH_FASTCALL -PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds -#else -PyObject *__pyx_args, PyObject *__pyx_kwds -#endif -) { - __pyx_t_double_complex __pyx_v_p0; - __pyx_t_double_complex __pyx_v_p1; - __pyx_t_double_complex __pyx_v_p2; - __pyx_t_double_complex __pyx_v_p3; - int __pyx_v_n; - #if !CYTHON_METH_FASTCALL - CYTHON_UNUSED Py_ssize_t __pyx_nargs; - #endif - CYTHON_UNUSED PyObject *const *__pyx_kwvalues; - PyObject* values[5] = {0,0,0,0,0}; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - PyObject *__pyx_r = 0; - __Pyx_RefNannyDeclarations - __Pyx_RefNannySetupContext("_split_cubic_into_n_gen (wrapper)", 0); - #if !CYTHON_METH_FASTCALL - #if CYTHON_ASSUME_SAFE_SIZE - __pyx_nargs = PyTuple_GET_SIZE(__pyx_args); - #else - __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL; - #endif - #endif - __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs); - { - PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_p0,&__pyx_mstate_global->__pyx_n_u_p1,&__pyx_mstate_global->__pyx_n_u_p2,&__pyx_mstate_global->__pyx_n_u_p3,&__pyx_mstate_global->__pyx_n_u_n,0}; - const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0; - if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 150, __pyx_L3_error) - if (__pyx_kwds_len > 0) { - switch (__pyx_nargs) { - case 5: - values[4] = __Pyx_ArgRef_FASTCALL(__pyx_args, 4); - if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[4])) __PYX_ERR(0, 150, __pyx_L3_error) - CYTHON_FALLTHROUGH; - case 4: - values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3); - if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 150, __pyx_L3_error) - CYTHON_FALLTHROUGH; - case 3: - values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2); - if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 150, __pyx_L3_error) - CYTHON_FALLTHROUGH; - case 2: - values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1); - if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 150, __pyx_L3_error) - CYTHON_FALLTHROUGH; - case 1: - values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0); - if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 150, __pyx_L3_error) - CYTHON_FALLTHROUGH; - case 0: break; - default: goto __pyx_L5_argtuple_error; - } - const Py_ssize_t kwd_pos_args = __pyx_nargs; - if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "_split_cubic_into_n_gen", 0) < 0) __PYX_ERR(0, 150, __pyx_L3_error) - for (Py_ssize_t i = __pyx_nargs; i < 5; i++) { - if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("_split_cubic_into_n_gen", 1, 5, 5, i); __PYX_ERR(0, 150, __pyx_L3_error) } - } - } else if (unlikely(__pyx_nargs != 5)) { - goto __pyx_L5_argtuple_error; - } else { - values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0); - if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 150, __pyx_L3_error) - values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1); - if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 150, __pyx_L3_error) - values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2); - if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 150, __pyx_L3_error) - values[3] = __Pyx_ArgRef_FASTCALL(__pyx_args, 3); - if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[3])) __PYX_ERR(0, 150, __pyx_L3_error) - values[4] = __Pyx_ArgRef_FASTCALL(__pyx_args, 4); - if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[4])) __PYX_ERR(0, 150, __pyx_L3_error) - } - __pyx_v_p0 = __Pyx_PyComplex_As___pyx_t_double_complex(values[0]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 164, __pyx_L3_error) - __pyx_v_p1 = __Pyx_PyComplex_As___pyx_t_double_complex(values[1]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 164, __pyx_L3_error) - __pyx_v_p2 = __Pyx_PyComplex_As___pyx_t_double_complex(values[2]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 164, __pyx_L3_error) - __pyx_v_p3 = __Pyx_PyComplex_As___pyx_t_double_complex(values[3]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 164, __pyx_L3_error) - __pyx_v_n = __Pyx_PyLong_As_int(values[4]); if (unlikely((__pyx_v_n == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 164, __pyx_L3_error) - } - goto __pyx_L6_skip; - __pyx_L5_argtuple_error:; - __Pyx_RaiseArgtupleInvalid("_split_cubic_into_n_gen", 1, 5, 5, __pyx_nargs); __PYX_ERR(0, 150, __pyx_L3_error) - __pyx_L6_skip:; - goto __pyx_L4_argument_unpacking_done; - __pyx_L3_error:; - for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) { - Py_XDECREF(values[__pyx_temp]); - } - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu._split_cubic_into_n_gen", __pyx_clineno, __pyx_lineno, __pyx_filename); - __Pyx_RefNannyFinishContext(); - return NULL; - __pyx_L4_argument_unpacking_done:; - __pyx_r = __pyx_pf_9fontTools_5cu2qu_5cu2qu__split_cubic_into_n_gen(__pyx_self, __pyx_v_p0, __pyx_v_p1, __pyx_v_p2, __pyx_v_p3, __pyx_v_n); - - /* function exit code */ - for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) { - Py_XDECREF(values[__pyx_temp]); - } - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -static PyObject *__pyx_pf_9fontTools_5cu2qu_5cu2qu__split_cubic_into_n_gen(CYTHON_UNUSED PyObject *__pyx_self, __pyx_t_double_complex __pyx_v_p0, __pyx_t_double_complex __pyx_v_p1, __pyx_t_double_complex __pyx_v_p2, __pyx_t_double_complex __pyx_v_p3, int __pyx_v_n) { - struct __pyx_obj_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen *__pyx_cur_scope; - PyObject *__pyx_r = NULL; - __Pyx_RefNannyDeclarations - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("_split_cubic_into_n_gen", 0); - __pyx_cur_scope = (struct __pyx_obj_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen *)__pyx_tp_new_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen(__pyx_mstate_global->__pyx_ptype_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen, __pyx_mstate_global->__pyx_empty_tuple, NULL); - if (unlikely(!__pyx_cur_scope)) { - __pyx_cur_scope = ((struct __pyx_obj_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen *)Py_None); - __Pyx_INCREF(Py_None); - __PYX_ERR(0, 150, __pyx_L1_error) - } else { - __Pyx_GOTREF((PyObject *)__pyx_cur_scope); - } - __pyx_cur_scope->__pyx_v_p0 = __pyx_v_p0; - __pyx_cur_scope->__pyx_v_p1 = __pyx_v_p1; - __pyx_cur_scope->__pyx_v_p2 = __pyx_v_p2; - __pyx_cur_scope->__pyx_v_p3 = __pyx_v_p3; - __pyx_cur_scope->__pyx_v_n = __pyx_v_n; - { - __pyx_CoroutineObject *gen = __Pyx_Generator_New((__pyx_coroutine_body_t) __pyx_gb_9fontTools_5cu2qu_5cu2qu_2generator, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[0]), (PyObject *) __pyx_cur_scope, __pyx_mstate_global->__pyx_n_u_split_cubic_into_n_gen, __pyx_mstate_global->__pyx_n_u_split_cubic_into_n_gen, __pyx_mstate_global->__pyx_n_u_fontTools_cu2qu_cu2qu); if (unlikely(!gen)) __PYX_ERR(0, 150, __pyx_L1_error) - __Pyx_DECREF(__pyx_cur_scope); - __Pyx_RefNannyFinishContext(); - return (PyObject *) gen; - } - - /* function exit code */ - __pyx_L1_error:; - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu._split_cubic_into_n_gen", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = NULL; - __Pyx_DECREF((PyObject *)__pyx_cur_scope); - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -static PyObject *__pyx_gb_9fontTools_5cu2qu_5cu2qu_2generator(__pyx_CoroutineObject *__pyx_generator, CYTHON_UNUSED PyThreadState *__pyx_tstate, PyObject *__pyx_sent_value) /* generator body */ -{ - struct __pyx_obj_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen *__pyx_cur_scope = ((struct __pyx_obj_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen *)__pyx_generator->closure); - PyObject *__pyx_r = NULL; - PyObject *__pyx_t_1 = NULL; - PyObject *__pyx_t_2 = NULL; - PyObject *__pyx_t_3 = NULL; - PyObject *__pyx_t_4 = NULL; - PyObject *__pyx_t_5 = NULL; - PyObject *__pyx_t_6 = NULL; - PyObject *(*__pyx_t_7)(PyObject *); - __pyx_t_double_complex __pyx_t_8; - __pyx_t_double_complex __pyx_t_9; - __pyx_t_double_complex __pyx_t_10; - __pyx_t_double_complex __pyx_t_11; - int __pyx_t_12; - int __pyx_t_13; - int __pyx_t_14; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannyDeclarations - __Pyx_RefNannySetupContext("_split_cubic_into_n_gen", 0); - switch (__pyx_generator->resume_label) { - case 0: goto __pyx_L3_first_run; - case 1: goto __pyx_L8_resume_from_yield; - default: /* CPython raises the right error here */ - __Pyx_RefNannyFinishContext(); - return NULL; - } - __pyx_L3_first_run:; - if (unlikely(__pyx_sent_value != Py_None)) { - if (unlikely(__pyx_sent_value)) PyErr_SetString(PyExc_TypeError, "can't send non-None value to a just-started generator"); - __PYX_ERR(0, 150, __pyx_L1_error) - } - - /* "fontTools/cu2qu/cu2qu.py":165 - * ) - * def _split_cubic_into_n_gen(p0, p1, p2, p3, n): - * a, b, c, d = calc_cubic_parameters(p0, p1, p2, p3) # <<<<<<<<<<<<<< - * dt = 1 / n - * delta_2 = dt * dt -*/ - __pyx_t_1 = __pyx_f_9fontTools_5cu2qu_5cu2qu_calc_cubic_parameters(__pyx_cur_scope->__pyx_v_p0, __pyx_cur_scope->__pyx_v_p1, __pyx_cur_scope->__pyx_v_p2, __pyx_cur_scope->__pyx_v_p3); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 165, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - if ((likely(PyTuple_CheckExact(__pyx_t_1))) || (PyList_CheckExact(__pyx_t_1))) { - PyObject* sequence = __pyx_t_1; - Py_ssize_t size = __Pyx_PySequence_SIZE(sequence); - if (unlikely(size != 4)) { - if (size > 4) __Pyx_RaiseTooManyValuesError(4); - else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size); - __PYX_ERR(0, 165, __pyx_L1_error) - } - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - if (likely(PyTuple_CheckExact(sequence))) { - __pyx_t_2 = PyTuple_GET_ITEM(sequence, 0); - __Pyx_INCREF(__pyx_t_2); - __pyx_t_3 = PyTuple_GET_ITEM(sequence, 1); - __Pyx_INCREF(__pyx_t_3); - __pyx_t_4 = PyTuple_GET_ITEM(sequence, 2); - __Pyx_INCREF(__pyx_t_4); - __pyx_t_5 = PyTuple_GET_ITEM(sequence, 3); - __Pyx_INCREF(__pyx_t_5); - } else { - __pyx_t_2 = __Pyx_PyList_GetItemRef(sequence, 0); - if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 165, __pyx_L1_error) - __Pyx_XGOTREF(__pyx_t_2); - __pyx_t_3 = __Pyx_PyList_GetItemRef(sequence, 1); - if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 165, __pyx_L1_error) - __Pyx_XGOTREF(__pyx_t_3); - __pyx_t_4 = __Pyx_PyList_GetItemRef(sequence, 2); - if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 165, __pyx_L1_error) - __Pyx_XGOTREF(__pyx_t_4); - __pyx_t_5 = __Pyx_PyList_GetItemRef(sequence, 3); - if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 165, __pyx_L1_error) - __Pyx_XGOTREF(__pyx_t_5); - } - #else - { - Py_ssize_t i; - PyObject** temps[4] = {&__pyx_t_2,&__pyx_t_3,&__pyx_t_4,&__pyx_t_5}; - for (i=0; i < 4; i++) { - PyObject* item = __Pyx_PySequence_ITEM(sequence, i); if (unlikely(!item)) __PYX_ERR(0, 165, __pyx_L1_error) - __Pyx_GOTREF(item); - *(temps[i]) = item; - } - } - #endif - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - } else { - Py_ssize_t index = -1; - PyObject** temps[4] = {&__pyx_t_2,&__pyx_t_3,&__pyx_t_4,&__pyx_t_5}; - __pyx_t_6 = PyObject_GetIter(__pyx_t_1); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 165, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_6); - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __pyx_t_7 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_6); - for (index=0; index < 4; index++) { - PyObject* item = __pyx_t_7(__pyx_t_6); if (unlikely(!item)) goto __pyx_L4_unpacking_failed; - __Pyx_GOTREF(item); - *(temps[index]) = item; - } - if (__Pyx_IternextUnpackEndCheck(__pyx_t_7(__pyx_t_6), 4) < 0) __PYX_ERR(0, 165, __pyx_L1_error) - __pyx_t_7 = NULL; - __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0; - goto __pyx_L5_unpacking_done; - __pyx_L4_unpacking_failed:; - __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0; - __pyx_t_7 = NULL; - if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index); - __PYX_ERR(0, 165, __pyx_L1_error) - __pyx_L5_unpacking_done:; - } - __pyx_t_8 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_2); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 165, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_9 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_3); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 165, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; - __pyx_t_10 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_4); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 165, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - __pyx_t_11 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_5); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 165, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - __pyx_cur_scope->__pyx_v_a = __pyx_t_8; - __pyx_cur_scope->__pyx_v_b = __pyx_t_9; - __pyx_cur_scope->__pyx_v_c = __pyx_t_10; - __pyx_cur_scope->__pyx_v_d = __pyx_t_11; - - /* "fontTools/cu2qu/cu2qu.py":166 - * def _split_cubic_into_n_gen(p0, p1, p2, p3, n): - * a, b, c, d = calc_cubic_parameters(p0, p1, p2, p3) - * dt = 1 / n # <<<<<<<<<<<<<< - * delta_2 = dt * dt - * delta_3 = dt * delta_2 -*/ - if (unlikely(__pyx_cur_scope->__pyx_v_n == 0)) { - PyErr_SetString(PyExc_ZeroDivisionError, "float division"); - __PYX_ERR(0, 166, __pyx_L1_error) - } - __pyx_cur_scope->__pyx_v_dt = (1.0 / ((double)__pyx_cur_scope->__pyx_v_n)); - - /* "fontTools/cu2qu/cu2qu.py":167 - * a, b, c, d = calc_cubic_parameters(p0, p1, p2, p3) - * dt = 1 / n - * delta_2 = dt * dt # <<<<<<<<<<<<<< - * delta_3 = dt * delta_2 - * for i in range(n): -*/ - __pyx_cur_scope->__pyx_v_delta_2 = (__pyx_cur_scope->__pyx_v_dt * __pyx_cur_scope->__pyx_v_dt); - - /* "fontTools/cu2qu/cu2qu.py":168 - * dt = 1 / n - * delta_2 = dt * dt - * delta_3 = dt * delta_2 # <<<<<<<<<<<<<< - * for i in range(n): - * t1 = i * dt -*/ - __pyx_cur_scope->__pyx_v_delta_3 = (__pyx_cur_scope->__pyx_v_dt * __pyx_cur_scope->__pyx_v_delta_2); - - /* "fontTools/cu2qu/cu2qu.py":169 - * delta_2 = dt * dt - * delta_3 = dt * delta_2 - * for i in range(n): # <<<<<<<<<<<<<< - * t1 = i * dt - * t1_2 = t1 * t1 -*/ - __pyx_t_12 = __pyx_cur_scope->__pyx_v_n; - __pyx_t_13 = __pyx_t_12; - for (__pyx_t_14 = 0; __pyx_t_14 < __pyx_t_13; __pyx_t_14+=1) { - __pyx_cur_scope->__pyx_v_i = __pyx_t_14; - - /* "fontTools/cu2qu/cu2qu.py":170 - * delta_3 = dt * delta_2 - * for i in range(n): - * t1 = i * dt # <<<<<<<<<<<<<< - * t1_2 = t1 * t1 - * # calc new a, b, c and d -*/ - __pyx_cur_scope->__pyx_v_t1 = (__pyx_cur_scope->__pyx_v_i * __pyx_cur_scope->__pyx_v_dt); - - /* "fontTools/cu2qu/cu2qu.py":171 - * for i in range(n): - * t1 = i * dt - * t1_2 = t1 * t1 # <<<<<<<<<<<<<< - * # calc new a, b, c and d - * a1 = a * delta_3 -*/ - __pyx_cur_scope->__pyx_v_t1_2 = (__pyx_cur_scope->__pyx_v_t1 * __pyx_cur_scope->__pyx_v_t1); - - /* "fontTools/cu2qu/cu2qu.py":173 - * t1_2 = t1 * t1 - * # calc new a, b, c and d - * a1 = a * delta_3 # <<<<<<<<<<<<<< - * b1 = (3 * a * t1 + b) * delta_2 - * c1 = (2 * b * t1 + c + 3 * a * t1_2) * dt -*/ - __pyx_cur_scope->__pyx_v_a1 = __Pyx_c_prod_double(__pyx_cur_scope->__pyx_v_a, __pyx_t_double_complex_from_parts(__pyx_cur_scope->__pyx_v_delta_3, 0)); - - /* "fontTools/cu2qu/cu2qu.py":174 - * # calc new a, b, c and d - * a1 = a * delta_3 - * b1 = (3 * a * t1 + b) * delta_2 # <<<<<<<<<<<<<< - * c1 = (2 * b * t1 + c + 3 * a * t1_2) * dt - * d1 = a * t1 * t1_2 + b * t1_2 + c * t1 + d -*/ - __pyx_cur_scope->__pyx_v_b1 = __Pyx_c_prod_double(__Pyx_c_sum_double(__Pyx_c_prod_double(__Pyx_c_prod_double(__pyx_t_double_complex_from_parts(3, 0), __pyx_cur_scope->__pyx_v_a), __pyx_t_double_complex_from_parts(__pyx_cur_scope->__pyx_v_t1, 0)), __pyx_cur_scope->__pyx_v_b), __pyx_t_double_complex_from_parts(__pyx_cur_scope->__pyx_v_delta_2, 0)); - - /* "fontTools/cu2qu/cu2qu.py":175 - * a1 = a * delta_3 - * b1 = (3 * a * t1 + b) * delta_2 - * c1 = (2 * b * t1 + c + 3 * a * t1_2) * dt # <<<<<<<<<<<<<< - * d1 = a * t1 * t1_2 + b * t1_2 + c * t1 + d - * yield calc_cubic_points(a1, b1, c1, d1) -*/ - __pyx_cur_scope->__pyx_v_c1 = __Pyx_c_prod_double(__Pyx_c_sum_double(__Pyx_c_sum_double(__Pyx_c_prod_double(__Pyx_c_prod_double(__pyx_t_double_complex_from_parts(2, 0), __pyx_cur_scope->__pyx_v_b), __pyx_t_double_complex_from_parts(__pyx_cur_scope->__pyx_v_t1, 0)), __pyx_cur_scope->__pyx_v_c), __Pyx_c_prod_double(__Pyx_c_prod_double(__pyx_t_double_complex_from_parts(3, 0), __pyx_cur_scope->__pyx_v_a), __pyx_t_double_complex_from_parts(__pyx_cur_scope->__pyx_v_t1_2, 0))), __pyx_t_double_complex_from_parts(__pyx_cur_scope->__pyx_v_dt, 0)); - - /* "fontTools/cu2qu/cu2qu.py":176 - * b1 = (3 * a * t1 + b) * delta_2 - * c1 = (2 * b * t1 + c + 3 * a * t1_2) * dt - * d1 = a * t1 * t1_2 + b * t1_2 + c * t1 + d # <<<<<<<<<<<<<< - * yield calc_cubic_points(a1, b1, c1, d1) - * -*/ - __pyx_cur_scope->__pyx_v_d1 = __Pyx_c_sum_double(__Pyx_c_sum_double(__Pyx_c_sum_double(__Pyx_c_prod_double(__Pyx_c_prod_double(__pyx_cur_scope->__pyx_v_a, __pyx_t_double_complex_from_parts(__pyx_cur_scope->__pyx_v_t1, 0)), __pyx_t_double_complex_from_parts(__pyx_cur_scope->__pyx_v_t1_2, 0)), __Pyx_c_prod_double(__pyx_cur_scope->__pyx_v_b, __pyx_t_double_complex_from_parts(__pyx_cur_scope->__pyx_v_t1_2, 0))), __Pyx_c_prod_double(__pyx_cur_scope->__pyx_v_c, __pyx_t_double_complex_from_parts(__pyx_cur_scope->__pyx_v_t1, 0))), __pyx_cur_scope->__pyx_v_d); - - /* "fontTools/cu2qu/cu2qu.py":177 - * c1 = (2 * b * t1 + c + 3 * a * t1_2) * dt - * d1 = a * t1 * t1_2 + b * t1_2 + c * t1 + d - * yield calc_cubic_points(a1, b1, c1, d1) # <<<<<<<<<<<<<< - * - * -*/ - __pyx_t_1 = __pyx_f_9fontTools_5cu2qu_5cu2qu_calc_cubic_points(__pyx_cur_scope->__pyx_v_a1, __pyx_cur_scope->__pyx_v_b1, __pyx_cur_scope->__pyx_v_c1, __pyx_cur_scope->__pyx_v_d1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 177, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_r = __pyx_t_1; - __pyx_t_1 = 0; - __pyx_cur_scope->__pyx_t_0 = __pyx_t_12; - __pyx_cur_scope->__pyx_t_1 = __pyx_t_13; - __pyx_cur_scope->__pyx_t_2 = __pyx_t_14; - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - __Pyx_Coroutine_ResetAndClearException(__pyx_generator); - /* return from generator, yielding value */ - __pyx_generator->resume_label = 1; - return __pyx_r; - __pyx_L8_resume_from_yield:; - __pyx_t_12 = __pyx_cur_scope->__pyx_t_0; - __pyx_t_13 = __pyx_cur_scope->__pyx_t_1; - __pyx_t_14 = __pyx_cur_scope->__pyx_t_2; - if (unlikely(!__pyx_sent_value)) __PYX_ERR(0, 177, __pyx_L1_error) - } - CYTHON_MAYBE_UNUSED_VAR(__pyx_cur_scope); - - /* "fontTools/cu2qu/cu2qu.py":150 - * - * - * @cython.locals( # <<<<<<<<<<<<<< - * p0=cython.complex, - * p1=cython.complex, -*/ - - /* function exit code */ - __pyx_r = Py_None; __Pyx_INCREF(Py_None); - goto __pyx_L0; - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_1); - __Pyx_XDECREF(__pyx_t_2); - __Pyx_XDECREF(__pyx_t_3); - __Pyx_XDECREF(__pyx_t_4); - __Pyx_XDECREF(__pyx_t_5); - __Pyx_XDECREF(__pyx_t_6); - if (__Pyx_PyErr_Occurred()) { - __Pyx_Generator_Replace_StopIteration(0); - __Pyx_AddTraceback("_split_cubic_into_n_gen", __pyx_clineno, __pyx_lineno, __pyx_filename); - } - __pyx_L0:; - __Pyx_XGIVEREF(__pyx_r); - #if !CYTHON_USE_EXC_INFO_STACK - __Pyx_Coroutine_ResetAndClearException(__pyx_generator); - #endif - __pyx_generator->resume_label = -1; - __Pyx_Coroutine_clear((PyObject*)__pyx_generator); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* "fontTools/cu2qu/cu2qu.py":180 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.locals( -*/ - -static CYTHON_INLINE PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_two(__pyx_t_double_complex __pyx_v_p0, __pyx_t_double_complex __pyx_v_p1, __pyx_t_double_complex __pyx_v_p2, __pyx_t_double_complex __pyx_v_p3) { - __pyx_t_double_complex __pyx_v_mid; - __pyx_t_double_complex __pyx_v_deriv3; - PyObject *__pyx_r = NULL; - __Pyx_RefNannyDeclarations - PyObject *__pyx_t_1 = NULL; - __pyx_t_double_complex __pyx_t_2; - PyObject *__pyx_t_3 = NULL; - PyObject *__pyx_t_4 = NULL; - PyObject *__pyx_t_5 = NULL; - PyObject *__pyx_t_6 = NULL; - PyObject *__pyx_t_7 = NULL; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("split_cubic_into_two", 0); - - /* "fontTools/cu2qu/cu2qu.py":201 - * values). - * """ - * mid = (p0 + 3 * (p1 + p2) + p3) * 0.125 # <<<<<<<<<<<<<< - * deriv3 = (p3 + p2 - p1 - p0) * 0.125 - * return ( -*/ - __pyx_v_mid = __Pyx_c_prod_double(__Pyx_c_sum_double(__Pyx_c_sum_double(__pyx_v_p0, __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(3, 0), __Pyx_c_sum_double(__pyx_v_p1, __pyx_v_p2))), __pyx_v_p3), __pyx_t_double_complex_from_parts(0.125, 0)); - - /* "fontTools/cu2qu/cu2qu.py":202 - * """ - * mid = (p0 + 3 * (p1 + p2) + p3) * 0.125 - * deriv3 = (p3 + p2 - p1 - p0) * 0.125 # <<<<<<<<<<<<<< - * return ( - * (p0, (p0 + p1) * 0.5, mid - deriv3, mid), -*/ - __pyx_v_deriv3 = __Pyx_c_prod_double(__Pyx_c_diff_double(__Pyx_c_diff_double(__Pyx_c_sum_double(__pyx_v_p3, __pyx_v_p2), __pyx_v_p1), __pyx_v_p0), __pyx_t_double_complex_from_parts(0.125, 0)); - - /* "fontTools/cu2qu/cu2qu.py":203 - * mid = (p0 + 3 * (p1 + p2) + p3) * 0.125 - * deriv3 = (p3 + p2 - p1 - p0) * 0.125 - * return ( # <<<<<<<<<<<<<< - * (p0, (p0 + p1) * 0.5, mid - deriv3, mid), - * (mid, mid + deriv3, (p2 + p3) * 0.5, p3), -*/ - __Pyx_XDECREF(__pyx_r); - - /* "fontTools/cu2qu/cu2qu.py":204 - * deriv3 = (p3 + p2 - p1 - p0) * 0.125 - * return ( - * (p0, (p0 + p1) * 0.5, mid - deriv3, mid), # <<<<<<<<<<<<<< - * (mid, mid + deriv3, (p2 + p3) * 0.5, p3), - * ) -*/ - __pyx_t_1 = __pyx_PyComplex_FromComplex(__pyx_v_p0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 204, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_2 = __Pyx_c_prod_double(__Pyx_c_sum_double(__pyx_v_p0, __pyx_v_p1), __pyx_t_double_complex_from_parts(0.5, 0)); - __pyx_t_3 = __pyx_PyComplex_FromComplex(__pyx_t_2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 204, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_3); - __pyx_t_2 = __Pyx_c_diff_double(__pyx_v_mid, __pyx_v_deriv3); - __pyx_t_4 = __pyx_PyComplex_FromComplex(__pyx_t_2); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 204, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __pyx_t_5 = __pyx_PyComplex_FromComplex(__pyx_v_mid); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 204, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - __pyx_t_6 = PyTuple_New(4); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 204, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_6); - __Pyx_GIVEREF(__pyx_t_1); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 0, __pyx_t_1) != (0)) __PYX_ERR(0, 204, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_3); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 1, __pyx_t_3) != (0)) __PYX_ERR(0, 204, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_4); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 2, __pyx_t_4) != (0)) __PYX_ERR(0, 204, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_5); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 3, __pyx_t_5) != (0)) __PYX_ERR(0, 204, __pyx_L1_error); - __pyx_t_1 = 0; - __pyx_t_3 = 0; - __pyx_t_4 = 0; - __pyx_t_5 = 0; - - /* "fontTools/cu2qu/cu2qu.py":205 - * return ( - * (p0, (p0 + p1) * 0.5, mid - deriv3, mid), - * (mid, mid + deriv3, (p2 + p3) * 0.5, p3), # <<<<<<<<<<<<<< - * ) - * -*/ - __pyx_t_5 = __pyx_PyComplex_FromComplex(__pyx_v_mid); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 205, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - __pyx_t_2 = __Pyx_c_sum_double(__pyx_v_mid, __pyx_v_deriv3); - __pyx_t_4 = __pyx_PyComplex_FromComplex(__pyx_t_2); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 205, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __pyx_t_2 = __Pyx_c_prod_double(__Pyx_c_sum_double(__pyx_v_p2, __pyx_v_p3), __pyx_t_double_complex_from_parts(0.5, 0)); - __pyx_t_3 = __pyx_PyComplex_FromComplex(__pyx_t_2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 205, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_3); - __pyx_t_1 = __pyx_PyComplex_FromComplex(__pyx_v_p3); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 205, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_7 = PyTuple_New(4); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 205, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_7); - __Pyx_GIVEREF(__pyx_t_5); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_7, 0, __pyx_t_5) != (0)) __PYX_ERR(0, 205, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_4); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_7, 1, __pyx_t_4) != (0)) __PYX_ERR(0, 205, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_3); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_7, 2, __pyx_t_3) != (0)) __PYX_ERR(0, 205, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_1); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_7, 3, __pyx_t_1) != (0)) __PYX_ERR(0, 205, __pyx_L1_error); - __pyx_t_5 = 0; - __pyx_t_4 = 0; - __pyx_t_3 = 0; - __pyx_t_1 = 0; - - /* "fontTools/cu2qu/cu2qu.py":204 - * deriv3 = (p3 + p2 - p1 - p0) * 0.125 - * return ( - * (p0, (p0 + p1) * 0.5, mid - deriv3, mid), # <<<<<<<<<<<<<< - * (mid, mid + deriv3, (p2 + p3) * 0.5, p3), - * ) -*/ - __pyx_t_1 = PyTuple_New(2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 204, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __Pyx_GIVEREF(__pyx_t_6); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_t_6) != (0)) __PYX_ERR(0, 204, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_7); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_t_7) != (0)) __PYX_ERR(0, 204, __pyx_L1_error); - __pyx_t_6 = 0; - __pyx_t_7 = 0; - __pyx_r = __pyx_t_1; - __pyx_t_1 = 0; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":180 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.locals( -*/ - - /* function exit code */ - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_1); - __Pyx_XDECREF(__pyx_t_3); - __Pyx_XDECREF(__pyx_t_4); - __Pyx_XDECREF(__pyx_t_5); - __Pyx_XDECREF(__pyx_t_6); - __Pyx_XDECREF(__pyx_t_7); - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu.split_cubic_into_two", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = 0; - __pyx_L0:; - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* "fontTools/cu2qu/cu2qu.py":209 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.locals( -*/ - -static CYTHON_INLINE PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_three(__pyx_t_double_complex __pyx_v_p0, __pyx_t_double_complex __pyx_v_p1, __pyx_t_double_complex __pyx_v_p2, __pyx_t_double_complex __pyx_v_p3) { - __pyx_t_double_complex __pyx_v_mid1; - __pyx_t_double_complex __pyx_v_deriv1; - __pyx_t_double_complex __pyx_v_mid2; - __pyx_t_double_complex __pyx_v_deriv2; - PyObject *__pyx_r = NULL; - __Pyx_RefNannyDeclarations - PyObject *__pyx_t_1 = NULL; - __pyx_t_double_complex __pyx_t_2; - __pyx_t_double_complex __pyx_t_3; - __pyx_t_double_complex __pyx_t_4; - PyObject *__pyx_t_5 = NULL; - PyObject *__pyx_t_6 = NULL; - PyObject *__pyx_t_7 = NULL; - PyObject *__pyx_t_8 = NULL; - PyObject *__pyx_t_9 = NULL; - PyObject *__pyx_t_10 = NULL; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("split_cubic_into_three", 0); - - /* "fontTools/cu2qu/cu2qu.py":238 - * values). - * """ - * mid1 = (8 * p0 + 12 * p1 + 6 * p2 + p3) * (1 / 27) # <<<<<<<<<<<<<< - * deriv1 = (p3 + 3 * p2 - 4 * p0) * (1 / 27) - * mid2 = (p0 + 6 * p1 + 12 * p2 + 8 * p3) * (1 / 27) -*/ - __pyx_v_mid1 = __Pyx_c_prod_double(__Pyx_c_sum_double(__Pyx_c_sum_double(__Pyx_c_sum_double(__Pyx_c_prod_double(__pyx_t_double_complex_from_parts(8, 0), __pyx_v_p0), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(12, 0), __pyx_v_p1)), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(6, 0), __pyx_v_p2)), __pyx_v_p3), __pyx_t_double_complex_from_parts((1.0 / 27.0), 0)); - - /* "fontTools/cu2qu/cu2qu.py":239 - * """ - * mid1 = (8 * p0 + 12 * p1 + 6 * p2 + p3) * (1 / 27) - * deriv1 = (p3 + 3 * p2 - 4 * p0) * (1 / 27) # <<<<<<<<<<<<<< - * mid2 = (p0 + 6 * p1 + 12 * p2 + 8 * p3) * (1 / 27) - * deriv2 = (4 * p3 - 3 * p1 - p0) * (1 / 27) -*/ - __pyx_v_deriv1 = __Pyx_c_prod_double(__Pyx_c_diff_double(__Pyx_c_sum_double(__pyx_v_p3, __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(3, 0), __pyx_v_p2)), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(4, 0), __pyx_v_p0)), __pyx_t_double_complex_from_parts((1.0 / 27.0), 0)); - - /* "fontTools/cu2qu/cu2qu.py":240 - * mid1 = (8 * p0 + 12 * p1 + 6 * p2 + p3) * (1 / 27) - * deriv1 = (p3 + 3 * p2 - 4 * p0) * (1 / 27) - * mid2 = (p0 + 6 * p1 + 12 * p2 + 8 * p3) * (1 / 27) # <<<<<<<<<<<<<< - * deriv2 = (4 * p3 - 3 * p1 - p0) * (1 / 27) - * return ( -*/ - __pyx_v_mid2 = __Pyx_c_prod_double(__Pyx_c_sum_double(__Pyx_c_sum_double(__Pyx_c_sum_double(__pyx_v_p0, __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(6, 0), __pyx_v_p1)), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(12, 0), __pyx_v_p2)), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(8, 0), __pyx_v_p3)), __pyx_t_double_complex_from_parts((1.0 / 27.0), 0)); - - /* "fontTools/cu2qu/cu2qu.py":241 - * deriv1 = (p3 + 3 * p2 - 4 * p0) * (1 / 27) - * mid2 = (p0 + 6 * p1 + 12 * p2 + 8 * p3) * (1 / 27) - * deriv2 = (4 * p3 - 3 * p1 - p0) * (1 / 27) # <<<<<<<<<<<<<< - * return ( - * (p0, (2 * p0 + p1) / 3.0, mid1 - deriv1, mid1), -*/ - __pyx_v_deriv2 = __Pyx_c_prod_double(__Pyx_c_diff_double(__Pyx_c_diff_double(__Pyx_c_prod_double(__pyx_t_double_complex_from_parts(4, 0), __pyx_v_p3), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(3, 0), __pyx_v_p1)), __pyx_v_p0), __pyx_t_double_complex_from_parts((1.0 / 27.0), 0)); - - /* "fontTools/cu2qu/cu2qu.py":242 - * mid2 = (p0 + 6 * p1 + 12 * p2 + 8 * p3) * (1 / 27) - * deriv2 = (4 * p3 - 3 * p1 - p0) * (1 / 27) - * return ( # <<<<<<<<<<<<<< - * (p0, (2 * p0 + p1) / 3.0, mid1 - deriv1, mid1), - * (mid1, mid1 + deriv1, mid2 - deriv2, mid2), -*/ - __Pyx_XDECREF(__pyx_r); - - /* "fontTools/cu2qu/cu2qu.py":243 - * deriv2 = (4 * p3 - 3 * p1 - p0) * (1 / 27) - * return ( - * (p0, (2 * p0 + p1) / 3.0, mid1 - deriv1, mid1), # <<<<<<<<<<<<<< - * (mid1, mid1 + deriv1, mid2 - deriv2, mid2), - * (mid2, mid2 + deriv2, (p2 + 2 * p3) / 3.0, p3), -*/ - __pyx_t_1 = __pyx_PyComplex_FromComplex(__pyx_v_p0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 243, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_2 = __Pyx_c_sum_double(__Pyx_c_prod_double(__pyx_t_double_complex_from_parts(2, 0), __pyx_v_p0), __pyx_v_p1); - __pyx_t_3 = __pyx_t_double_complex_from_parts(3.0, 0); - if (unlikely(__Pyx_c_is_zero_double(__pyx_t_3))) { - PyErr_SetString(PyExc_ZeroDivisionError, "float division"); - __PYX_ERR(0, 243, __pyx_L1_error) - } - __pyx_t_4 = __Pyx_c_quot_double(__pyx_t_2, __pyx_t_3); - __pyx_t_5 = __pyx_PyComplex_FromComplex(__pyx_t_4); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 243, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - __pyx_t_4 = __Pyx_c_diff_double(__pyx_v_mid1, __pyx_v_deriv1); - __pyx_t_6 = __pyx_PyComplex_FromComplex(__pyx_t_4); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 243, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_6); - __pyx_t_7 = __pyx_PyComplex_FromComplex(__pyx_v_mid1); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 243, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_7); - __pyx_t_8 = PyTuple_New(4); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 243, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_8); - __Pyx_GIVEREF(__pyx_t_1); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_8, 0, __pyx_t_1) != (0)) __PYX_ERR(0, 243, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_5); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_8, 1, __pyx_t_5) != (0)) __PYX_ERR(0, 243, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_6); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_8, 2, __pyx_t_6) != (0)) __PYX_ERR(0, 243, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_7); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_8, 3, __pyx_t_7) != (0)) __PYX_ERR(0, 243, __pyx_L1_error); - __pyx_t_1 = 0; - __pyx_t_5 = 0; - __pyx_t_6 = 0; - __pyx_t_7 = 0; - - /* "fontTools/cu2qu/cu2qu.py":244 - * return ( - * (p0, (2 * p0 + p1) / 3.0, mid1 - deriv1, mid1), - * (mid1, mid1 + deriv1, mid2 - deriv2, mid2), # <<<<<<<<<<<<<< - * (mid2, mid2 + deriv2, (p2 + 2 * p3) / 3.0, p3), - * ) -*/ - __pyx_t_7 = __pyx_PyComplex_FromComplex(__pyx_v_mid1); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 244, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_7); - __pyx_t_4 = __Pyx_c_sum_double(__pyx_v_mid1, __pyx_v_deriv1); - __pyx_t_6 = __pyx_PyComplex_FromComplex(__pyx_t_4); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 244, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_6); - __pyx_t_4 = __Pyx_c_diff_double(__pyx_v_mid2, __pyx_v_deriv2); - __pyx_t_5 = __pyx_PyComplex_FromComplex(__pyx_t_4); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 244, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - __pyx_t_1 = __pyx_PyComplex_FromComplex(__pyx_v_mid2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 244, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_9 = PyTuple_New(4); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 244, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_9); - __Pyx_GIVEREF(__pyx_t_7); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_9, 0, __pyx_t_7) != (0)) __PYX_ERR(0, 244, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_6); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_9, 1, __pyx_t_6) != (0)) __PYX_ERR(0, 244, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_5); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_9, 2, __pyx_t_5) != (0)) __PYX_ERR(0, 244, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_1); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_9, 3, __pyx_t_1) != (0)) __PYX_ERR(0, 244, __pyx_L1_error); - __pyx_t_7 = 0; - __pyx_t_6 = 0; - __pyx_t_5 = 0; - __pyx_t_1 = 0; - - /* "fontTools/cu2qu/cu2qu.py":245 - * (p0, (2 * p0 + p1) / 3.0, mid1 - deriv1, mid1), - * (mid1, mid1 + deriv1, mid2 - deriv2, mid2), - * (mid2, mid2 + deriv2, (p2 + 2 * p3) / 3.0, p3), # <<<<<<<<<<<<<< - * ) - * -*/ - __pyx_t_1 = __pyx_PyComplex_FromComplex(__pyx_v_mid2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 245, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_4 = __Pyx_c_sum_double(__pyx_v_mid2, __pyx_v_deriv2); - __pyx_t_5 = __pyx_PyComplex_FromComplex(__pyx_t_4); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 245, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - __pyx_t_4 = __Pyx_c_sum_double(__pyx_v_p2, __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(2, 0), __pyx_v_p3)); - __pyx_t_3 = __pyx_t_double_complex_from_parts(3.0, 0); - if (unlikely(__Pyx_c_is_zero_double(__pyx_t_3))) { - PyErr_SetString(PyExc_ZeroDivisionError, "float division"); - __PYX_ERR(0, 245, __pyx_L1_error) - } - __pyx_t_2 = __Pyx_c_quot_double(__pyx_t_4, __pyx_t_3); - __pyx_t_6 = __pyx_PyComplex_FromComplex(__pyx_t_2); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 245, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_6); - __pyx_t_7 = __pyx_PyComplex_FromComplex(__pyx_v_p3); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 245, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_7); - __pyx_t_10 = PyTuple_New(4); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 245, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_10); - __Pyx_GIVEREF(__pyx_t_1); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_10, 0, __pyx_t_1) != (0)) __PYX_ERR(0, 245, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_5); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_10, 1, __pyx_t_5) != (0)) __PYX_ERR(0, 245, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_6); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_10, 2, __pyx_t_6) != (0)) __PYX_ERR(0, 245, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_7); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_10, 3, __pyx_t_7) != (0)) __PYX_ERR(0, 245, __pyx_L1_error); - __pyx_t_1 = 0; - __pyx_t_5 = 0; - __pyx_t_6 = 0; - __pyx_t_7 = 0; - - /* "fontTools/cu2qu/cu2qu.py":243 - * deriv2 = (4 * p3 - 3 * p1 - p0) * (1 / 27) - * return ( - * (p0, (2 * p0 + p1) / 3.0, mid1 - deriv1, mid1), # <<<<<<<<<<<<<< - * (mid1, mid1 + deriv1, mid2 - deriv2, mid2), - * (mid2, mid2 + deriv2, (p2 + 2 * p3) / 3.0, p3), -*/ - __pyx_t_7 = PyTuple_New(3); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 243, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_7); - __Pyx_GIVEREF(__pyx_t_8); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_7, 0, __pyx_t_8) != (0)) __PYX_ERR(0, 243, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_9); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_7, 1, __pyx_t_9) != (0)) __PYX_ERR(0, 243, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_10); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_7, 2, __pyx_t_10) != (0)) __PYX_ERR(0, 243, __pyx_L1_error); - __pyx_t_8 = 0; - __pyx_t_9 = 0; - __pyx_t_10 = 0; - __pyx_r = __pyx_t_7; - __pyx_t_7 = 0; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":209 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.locals( -*/ - - /* function exit code */ - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_1); - __Pyx_XDECREF(__pyx_t_5); - __Pyx_XDECREF(__pyx_t_6); - __Pyx_XDECREF(__pyx_t_7); - __Pyx_XDECREF(__pyx_t_8); - __Pyx_XDECREF(__pyx_t_9); - __Pyx_XDECREF(__pyx_t_10); - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu.split_cubic_into_three", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = 0; - __pyx_L0:; - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* "fontTools/cu2qu/cu2qu.py":249 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.returns(cython.complex) -*/ - -static CYTHON_INLINE __pyx_t_double_complex __pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_approx_control(double __pyx_v_t, __pyx_t_double_complex __pyx_v_p0, __pyx_t_double_complex __pyx_v_p1, __pyx_t_double_complex __pyx_v_p2, __pyx_t_double_complex __pyx_v_p3) { - __pyx_t_double_complex __pyx_v__p1; - __pyx_t_double_complex __pyx_v__p2; - __pyx_t_double_complex __pyx_r; - - /* "fontTools/cu2qu/cu2qu.py":273 - * complex: Location of candidate control point on quadratic curve. - * """ - * _p1 = p0 + (p1 - p0) * 1.5 # <<<<<<<<<<<<<< - * _p2 = p3 + (p2 - p3) * 1.5 - * return _p1 + (_p2 - _p1) * t -*/ - __pyx_v__p1 = __Pyx_c_sum_double(__pyx_v_p0, __Pyx_c_prod_double(__Pyx_c_diff_double(__pyx_v_p1, __pyx_v_p0), __pyx_t_double_complex_from_parts(1.5, 0))); - - /* "fontTools/cu2qu/cu2qu.py":274 - * """ - * _p1 = p0 + (p1 - p0) * 1.5 - * _p2 = p3 + (p2 - p3) * 1.5 # <<<<<<<<<<<<<< - * return _p1 + (_p2 - _p1) * t - * -*/ - __pyx_v__p2 = __Pyx_c_sum_double(__pyx_v_p3, __Pyx_c_prod_double(__Pyx_c_diff_double(__pyx_v_p2, __pyx_v_p3), __pyx_t_double_complex_from_parts(1.5, 0))); - - /* "fontTools/cu2qu/cu2qu.py":275 - * _p1 = p0 + (p1 - p0) * 1.5 - * _p2 = p3 + (p2 - p3) * 1.5 - * return _p1 + (_p2 - _p1) * t # <<<<<<<<<<<<<< - * - * -*/ - __pyx_r = __Pyx_c_sum_double(__pyx_v__p1, __Pyx_c_prod_double(__Pyx_c_diff_double(__pyx_v__p2, __pyx_v__p1), __pyx_t_double_complex_from_parts(__pyx_v_t, 0))); - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":249 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.returns(cython.complex) -*/ - - /* function exit code */ - __pyx_L0:; - return __pyx_r; -} - -/* "fontTools/cu2qu/cu2qu.py":278 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.returns(cython.complex) -*/ - -static CYTHON_INLINE __pyx_t_double_complex __pyx_f_9fontTools_5cu2qu_5cu2qu_calc_intersect(__pyx_t_double_complex __pyx_v_a, __pyx_t_double_complex __pyx_v_b, __pyx_t_double_complex __pyx_v_c, __pyx_t_double_complex __pyx_v_d) { - __pyx_t_double_complex __pyx_v_ab; - __pyx_t_double_complex __pyx_v_cd; - __pyx_t_double_complex __pyx_v_p; - double __pyx_v_h; - __pyx_t_double_complex __pyx_r; - __Pyx_RefNannyDeclarations - PyObject *__pyx_t_1 = NULL; - PyObject *__pyx_t_2 = NULL; - PyObject *__pyx_t_3 = NULL; - double __pyx_t_4; - double __pyx_t_5; - int __pyx_t_6; - PyObject *__pyx_t_7 = NULL; - PyObject *__pyx_t_8 = NULL; - PyObject *__pyx_t_9 = NULL; - int __pyx_t_10; - int __pyx_t_11; - PyObject *__pyx_t_12 = NULL; - PyObject *__pyx_t_13 = NULL; - PyObject *__pyx_t_14 = NULL; - PyObject *__pyx_t_15 = NULL; - PyObject *__pyx_t_16 = NULL; - size_t __pyx_t_17; - __pyx_t_double_complex __pyx_t_18; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("calc_intersect", 0); - - /* "fontTools/cu2qu/cu2qu.py":296 - * if no intersection was found. - * """ - * ab = b - a # <<<<<<<<<<<<<< - * cd = d - c - * p = ab * 1j -*/ - __pyx_v_ab = __Pyx_c_diff_double(__pyx_v_b, __pyx_v_a); - - /* "fontTools/cu2qu/cu2qu.py":297 - * """ - * ab = b - a - * cd = d - c # <<<<<<<<<<<<<< - * p = ab * 1j - * try: -*/ - __pyx_v_cd = __Pyx_c_diff_double(__pyx_v_d, __pyx_v_c); - - /* "fontTools/cu2qu/cu2qu.py":298 - * ab = b - a - * cd = d - c - * p = ab * 1j # <<<<<<<<<<<<<< - * try: - * h = dot(p, a - c) / dot(p, cd) -*/ - __pyx_v_p = __Pyx_c_prod_double(__pyx_v_ab, __pyx_t_double_complex_from_parts(0, 1.0)); - - /* "fontTools/cu2qu/cu2qu.py":299 - * cd = d - c - * p = ab * 1j - * try: # <<<<<<<<<<<<<< - * h = dot(p, a - c) / dot(p, cd) - * except ZeroDivisionError: -*/ - { - __Pyx_PyThreadState_declare - __Pyx_PyThreadState_assign - __Pyx_ExceptionSave(&__pyx_t_1, &__pyx_t_2, &__pyx_t_3); - __Pyx_XGOTREF(__pyx_t_1); - __Pyx_XGOTREF(__pyx_t_2); - __Pyx_XGOTREF(__pyx_t_3); - /*try:*/ { - - /* "fontTools/cu2qu/cu2qu.py":300 - * p = ab * 1j - * try: - * h = dot(p, a - c) / dot(p, cd) # <<<<<<<<<<<<<< - * except ZeroDivisionError: - * # if 3 or 4 points are equal, we do have an intersection despite the zero-div: -*/ - __pyx_t_4 = __pyx_f_9fontTools_5cu2qu_5cu2qu_dot(__pyx_v_p, __Pyx_c_diff_double(__pyx_v_a, __pyx_v_c)); if (unlikely(__pyx_t_4 == ((double)-1) && PyErr_Occurred())) __PYX_ERR(0, 300, __pyx_L3_error) - __pyx_t_5 = __pyx_f_9fontTools_5cu2qu_5cu2qu_dot(__pyx_v_p, __pyx_v_cd); if (unlikely(__pyx_t_5 == ((double)-1) && PyErr_Occurred())) __PYX_ERR(0, 300, __pyx_L3_error) - if (unlikely(__pyx_t_5 == 0)) { - PyErr_SetString(PyExc_ZeroDivisionError, "float division"); - __PYX_ERR(0, 300, __pyx_L3_error) - } - __pyx_v_h = (__pyx_t_4 / __pyx_t_5); - - /* "fontTools/cu2qu/cu2qu.py":299 - * cd = d - c - * p = ab * 1j - * try: # <<<<<<<<<<<<<< - * h = dot(p, a - c) / dot(p, cd) - * except ZeroDivisionError: -*/ - } - __Pyx_XDECREF(__pyx_t_1); __pyx_t_1 = 0; - __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0; - __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0; - goto __pyx_L8_try_end; - __pyx_L3_error:; - - /* "fontTools/cu2qu/cu2qu.py":301 - * try: - * h = dot(p, a - c) / dot(p, cd) - * except ZeroDivisionError: # <<<<<<<<<<<<<< - * # if 3 or 4 points are equal, we do have an intersection despite the zero-div: - * # return one of the off-curves so that the algorithm can attempt a one-curve -*/ - __pyx_t_6 = __Pyx_PyErr_ExceptionMatches(__pyx_builtin_ZeroDivisionError); - if (__pyx_t_6) { - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu.calc_intersect", __pyx_clineno, __pyx_lineno, __pyx_filename); - if (__Pyx_GetException(&__pyx_t_7, &__pyx_t_8, &__pyx_t_9) < 0) __PYX_ERR(0, 301, __pyx_L5_except_error) - __Pyx_XGOTREF(__pyx_t_7); - __Pyx_XGOTREF(__pyx_t_8); - __Pyx_XGOTREF(__pyx_t_9); - - /* "fontTools/cu2qu/cu2qu.py":306 - * # solution if it's within tolerance: - * # https://github.com/linebender/kurbo/pull/484 - * if b == c and (a == b or c == d): # <<<<<<<<<<<<<< - * return b - * return complex(NAN, NAN) -*/ - __pyx_t_11 = (__Pyx_c_eq_double(__pyx_v_b, __pyx_v_c)); - if (__pyx_t_11) { - } else { - __pyx_t_10 = __pyx_t_11; - goto __pyx_L12_bool_binop_done; - } - __pyx_t_11 = (__Pyx_c_eq_double(__pyx_v_a, __pyx_v_b)); - if (!__pyx_t_11) { - } else { - __pyx_t_10 = __pyx_t_11; - goto __pyx_L12_bool_binop_done; - } - __pyx_t_11 = (__Pyx_c_eq_double(__pyx_v_c, __pyx_v_d)); - __pyx_t_10 = __pyx_t_11; - __pyx_L12_bool_binop_done:; - if (__pyx_t_10) { - - /* "fontTools/cu2qu/cu2qu.py":307 - * # https://github.com/linebender/kurbo/pull/484 - * if b == c and (a == b or c == d): - * return b # <<<<<<<<<<<<<< - * return complex(NAN, NAN) - * return c + cd * h -*/ - __pyx_r = __pyx_v_b; - __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0; - __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0; - __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0; - goto __pyx_L6_except_return; - - /* "fontTools/cu2qu/cu2qu.py":306 - * # solution if it's within tolerance: - * # https://github.com/linebender/kurbo/pull/484 - * if b == c and (a == b or c == d): # <<<<<<<<<<<<<< - * return b - * return complex(NAN, NAN) -*/ - } - - /* "fontTools/cu2qu/cu2qu.py":308 - * if b == c and (a == b or c == d): - * return b - * return complex(NAN, NAN) # <<<<<<<<<<<<<< - * return c + cd * h - * -*/ - __pyx_t_13 = NULL; - __Pyx_INCREF((PyObject *)(&PyComplex_Type)); - __pyx_t_14 = ((PyObject *)(&PyComplex_Type)); - __Pyx_GetModuleGlobalName(__pyx_t_15, __pyx_mstate_global->__pyx_n_u_NAN); if (unlikely(!__pyx_t_15)) __PYX_ERR(0, 308, __pyx_L5_except_error) - __Pyx_GOTREF(__pyx_t_15); - __Pyx_GetModuleGlobalName(__pyx_t_16, __pyx_mstate_global->__pyx_n_u_NAN); if (unlikely(!__pyx_t_16)) __PYX_ERR(0, 308, __pyx_L5_except_error) - __Pyx_GOTREF(__pyx_t_16); - __pyx_t_17 = 1; - { - PyObject *__pyx_callargs[3] = {__pyx_t_13, __pyx_t_15, __pyx_t_16}; - __pyx_t_12 = __Pyx_PyObject_FastCall(__pyx_t_14, __pyx_callargs+__pyx_t_17, (3-__pyx_t_17) | (__pyx_t_17*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET)); - __Pyx_XDECREF(__pyx_t_13); __pyx_t_13 = 0; - __Pyx_DECREF(__pyx_t_15); __pyx_t_15 = 0; - __Pyx_DECREF(__pyx_t_16); __pyx_t_16 = 0; - __Pyx_DECREF(__pyx_t_14); __pyx_t_14 = 0; - if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 308, __pyx_L5_except_error) - __Pyx_GOTREF(__pyx_t_12); - } - __pyx_t_18 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_12); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 308, __pyx_L5_except_error) - __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0; - __pyx_r = __pyx_t_18; - __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0; - __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0; - __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0; - goto __pyx_L6_except_return; - } - goto __pyx_L5_except_error; - - /* "fontTools/cu2qu/cu2qu.py":299 - * cd = d - c - * p = ab * 1j - * try: # <<<<<<<<<<<<<< - * h = dot(p, a - c) / dot(p, cd) - * except ZeroDivisionError: -*/ - __pyx_L5_except_error:; - __Pyx_XGIVEREF(__pyx_t_1); - __Pyx_XGIVEREF(__pyx_t_2); - __Pyx_XGIVEREF(__pyx_t_3); - __Pyx_ExceptionReset(__pyx_t_1, __pyx_t_2, __pyx_t_3); - goto __pyx_L1_error; - __pyx_L6_except_return:; - __Pyx_XGIVEREF(__pyx_t_1); - __Pyx_XGIVEREF(__pyx_t_2); - __Pyx_XGIVEREF(__pyx_t_3); - __Pyx_ExceptionReset(__pyx_t_1, __pyx_t_2, __pyx_t_3); - goto __pyx_L0; - __pyx_L8_try_end:; - } - - /* "fontTools/cu2qu/cu2qu.py":309 - * return b - * return complex(NAN, NAN) - * return c + cd * h # <<<<<<<<<<<<<< - * - * -*/ - __pyx_r = __Pyx_c_sum_double(__pyx_v_c, __Pyx_c_prod_double(__pyx_v_cd, __pyx_t_double_complex_from_parts(__pyx_v_h, 0))); - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":278 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.returns(cython.complex) -*/ - - /* function exit code */ - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_7); - __Pyx_XDECREF(__pyx_t_8); - __Pyx_XDECREF(__pyx_t_9); - __Pyx_XDECREF(__pyx_t_12); - __Pyx_XDECREF(__pyx_t_13); - __Pyx_XDECREF(__pyx_t_14); - __Pyx_XDECREF(__pyx_t_15); - __Pyx_XDECREF(__pyx_t_16); - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu.calc_intersect", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = __pyx_t_double_complex_from_parts(0, 0); - __pyx_L0:; - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* "fontTools/cu2qu/cu2qu.py":312 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.returns(cython.int) - * @cython.locals( -*/ - -static int __pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_farthest_fit_inside(__pyx_t_double_complex __pyx_v_p0, __pyx_t_double_complex __pyx_v_p1, __pyx_t_double_complex __pyx_v_p2, __pyx_t_double_complex __pyx_v_p3, double __pyx_v_tolerance) { - __pyx_t_double_complex __pyx_v_mid; - __pyx_t_double_complex __pyx_v_deriv3; - int __pyx_r; - int __pyx_t_1; - int __pyx_t_2; - int __pyx_t_3; - int __pyx_t_4; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - - /* "fontTools/cu2qu/cu2qu.py":341 - * """ - * # First check p2 then p1, as p2 has higher error early on. - * if abs(p2) <= tolerance and abs(p1) <= tolerance: # <<<<<<<<<<<<<< - * return True - * -*/ - __pyx_t_2 = (__Pyx_c_abs_double(__pyx_v_p2) <= __pyx_v_tolerance); - if (__pyx_t_2) { - } else { - __pyx_t_1 = __pyx_t_2; - goto __pyx_L4_bool_binop_done; - } - __pyx_t_2 = (__Pyx_c_abs_double(__pyx_v_p1) <= __pyx_v_tolerance); - __pyx_t_1 = __pyx_t_2; - __pyx_L4_bool_binop_done:; - if (__pyx_t_1) { - - /* "fontTools/cu2qu/cu2qu.py":342 - * # First check p2 then p1, as p2 has higher error early on. - * if abs(p2) <= tolerance and abs(p1) <= tolerance: - * return True # <<<<<<<<<<<<<< - * - * # Split. -*/ - __pyx_r = 1; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":341 - * """ - * # First check p2 then p1, as p2 has higher error early on. - * if abs(p2) <= tolerance and abs(p1) <= tolerance: # <<<<<<<<<<<<<< - * return True - * -*/ - } - - /* "fontTools/cu2qu/cu2qu.py":345 - * - * # Split. - * mid = (p0 + 3 * (p1 + p2) + p3) * 0.125 # <<<<<<<<<<<<<< - * if abs(mid) > tolerance: - * return False -*/ - __pyx_v_mid = __Pyx_c_prod_double(__Pyx_c_sum_double(__Pyx_c_sum_double(__pyx_v_p0, __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(3, 0), __Pyx_c_sum_double(__pyx_v_p1, __pyx_v_p2))), __pyx_v_p3), __pyx_t_double_complex_from_parts(0.125, 0)); - - /* "fontTools/cu2qu/cu2qu.py":346 - * # Split. - * mid = (p0 + 3 * (p1 + p2) + p3) * 0.125 - * if abs(mid) > tolerance: # <<<<<<<<<<<<<< - * return False - * deriv3 = (p3 + p2 - p1 - p0) * 0.125 -*/ - __pyx_t_1 = (__Pyx_c_abs_double(__pyx_v_mid) > __pyx_v_tolerance); - if (__pyx_t_1) { - - /* "fontTools/cu2qu/cu2qu.py":347 - * mid = (p0 + 3 * (p1 + p2) + p3) * 0.125 - * if abs(mid) > tolerance: - * return False # <<<<<<<<<<<<<< - * deriv3 = (p3 + p2 - p1 - p0) * 0.125 - * return cubic_farthest_fit_inside( -*/ - __pyx_r = 0; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":346 - * # Split. - * mid = (p0 + 3 * (p1 + p2) + p3) * 0.125 - * if abs(mid) > tolerance: # <<<<<<<<<<<<<< - * return False - * deriv3 = (p3 + p2 - p1 - p0) * 0.125 -*/ - } - - /* "fontTools/cu2qu/cu2qu.py":348 - * if abs(mid) > tolerance: - * return False - * deriv3 = (p3 + p2 - p1 - p0) * 0.125 # <<<<<<<<<<<<<< - * return cubic_farthest_fit_inside( - * p0, (p0 + p1) * 0.5, mid - deriv3, mid, tolerance -*/ - __pyx_v_deriv3 = __Pyx_c_prod_double(__Pyx_c_diff_double(__Pyx_c_diff_double(__Pyx_c_sum_double(__pyx_v_p3, __pyx_v_p2), __pyx_v_p1), __pyx_v_p0), __pyx_t_double_complex_from_parts(0.125, 0)); - - /* "fontTools/cu2qu/cu2qu.py":349 - * return False - * deriv3 = (p3 + p2 - p1 - p0) * 0.125 - * return cubic_farthest_fit_inside( # <<<<<<<<<<<<<< - * p0, (p0 + p1) * 0.5, mid - deriv3, mid, tolerance - * ) and cubic_farthest_fit_inside(mid, mid + deriv3, (p2 + p3) * 0.5, p3, tolerance) -*/ - __pyx_t_4 = __pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_farthest_fit_inside(__pyx_v_p0, __Pyx_c_prod_double(__Pyx_c_sum_double(__pyx_v_p0, __pyx_v_p1), __pyx_t_double_complex_from_parts(0.5, 0)), __Pyx_c_diff_double(__pyx_v_mid, __pyx_v_deriv3), __pyx_v_mid, __pyx_v_tolerance); if (unlikely(__pyx_t_4 == ((int)-1) && PyErr_Occurred())) __PYX_ERR(0, 349, __pyx_L1_error) - if (__pyx_t_4) { - } else { - __pyx_t_3 = __pyx_t_4; - goto __pyx_L7_bool_binop_done; - } - - /* "fontTools/cu2qu/cu2qu.py":351 - * return cubic_farthest_fit_inside( - * p0, (p0 + p1) * 0.5, mid - deriv3, mid, tolerance - * ) and cubic_farthest_fit_inside(mid, mid + deriv3, (p2 + p3) * 0.5, p3, tolerance) # <<<<<<<<<<<<<< - * - * -*/ - __pyx_t_4 = __pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_farthest_fit_inside(__pyx_v_mid, __Pyx_c_sum_double(__pyx_v_mid, __pyx_v_deriv3), __Pyx_c_prod_double(__Pyx_c_sum_double(__pyx_v_p2, __pyx_v_p3), __pyx_t_double_complex_from_parts(0.5, 0)), __pyx_v_p3, __pyx_v_tolerance); if (unlikely(__pyx_t_4 == ((int)-1) && PyErr_Occurred())) __PYX_ERR(0, 351, __pyx_L1_error) - __pyx_t_3 = __pyx_t_4; - __pyx_L7_bool_binop_done:; - __pyx_r = __pyx_t_3; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":312 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.returns(cython.int) - * @cython.locals( -*/ - - /* function exit code */ - __pyx_L1_error:; - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu.cubic_farthest_fit_inside", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = -1; - __pyx_L0:; - return __pyx_r; -} - -/* "fontTools/cu2qu/cu2qu.py":354 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.locals(tolerance=cython.double) -*/ - -static CYTHON_INLINE PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_approx_quadratic(PyObject *__pyx_v_cubic, double __pyx_v_tolerance) { - __pyx_t_double_complex __pyx_v_q1; - __pyx_t_double_complex __pyx_v_c0; - __pyx_t_double_complex __pyx_v_c1; - __pyx_t_double_complex __pyx_v_c2; - __pyx_t_double_complex __pyx_v_c3; - PyObject *__pyx_r = NULL; - __Pyx_RefNannyDeclarations - PyObject *__pyx_t_1 = NULL; - __pyx_t_double_complex __pyx_t_2; - __pyx_t_double_complex __pyx_t_3; - __pyx_t_double_complex __pyx_t_4; - __pyx_t_double_complex __pyx_t_5; - __pyx_t_double_complex __pyx_t_6; - PyObject *__pyx_t_7 = NULL; - PyObject *__pyx_t_8 = NULL; - PyObject *__pyx_t_9 = NULL; - size_t __pyx_t_10; - int __pyx_t_11; - int __pyx_t_12; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("cubic_approx_quadratic", 0); - - /* "fontTools/cu2qu/cu2qu.py":378 - * """ - * - * q1 = calc_intersect(cubic[0], cubic[1], cubic[2], cubic[3]) # <<<<<<<<<<<<<< - * if math.isnan(q1.imag): - * return None -*/ - __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_cubic, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 378, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_2 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_1); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 378, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_cubic, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 378, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_3 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_1); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 378, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_cubic, 2, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 378, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_4 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_1); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 378, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_cubic, 3, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 378, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_5 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_1); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 378, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __pyx_t_6 = __pyx_f_9fontTools_5cu2qu_5cu2qu_calc_intersect(__pyx_t_2, __pyx_t_3, __pyx_t_4, __pyx_t_5); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 378, __pyx_L1_error) - __pyx_v_q1 = __pyx_t_6; - - /* "fontTools/cu2qu/cu2qu.py":379 - * - * q1 = calc_intersect(cubic[0], cubic[1], cubic[2], cubic[3]) - * if math.isnan(q1.imag): # <<<<<<<<<<<<<< - * return None - * c0 = cubic[0] -*/ - __pyx_t_7 = NULL; - __Pyx_GetModuleGlobalName(__pyx_t_8, __pyx_mstate_global->__pyx_n_u_math); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 379, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_8); - __pyx_t_9 = __Pyx_PyObject_GetAttrStr(__pyx_t_8, __pyx_mstate_global->__pyx_n_u_isnan); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 379, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_9); - __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0; - __pyx_t_8 = PyFloat_FromDouble(__Pyx_CIMAG(__pyx_v_q1)); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 379, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_8); - __pyx_t_10 = 1; - #if CYTHON_UNPACK_METHODS - if (unlikely(PyMethod_Check(__pyx_t_9))) { - __pyx_t_7 = PyMethod_GET_SELF(__pyx_t_9); - assert(__pyx_t_7); - PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_9); - __Pyx_INCREF(__pyx_t_7); - __Pyx_INCREF(__pyx__function); - __Pyx_DECREF_SET(__pyx_t_9, __pyx__function); - __pyx_t_10 = 0; - } - #endif - { - PyObject *__pyx_callargs[2] = {__pyx_t_7, __pyx_t_8}; - __pyx_t_1 = __Pyx_PyObject_FastCall(__pyx_t_9, __pyx_callargs+__pyx_t_10, (2-__pyx_t_10) | (__pyx_t_10*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET)); - __Pyx_XDECREF(__pyx_t_7); __pyx_t_7 = 0; - __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0; - __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0; - if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 379, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - } - __pyx_t_11 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely((__pyx_t_11 < 0))) __PYX_ERR(0, 379, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - if (__pyx_t_11) { - - /* "fontTools/cu2qu/cu2qu.py":380 - * q1 = calc_intersect(cubic[0], cubic[1], cubic[2], cubic[3]) - * if math.isnan(q1.imag): - * return None # <<<<<<<<<<<<<< - * c0 = cubic[0] - * c3 = cubic[3] -*/ - __Pyx_XDECREF(__pyx_r); - __pyx_r = Py_None; __Pyx_INCREF(Py_None); - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":379 - * - * q1 = calc_intersect(cubic[0], cubic[1], cubic[2], cubic[3]) - * if math.isnan(q1.imag): # <<<<<<<<<<<<<< - * return None - * c0 = cubic[0] -*/ - } - - /* "fontTools/cu2qu/cu2qu.py":381 - * if math.isnan(q1.imag): - * return None - * c0 = cubic[0] # <<<<<<<<<<<<<< - * c3 = cubic[3] - * c1 = c0 + (q1 - c0) * (2 / 3) -*/ - __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_cubic, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 381, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_6 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_1); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 381, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __pyx_v_c0 = __pyx_t_6; - - /* "fontTools/cu2qu/cu2qu.py":382 - * return None - * c0 = cubic[0] - * c3 = cubic[3] # <<<<<<<<<<<<<< - * c1 = c0 + (q1 - c0) * (2 / 3) - * c2 = c3 + (q1 - c3) * (2 / 3) -*/ - __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_cubic, 3, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 382, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_6 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_1); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 382, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __pyx_v_c3 = __pyx_t_6; - - /* "fontTools/cu2qu/cu2qu.py":383 - * c0 = cubic[0] - * c3 = cubic[3] - * c1 = c0 + (q1 - c0) * (2 / 3) # <<<<<<<<<<<<<< - * c2 = c3 + (q1 - c3) * (2 / 3) - * if not cubic_farthest_fit_inside(0, c1 - cubic[1], c2 - cubic[2], 0, tolerance): -*/ - __pyx_v_c1 = __Pyx_c_sum_double(__pyx_v_c0, __Pyx_c_prod_double(__Pyx_c_diff_double(__pyx_v_q1, __pyx_v_c0), __pyx_t_double_complex_from_parts((2.0 / 3.0), 0))); - - /* "fontTools/cu2qu/cu2qu.py":384 - * c3 = cubic[3] - * c1 = c0 + (q1 - c0) * (2 / 3) - * c2 = c3 + (q1 - c3) * (2 / 3) # <<<<<<<<<<<<<< - * if not cubic_farthest_fit_inside(0, c1 - cubic[1], c2 - cubic[2], 0, tolerance): - * return None -*/ - __pyx_v_c2 = __Pyx_c_sum_double(__pyx_v_c3, __Pyx_c_prod_double(__Pyx_c_diff_double(__pyx_v_q1, __pyx_v_c3), __pyx_t_double_complex_from_parts((2.0 / 3.0), 0))); - - /* "fontTools/cu2qu/cu2qu.py":385 - * c1 = c0 + (q1 - c0) * (2 / 3) - * c2 = c3 + (q1 - c3) * (2 / 3) - * if not cubic_farthest_fit_inside(0, c1 - cubic[1], c2 - cubic[2], 0, tolerance): # <<<<<<<<<<<<<< - * return None - * return c0, q1, c3 -*/ - __pyx_t_1 = __pyx_PyComplex_FromComplex(__pyx_v_c1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 385, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_9 = __Pyx_GetItemInt(__pyx_v_cubic, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 385, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_9); - __pyx_t_8 = PyNumber_Subtract(__pyx_t_1, __pyx_t_9); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 385, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_8); - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0; - __pyx_t_6 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_8); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 385, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0; - __pyx_t_8 = __pyx_PyComplex_FromComplex(__pyx_v_c2); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 385, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_8); - __pyx_t_9 = __Pyx_GetItemInt(__pyx_v_cubic, 2, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 385, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_9); - __pyx_t_1 = PyNumber_Subtract(__pyx_t_8, __pyx_t_9); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 385, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0; - __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0; - __pyx_t_5 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_1); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 385, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __pyx_t_12 = __pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_farthest_fit_inside(__pyx_t_double_complex_from_parts(0, 0), __pyx_t_6, __pyx_t_5, __pyx_t_double_complex_from_parts(0, 0), __pyx_v_tolerance); if (unlikely(__pyx_t_12 == ((int)-1) && PyErr_Occurred())) __PYX_ERR(0, 385, __pyx_L1_error) - __pyx_t_11 = (!(__pyx_t_12 != 0)); - if (__pyx_t_11) { - - /* "fontTools/cu2qu/cu2qu.py":386 - * c2 = c3 + (q1 - c3) * (2 / 3) - * if not cubic_farthest_fit_inside(0, c1 - cubic[1], c2 - cubic[2], 0, tolerance): - * return None # <<<<<<<<<<<<<< - * return c0, q1, c3 - * -*/ - __Pyx_XDECREF(__pyx_r); - __pyx_r = Py_None; __Pyx_INCREF(Py_None); - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":385 - * c1 = c0 + (q1 - c0) * (2 / 3) - * c2 = c3 + (q1 - c3) * (2 / 3) - * if not cubic_farthest_fit_inside(0, c1 - cubic[1], c2 - cubic[2], 0, tolerance): # <<<<<<<<<<<<<< - * return None - * return c0, q1, c3 -*/ - } - - /* "fontTools/cu2qu/cu2qu.py":387 - * if not cubic_farthest_fit_inside(0, c1 - cubic[1], c2 - cubic[2], 0, tolerance): - * return None - * return c0, q1, c3 # <<<<<<<<<<<<<< - * - * -*/ - __Pyx_XDECREF(__pyx_r); - __pyx_t_1 = __pyx_PyComplex_FromComplex(__pyx_v_c0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 387, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_9 = __pyx_PyComplex_FromComplex(__pyx_v_q1); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 387, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_9); - __pyx_t_8 = __pyx_PyComplex_FromComplex(__pyx_v_c3); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 387, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_8); - __pyx_t_7 = PyTuple_New(3); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 387, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_7); - __Pyx_GIVEREF(__pyx_t_1); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_7, 0, __pyx_t_1) != (0)) __PYX_ERR(0, 387, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_9); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_7, 1, __pyx_t_9) != (0)) __PYX_ERR(0, 387, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_8); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_7, 2, __pyx_t_8) != (0)) __PYX_ERR(0, 387, __pyx_L1_error); - __pyx_t_1 = 0; - __pyx_t_9 = 0; - __pyx_t_8 = 0; - __pyx_r = __pyx_t_7; - __pyx_t_7 = 0; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":354 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.locals(tolerance=cython.double) -*/ - - /* function exit code */ - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_1); - __Pyx_XDECREF(__pyx_t_7); - __Pyx_XDECREF(__pyx_t_8); - __Pyx_XDECREF(__pyx_t_9); - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu.cubic_approx_quadratic", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = 0; - __pyx_L0:; - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* "fontTools/cu2qu/cu2qu.py":390 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.locals(n=cython.int, tolerance=cython.double) - * @cython.locals(i=cython.int) -*/ - -static PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_approx_spline(PyObject *__pyx_v_cubic, int __pyx_v_n, double __pyx_v_tolerance, int __pyx_v_all_quadratic) { - __pyx_t_double_complex __pyx_v_q0; - __pyx_t_double_complex __pyx_v_q1; - __pyx_t_double_complex __pyx_v_next_q1; - __pyx_t_double_complex __pyx_v_q2; - __pyx_t_double_complex __pyx_v_d1; - CYTHON_UNUSED __pyx_t_double_complex __pyx_v_c0; - __pyx_t_double_complex __pyx_v_c1; - __pyx_t_double_complex __pyx_v_c2; - __pyx_t_double_complex __pyx_v_c3; - int __pyx_v_i; - PyObject *__pyx_v_cubics = NULL; - PyObject *__pyx_v_next_cubic = NULL; - PyObject *__pyx_v_spline = NULL; - __pyx_t_double_complex __pyx_v_d0; - PyObject *__pyx_r = NULL; - __Pyx_RefNannyDeclarations - int __pyx_t_1; - PyObject *__pyx_t_2 = NULL; - int __pyx_t_3; - __pyx_t_double_complex __pyx_t_4; - __pyx_t_double_complex __pyx_t_5; - __pyx_t_double_complex __pyx_t_6; - __pyx_t_double_complex __pyx_t_7; - PyObject *__pyx_t_8 = NULL; - __pyx_t_double_complex __pyx_t_9; - PyObject *__pyx_t_10 = NULL; - long __pyx_t_11; - long __pyx_t_12; - int __pyx_t_13; - PyObject *__pyx_t_14 = NULL; - PyObject *__pyx_t_15 = NULL; - PyObject *(*__pyx_t_16)(PyObject *); - long __pyx_t_17; - int __pyx_t_18; - int __pyx_t_19; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("cubic_approx_spline", 0); - - /* "fontTools/cu2qu/cu2qu.py":419 - * """ - * - * if n == 1: # <<<<<<<<<<<<<< - * return cubic_approx_quadratic(cubic, tolerance) - * if n == 2 and all_quadratic == False: -*/ - __pyx_t_1 = (__pyx_v_n == 1); - if (__pyx_t_1) { - - /* "fontTools/cu2qu/cu2qu.py":420 - * - * if n == 1: - * return cubic_approx_quadratic(cubic, tolerance) # <<<<<<<<<<<<<< - * if n == 2 and all_quadratic == False: - * return cubic -*/ - __Pyx_XDECREF(__pyx_r); - __pyx_t_2 = __pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_approx_quadratic(__pyx_v_cubic, __pyx_v_tolerance); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 420, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_r = __pyx_t_2; - __pyx_t_2 = 0; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":419 - * """ - * - * if n == 1: # <<<<<<<<<<<<<< - * return cubic_approx_quadratic(cubic, tolerance) - * if n == 2 and all_quadratic == False: -*/ - } - - /* "fontTools/cu2qu/cu2qu.py":421 - * if n == 1: - * return cubic_approx_quadratic(cubic, tolerance) - * if n == 2 and all_quadratic == False: # <<<<<<<<<<<<<< - * return cubic - * -*/ - __pyx_t_3 = (__pyx_v_n == 2); - if (__pyx_t_3) { - } else { - __pyx_t_1 = __pyx_t_3; - goto __pyx_L5_bool_binop_done; - } - __pyx_t_3 = (__pyx_v_all_quadratic == 0); - __pyx_t_1 = __pyx_t_3; - __pyx_L5_bool_binop_done:; - if (__pyx_t_1) { - - /* "fontTools/cu2qu/cu2qu.py":422 - * return cubic_approx_quadratic(cubic, tolerance) - * if n == 2 and all_quadratic == False: - * return cubic # <<<<<<<<<<<<<< - * - * cubics = split_cubic_into_n_iter(cubic[0], cubic[1], cubic[2], cubic[3], n) -*/ - __Pyx_XDECREF(__pyx_r); - __Pyx_INCREF(__pyx_v_cubic); - __pyx_r = __pyx_v_cubic; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":421 - * if n == 1: - * return cubic_approx_quadratic(cubic, tolerance) - * if n == 2 and all_quadratic == False: # <<<<<<<<<<<<<< - * return cubic - * -*/ - } - - /* "fontTools/cu2qu/cu2qu.py":424 - * return cubic - * - * cubics = split_cubic_into_n_iter(cubic[0], cubic[1], cubic[2], cubic[3], n) # <<<<<<<<<<<<<< - * - * # calculate the spline of quadratics and check errors at the same time. -*/ - __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_cubic, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 424, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_4 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_2); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 424, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_cubic, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 424, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_5 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_2); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 424, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_cubic, 2, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 424, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_6 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_2); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 424, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_cubic, 3, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 424, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_7 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_2); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 424, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_2 = __Pyx_PyLong_From_int(__pyx_v_n); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 424, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_8 = __pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_n_iter(__pyx_t_4, __pyx_t_5, __pyx_t_6, __pyx_t_7, __pyx_t_2); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 424, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_8); - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_v_cubics = __pyx_t_8; - __pyx_t_8 = 0; - - /* "fontTools/cu2qu/cu2qu.py":427 - * - * # calculate the spline of quadratics and check errors at the same time. - * next_cubic = next(cubics) # <<<<<<<<<<<<<< - * next_q1 = cubic_approx_control( - * 0, next_cubic[0], next_cubic[1], next_cubic[2], next_cubic[3] -*/ - __pyx_t_8 = __Pyx_PyIter_Next(__pyx_v_cubics); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 427, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_8); - __pyx_v_next_cubic = __pyx_t_8; - __pyx_t_8 = 0; - - /* "fontTools/cu2qu/cu2qu.py":429 - * next_cubic = next(cubics) - * next_q1 = cubic_approx_control( - * 0, next_cubic[0], next_cubic[1], next_cubic[2], next_cubic[3] # <<<<<<<<<<<<<< - * ) - * q2 = cubic[0] -*/ - __pyx_t_8 = __Pyx_GetItemInt(__pyx_v_next_cubic, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 429, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_8); - __pyx_t_7 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_8); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 429, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0; - __pyx_t_8 = __Pyx_GetItemInt(__pyx_v_next_cubic, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 429, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_8); - __pyx_t_6 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_8); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 429, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0; - __pyx_t_8 = __Pyx_GetItemInt(__pyx_v_next_cubic, 2, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 429, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_8); - __pyx_t_5 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_8); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 429, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0; - __pyx_t_8 = __Pyx_GetItemInt(__pyx_v_next_cubic, 3, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 429, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_8); - __pyx_t_4 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_8); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 429, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0; - - /* "fontTools/cu2qu/cu2qu.py":428 - * # calculate the spline of quadratics and check errors at the same time. - * next_cubic = next(cubics) - * next_q1 = cubic_approx_control( # <<<<<<<<<<<<<< - * 0, next_cubic[0], next_cubic[1], next_cubic[2], next_cubic[3] - * ) -*/ - __pyx_t_9 = __pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_approx_control(0.0, __pyx_t_7, __pyx_t_6, __pyx_t_5, __pyx_t_4); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 428, __pyx_L1_error) - __pyx_v_next_q1 = __pyx_t_9; - - /* "fontTools/cu2qu/cu2qu.py":431 - * 0, next_cubic[0], next_cubic[1], next_cubic[2], next_cubic[3] - * ) - * q2 = cubic[0] # <<<<<<<<<<<<<< - * d1 = 0j - * spline = [cubic[0], next_q1] -*/ - __pyx_t_8 = __Pyx_GetItemInt(__pyx_v_cubic, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 431, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_8); - __pyx_t_9 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_8); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 431, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0; - __pyx_v_q2 = __pyx_t_9; - - /* "fontTools/cu2qu/cu2qu.py":432 - * ) - * q2 = cubic[0] - * d1 = 0j # <<<<<<<<<<<<<< - * spline = [cubic[0], next_q1] - * for i in range(1, n + 1): -*/ - __pyx_v_d1 = __pyx_t_double_complex_from_parts(0, 0.0); - - /* "fontTools/cu2qu/cu2qu.py":433 - * q2 = cubic[0] - * d1 = 0j - * spline = [cubic[0], next_q1] # <<<<<<<<<<<<<< - * for i in range(1, n + 1): - * # Current cubic to convert -*/ - __pyx_t_8 = __Pyx_GetItemInt(__pyx_v_cubic, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 433, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_8); - __pyx_t_2 = __pyx_PyComplex_FromComplex(__pyx_v_next_q1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 433, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_10 = PyList_New(2); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 433, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_10); - __Pyx_GIVEREF(__pyx_t_8); - if (__Pyx_PyList_SET_ITEM(__pyx_t_10, 0, __pyx_t_8) != (0)) __PYX_ERR(0, 433, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_2); - if (__Pyx_PyList_SET_ITEM(__pyx_t_10, 1, __pyx_t_2) != (0)) __PYX_ERR(0, 433, __pyx_L1_error); - __pyx_t_8 = 0; - __pyx_t_2 = 0; - __pyx_v_spline = ((PyObject*)__pyx_t_10); - __pyx_t_10 = 0; - - /* "fontTools/cu2qu/cu2qu.py":434 - * d1 = 0j - * spline = [cubic[0], next_q1] - * for i in range(1, n + 1): # <<<<<<<<<<<<<< - * # Current cubic to convert - * c0, c1, c2, c3 = next_cubic -*/ - __pyx_t_11 = (__pyx_v_n + 1); - __pyx_t_12 = __pyx_t_11; - for (__pyx_t_13 = 1; __pyx_t_13 < __pyx_t_12; __pyx_t_13+=1) { - __pyx_v_i = __pyx_t_13; - - /* "fontTools/cu2qu/cu2qu.py":436 - * for i in range(1, n + 1): - * # Current cubic to convert - * c0, c1, c2, c3 = next_cubic # <<<<<<<<<<<<<< - * - * # Current quadratic approximation of current cubic -*/ - if ((likely(PyTuple_CheckExact(__pyx_v_next_cubic))) || (PyList_CheckExact(__pyx_v_next_cubic))) { - PyObject* sequence = __pyx_v_next_cubic; - Py_ssize_t size = __Pyx_PySequence_SIZE(sequence); - if (unlikely(size != 4)) { - if (size > 4) __Pyx_RaiseTooManyValuesError(4); - else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size); - __PYX_ERR(0, 436, __pyx_L1_error) - } - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - if (likely(PyTuple_CheckExact(sequence))) { - __pyx_t_10 = PyTuple_GET_ITEM(sequence, 0); - __Pyx_INCREF(__pyx_t_10); - __pyx_t_2 = PyTuple_GET_ITEM(sequence, 1); - __Pyx_INCREF(__pyx_t_2); - __pyx_t_8 = PyTuple_GET_ITEM(sequence, 2); - __Pyx_INCREF(__pyx_t_8); - __pyx_t_14 = PyTuple_GET_ITEM(sequence, 3); - __Pyx_INCREF(__pyx_t_14); - } else { - __pyx_t_10 = __Pyx_PyList_GetItemRef(sequence, 0); - if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 436, __pyx_L1_error) - __Pyx_XGOTREF(__pyx_t_10); - __pyx_t_2 = __Pyx_PyList_GetItemRef(sequence, 1); - if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 436, __pyx_L1_error) - __Pyx_XGOTREF(__pyx_t_2); - __pyx_t_8 = __Pyx_PyList_GetItemRef(sequence, 2); - if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 436, __pyx_L1_error) - __Pyx_XGOTREF(__pyx_t_8); - __pyx_t_14 = __Pyx_PyList_GetItemRef(sequence, 3); - if (unlikely(!__pyx_t_14)) __PYX_ERR(0, 436, __pyx_L1_error) - __Pyx_XGOTREF(__pyx_t_14); - } - #else - { - Py_ssize_t i; - PyObject** temps[4] = {&__pyx_t_10,&__pyx_t_2,&__pyx_t_8,&__pyx_t_14}; - for (i=0; i < 4; i++) { - PyObject* item = __Pyx_PySequence_ITEM(sequence, i); if (unlikely(!item)) __PYX_ERR(0, 436, __pyx_L1_error) - __Pyx_GOTREF(item); - *(temps[i]) = item; - } - } - #endif - } else { - Py_ssize_t index = -1; - PyObject** temps[4] = {&__pyx_t_10,&__pyx_t_2,&__pyx_t_8,&__pyx_t_14}; - __pyx_t_15 = PyObject_GetIter(__pyx_v_next_cubic); if (unlikely(!__pyx_t_15)) __PYX_ERR(0, 436, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_15); - __pyx_t_16 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_15); - for (index=0; index < 4; index++) { - PyObject* item = __pyx_t_16(__pyx_t_15); if (unlikely(!item)) goto __pyx_L9_unpacking_failed; - __Pyx_GOTREF(item); - *(temps[index]) = item; - } - if (__Pyx_IternextUnpackEndCheck(__pyx_t_16(__pyx_t_15), 4) < 0) __PYX_ERR(0, 436, __pyx_L1_error) - __pyx_t_16 = NULL; - __Pyx_DECREF(__pyx_t_15); __pyx_t_15 = 0; - goto __pyx_L10_unpacking_done; - __pyx_L9_unpacking_failed:; - __Pyx_DECREF(__pyx_t_15); __pyx_t_15 = 0; - __pyx_t_16 = NULL; - if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index); - __PYX_ERR(0, 436, __pyx_L1_error) - __pyx_L10_unpacking_done:; - } - __pyx_t_9 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_10); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 436, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0; - __pyx_t_4 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_2); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 436, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_5 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_8); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 436, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0; - __pyx_t_6 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_14); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 436, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_14); __pyx_t_14 = 0; - __pyx_v_c0 = __pyx_t_9; - __pyx_v_c1 = __pyx_t_4; - __pyx_v_c2 = __pyx_t_5; - __pyx_v_c3 = __pyx_t_6; - - /* "fontTools/cu2qu/cu2qu.py":439 - * - * # Current quadratic approximation of current cubic - * q0 = q2 # <<<<<<<<<<<<<< - * q1 = next_q1 - * if i < n: -*/ - __pyx_v_q0 = __pyx_v_q2; - - /* "fontTools/cu2qu/cu2qu.py":440 - * # Current quadratic approximation of current cubic - * q0 = q2 - * q1 = next_q1 # <<<<<<<<<<<<<< - * if i < n: - * next_cubic = next(cubics) -*/ - __pyx_v_q1 = __pyx_v_next_q1; - - /* "fontTools/cu2qu/cu2qu.py":441 - * q0 = q2 - * q1 = next_q1 - * if i < n: # <<<<<<<<<<<<<< - * next_cubic = next(cubics) - * next_q1 = cubic_approx_control( -*/ - __pyx_t_1 = (__pyx_v_i < __pyx_v_n); - if (__pyx_t_1) { - - /* "fontTools/cu2qu/cu2qu.py":442 - * q1 = next_q1 - * if i < n: - * next_cubic = next(cubics) # <<<<<<<<<<<<<< - * next_q1 = cubic_approx_control( - * i / (n - 1), next_cubic[0], next_cubic[1], next_cubic[2], next_cubic[3] -*/ - __pyx_t_14 = __Pyx_PyIter_Next(__pyx_v_cubics); if (unlikely(!__pyx_t_14)) __PYX_ERR(0, 442, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_14); - __Pyx_DECREF_SET(__pyx_v_next_cubic, __pyx_t_14); - __pyx_t_14 = 0; - - /* "fontTools/cu2qu/cu2qu.py":444 - * next_cubic = next(cubics) - * next_q1 = cubic_approx_control( - * i / (n - 1), next_cubic[0], next_cubic[1], next_cubic[2], next_cubic[3] # <<<<<<<<<<<<<< - * ) - * spline.append(next_q1) -*/ - __pyx_t_17 = (__pyx_v_n - 1); - if (unlikely(__pyx_t_17 == 0)) { - PyErr_SetString(PyExc_ZeroDivisionError, "float division"); - __PYX_ERR(0, 444, __pyx_L1_error) - } - __pyx_t_14 = __Pyx_GetItemInt(__pyx_v_next_cubic, 0, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_14)) __PYX_ERR(0, 444, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_14); - __pyx_t_6 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_14); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 444, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_14); __pyx_t_14 = 0; - __pyx_t_14 = __Pyx_GetItemInt(__pyx_v_next_cubic, 1, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_14)) __PYX_ERR(0, 444, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_14); - __pyx_t_5 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_14); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 444, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_14); __pyx_t_14 = 0; - __pyx_t_14 = __Pyx_GetItemInt(__pyx_v_next_cubic, 2, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_14)) __PYX_ERR(0, 444, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_14); - __pyx_t_4 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_14); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 444, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_14); __pyx_t_14 = 0; - __pyx_t_14 = __Pyx_GetItemInt(__pyx_v_next_cubic, 3, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_14)) __PYX_ERR(0, 444, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_14); - __pyx_t_9 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_14); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 444, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_14); __pyx_t_14 = 0; - - /* "fontTools/cu2qu/cu2qu.py":443 - * if i < n: - * next_cubic = next(cubics) - * next_q1 = cubic_approx_control( # <<<<<<<<<<<<<< - * i / (n - 1), next_cubic[0], next_cubic[1], next_cubic[2], next_cubic[3] - * ) -*/ - __pyx_t_7 = __pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_approx_control((((double)__pyx_v_i) / ((double)__pyx_t_17)), __pyx_t_6, __pyx_t_5, __pyx_t_4, __pyx_t_9); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 443, __pyx_L1_error) - __pyx_v_next_q1 = __pyx_t_7; - - /* "fontTools/cu2qu/cu2qu.py":446 - * i / (n - 1), next_cubic[0], next_cubic[1], next_cubic[2], next_cubic[3] - * ) - * spline.append(next_q1) # <<<<<<<<<<<<<< - * q2 = (q1 + next_q1) * 0.5 - * else: -*/ - __pyx_t_14 = __pyx_PyComplex_FromComplex(__pyx_v_next_q1); if (unlikely(!__pyx_t_14)) __PYX_ERR(0, 446, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_14); - __pyx_t_18 = __Pyx_PyList_Append(__pyx_v_spline, __pyx_t_14); if (unlikely(__pyx_t_18 == ((int)-1))) __PYX_ERR(0, 446, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_14); __pyx_t_14 = 0; - - /* "fontTools/cu2qu/cu2qu.py":447 - * ) - * spline.append(next_q1) - * q2 = (q1 + next_q1) * 0.5 # <<<<<<<<<<<<<< - * else: - * q2 = c3 -*/ - __pyx_v_q2 = __Pyx_c_prod_double(__Pyx_c_sum_double(__pyx_v_q1, __pyx_v_next_q1), __pyx_t_double_complex_from_parts(0.5, 0)); - - /* "fontTools/cu2qu/cu2qu.py":441 - * q0 = q2 - * q1 = next_q1 - * if i < n: # <<<<<<<<<<<<<< - * next_cubic = next(cubics) - * next_q1 = cubic_approx_control( -*/ - goto __pyx_L11; - } - - /* "fontTools/cu2qu/cu2qu.py":449 - * q2 = (q1 + next_q1) * 0.5 - * else: - * q2 = c3 # <<<<<<<<<<<<<< - * - * # End-point deltas -*/ - /*else*/ { - __pyx_v_q2 = __pyx_v_c3; - } - __pyx_L11:; - - /* "fontTools/cu2qu/cu2qu.py":452 - * - * # End-point deltas - * d0 = d1 # <<<<<<<<<<<<<< - * d1 = q2 - c3 - * -*/ - __pyx_v_d0 = __pyx_v_d1; - - /* "fontTools/cu2qu/cu2qu.py":453 - * # End-point deltas - * d0 = d1 - * d1 = q2 - c3 # <<<<<<<<<<<<<< - * - * if abs(d1) > tolerance or not cubic_farthest_fit_inside( -*/ - __pyx_v_d1 = __Pyx_c_diff_double(__pyx_v_q2, __pyx_v_c3); - - /* "fontTools/cu2qu/cu2qu.py":455 - * d1 = q2 - c3 - * - * if abs(d1) > tolerance or not cubic_farthest_fit_inside( # <<<<<<<<<<<<<< - * d0, - * q0 + (q1 - q0) * (2 / 3) - c1, -*/ - __pyx_t_3 = (__Pyx_c_abs_double(__pyx_v_d1) > __pyx_v_tolerance); - if (!__pyx_t_3) { - } else { - __pyx_t_1 = __pyx_t_3; - goto __pyx_L13_bool_binop_done; - } - - /* "fontTools/cu2qu/cu2qu.py":460 - * q2 + (q1 - q2) * (2 / 3) - c2, - * d1, - * tolerance, # <<<<<<<<<<<<<< - * ): - * return None -*/ - __pyx_t_19 = __pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_farthest_fit_inside(__pyx_v_d0, __Pyx_c_diff_double(__Pyx_c_sum_double(__pyx_v_q0, __Pyx_c_prod_double(__Pyx_c_diff_double(__pyx_v_q1, __pyx_v_q0), __pyx_t_double_complex_from_parts((2.0 / 3.0), 0))), __pyx_v_c1), __Pyx_c_diff_double(__Pyx_c_sum_double(__pyx_v_q2, __Pyx_c_prod_double(__Pyx_c_diff_double(__pyx_v_q1, __pyx_v_q2), __pyx_t_double_complex_from_parts((2.0 / 3.0), 0))), __pyx_v_c2), __pyx_v_d1, __pyx_v_tolerance); if (unlikely(__pyx_t_19 == ((int)-1) && PyErr_Occurred())) __PYX_ERR(0, 455, __pyx_L1_error) - - /* "fontTools/cu2qu/cu2qu.py":455 - * d1 = q2 - c3 - * - * if abs(d1) > tolerance or not cubic_farthest_fit_inside( # <<<<<<<<<<<<<< - * d0, - * q0 + (q1 - q0) * (2 / 3) - c1, -*/ - __pyx_t_3 = (!(__pyx_t_19 != 0)); - __pyx_t_1 = __pyx_t_3; - __pyx_L13_bool_binop_done:; - if (__pyx_t_1) { - - /* "fontTools/cu2qu/cu2qu.py":462 - * tolerance, - * ): - * return None # <<<<<<<<<<<<<< - * spline.append(cubic[3]) - * -*/ - __Pyx_XDECREF(__pyx_r); - __pyx_r = Py_None; __Pyx_INCREF(Py_None); - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":455 - * d1 = q2 - c3 - * - * if abs(d1) > tolerance or not cubic_farthest_fit_inside( # <<<<<<<<<<<<<< - * d0, - * q0 + (q1 - q0) * (2 / 3) - c1, -*/ - } - } - - /* "fontTools/cu2qu/cu2qu.py":463 - * ): - * return None - * spline.append(cubic[3]) # <<<<<<<<<<<<<< - * - * return spline -*/ - __pyx_t_14 = __Pyx_GetItemInt(__pyx_v_cubic, 3, long, 1, __Pyx_PyLong_From_long, 0, 0, 1, 1); if (unlikely(!__pyx_t_14)) __PYX_ERR(0, 463, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_14); - __pyx_t_18 = __Pyx_PyList_Append(__pyx_v_spline, __pyx_t_14); if (unlikely(__pyx_t_18 == ((int)-1))) __PYX_ERR(0, 463, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_14); __pyx_t_14 = 0; - - /* "fontTools/cu2qu/cu2qu.py":465 - * spline.append(cubic[3]) - * - * return spline # <<<<<<<<<<<<<< - * - * -*/ - __Pyx_XDECREF(__pyx_r); - __Pyx_INCREF(__pyx_v_spline); - __pyx_r = __pyx_v_spline; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":390 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.locals(n=cython.int, tolerance=cython.double) - * @cython.locals(i=cython.int) -*/ - - /* function exit code */ - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_2); - __Pyx_XDECREF(__pyx_t_8); - __Pyx_XDECREF(__pyx_t_10); - __Pyx_XDECREF(__pyx_t_14); - __Pyx_XDECREF(__pyx_t_15); - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu.cubic_approx_spline", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = 0; - __pyx_L0:; - __Pyx_XDECREF(__pyx_v_cubics); - __Pyx_XDECREF(__pyx_v_next_cubic); - __Pyx_XDECREF(__pyx_v_spline); - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* "fontTools/cu2qu/cu2qu.py":468 - * - * - * @cython.locals(max_err=cython.double) # <<<<<<<<<<<<<< - * @cython.locals(n=cython.int) - * @cython.locals(all_quadratic=cython.int) -*/ - -/* Python wrapper */ -static PyObject *__pyx_pw_9fontTools_5cu2qu_5cu2qu_4curve_to_quadratic(PyObject *__pyx_self, -#if CYTHON_METH_FASTCALL -PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds -#else -PyObject *__pyx_args, PyObject *__pyx_kwds -#endif -); /*proto*/ -PyDoc_STRVAR(__pyx_doc_9fontTools_5cu2qu_5cu2qu_3curve_to_quadratic, "curve_to_quadratic(curve, double max_err, int all_quadratic=True)\n\nApproximate a cubic Bezier curve with a spline of n quadratics.\n\nArgs:\n cubic (sequence): Four 2D tuples representing control points of\n the cubic Bezier curve.\n max_err (double): Permitted deviation from the original curve.\n all_quadratic (bool): If True (default) returned value is a\n quadratic spline. If False, it's either a single quadratic\n curve or a single cubic curve.\n\nReturns:\n If all_quadratic is True: A list of 2D tuples, representing\n control points of the quadratic spline if it fits within the\n given tolerance, or ``None`` if no suitable spline could be\n calculated.\n\n If all_quadratic is False: Either a quadratic curve (if length\n of output is 3), or a cubic curve (if length of output is 4)."); -static PyMethodDef __pyx_mdef_9fontTools_5cu2qu_5cu2qu_4curve_to_quadratic = {"curve_to_quadratic", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_5cu2qu_5cu2qu_4curve_to_quadratic, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_5cu2qu_5cu2qu_3curve_to_quadratic}; -static PyObject *__pyx_pw_9fontTools_5cu2qu_5cu2qu_4curve_to_quadratic(PyObject *__pyx_self, -#if CYTHON_METH_FASTCALL -PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds -#else -PyObject *__pyx_args, PyObject *__pyx_kwds -#endif -) { - PyObject *__pyx_v_curve = 0; - double __pyx_v_max_err; - int __pyx_v_all_quadratic; - #if !CYTHON_METH_FASTCALL - CYTHON_UNUSED Py_ssize_t __pyx_nargs; - #endif - CYTHON_UNUSED PyObject *const *__pyx_kwvalues; - PyObject* values[3] = {0,0,0}; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - PyObject *__pyx_r = 0; - __Pyx_RefNannyDeclarations - __Pyx_RefNannySetupContext("curve_to_quadratic (wrapper)", 0); - #if !CYTHON_METH_FASTCALL - #if CYTHON_ASSUME_SAFE_SIZE - __pyx_nargs = PyTuple_GET_SIZE(__pyx_args); - #else - __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL; - #endif - #endif - __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs); - { - PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_curve,&__pyx_mstate_global->__pyx_n_u_max_err,&__pyx_mstate_global->__pyx_n_u_all_quadratic,0}; - const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0; - if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 468, __pyx_L3_error) - if (__pyx_kwds_len > 0) { - switch (__pyx_nargs) { - case 3: - values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2); - if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 468, __pyx_L3_error) - CYTHON_FALLTHROUGH; - case 2: - values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1); - if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 468, __pyx_L3_error) - CYTHON_FALLTHROUGH; - case 1: - values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0); - if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 468, __pyx_L3_error) - CYTHON_FALLTHROUGH; - case 0: break; - default: goto __pyx_L5_argtuple_error; - } - const Py_ssize_t kwd_pos_args = __pyx_nargs; - if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "curve_to_quadratic", 0) < 0) __PYX_ERR(0, 468, __pyx_L3_error) - for (Py_ssize_t i = __pyx_nargs; i < 2; i++) { - if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("curve_to_quadratic", 0, 2, 3, i); __PYX_ERR(0, 468, __pyx_L3_error) } - } - } else { - switch (__pyx_nargs) { - case 3: - values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2); - if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 468, __pyx_L3_error) - CYTHON_FALLTHROUGH; - case 2: - values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1); - if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 468, __pyx_L3_error) - values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0); - if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 468, __pyx_L3_error) - break; - default: goto __pyx_L5_argtuple_error; - } - } - __pyx_v_curve = values[0]; - __pyx_v_max_err = __Pyx_PyFloat_AsDouble(values[1]); if (unlikely((__pyx_v_max_err == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 471, __pyx_L3_error) - if (values[2]) { - __pyx_v_all_quadratic = __Pyx_PyLong_As_int(values[2]); if (unlikely((__pyx_v_all_quadratic == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 471, __pyx_L3_error) - } else { - - /* "fontTools/cu2qu/cu2qu.py":471 - * @cython.locals(n=cython.int) - * @cython.locals(all_quadratic=cython.int) - * def curve_to_quadratic(curve, max_err, all_quadratic=True): # <<<<<<<<<<<<<< - * """Approximate a cubic Bezier curve with a spline of n quadratics. - * -*/ - __pyx_v_all_quadratic = ((int)((int)1)); - } - } - goto __pyx_L6_skip; - __pyx_L5_argtuple_error:; - __Pyx_RaiseArgtupleInvalid("curve_to_quadratic", 0, 2, 3, __pyx_nargs); __PYX_ERR(0, 468, __pyx_L3_error) - __pyx_L6_skip:; - goto __pyx_L4_argument_unpacking_done; - __pyx_L3_error:; - for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) { - Py_XDECREF(values[__pyx_temp]); - } - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu.curve_to_quadratic", __pyx_clineno, __pyx_lineno, __pyx_filename); - __Pyx_RefNannyFinishContext(); - return NULL; - __pyx_L4_argument_unpacking_done:; - __pyx_r = __pyx_pf_9fontTools_5cu2qu_5cu2qu_3curve_to_quadratic(__pyx_self, __pyx_v_curve, __pyx_v_max_err, __pyx_v_all_quadratic); - - /* "fontTools/cu2qu/cu2qu.py":468 - * - * - * @cython.locals(max_err=cython.double) # <<<<<<<<<<<<<< - * @cython.locals(n=cython.int) - * @cython.locals(all_quadratic=cython.int) -*/ - - /* function exit code */ - for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) { - Py_XDECREF(values[__pyx_temp]); - } - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -static PyObject *__pyx_pf_9fontTools_5cu2qu_5cu2qu_3curve_to_quadratic(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_curve, double __pyx_v_max_err, int __pyx_v_all_quadratic) { - int __pyx_v_n; - PyObject *__pyx_v_spline = NULL; - PyObject *__pyx_7genexpr__pyx_v_p = NULL; - PyObject *__pyx_8genexpr1__pyx_v_s = NULL; - PyObject *__pyx_r = NULL; - __Pyx_RefNannyDeclarations - PyObject *__pyx_t_1 = NULL; - PyObject *__pyx_t_2 = NULL; - Py_ssize_t __pyx_t_3; - PyObject *(*__pyx_t_4)(PyObject *); - PyObject *__pyx_t_5 = NULL; - PyObject *__pyx_t_6 = NULL; - Py_ssize_t __pyx_t_7; - int __pyx_t_8; - int __pyx_t_9; - Py_ssize_t __pyx_t_10; - PyObject *__pyx_t_11 = NULL; - size_t __pyx_t_12; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("curve_to_quadratic", 0); - __Pyx_INCREF(__pyx_v_curve); - - /* "fontTools/cu2qu/cu2qu.py":492 - * """ - * - * curve = [complex(*p) for p in curve] # <<<<<<<<<<<<<< - * - * for n in range(1, MAX_N + 1): -*/ - { /* enter inner scope */ - __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 492, __pyx_L5_error) - __Pyx_GOTREF(__pyx_t_1); - if (likely(PyList_CheckExact(__pyx_v_curve)) || PyTuple_CheckExact(__pyx_v_curve)) { - __pyx_t_2 = __pyx_v_curve; __Pyx_INCREF(__pyx_t_2); - __pyx_t_3 = 0; - __pyx_t_4 = NULL; - } else { - __pyx_t_3 = -1; __pyx_t_2 = PyObject_GetIter(__pyx_v_curve); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 492, __pyx_L5_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_4 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_2); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 492, __pyx_L5_error) - } - for (;;) { - if (likely(!__pyx_t_4)) { - if (likely(PyList_CheckExact(__pyx_t_2))) { - { - Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_2); - #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 492, __pyx_L5_error) - #endif - if (__pyx_t_3 >= __pyx_temp) break; - } - __pyx_t_5 = __Pyx_PyList_GetItemRef(__pyx_t_2, __pyx_t_3); - ++__pyx_t_3; - } else { - { - Py_ssize_t __pyx_temp = __Pyx_PyTuple_GET_SIZE(__pyx_t_2); - #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 492, __pyx_L5_error) - #endif - if (__pyx_t_3 >= __pyx_temp) break; - } - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - __pyx_t_5 = __Pyx_NewRef(PyTuple_GET_ITEM(__pyx_t_2, __pyx_t_3)); - #else - __pyx_t_5 = __Pyx_PySequence_ITEM(__pyx_t_2, __pyx_t_3); - #endif - ++__pyx_t_3; - } - if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 492, __pyx_L5_error) - } else { - __pyx_t_5 = __pyx_t_4(__pyx_t_2); - if (unlikely(!__pyx_t_5)) { - PyObject* exc_type = PyErr_Occurred(); - if (exc_type) { - if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) __PYX_ERR(0, 492, __pyx_L5_error) - PyErr_Clear(); - } - break; - } - } - __Pyx_GOTREF(__pyx_t_5); - __Pyx_XDECREF_SET(__pyx_7genexpr__pyx_v_p, __pyx_t_5); - __pyx_t_5 = 0; - __pyx_t_5 = __Pyx_PySequence_Tuple(__pyx_7genexpr__pyx_v_p); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 492, __pyx_L5_error) - __Pyx_GOTREF(__pyx_t_5); - __pyx_t_6 = __Pyx_PyObject_Call(((PyObject *)(&PyComplex_Type)), __pyx_t_5, NULL); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 492, __pyx_L5_error) - __Pyx_GOTREF(__pyx_t_6); - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - if (unlikely(__Pyx_ListComp_Append(__pyx_t_1, (PyObject*)__pyx_t_6))) __PYX_ERR(0, 492, __pyx_L5_error) - __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0; - } - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __Pyx_XDECREF(__pyx_7genexpr__pyx_v_p); __pyx_7genexpr__pyx_v_p = 0; - goto __pyx_L9_exit_scope; - __pyx_L5_error:; - __Pyx_XDECREF(__pyx_7genexpr__pyx_v_p); __pyx_7genexpr__pyx_v_p = 0; - goto __pyx_L1_error; - __pyx_L9_exit_scope:; - } /* exit inner scope */ - __Pyx_DECREF_SET(__pyx_v_curve, __pyx_t_1); - __pyx_t_1 = 0; - - /* "fontTools/cu2qu/cu2qu.py":494 - * curve = [complex(*p) for p in curve] - * - * for n in range(1, MAX_N + 1): # <<<<<<<<<<<<<< - * spline = cubic_approx_spline(curve, n, max_err, all_quadratic) - * if spline is not None: -*/ - __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_mstate_global->__pyx_n_u_MAX_N); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 494, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_2 = __Pyx_PyLong_AddObjC(__pyx_t_1, __pyx_mstate_global->__pyx_int_1, 1, 0, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 494, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __pyx_t_3 = __Pyx_PyIndex_AsSsize_t(__pyx_t_2); if (unlikely((__pyx_t_3 == (Py_ssize_t)-1) && PyErr_Occurred())) __PYX_ERR(0, 494, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_7 = __pyx_t_3; - for (__pyx_t_8 = 1; __pyx_t_8 < __pyx_t_7; __pyx_t_8+=1) { - __pyx_v_n = __pyx_t_8; - - /* "fontTools/cu2qu/cu2qu.py":495 - * - * for n in range(1, MAX_N + 1): - * spline = cubic_approx_spline(curve, n, max_err, all_quadratic) # <<<<<<<<<<<<<< - * if spline is not None: - * # done. go home -*/ - __pyx_t_2 = __pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_approx_spline(__pyx_v_curve, __pyx_v_n, __pyx_v_max_err, __pyx_v_all_quadratic); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 495, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __Pyx_XDECREF_SET(__pyx_v_spline, __pyx_t_2); - __pyx_t_2 = 0; - - /* "fontTools/cu2qu/cu2qu.py":496 - * for n in range(1, MAX_N + 1): - * spline = cubic_approx_spline(curve, n, max_err, all_quadratic) - * if spline is not None: # <<<<<<<<<<<<<< - * # done. go home - * return [(s.real, s.imag) for s in spline] -*/ - __pyx_t_9 = (__pyx_v_spline != Py_None); - if (__pyx_t_9) { - - /* "fontTools/cu2qu/cu2qu.py":498 - * if spline is not None: - * # done. go home - * return [(s.real, s.imag) for s in spline] # <<<<<<<<<<<<<< - * - * raise ApproxNotFoundError(curve) -*/ - __Pyx_XDECREF(__pyx_r); - { /* enter inner scope */ - __pyx_t_2 = PyList_New(0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 498, __pyx_L15_error) - __Pyx_GOTREF(__pyx_t_2); - if (likely(PyList_CheckExact(__pyx_v_spline)) || PyTuple_CheckExact(__pyx_v_spline)) { - __pyx_t_1 = __pyx_v_spline; __Pyx_INCREF(__pyx_t_1); - __pyx_t_10 = 0; - __pyx_t_4 = NULL; - } else { - __pyx_t_10 = -1; __pyx_t_1 = PyObject_GetIter(__pyx_v_spline); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 498, __pyx_L15_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_4 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 498, __pyx_L15_error) - } - for (;;) { - if (likely(!__pyx_t_4)) { - if (likely(PyList_CheckExact(__pyx_t_1))) { - { - Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_1); - #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 498, __pyx_L15_error) - #endif - if (__pyx_t_10 >= __pyx_temp) break; - } - __pyx_t_6 = __Pyx_PyList_GetItemRef(__pyx_t_1, __pyx_t_10); - ++__pyx_t_10; - } else { - { - Py_ssize_t __pyx_temp = __Pyx_PyTuple_GET_SIZE(__pyx_t_1); - #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 498, __pyx_L15_error) - #endif - if (__pyx_t_10 >= __pyx_temp) break; - } - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - __pyx_t_6 = __Pyx_NewRef(PyTuple_GET_ITEM(__pyx_t_1, __pyx_t_10)); - #else - __pyx_t_6 = __Pyx_PySequence_ITEM(__pyx_t_1, __pyx_t_10); - #endif - ++__pyx_t_10; - } - if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 498, __pyx_L15_error) - } else { - __pyx_t_6 = __pyx_t_4(__pyx_t_1); - if (unlikely(!__pyx_t_6)) { - PyObject* exc_type = PyErr_Occurred(); - if (exc_type) { - if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) __PYX_ERR(0, 498, __pyx_L15_error) - PyErr_Clear(); - } - break; - } - } - __Pyx_GOTREF(__pyx_t_6); - __Pyx_XDECREF_SET(__pyx_8genexpr1__pyx_v_s, __pyx_t_6); - __pyx_t_6 = 0; - __pyx_t_6 = __Pyx_PyObject_GetAttrStr(__pyx_8genexpr1__pyx_v_s, __pyx_mstate_global->__pyx_n_u_real); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 498, __pyx_L15_error) - __Pyx_GOTREF(__pyx_t_6); - __pyx_t_5 = __Pyx_PyObject_GetAttrStr(__pyx_8genexpr1__pyx_v_s, __pyx_mstate_global->__pyx_n_u_imag); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 498, __pyx_L15_error) - __Pyx_GOTREF(__pyx_t_5); - __pyx_t_11 = PyTuple_New(2); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 498, __pyx_L15_error) - __Pyx_GOTREF(__pyx_t_11); - __Pyx_GIVEREF(__pyx_t_6); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_11, 0, __pyx_t_6) != (0)) __PYX_ERR(0, 498, __pyx_L15_error); - __Pyx_GIVEREF(__pyx_t_5); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_11, 1, __pyx_t_5) != (0)) __PYX_ERR(0, 498, __pyx_L15_error); - __pyx_t_6 = 0; - __pyx_t_5 = 0; - if (unlikely(__Pyx_ListComp_Append(__pyx_t_2, (PyObject*)__pyx_t_11))) __PYX_ERR(0, 498, __pyx_L15_error) - __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0; - } - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __Pyx_XDECREF(__pyx_8genexpr1__pyx_v_s); __pyx_8genexpr1__pyx_v_s = 0; - goto __pyx_L19_exit_scope; - __pyx_L15_error:; - __Pyx_XDECREF(__pyx_8genexpr1__pyx_v_s); __pyx_8genexpr1__pyx_v_s = 0; - goto __pyx_L1_error; - __pyx_L19_exit_scope:; - } /* exit inner scope */ - __pyx_r = __pyx_t_2; - __pyx_t_2 = 0; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":496 - * for n in range(1, MAX_N + 1): - * spline = cubic_approx_spline(curve, n, max_err, all_quadratic) - * if spline is not None: # <<<<<<<<<<<<<< - * # done. go home - * return [(s.real, s.imag) for s in spline] -*/ - } - } - - /* "fontTools/cu2qu/cu2qu.py":500 - * return [(s.real, s.imag) for s in spline] - * - * raise ApproxNotFoundError(curve) # <<<<<<<<<<<<<< - * - * -*/ - __pyx_t_1 = NULL; - __Pyx_GetModuleGlobalName(__pyx_t_11, __pyx_mstate_global->__pyx_n_u_ApproxNotFoundError); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 500, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_11); - __pyx_t_12 = 1; - #if CYTHON_UNPACK_METHODS - if (unlikely(PyMethod_Check(__pyx_t_11))) { - __pyx_t_1 = PyMethod_GET_SELF(__pyx_t_11); - assert(__pyx_t_1); - PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_11); - __Pyx_INCREF(__pyx_t_1); - __Pyx_INCREF(__pyx__function); - __Pyx_DECREF_SET(__pyx_t_11, __pyx__function); - __pyx_t_12 = 0; - } - #endif - { - PyObject *__pyx_callargs[2] = {__pyx_t_1, __pyx_v_curve}; - __pyx_t_2 = __Pyx_PyObject_FastCall(__pyx_t_11, __pyx_callargs+__pyx_t_12, (2-__pyx_t_12) | (__pyx_t_12*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET)); - __Pyx_XDECREF(__pyx_t_1); __pyx_t_1 = 0; - __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0; - if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 500, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - } - __Pyx_Raise(__pyx_t_2, 0, 0, 0); - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __PYX_ERR(0, 500, __pyx_L1_error) - - /* "fontTools/cu2qu/cu2qu.py":468 - * - * - * @cython.locals(max_err=cython.double) # <<<<<<<<<<<<<< - * @cython.locals(n=cython.int) - * @cython.locals(all_quadratic=cython.int) -*/ - - /* function exit code */ - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_1); - __Pyx_XDECREF(__pyx_t_2); - __Pyx_XDECREF(__pyx_t_5); - __Pyx_XDECREF(__pyx_t_6); - __Pyx_XDECREF(__pyx_t_11); - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu.curve_to_quadratic", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = NULL; - __pyx_L0:; - __Pyx_XDECREF(__pyx_v_spline); - __Pyx_XDECREF(__pyx_7genexpr__pyx_v_p); - __Pyx_XDECREF(__pyx_8genexpr1__pyx_v_s); - __Pyx_XDECREF(__pyx_v_curve); - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* "fontTools/cu2qu/cu2qu.py":503 - * - * - * @cython.locals(l=cython.int, last_i=cython.int, i=cython.int) # <<<<<<<<<<<<<< - * @cython.locals(all_quadratic=cython.int) - * def curves_to_quadratic(curves, max_errors, all_quadratic=True): -*/ - -/* Python wrapper */ -static PyObject *__pyx_pw_9fontTools_5cu2qu_5cu2qu_6curves_to_quadratic(PyObject *__pyx_self, -#if CYTHON_METH_FASTCALL -PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds -#else -PyObject *__pyx_args, PyObject *__pyx_kwds -#endif -); /*proto*/ -PyDoc_STRVAR(__pyx_doc_9fontTools_5cu2qu_5cu2qu_5curves_to_quadratic, "curves_to_quadratic(curves, max_errors, int all_quadratic=True)\n\nReturn quadratic Bezier splines approximating the input cubic Beziers.\n\nArgs:\n curves: A sequence of *n* curves, each curve being a sequence of four\n 2D tuples.\n max_errors: A sequence of *n* floats representing the maximum permissible\n deviation from each of the cubic Bezier curves.\n all_quadratic (bool): If True (default) returned values are a\n quadratic spline. If False, they are either a single quadratic\n curve or a single cubic curve.\n\nExample::\n\n >>> curves_to_quadratic( [\n ... [ (50,50), (100,100), (150,100), (200,50) ],\n ... [ (75,50), (120,100), (150,75), (200,60) ]\n ... ], [1,1] )\n [[(50.0, 50.0), (75.0, 75.0), (125.0, 91.66666666666666), (175.0, 75.0), (200.0, 50.0)], [(75.0, 50.0), (97.5, 75.0), (135.41666666666666, 82.08333333333333), (175.0, 67.5), (200.0, 60.0)]]\n\nThe returned splines have \"implied oncurve points\" suitable for use in\nTrueType ``glif`` outlines - i.e. in the first spline returned above,\nthe first quadratic segment runs from (50,50) to\n( (75 + 125)/2 , (120 + 91.666..)/2 ) = (100, 83.333...).\n\nReturns:\n If all_quadratic is True, a list of splines, each spline being a list\n of 2D tuples.\n\n If all_quadratic is False, a list of curves, each curve being a quadratic\n (length 3), or cubic (length 4).\n\nRaises:\n fontTools.cu2qu.Errors.ApproxNotFoundError: if no suitable approximation\n can be found for all curves with the given parameters."); -static PyMethodDef __pyx_mdef_9fontTools_5cu2qu_5cu2qu_6curves_to_quadratic = {"curves_to_quadratic", (PyCFunction)(void(*)(void))(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_5cu2qu_5cu2qu_6curves_to_quadratic, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_5cu2qu_5cu2qu_5curves_to_quadratic}; -static PyObject *__pyx_pw_9fontTools_5cu2qu_5cu2qu_6curves_to_quadratic(PyObject *__pyx_self, -#if CYTHON_METH_FASTCALL -PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds -#else -PyObject *__pyx_args, PyObject *__pyx_kwds -#endif -) { - PyObject *__pyx_v_curves = 0; - PyObject *__pyx_v_max_errors = 0; - int __pyx_v_all_quadratic; - #if !CYTHON_METH_FASTCALL - CYTHON_UNUSED Py_ssize_t __pyx_nargs; - #endif - CYTHON_UNUSED PyObject *const *__pyx_kwvalues; - PyObject* values[3] = {0,0,0}; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - PyObject *__pyx_r = 0; - __Pyx_RefNannyDeclarations - __Pyx_RefNannySetupContext("curves_to_quadratic (wrapper)", 0); - #if !CYTHON_METH_FASTCALL - #if CYTHON_ASSUME_SAFE_SIZE - __pyx_nargs = PyTuple_GET_SIZE(__pyx_args); - #else - __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL; - #endif - #endif - __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs); - { - PyObject ** const __pyx_pyargnames[] = {&__pyx_mstate_global->__pyx_n_u_curves,&__pyx_mstate_global->__pyx_n_u_max_errors,&__pyx_mstate_global->__pyx_n_u_all_quadratic,0}; - const Py_ssize_t __pyx_kwds_len = (__pyx_kwds) ? __Pyx_NumKwargs_FASTCALL(__pyx_kwds) : 0; - if (unlikely(__pyx_kwds_len) < 0) __PYX_ERR(0, 503, __pyx_L3_error) - if (__pyx_kwds_len > 0) { - switch (__pyx_nargs) { - case 3: - values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2); - if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 503, __pyx_L3_error) - CYTHON_FALLTHROUGH; - case 2: - values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1); - if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 503, __pyx_L3_error) - CYTHON_FALLTHROUGH; - case 1: - values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0); - if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 503, __pyx_L3_error) - CYTHON_FALLTHROUGH; - case 0: break; - default: goto __pyx_L5_argtuple_error; - } - const Py_ssize_t kwd_pos_args = __pyx_nargs; - if (__Pyx_ParseKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values, kwd_pos_args, __pyx_kwds_len, "curves_to_quadratic", 0) < 0) __PYX_ERR(0, 503, __pyx_L3_error) - for (Py_ssize_t i = __pyx_nargs; i < 2; i++) { - if (unlikely(!values[i])) { __Pyx_RaiseArgtupleInvalid("curves_to_quadratic", 0, 2, 3, i); __PYX_ERR(0, 503, __pyx_L3_error) } - } - } else { - switch (__pyx_nargs) { - case 3: - values[2] = __Pyx_ArgRef_FASTCALL(__pyx_args, 2); - if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[2])) __PYX_ERR(0, 503, __pyx_L3_error) - CYTHON_FALLTHROUGH; - case 2: - values[1] = __Pyx_ArgRef_FASTCALL(__pyx_args, 1); - if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[1])) __PYX_ERR(0, 503, __pyx_L3_error) - values[0] = __Pyx_ArgRef_FASTCALL(__pyx_args, 0); - if (!CYTHON_ASSUME_SAFE_MACROS && unlikely(!values[0])) __PYX_ERR(0, 503, __pyx_L3_error) - break; - default: goto __pyx_L5_argtuple_error; - } - } - __pyx_v_curves = values[0]; - __pyx_v_max_errors = values[1]; - if (values[2]) { - __pyx_v_all_quadratic = __Pyx_PyLong_As_int(values[2]); if (unlikely((__pyx_v_all_quadratic == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 505, __pyx_L3_error) - } else { - - /* "fontTools/cu2qu/cu2qu.py":505 - * @cython.locals(l=cython.int, last_i=cython.int, i=cython.int) - * @cython.locals(all_quadratic=cython.int) - * def curves_to_quadratic(curves, max_errors, all_quadratic=True): # <<<<<<<<<<<<<< - * """Return quadratic Bezier splines approximating the input cubic Beziers. - * -*/ - __pyx_v_all_quadratic = ((int)((int)1)); - } - } - goto __pyx_L6_skip; - __pyx_L5_argtuple_error:; - __Pyx_RaiseArgtupleInvalid("curves_to_quadratic", 0, 2, 3, __pyx_nargs); __PYX_ERR(0, 503, __pyx_L3_error) - __pyx_L6_skip:; - goto __pyx_L4_argument_unpacking_done; - __pyx_L3_error:; - for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) { - Py_XDECREF(values[__pyx_temp]); - } - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu.curves_to_quadratic", __pyx_clineno, __pyx_lineno, __pyx_filename); - __Pyx_RefNannyFinishContext(); - return NULL; - __pyx_L4_argument_unpacking_done:; - __pyx_r = __pyx_pf_9fontTools_5cu2qu_5cu2qu_5curves_to_quadratic(__pyx_self, __pyx_v_curves, __pyx_v_max_errors, __pyx_v_all_quadratic); - - /* "fontTools/cu2qu/cu2qu.py":503 - * - * - * @cython.locals(l=cython.int, last_i=cython.int, i=cython.int) # <<<<<<<<<<<<<< - * @cython.locals(all_quadratic=cython.int) - * def curves_to_quadratic(curves, max_errors, all_quadratic=True): -*/ - - /* function exit code */ - for (Py_ssize_t __pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) { - Py_XDECREF(values[__pyx_temp]); - } - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -static PyObject *__pyx_pf_9fontTools_5cu2qu_5cu2qu_5curves_to_quadratic(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_curves, PyObject *__pyx_v_max_errors, int __pyx_v_all_quadratic) { - int __pyx_v_l; - int __pyx_v_last_i; - int __pyx_v_i; - PyObject *__pyx_v_splines = NULL; - PyObject *__pyx_v_n = NULL; - PyObject *__pyx_v_spline = NULL; - PyObject *__pyx_8genexpr2__pyx_v_curve = NULL; - PyObject *__pyx_8genexpr3__pyx_v_p = NULL; - PyObject *__pyx_8genexpr4__pyx_v_spline = NULL; - PyObject *__pyx_8genexpr5__pyx_v_s = NULL; - PyObject *__pyx_r = NULL; - __Pyx_RefNannyDeclarations - PyObject *__pyx_t_1 = NULL; - PyObject *__pyx_t_2 = NULL; - Py_ssize_t __pyx_t_3; - PyObject *(*__pyx_t_4)(PyObject *); - PyObject *__pyx_t_5 = NULL; - PyObject *__pyx_t_6 = NULL; - Py_ssize_t __pyx_t_7; - PyObject *(*__pyx_t_8)(PyObject *); - PyObject *__pyx_t_9 = NULL; - PyObject *__pyx_t_10 = NULL; - int __pyx_t_11; - int __pyx_t_12; - double __pyx_t_13; - long __pyx_t_14; - PyObject *__pyx_t_15 = NULL; - size_t __pyx_t_16; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("curves_to_quadratic", 0); - __Pyx_INCREF(__pyx_v_curves); - - /* "fontTools/cu2qu/cu2qu.py":542 - * """ - * - * curves = [[complex(*p) for p in curve] for curve in curves] # <<<<<<<<<<<<<< - * assert len(max_errors) == len(curves) - * -*/ - { /* enter inner scope */ - __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 542, __pyx_L5_error) - __Pyx_GOTREF(__pyx_t_1); - if (likely(PyList_CheckExact(__pyx_v_curves)) || PyTuple_CheckExact(__pyx_v_curves)) { - __pyx_t_2 = __pyx_v_curves; __Pyx_INCREF(__pyx_t_2); - __pyx_t_3 = 0; - __pyx_t_4 = NULL; - } else { - __pyx_t_3 = -1; __pyx_t_2 = PyObject_GetIter(__pyx_v_curves); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 542, __pyx_L5_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_4 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_2); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 542, __pyx_L5_error) - } - for (;;) { - if (likely(!__pyx_t_4)) { - if (likely(PyList_CheckExact(__pyx_t_2))) { - { - Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_2); - #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 542, __pyx_L5_error) - #endif - if (__pyx_t_3 >= __pyx_temp) break; - } - __pyx_t_5 = __Pyx_PyList_GetItemRef(__pyx_t_2, __pyx_t_3); - ++__pyx_t_3; - } else { - { - Py_ssize_t __pyx_temp = __Pyx_PyTuple_GET_SIZE(__pyx_t_2); - #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 542, __pyx_L5_error) - #endif - if (__pyx_t_3 >= __pyx_temp) break; - } - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - __pyx_t_5 = __Pyx_NewRef(PyTuple_GET_ITEM(__pyx_t_2, __pyx_t_3)); - #else - __pyx_t_5 = __Pyx_PySequence_ITEM(__pyx_t_2, __pyx_t_3); - #endif - ++__pyx_t_3; - } - if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 542, __pyx_L5_error) - } else { - __pyx_t_5 = __pyx_t_4(__pyx_t_2); - if (unlikely(!__pyx_t_5)) { - PyObject* exc_type = PyErr_Occurred(); - if (exc_type) { - if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) __PYX_ERR(0, 542, __pyx_L5_error) - PyErr_Clear(); - } - break; - } - } - __Pyx_GOTREF(__pyx_t_5); - __Pyx_XDECREF_SET(__pyx_8genexpr2__pyx_v_curve, __pyx_t_5); - __pyx_t_5 = 0; - { /* enter inner scope */ - __pyx_t_5 = PyList_New(0); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 542, __pyx_L10_error) - __Pyx_GOTREF(__pyx_t_5); - if (likely(PyList_CheckExact(__pyx_8genexpr2__pyx_v_curve)) || PyTuple_CheckExact(__pyx_8genexpr2__pyx_v_curve)) { - __pyx_t_6 = __pyx_8genexpr2__pyx_v_curve; __Pyx_INCREF(__pyx_t_6); - __pyx_t_7 = 0; - __pyx_t_8 = NULL; - } else { - __pyx_t_7 = -1; __pyx_t_6 = PyObject_GetIter(__pyx_8genexpr2__pyx_v_curve); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 542, __pyx_L10_error) - __Pyx_GOTREF(__pyx_t_6); - __pyx_t_8 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_6); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 542, __pyx_L10_error) - } - for (;;) { - if (likely(!__pyx_t_8)) { - if (likely(PyList_CheckExact(__pyx_t_6))) { - { - Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_6); - #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 542, __pyx_L10_error) - #endif - if (__pyx_t_7 >= __pyx_temp) break; - } - __pyx_t_9 = __Pyx_PyList_GetItemRef(__pyx_t_6, __pyx_t_7); - ++__pyx_t_7; - } else { - { - Py_ssize_t __pyx_temp = __Pyx_PyTuple_GET_SIZE(__pyx_t_6); - #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 542, __pyx_L10_error) - #endif - if (__pyx_t_7 >= __pyx_temp) break; - } - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - __pyx_t_9 = __Pyx_NewRef(PyTuple_GET_ITEM(__pyx_t_6, __pyx_t_7)); - #else - __pyx_t_9 = __Pyx_PySequence_ITEM(__pyx_t_6, __pyx_t_7); - #endif - ++__pyx_t_7; - } - if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 542, __pyx_L10_error) - } else { - __pyx_t_9 = __pyx_t_8(__pyx_t_6); - if (unlikely(!__pyx_t_9)) { - PyObject* exc_type = PyErr_Occurred(); - if (exc_type) { - if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) __PYX_ERR(0, 542, __pyx_L10_error) - PyErr_Clear(); - } - break; - } - } - __Pyx_GOTREF(__pyx_t_9); - __Pyx_XDECREF_SET(__pyx_8genexpr3__pyx_v_p, __pyx_t_9); - __pyx_t_9 = 0; - __pyx_t_9 = __Pyx_PySequence_Tuple(__pyx_8genexpr3__pyx_v_p); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 542, __pyx_L10_error) - __Pyx_GOTREF(__pyx_t_9); - __pyx_t_10 = __Pyx_PyObject_Call(((PyObject *)(&PyComplex_Type)), __pyx_t_9, NULL); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 542, __pyx_L10_error) - __Pyx_GOTREF(__pyx_t_10); - __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0; - if (unlikely(__Pyx_ListComp_Append(__pyx_t_5, (PyObject*)__pyx_t_10))) __PYX_ERR(0, 542, __pyx_L10_error) - __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0; - } - __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0; - __Pyx_XDECREF(__pyx_8genexpr3__pyx_v_p); __pyx_8genexpr3__pyx_v_p = 0; - goto __pyx_L14_exit_scope; - __pyx_L10_error:; - __Pyx_XDECREF(__pyx_8genexpr3__pyx_v_p); __pyx_8genexpr3__pyx_v_p = 0; - goto __pyx_L5_error; - __pyx_L14_exit_scope:; - } /* exit inner scope */ - if (unlikely(__Pyx_ListComp_Append(__pyx_t_1, (PyObject*)__pyx_t_5))) __PYX_ERR(0, 542, __pyx_L5_error) - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - } - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __Pyx_XDECREF(__pyx_8genexpr2__pyx_v_curve); __pyx_8genexpr2__pyx_v_curve = 0; - goto __pyx_L16_exit_scope; - __pyx_L5_error:; - __Pyx_XDECREF(__pyx_8genexpr2__pyx_v_curve); __pyx_8genexpr2__pyx_v_curve = 0; - goto __pyx_L1_error; - __pyx_L16_exit_scope:; - } /* exit inner scope */ - __Pyx_DECREF_SET(__pyx_v_curves, __pyx_t_1); - __pyx_t_1 = 0; - - /* "fontTools/cu2qu/cu2qu.py":543 - * - * curves = [[complex(*p) for p in curve] for curve in curves] - * assert len(max_errors) == len(curves) # <<<<<<<<<<<<<< - * - * l = len(curves) -*/ - #ifndef CYTHON_WITHOUT_ASSERTIONS - if (unlikely(__pyx_assertions_enabled())) { - __pyx_t_3 = PyObject_Length(__pyx_v_max_errors); if (unlikely(__pyx_t_3 == ((Py_ssize_t)-1))) __PYX_ERR(0, 543, __pyx_L1_error) - __pyx_t_7 = PyObject_Length(__pyx_v_curves); if (unlikely(__pyx_t_7 == ((Py_ssize_t)-1))) __PYX_ERR(0, 543, __pyx_L1_error) - __pyx_t_11 = (__pyx_t_3 == __pyx_t_7); - if (unlikely(!__pyx_t_11)) { - __Pyx_Raise(__pyx_builtin_AssertionError, 0, 0, 0); - __PYX_ERR(0, 543, __pyx_L1_error) - } - } - #else - if ((1)); else __PYX_ERR(0, 543, __pyx_L1_error) - #endif - - /* "fontTools/cu2qu/cu2qu.py":545 - * assert len(max_errors) == len(curves) - * - * l = len(curves) # <<<<<<<<<<<<<< - * splines = [None] * l - * last_i = i = 0 -*/ - __pyx_t_7 = PyObject_Length(__pyx_v_curves); if (unlikely(__pyx_t_7 == ((Py_ssize_t)-1))) __PYX_ERR(0, 545, __pyx_L1_error) - __pyx_v_l = __pyx_t_7; - - /* "fontTools/cu2qu/cu2qu.py":546 - * - * l = len(curves) - * splines = [None] * l # <<<<<<<<<<<<<< - * last_i = i = 0 - * n = 1 -*/ - __pyx_t_1 = PyList_New(1 * ((__pyx_v_l<0) ? 0:__pyx_v_l)); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 546, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - { Py_ssize_t __pyx_temp; - for (__pyx_temp=0; __pyx_temp < __pyx_v_l; __pyx_temp++) { - __Pyx_INCREF(Py_None); - __Pyx_GIVEREF(Py_None); - if (__Pyx_PyList_SET_ITEM(__pyx_t_1, __pyx_temp, Py_None) != (0)) __PYX_ERR(0, 546, __pyx_L1_error); - } - } - __pyx_v_splines = ((PyObject*)__pyx_t_1); - __pyx_t_1 = 0; - - /* "fontTools/cu2qu/cu2qu.py":547 - * l = len(curves) - * splines = [None] * l - * last_i = i = 0 # <<<<<<<<<<<<<< - * n = 1 - * while True: -*/ - __pyx_v_last_i = 0; - __pyx_v_i = 0; - - /* "fontTools/cu2qu/cu2qu.py":548 - * splines = [None] * l - * last_i = i = 0 - * n = 1 # <<<<<<<<<<<<<< - * while True: - * spline = cubic_approx_spline(curves[i], n, max_errors[i], all_quadratic) -*/ - __Pyx_INCREF(__pyx_mstate_global->__pyx_int_1); - __pyx_v_n = __pyx_mstate_global->__pyx_int_1; - - /* "fontTools/cu2qu/cu2qu.py":549 - * last_i = i = 0 - * n = 1 - * while True: # <<<<<<<<<<<<<< - * spline = cubic_approx_spline(curves[i], n, max_errors[i], all_quadratic) - * if spline is None: -*/ - while (1) { - - /* "fontTools/cu2qu/cu2qu.py":550 - * n = 1 - * while True: - * spline = cubic_approx_spline(curves[i], n, max_errors[i], all_quadratic) # <<<<<<<<<<<<<< - * if spline is None: - * if n == MAX_N: -*/ - __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_curves, __pyx_v_i, int, 1, __Pyx_PyLong_From_int, 0, 1, 1, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 550, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_12 = __Pyx_PyLong_As_int(__pyx_v_n); if (unlikely((__pyx_t_12 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 550, __pyx_L1_error) - __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_max_errors, __pyx_v_i, int, 1, __Pyx_PyLong_From_int, 0, 1, 1, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 550, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_13 = __Pyx_PyFloat_AsDouble(__pyx_t_2); if (unlikely((__pyx_t_13 == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 550, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_2 = __pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_approx_spline(__pyx_t_1, __pyx_t_12, __pyx_t_13, __pyx_v_all_quadratic); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 550, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __Pyx_XDECREF_SET(__pyx_v_spline, __pyx_t_2); - __pyx_t_2 = 0; - - /* "fontTools/cu2qu/cu2qu.py":551 - * while True: - * spline = cubic_approx_spline(curves[i], n, max_errors[i], all_quadratic) - * if spline is None: # <<<<<<<<<<<<<< - * if n == MAX_N: - * break -*/ - __pyx_t_11 = (__pyx_v_spline == Py_None); - if (__pyx_t_11) { - - /* "fontTools/cu2qu/cu2qu.py":552 - * spline = cubic_approx_spline(curves[i], n, max_errors[i], all_quadratic) - * if spline is None: - * if n == MAX_N: # <<<<<<<<<<<<<< - * break - * n += 1 -*/ - __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_mstate_global->__pyx_n_u_MAX_N); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 552, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_1 = PyObject_RichCompare(__pyx_v_n, __pyx_t_2, Py_EQ); __Pyx_XGOTREF(__pyx_t_1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 552, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_11 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely((__pyx_t_11 < 0))) __PYX_ERR(0, 552, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - if (__pyx_t_11) { - - /* "fontTools/cu2qu/cu2qu.py":553 - * if spline is None: - * if n == MAX_N: - * break # <<<<<<<<<<<<<< - * n += 1 - * last_i = i -*/ - goto __pyx_L18_break; - - /* "fontTools/cu2qu/cu2qu.py":552 - * spline = cubic_approx_spline(curves[i], n, max_errors[i], all_quadratic) - * if spline is None: - * if n == MAX_N: # <<<<<<<<<<<<<< - * break - * n += 1 -*/ - } - - /* "fontTools/cu2qu/cu2qu.py":554 - * if n == MAX_N: - * break - * n += 1 # <<<<<<<<<<<<<< - * last_i = i - * continue -*/ - __pyx_t_1 = __Pyx_PyLong_AddObjC(__pyx_v_n, __pyx_mstate_global->__pyx_int_1, 1, 1, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 554, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __Pyx_DECREF_SET(__pyx_v_n, __pyx_t_1); - __pyx_t_1 = 0; - - /* "fontTools/cu2qu/cu2qu.py":555 - * break - * n += 1 - * last_i = i # <<<<<<<<<<<<<< - * continue - * splines[i] = spline -*/ - __pyx_v_last_i = __pyx_v_i; - - /* "fontTools/cu2qu/cu2qu.py":556 - * n += 1 - * last_i = i - * continue # <<<<<<<<<<<<<< - * splines[i] = spline - * i = (i + 1) % l -*/ - goto __pyx_L17_continue; - - /* "fontTools/cu2qu/cu2qu.py":551 - * while True: - * spline = cubic_approx_spline(curves[i], n, max_errors[i], all_quadratic) - * if spline is None: # <<<<<<<<<<<<<< - * if n == MAX_N: - * break -*/ - } - - /* "fontTools/cu2qu/cu2qu.py":557 - * last_i = i - * continue - * splines[i] = spline # <<<<<<<<<<<<<< - * i = (i + 1) % l - * if i == last_i: -*/ - if (unlikely((__Pyx_SetItemInt(__pyx_v_splines, __pyx_v_i, __pyx_v_spline, int, 1, __Pyx_PyLong_From_int, 1, 1, 1, 1) < 0))) __PYX_ERR(0, 557, __pyx_L1_error) - - /* "fontTools/cu2qu/cu2qu.py":558 - * continue - * splines[i] = spline - * i = (i + 1) % l # <<<<<<<<<<<<<< - * if i == last_i: - * # done. go home -*/ - __pyx_t_14 = (__pyx_v_i + 1); - if (unlikely(__pyx_v_l == 0)) { - PyErr_SetString(PyExc_ZeroDivisionError, "integer division or modulo by zero"); - __PYX_ERR(0, 558, __pyx_L1_error) - } - __pyx_v_i = __Pyx_mod_long(__pyx_t_14, __pyx_v_l, 0); - - /* "fontTools/cu2qu/cu2qu.py":559 - * splines[i] = spline - * i = (i + 1) % l - * if i == last_i: # <<<<<<<<<<<<<< - * # done. go home - * return [[(s.real, s.imag) for s in spline] for spline in splines] -*/ - __pyx_t_11 = (__pyx_v_i == __pyx_v_last_i); - if (__pyx_t_11) { - - /* "fontTools/cu2qu/cu2qu.py":561 - * if i == last_i: - * # done. go home - * return [[(s.real, s.imag) for s in spline] for spline in splines] # <<<<<<<<<<<<<< - * - * raise ApproxNotFoundError(curves) -*/ - __Pyx_XDECREF(__pyx_r); - { /* enter inner scope */ - __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 561, __pyx_L24_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_2 = __pyx_v_splines; __Pyx_INCREF(__pyx_t_2); - __pyx_t_7 = 0; - for (;;) { - { - Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_2); - #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 561, __pyx_L24_error) - #endif - if (__pyx_t_7 >= __pyx_temp) break; - } - __pyx_t_5 = __Pyx_PyList_GetItemRef(__pyx_t_2, __pyx_t_7); - ++__pyx_t_7; - if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 561, __pyx_L24_error) - __Pyx_GOTREF(__pyx_t_5); - __Pyx_XDECREF_SET(__pyx_8genexpr4__pyx_v_spline, __pyx_t_5); - __pyx_t_5 = 0; - { /* enter inner scope */ - __pyx_t_5 = PyList_New(0); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 561, __pyx_L29_error) - __Pyx_GOTREF(__pyx_t_5); - if (likely(PyList_CheckExact(__pyx_8genexpr4__pyx_v_spline)) || PyTuple_CheckExact(__pyx_8genexpr4__pyx_v_spline)) { - __pyx_t_6 = __pyx_8genexpr4__pyx_v_spline; __Pyx_INCREF(__pyx_t_6); - __pyx_t_3 = 0; - __pyx_t_4 = NULL; - } else { - __pyx_t_3 = -1; __pyx_t_6 = PyObject_GetIter(__pyx_8genexpr4__pyx_v_spline); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 561, __pyx_L29_error) - __Pyx_GOTREF(__pyx_t_6); - __pyx_t_4 = (CYTHON_COMPILING_IN_LIMITED_API) ? PyIter_Next : __Pyx_PyObject_GetIterNextFunc(__pyx_t_6); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 561, __pyx_L29_error) - } - for (;;) { - if (likely(!__pyx_t_4)) { - if (likely(PyList_CheckExact(__pyx_t_6))) { - { - Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_6); - #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 561, __pyx_L29_error) - #endif - if (__pyx_t_3 >= __pyx_temp) break; - } - __pyx_t_10 = __Pyx_PyList_GetItemRef(__pyx_t_6, __pyx_t_3); - ++__pyx_t_3; - } else { - { - Py_ssize_t __pyx_temp = __Pyx_PyTuple_GET_SIZE(__pyx_t_6); - #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 561, __pyx_L29_error) - #endif - if (__pyx_t_3 >= __pyx_temp) break; - } - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - __pyx_t_10 = __Pyx_NewRef(PyTuple_GET_ITEM(__pyx_t_6, __pyx_t_3)); - #else - __pyx_t_10 = __Pyx_PySequence_ITEM(__pyx_t_6, __pyx_t_3); - #endif - ++__pyx_t_3; - } - if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 561, __pyx_L29_error) - } else { - __pyx_t_10 = __pyx_t_4(__pyx_t_6); - if (unlikely(!__pyx_t_10)) { - PyObject* exc_type = PyErr_Occurred(); - if (exc_type) { - if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) __PYX_ERR(0, 561, __pyx_L29_error) - PyErr_Clear(); - } - break; - } - } - __Pyx_GOTREF(__pyx_t_10); - __Pyx_XDECREF_SET(__pyx_8genexpr5__pyx_v_s, __pyx_t_10); - __pyx_t_10 = 0; - __pyx_t_10 = __Pyx_PyObject_GetAttrStr(__pyx_8genexpr5__pyx_v_s, __pyx_mstate_global->__pyx_n_u_real); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 561, __pyx_L29_error) - __Pyx_GOTREF(__pyx_t_10); - __pyx_t_9 = __Pyx_PyObject_GetAttrStr(__pyx_8genexpr5__pyx_v_s, __pyx_mstate_global->__pyx_n_u_imag); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 561, __pyx_L29_error) - __Pyx_GOTREF(__pyx_t_9); - __pyx_t_15 = PyTuple_New(2); if (unlikely(!__pyx_t_15)) __PYX_ERR(0, 561, __pyx_L29_error) - __Pyx_GOTREF(__pyx_t_15); - __Pyx_GIVEREF(__pyx_t_10); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_15, 0, __pyx_t_10) != (0)) __PYX_ERR(0, 561, __pyx_L29_error); - __Pyx_GIVEREF(__pyx_t_9); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_15, 1, __pyx_t_9) != (0)) __PYX_ERR(0, 561, __pyx_L29_error); - __pyx_t_10 = 0; - __pyx_t_9 = 0; - if (unlikely(__Pyx_ListComp_Append(__pyx_t_5, (PyObject*)__pyx_t_15))) __PYX_ERR(0, 561, __pyx_L29_error) - __Pyx_DECREF(__pyx_t_15); __pyx_t_15 = 0; - } - __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0; - __Pyx_XDECREF(__pyx_8genexpr5__pyx_v_s); __pyx_8genexpr5__pyx_v_s = 0; - goto __pyx_L33_exit_scope; - __pyx_L29_error:; - __Pyx_XDECREF(__pyx_8genexpr5__pyx_v_s); __pyx_8genexpr5__pyx_v_s = 0; - goto __pyx_L24_error; - __pyx_L33_exit_scope:; - } /* exit inner scope */ - if (unlikely(__Pyx_ListComp_Append(__pyx_t_1, (PyObject*)__pyx_t_5))) __PYX_ERR(0, 561, __pyx_L24_error) - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - } - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __Pyx_XDECREF(__pyx_8genexpr4__pyx_v_spline); __pyx_8genexpr4__pyx_v_spline = 0; - goto __pyx_L35_exit_scope; - __pyx_L24_error:; - __Pyx_XDECREF(__pyx_8genexpr4__pyx_v_spline); __pyx_8genexpr4__pyx_v_spline = 0; - goto __pyx_L1_error; - __pyx_L35_exit_scope:; - } /* exit inner scope */ - __pyx_r = __pyx_t_1; - __pyx_t_1 = 0; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":559 - * splines[i] = spline - * i = (i + 1) % l - * if i == last_i: # <<<<<<<<<<<<<< - * # done. go home - * return [[(s.real, s.imag) for s in spline] for spline in splines] -*/ - } - __pyx_L17_continue:; - } - __pyx_L18_break:; - - /* "fontTools/cu2qu/cu2qu.py":563 - * return [[(s.real, s.imag) for s in spline] for spline in splines] - * - * raise ApproxNotFoundError(curves) # <<<<<<<<<<<<<< -*/ - __pyx_t_2 = NULL; - __Pyx_GetModuleGlobalName(__pyx_t_5, __pyx_mstate_global->__pyx_n_u_ApproxNotFoundError); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 563, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - __pyx_t_16 = 1; - #if CYTHON_UNPACK_METHODS - if (unlikely(PyMethod_Check(__pyx_t_5))) { - __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_5); - assert(__pyx_t_2); - PyObject* __pyx__function = PyMethod_GET_FUNCTION(__pyx_t_5); - __Pyx_INCREF(__pyx_t_2); - __Pyx_INCREF(__pyx__function); - __Pyx_DECREF_SET(__pyx_t_5, __pyx__function); - __pyx_t_16 = 0; - } - #endif - { - PyObject *__pyx_callargs[2] = {__pyx_t_2, __pyx_v_curves}; - __pyx_t_1 = __Pyx_PyObject_FastCall(__pyx_t_5, __pyx_callargs+__pyx_t_16, (2-__pyx_t_16) | (__pyx_t_16*__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET)); - __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0; - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 563, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - } - __Pyx_Raise(__pyx_t_1, 0, 0, 0); - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __PYX_ERR(0, 563, __pyx_L1_error) - - /* "fontTools/cu2qu/cu2qu.py":503 - * - * - * @cython.locals(l=cython.int, last_i=cython.int, i=cython.int) # <<<<<<<<<<<<<< - * @cython.locals(all_quadratic=cython.int) - * def curves_to_quadratic(curves, max_errors, all_quadratic=True): -*/ - - /* function exit code */ - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_1); - __Pyx_XDECREF(__pyx_t_2); - __Pyx_XDECREF(__pyx_t_5); - __Pyx_XDECREF(__pyx_t_6); - __Pyx_XDECREF(__pyx_t_9); - __Pyx_XDECREF(__pyx_t_10); - __Pyx_XDECREF(__pyx_t_15); - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu.curves_to_quadratic", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = NULL; - __pyx_L0:; - __Pyx_XDECREF(__pyx_v_splines); - __Pyx_XDECREF(__pyx_v_n); - __Pyx_XDECREF(__pyx_v_spline); - __Pyx_XDECREF(__pyx_8genexpr2__pyx_v_curve); - __Pyx_XDECREF(__pyx_8genexpr3__pyx_v_p); - __Pyx_XDECREF(__pyx_8genexpr4__pyx_v_spline); - __Pyx_XDECREF(__pyx_8genexpr5__pyx_v_s); - __Pyx_XDECREF(__pyx_v_curves); - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} -/* #### Code section: module_exttypes ### */ - -static PyObject *__pyx_tp_new_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen(PyTypeObject *t, CYTHON_UNUSED PyObject *a, CYTHON_UNUSED PyObject *k) { - PyObject *o; - #if CYTHON_COMPILING_IN_LIMITED_API - allocfunc alloc_func = (allocfunc)PyType_GetSlot(t, Py_tp_alloc); - o = alloc_func(t, 0); - #else - #if CYTHON_USE_FREELISTS - if (likely((int)(__pyx_mstate_global->__pyx_freecount_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen > 0) & (int)(t->tp_basicsize == sizeof(struct __pyx_obj_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen)))) { - o = (PyObject*)__pyx_mstate_global->__pyx_freelist_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen[--__pyx_mstate_global->__pyx_freecount_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen]; - memset(o, 0, sizeof(struct __pyx_obj_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen)); - (void) PyObject_INIT(o, t); - } else - #endif - { - o = (*t->tp_alloc)(t, 0); - if (unlikely(!o)) return 0; - } - #endif - return o; -} - -static void __pyx_tp_dealloc_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen(PyObject *o) { - #if CYTHON_USE_TP_FINALIZE - if (unlikely((PY_VERSION_HEX >= 0x03080000 || __Pyx_PyType_HasFeature(Py_TYPE(o), Py_TPFLAGS_HAVE_FINALIZE)) && __Pyx_PyObject_GetSlot(o, tp_finalize, destructor)) && (!PyType_IS_GC(Py_TYPE(o)) || !__Pyx_PyObject_GC_IsFinalized(o))) { - if (__Pyx_PyObject_GetSlot(o, tp_dealloc, destructor) == __pyx_tp_dealloc_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen) { - if (PyObject_CallFinalizerFromDealloc(o)) return; - } - } - #endif - #if CYTHON_USE_FREELISTS - if (((int)(__pyx_mstate_global->__pyx_freecount_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen < 8) & (int)(Py_TYPE(o)->tp_basicsize == sizeof(struct __pyx_obj_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen)))) { - __pyx_mstate_global->__pyx_freelist_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen[__pyx_mstate_global->__pyx_freecount_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen++] = ((struct __pyx_obj_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen *)o); - } else - #endif - { - #if CYTHON_USE_TYPE_SLOTS - (*Py_TYPE(o)->tp_free)(o); - #else - { - freefunc tp_free = (freefunc)PyType_GetSlot(Py_TYPE(o), Py_tp_free); - if (tp_free) tp_free(o); - } - #endif - } -} -#if CYTHON_USE_TYPE_SPECS -static PyType_Slot __pyx_type_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen_slots[] = { - {Py_tp_dealloc, (void *)__pyx_tp_dealloc_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen}, - {Py_tp_new, (void *)__pyx_tp_new_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen}, - {0, 0}, -}; -static PyType_Spec __pyx_type_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen_spec = { - "fontTools.cu2qu.cu2qu.__pyx_scope_struct___split_cubic_into_n_gen", - sizeof(struct __pyx_obj_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen), - 0, - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_HAVE_FINALIZE, - __pyx_type_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen_slots, -}; -#else - -static PyTypeObject __pyx_type_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen = { - PyVarObject_HEAD_INIT(0, 0) - "fontTools.cu2qu.cu2qu.""__pyx_scope_struct___split_cubic_into_n_gen", /*tp_name*/ - sizeof(struct __pyx_obj_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - __pyx_tp_dealloc_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen, /*tp_dealloc*/ - #if PY_VERSION_HEX < 0x030800b4 - 0, /*tp_print*/ - #endif - #if PY_VERSION_HEX >= 0x030800b4 - 0, /*tp_vectorcall_offset*/ - #endif - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_as_async*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_HAVE_FINALIZE, /*tp_flags*/ - 0, /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - 0, /*tp_methods*/ - 0, /*tp_members*/ - 0, /*tp_getset*/ - 0, /*tp_base*/ - 0, /*tp_dict*/ - 0, /*tp_descr_get*/ - 0, /*tp_descr_set*/ - #if !CYTHON_USE_TYPE_SPECS - 0, /*tp_dictoffset*/ - #endif - 0, /*tp_init*/ - 0, /*tp_alloc*/ - __pyx_tp_new_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen, /*tp_new*/ - 0, /*tp_free*/ - 0, /*tp_is_gc*/ - 0, /*tp_bases*/ - 0, /*tp_mro*/ - 0, /*tp_cache*/ - 0, /*tp_subclasses*/ - 0, /*tp_weaklist*/ - 0, /*tp_del*/ - 0, /*tp_version_tag*/ - #if CYTHON_USE_TP_FINALIZE - 0, /*tp_finalize*/ - #else - NULL, /*tp_finalize*/ - #endif - #if PY_VERSION_HEX >= 0x030800b1 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800) - 0, /*tp_vectorcall*/ - #endif - #if __PYX_NEED_TP_PRINT_SLOT == 1 - 0, /*tp_print*/ - #endif - #if PY_VERSION_HEX >= 0x030C0000 - 0, /*tp_watched*/ - #endif - #if PY_VERSION_HEX >= 0x030d00A4 - 0, /*tp_versions_used*/ - #endif - #if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000 && PY_VERSION_HEX < 0x030a0000 - 0, /*tp_pypy_flags*/ - #endif -}; -#endif - -static PyMethodDef __pyx_methods[] = { - {0, 0, 0, 0} -}; -/* #### Code section: initfunc_declarations ### */ -static CYTHON_SMALL_CODE int __Pyx_InitCachedBuiltins(__pyx_mstatetype *__pyx_mstate); /*proto*/ -static CYTHON_SMALL_CODE int __Pyx_InitCachedConstants(__pyx_mstatetype *__pyx_mstate); /*proto*/ -static CYTHON_SMALL_CODE int __Pyx_InitGlobals(void); /*proto*/ -static CYTHON_SMALL_CODE int __Pyx_InitConstants(__pyx_mstatetype *__pyx_mstate); /*proto*/ -static CYTHON_SMALL_CODE int __Pyx_modinit_global_init_code(__pyx_mstatetype *__pyx_mstate); /*proto*/ -static CYTHON_SMALL_CODE int __Pyx_modinit_variable_export_code(__pyx_mstatetype *__pyx_mstate); /*proto*/ -static CYTHON_SMALL_CODE int __Pyx_modinit_function_export_code(__pyx_mstatetype *__pyx_mstate); /*proto*/ -static CYTHON_SMALL_CODE int __Pyx_modinit_type_init_code(__pyx_mstatetype *__pyx_mstate); /*proto*/ -static CYTHON_SMALL_CODE int __Pyx_modinit_type_import_code(__pyx_mstatetype *__pyx_mstate); /*proto*/ -static CYTHON_SMALL_CODE int __Pyx_modinit_variable_import_code(__pyx_mstatetype *__pyx_mstate); /*proto*/ -static CYTHON_SMALL_CODE int __Pyx_modinit_function_import_code(__pyx_mstatetype *__pyx_mstate); /*proto*/ -static CYTHON_SMALL_CODE int __Pyx_CreateCodeObjects(__pyx_mstatetype *__pyx_mstate); /*proto*/ -/* #### Code section: init_module ### */ - -static int __Pyx_modinit_global_init_code(__pyx_mstatetype *__pyx_mstate) { - __Pyx_RefNannyDeclarations - CYTHON_UNUSED_VAR(__pyx_mstate); - __Pyx_RefNannySetupContext("__Pyx_modinit_global_init_code", 0); - /*--- Global init code ---*/ - __Pyx_RefNannyFinishContext(); - return 0; -} - -static int __Pyx_modinit_variable_export_code(__pyx_mstatetype *__pyx_mstate) { - __Pyx_RefNannyDeclarations - CYTHON_UNUSED_VAR(__pyx_mstate); - __Pyx_RefNannySetupContext("__Pyx_modinit_variable_export_code", 0); - /*--- Variable export code ---*/ - __Pyx_RefNannyFinishContext(); - return 0; -} - -static int __Pyx_modinit_function_export_code(__pyx_mstatetype *__pyx_mstate) { - __Pyx_RefNannyDeclarations - CYTHON_UNUSED_VAR(__pyx_mstate); - __Pyx_RefNannySetupContext("__Pyx_modinit_function_export_code", 0); - /*--- Function export code ---*/ - __Pyx_RefNannyFinishContext(); - return 0; -} - -static int __Pyx_modinit_type_init_code(__pyx_mstatetype *__pyx_mstate) { - __Pyx_RefNannyDeclarations - CYTHON_UNUSED_VAR(__pyx_mstate); - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("__Pyx_modinit_type_init_code", 0); - /*--- Type init code ---*/ - #if CYTHON_USE_TYPE_SPECS - __pyx_mstate->__pyx_ptype_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen = (PyTypeObject *) __Pyx_PyType_FromModuleAndSpec(__pyx_m, &__pyx_type_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen_spec, NULL); if (unlikely(!__pyx_mstate->__pyx_ptype_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen)) __PYX_ERR(0, 150, __pyx_L1_error) - if (__Pyx_fix_up_extension_type_from_spec(&__pyx_type_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen_spec, __pyx_mstate->__pyx_ptype_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen) < 0) __PYX_ERR(0, 150, __pyx_L1_error) - #else - __pyx_mstate->__pyx_ptype_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen = &__pyx_type_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen; - #endif - #if !CYTHON_COMPILING_IN_LIMITED_API - #endif - #if !CYTHON_USE_TYPE_SPECS - if (__Pyx_PyType_Ready(__pyx_mstate->__pyx_ptype_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen) < 0) __PYX_ERR(0, 150, __pyx_L1_error) - #endif - #if !CYTHON_COMPILING_IN_LIMITED_API - if ((CYTHON_USE_TYPE_SLOTS && CYTHON_USE_PYTYPE_LOOKUP) && likely(!__pyx_mstate->__pyx_ptype_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen->tp_dictoffset && __pyx_mstate->__pyx_ptype_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen->tp_getattro == PyObject_GenericGetAttr)) { - __pyx_mstate->__pyx_ptype_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen->tp_getattro = PyObject_GenericGetAttr; - } - #endif - __Pyx_RefNannyFinishContext(); - return 0; - __pyx_L1_error:; - __Pyx_RefNannyFinishContext(); - return -1; -} - -static int __Pyx_modinit_type_import_code(__pyx_mstatetype *__pyx_mstate) { - __Pyx_RefNannyDeclarations - CYTHON_UNUSED_VAR(__pyx_mstate); - __Pyx_RefNannySetupContext("__Pyx_modinit_type_import_code", 0); - /*--- Type import code ---*/ - __Pyx_RefNannyFinishContext(); - return 0; -} - -static int __Pyx_modinit_variable_import_code(__pyx_mstatetype *__pyx_mstate) { - __Pyx_RefNannyDeclarations - CYTHON_UNUSED_VAR(__pyx_mstate); - __Pyx_RefNannySetupContext("__Pyx_modinit_variable_import_code", 0); - /*--- Variable import code ---*/ - __Pyx_RefNannyFinishContext(); - return 0; -} - -static int __Pyx_modinit_function_import_code(__pyx_mstatetype *__pyx_mstate) { - __Pyx_RefNannyDeclarations - CYTHON_UNUSED_VAR(__pyx_mstate); - __Pyx_RefNannySetupContext("__Pyx_modinit_function_import_code", 0); - /*--- Function import code ---*/ - __Pyx_RefNannyFinishContext(); - return 0; -} - -#if CYTHON_PEP489_MULTI_PHASE_INIT -static PyObject* __pyx_pymod_create(PyObject *spec, PyModuleDef *def); /*proto*/ -static int __pyx_pymod_exec_cu2qu(PyObject* module); /*proto*/ -static PyModuleDef_Slot __pyx_moduledef_slots[] = { - {Py_mod_create, (void*)__pyx_pymod_create}, - {Py_mod_exec, (void*)__pyx_pymod_exec_cu2qu}, - #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING - {Py_mod_gil, Py_MOD_GIL_USED}, - #endif - #if PY_VERSION_HEX >= 0x030C0000 && CYTHON_USE_MODULE_STATE - {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED}, - #endif - {0, NULL} -}; -#endif - -#ifdef __cplusplus -namespace { - struct PyModuleDef __pyx_moduledef = - #else - static struct PyModuleDef __pyx_moduledef = - #endif - { - PyModuleDef_HEAD_INIT, - "cu2qu", - 0, /* m_doc */ - #if CYTHON_USE_MODULE_STATE - sizeof(__pyx_mstatetype), /* m_size */ - #else - (CYTHON_PEP489_MULTI_PHASE_INIT) ? 0 : -1, /* m_size */ - #endif - __pyx_methods /* m_methods */, - #if CYTHON_PEP489_MULTI_PHASE_INIT - __pyx_moduledef_slots, /* m_slots */ - #else - NULL, /* m_reload */ - #endif - #if CYTHON_USE_MODULE_STATE - __pyx_m_traverse, /* m_traverse */ - __pyx_m_clear, /* m_clear */ - NULL /* m_free */ - #else - NULL, /* m_traverse */ - NULL, /* m_clear */ - NULL /* m_free */ - #endif - }; - #ifdef __cplusplus -} /* anonymous namespace */ -#endif - -/* PyModInitFuncType */ -#ifndef CYTHON_NO_PYINIT_EXPORT - #define __Pyx_PyMODINIT_FUNC PyMODINIT_FUNC -#else - #ifdef __cplusplus - #define __Pyx_PyMODINIT_FUNC extern "C" PyObject * - #else - #define __Pyx_PyMODINIT_FUNC PyObject * - #endif -#endif - -__Pyx_PyMODINIT_FUNC PyInit_cu2qu(void) CYTHON_SMALL_CODE; /*proto*/ -__Pyx_PyMODINIT_FUNC PyInit_cu2qu(void) -#if CYTHON_PEP489_MULTI_PHASE_INIT -{ - return PyModuleDef_Init(&__pyx_moduledef); -} -/* ModuleCreationPEP489 */ -#if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x03090000 -static PY_INT64_T __Pyx_GetCurrentInterpreterId(void) { - { - PyObject *module = PyImport_ImportModule("_interpreters"); // 3.13+ I think - if (!module) { - PyErr_Clear(); // just try the 3.8-3.12 version - module = PyImport_ImportModule("_xxsubinterpreters"); - if (!module) goto bad; - } - PyObject *current = PyObject_CallMethod(module, "get_current", NULL); - Py_DECREF(module); - if (!current) goto bad; - if (PyTuple_Check(current)) { - PyObject *new_current = PySequence_GetItem(current, 0); - Py_DECREF(current); - current = new_current; - if (!new_current) goto bad; - } - long long as_c_int = PyLong_AsLongLong(current); - Py_DECREF(current); - return as_c_int; - } - bad: - PySys_WriteStderr("__Pyx_GetCurrentInterpreterId failed. Try setting the C define CYTHON_PEP489_MULTI_PHASE_INIT=0\n"); - return -1; -} -#endif -#if !CYTHON_USE_MODULE_STATE -static CYTHON_SMALL_CODE int __Pyx_check_single_interpreter(void) { - static PY_INT64_T main_interpreter_id = -1; -#if CYTHON_COMPILING_IN_GRAAL - PY_INT64_T current_id = PyInterpreterState_GetIDFromThreadState(PyThreadState_Get()); -#elif CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX >= 0x03090000 - PY_INT64_T current_id = PyInterpreterState_GetID(PyInterpreterState_Get()); -#elif CYTHON_COMPILING_IN_LIMITED_API - PY_INT64_T current_id = __Pyx_GetCurrentInterpreterId(); -#else - PY_INT64_T current_id = PyInterpreterState_GetID(PyThreadState_Get()->interp); -#endif - if (unlikely(current_id == -1)) { - return -1; - } - if (main_interpreter_id == -1) { - main_interpreter_id = current_id; - return 0; - } else if (unlikely(main_interpreter_id != current_id)) { - PyErr_SetString( - PyExc_ImportError, - "Interpreter change detected - this module can only be loaded into one interpreter per process."); - return -1; - } - return 0; -} -#endif -static CYTHON_SMALL_CODE int __Pyx_copy_spec_to_module(PyObject *spec, PyObject *moddict, const char* from_name, const char* to_name, int allow_none) -{ - PyObject *value = PyObject_GetAttrString(spec, from_name); - int result = 0; - if (likely(value)) { - if (allow_none || value != Py_None) { - result = PyDict_SetItemString(moddict, to_name, value); - } - Py_DECREF(value); - } else if (PyErr_ExceptionMatches(PyExc_AttributeError)) { - PyErr_Clear(); - } else { - result = -1; - } - return result; -} -static CYTHON_SMALL_CODE PyObject* __pyx_pymod_create(PyObject *spec, PyModuleDef *def) { - PyObject *module = NULL, *moddict, *modname; - CYTHON_UNUSED_VAR(def); - #if !CYTHON_USE_MODULE_STATE - if (__Pyx_check_single_interpreter()) - return NULL; - #endif - if (__pyx_m) - return __Pyx_NewRef(__pyx_m); - modname = PyObject_GetAttrString(spec, "name"); - if (unlikely(!modname)) goto bad; - module = PyModule_NewObject(modname); - Py_DECREF(modname); - if (unlikely(!module)) goto bad; - moddict = PyModule_GetDict(module); - if (unlikely(!moddict)) goto bad; - if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "loader", "__loader__", 1) < 0)) goto bad; - if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "origin", "__file__", 1) < 0)) goto bad; - if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "parent", "__package__", 1) < 0)) goto bad; - if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "submodule_search_locations", "__path__", 0) < 0)) goto bad; - return module; -bad: - Py_XDECREF(module); - return NULL; -} - - -static CYTHON_SMALL_CODE int __pyx_pymod_exec_cu2qu(PyObject *__pyx_pyinit_module) -#endif -{ - int stringtab_initialized = 0; - #if CYTHON_USE_MODULE_STATE - int pystate_addmodule_run = 0; - #endif - __pyx_mstatetype *__pyx_mstate = NULL; - PyObject *__pyx_t_1 = NULL; - PyObject *__pyx_t_2 = NULL; - PyObject *__pyx_t_3 = NULL; - PyObject *__pyx_t_4 = NULL; - PyObject *__pyx_t_5 = NULL; - double __pyx_t_6; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannyDeclarations - #if CYTHON_PEP489_MULTI_PHASE_INIT - if (__pyx_m) { - if (__pyx_m == __pyx_pyinit_module) return 0; - PyErr_SetString(PyExc_RuntimeError, "Module 'cu2qu' has already been imported. Re-initialisation is not supported."); - return -1; - } - #else - if (__pyx_m) return __Pyx_NewRef(__pyx_m); - #endif - /*--- Module creation code ---*/ - #if CYTHON_PEP489_MULTI_PHASE_INIT - __pyx_t_1 = __pyx_pyinit_module; - Py_INCREF(__pyx_t_1); - #else - __pyx_t_1 = PyModule_Create(&__pyx_moduledef); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1, __pyx_L1_error) - #endif - #if CYTHON_USE_MODULE_STATE - { - int add_module_result = __Pyx_State_AddModule(__pyx_t_1, &__pyx_moduledef); - __pyx_t_1 = 0; /* transfer ownership from __pyx_t_1 to "cu2qu" pseudovariable */ - if (unlikely((add_module_result < 0))) __PYX_ERR(0, 1, __pyx_L1_error) - pystate_addmodule_run = 1; - } - #else - __pyx_m = __pyx_t_1; - #endif - #if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING - PyUnstable_Module_SetGIL(__pyx_m, Py_MOD_GIL_USED); - #endif - __pyx_mstate = __pyx_mstate_global; - CYTHON_UNUSED_VAR(__pyx_t_1); - __pyx_mstate->__pyx_d = PyModule_GetDict(__pyx_m); if (unlikely(!__pyx_mstate->__pyx_d)) __PYX_ERR(0, 1, __pyx_L1_error) - Py_INCREF(__pyx_mstate->__pyx_d); - __pyx_mstate->__pyx_b = __Pyx_PyImport_AddModuleRef(__Pyx_BUILTIN_MODULE_NAME); if (unlikely(!__pyx_mstate->__pyx_b)) __PYX_ERR(0, 1, __pyx_L1_error) - __pyx_mstate->__pyx_cython_runtime = __Pyx_PyImport_AddModuleRef("cython_runtime"); if (unlikely(!__pyx_mstate->__pyx_cython_runtime)) __PYX_ERR(0, 1, __pyx_L1_error) - if (PyObject_SetAttrString(__pyx_m, "__builtins__", __pyx_mstate->__pyx_b) < 0) __PYX_ERR(0, 1, __pyx_L1_error) - /* ImportRefnannyAPI */ - #if CYTHON_REFNANNY -__Pyx_RefNanny = __Pyx_RefNannyImportAPI("refnanny"); -if (!__Pyx_RefNanny) { - PyErr_Clear(); - __Pyx_RefNanny = __Pyx_RefNannyImportAPI("Cython.Runtime.refnanny"); - if (!__Pyx_RefNanny) - Py_FatalError("failed to import 'refnanny' module"); -} -#endif - -__Pyx_RefNannySetupContext("PyInit_cu2qu", 0); - if (__Pyx_check_binary_version(__PYX_LIMITED_VERSION_HEX, __Pyx_get_runtime_version(), CYTHON_COMPILING_IN_LIMITED_API) < 0) __PYX_ERR(0, 1, __pyx_L1_error) - #ifdef __Pxy_PyFrame_Initialize_Offsets - __Pxy_PyFrame_Initialize_Offsets(); - #endif - __pyx_mstate->__pyx_empty_tuple = PyTuple_New(0); if (unlikely(!__pyx_mstate->__pyx_empty_tuple)) __PYX_ERR(0, 1, __pyx_L1_error) - __pyx_mstate->__pyx_empty_bytes = PyBytes_FromStringAndSize("", 0); if (unlikely(!__pyx_mstate->__pyx_empty_bytes)) __PYX_ERR(0, 1, __pyx_L1_error) - __pyx_mstate->__pyx_empty_unicode = PyUnicode_FromStringAndSize("", 0); if (unlikely(!__pyx_mstate->__pyx_empty_unicode)) __PYX_ERR(0, 1, __pyx_L1_error) - /*--- Initialize various global constants etc. ---*/ - if (__Pyx_InitConstants(__pyx_mstate) < 0) __PYX_ERR(0, 1, __pyx_L1_error) - stringtab_initialized = 1; - if (__Pyx_InitGlobals() < 0) __PYX_ERR(0, 1, __pyx_L1_error) - #if 0 || defined(__Pyx_CyFunction_USED) || defined(__Pyx_FusedFunction_USED) || defined(__Pyx_Coroutine_USED) || defined(__Pyx_Generator_USED) || defined(__Pyx_AsyncGen_USED) - if (__pyx_CommonTypesMetaclass_init(__pyx_m) < 0) __PYX_ERR(0, 1, __pyx_L1_error) - #endif - #ifdef __Pyx_CyFunction_USED - if (__pyx_CyFunction_init(__pyx_m) < 0) __PYX_ERR(0, 1, __pyx_L1_error) - #endif - #ifdef __Pyx_FusedFunction_USED - if (__pyx_FusedFunction_init(__pyx_m) < 0) __PYX_ERR(0, 1, __pyx_L1_error) - #endif - #ifdef __Pyx_Coroutine_USED - if (__pyx_Coroutine_init(__pyx_m) < 0) __PYX_ERR(0, 1, __pyx_L1_error) - #endif - #ifdef __Pyx_Generator_USED - if (__pyx_Generator_init(__pyx_m) < 0) __PYX_ERR(0, 1, __pyx_L1_error) - #endif - #ifdef __Pyx_AsyncGen_USED - if (__pyx_AsyncGen_init(__pyx_m) < 0) __PYX_ERR(0, 1, __pyx_L1_error) - #endif - /*--- Library function declarations ---*/ - if (__pyx_module_is_main_fontTools__cu2qu__cu2qu) { - if (PyObject_SetAttr(__pyx_m, __pyx_mstate_global->__pyx_n_u_name, __pyx_mstate_global->__pyx_n_u_main) < 0) __PYX_ERR(0, 1, __pyx_L1_error) - } - { - PyObject *modules = PyImport_GetModuleDict(); if (unlikely(!modules)) __PYX_ERR(0, 1, __pyx_L1_error) - if (!PyDict_GetItemString(modules, "fontTools.cu2qu.cu2qu")) { - if (unlikely((PyDict_SetItemString(modules, "fontTools.cu2qu.cu2qu", __pyx_m) < 0))) __PYX_ERR(0, 1, __pyx_L1_error) - } - } - /*--- Builtin init code ---*/ - if (__Pyx_InitCachedBuiltins(__pyx_mstate) < 0) __PYX_ERR(0, 1, __pyx_L1_error) - /*--- Constants init code ---*/ - if (__Pyx_InitCachedConstants(__pyx_mstate) < 0) __PYX_ERR(0, 1, __pyx_L1_error) - if (__Pyx_CreateCodeObjects(__pyx_mstate) < 0) __PYX_ERR(0, 1, __pyx_L1_error) - /*--- Global type/function init code ---*/ - (void)__Pyx_modinit_global_init_code(__pyx_mstate); - (void)__Pyx_modinit_variable_export_code(__pyx_mstate); - (void)__Pyx_modinit_function_export_code(__pyx_mstate); - if (unlikely((__Pyx_modinit_type_init_code(__pyx_mstate) < 0))) __PYX_ERR(0, 1, __pyx_L1_error) - (void)__Pyx_modinit_type_import_code(__pyx_mstate); - (void)__Pyx_modinit_variable_import_code(__pyx_mstate); - (void)__Pyx_modinit_function_import_code(__pyx_mstate); - /*--- Execution code ---*/ - - /* "fontTools/cu2qu/cu2qu.py":18 - * # limitations under the License. - * - * try: # <<<<<<<<<<<<<< - * import cython - * except (AttributeError, ImportError): -*/ - { - (void)__pyx_t_1; (void)__pyx_t_2; (void)__pyx_t_3; /* mark used */ - /*try:*/ { - - /* "fontTools/cu2qu/cu2qu.py":19 - * - * try: - * import cython # <<<<<<<<<<<<<< - * except (AttributeError, ImportError): - * # if cython not installed, use mock module with no-op decorators and types -*/ - } - } - - /* "fontTools/cu2qu/cu2qu.py":23 - * # if cython not installed, use mock module with no-op decorators and types - * from fontTools.misc import cython - * COMPILED = cython.compiled # <<<<<<<<<<<<<< - * - * import math -*/ - if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_COMPILED, Py_True) < 0) __PYX_ERR(0, 23, __pyx_L1_error) - - /* "fontTools/cu2qu/cu2qu.py":25 - * COMPILED = cython.compiled - * - * import math # <<<<<<<<<<<<<< - * - * from .errors import Error as Cu2QuError, ApproxNotFoundError -*/ - __pyx_t_4 = __Pyx_ImportDottedModule(__pyx_mstate_global->__pyx_n_u_math, NULL); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 25, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_math, __pyx_t_4) < 0) __PYX_ERR(0, 25, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - - /* "fontTools/cu2qu/cu2qu.py":27 - * import math - * - * from .errors import Error as Cu2QuError, ApproxNotFoundError # <<<<<<<<<<<<<< - * - * -*/ - __pyx_t_4 = __Pyx_PyList_Pack(2, __pyx_mstate_global->__pyx_n_u_Error, __pyx_mstate_global->__pyx_n_u_ApproxNotFoundError); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 27, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __pyx_t_5 = __Pyx_Import(__pyx_mstate_global->__pyx_n_u_errors, __pyx_t_4, 1); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 27, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - __pyx_t_4 = __Pyx_ImportFrom(__pyx_t_5, __pyx_mstate_global->__pyx_n_u_Error); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 27, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_Cu2QuError, __pyx_t_4) < 0) __PYX_ERR(0, 27, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - __pyx_t_4 = __Pyx_ImportFrom(__pyx_t_5, __pyx_mstate_global->__pyx_n_u_ApproxNotFoundError); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 27, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_ApproxNotFoundError, __pyx_t_4) < 0) __PYX_ERR(0, 27, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - - /* "fontTools/cu2qu/cu2qu.py":30 - * - * - * __all__ = ["curve_to_quadratic", "curves_to_quadratic"] # <<<<<<<<<<<<<< - * - * MAX_N = 100 -*/ - __pyx_t_5 = __Pyx_PyList_Pack(2, __pyx_mstate_global->__pyx_n_u_curve_to_quadratic, __pyx_mstate_global->__pyx_n_u_curves_to_quadratic); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 30, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_all, __pyx_t_5) < 0) __PYX_ERR(0, 30, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - - /* "fontTools/cu2qu/cu2qu.py":32 - * __all__ = ["curve_to_quadratic", "curves_to_quadratic"] - * - * MAX_N = 100 # <<<<<<<<<<<<<< - * - * NAN = float("NaN") -*/ - if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_MAX_N, __pyx_mstate_global->__pyx_int_100) < 0) __PYX_ERR(0, 32, __pyx_L1_error) - - /* "fontTools/cu2qu/cu2qu.py":34 - * MAX_N = 100 - * - * NAN = float("NaN") # <<<<<<<<<<<<<< - * - * -*/ - __pyx_t_6 = __Pyx_PyUnicode_AsDouble(__pyx_mstate_global->__pyx_n_u_NaN); if (unlikely(__pyx_t_6 == ((double)((double)-1)) && PyErr_Occurred())) __PYX_ERR(0, 34, __pyx_L1_error) - __pyx_t_5 = PyFloat_FromDouble(__pyx_t_6); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 34, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_NAN, __pyx_t_5) < 0) __PYX_ERR(0, 34, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - - /* "fontTools/cu2qu/cu2qu.py":150 - * - * - * @cython.locals( # <<<<<<<<<<<<<< - * p0=cython.complex, - * p1=cython.complex, -*/ - __pyx_t_5 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_5cu2qu_5cu2qu_1_split_cubic_into_n_gen, 0, __pyx_mstate_global->__pyx_n_u_split_cubic_into_n_gen, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_cu2qu_cu2qu, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[0])); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 150, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_split_cubic_into_n_gen, __pyx_t_5) < 0) __PYX_ERR(0, 150, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - - /* "fontTools/cu2qu/cu2qu.py":471 - * @cython.locals(n=cython.int) - * @cython.locals(all_quadratic=cython.int) - * def curve_to_quadratic(curve, max_err, all_quadratic=True): # <<<<<<<<<<<<<< - * """Approximate a cubic Bezier curve with a spline of n quadratics. - * -*/ - __pyx_t_5 = __Pyx_PyBool_FromLong(((int)1)); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 471, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - - /* "fontTools/cu2qu/cu2qu.py":468 - * - * - * @cython.locals(max_err=cython.double) # <<<<<<<<<<<<<< - * @cython.locals(n=cython.int) - * @cython.locals(all_quadratic=cython.int) -*/ - __pyx_t_4 = PyTuple_Pack(1, __pyx_t_5); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 468, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - __pyx_t_5 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_5cu2qu_5cu2qu_4curve_to_quadratic, 0, __pyx_mstate_global->__pyx_n_u_curve_to_quadratic, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_cu2qu_cu2qu, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[1])); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 468, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - __Pyx_CyFunction_SetDefaultsTuple(__pyx_t_5, __pyx_t_4); - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_curve_to_quadratic, __pyx_t_5) < 0) __PYX_ERR(0, 468, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - - /* "fontTools/cu2qu/cu2qu.py":505 - * @cython.locals(l=cython.int, last_i=cython.int, i=cython.int) - * @cython.locals(all_quadratic=cython.int) - * def curves_to_quadratic(curves, max_errors, all_quadratic=True): # <<<<<<<<<<<<<< - * """Return quadratic Bezier splines approximating the input cubic Beziers. - * -*/ - __pyx_t_5 = __Pyx_PyBool_FromLong(((int)1)); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 505, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - - /* "fontTools/cu2qu/cu2qu.py":503 - * - * - * @cython.locals(l=cython.int, last_i=cython.int, i=cython.int) # <<<<<<<<<<<<<< - * @cython.locals(all_quadratic=cython.int) - * def curves_to_quadratic(curves, max_errors, all_quadratic=True): -*/ - __pyx_t_4 = PyTuple_Pack(1, __pyx_t_5); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 503, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - __pyx_t_5 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_5cu2qu_5cu2qu_6curves_to_quadratic, 0, __pyx_mstate_global->__pyx_n_u_curves_to_quadratic, NULL, __pyx_mstate_global->__pyx_n_u_fontTools_cu2qu_cu2qu, __pyx_mstate_global->__pyx_d, ((PyObject *)__pyx_mstate_global->__pyx_codeobj_tab[2])); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 503, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - __Pyx_CyFunction_SetDefaultsTuple(__pyx_t_5, __pyx_t_4); - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_curves_to_quadratic, __pyx_t_5) < 0) __PYX_ERR(0, 503, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - - /* "fontTools/cu2qu/cu2qu.py":1 - * # cython: language_level=3 # <<<<<<<<<<<<<< - * # distutils: define_macros=CYTHON_TRACE_NOGIL=1 - * -*/ - __pyx_t_5 = __Pyx_PyDict_NewPresized(1); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 1, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - if (PyDict_SetItem(__pyx_t_5, __pyx_mstate_global->__pyx_kp_u_curves_to_quadratic_line_503, __pyx_mstate_global->__pyx_kp_u_Return_quadratic_Bezier_splines) < 0) __PYX_ERR(0, 1, __pyx_L1_error) - if (PyDict_SetItem(__pyx_mstate_global->__pyx_d, __pyx_mstate_global->__pyx_n_u_test, __pyx_t_5) < 0) __PYX_ERR(0, 1, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - - /*--- Wrapped vars code ---*/ - - goto __pyx_L0; - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_4); - __Pyx_XDECREF(__pyx_t_5); - if (__pyx_m) { - if (__pyx_mstate->__pyx_d && stringtab_initialized) { - __Pyx_AddTraceback("init fontTools.cu2qu.cu2qu", __pyx_clineno, __pyx_lineno, __pyx_filename); - } - #if !CYTHON_USE_MODULE_STATE - Py_CLEAR(__pyx_m); - #else - Py_DECREF(__pyx_m); - if (pystate_addmodule_run) { - PyObject *tp, *value, *tb; - PyErr_Fetch(&tp, &value, &tb); - PyState_RemoveModule(&__pyx_moduledef); - PyErr_Restore(tp, value, tb); - } - #endif - } else if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_ImportError, "init fontTools.cu2qu.cu2qu"); - } - __pyx_L0:; - __Pyx_RefNannyFinishContext(); - #if CYTHON_PEP489_MULTI_PHASE_INIT - return (__pyx_m != NULL) ? 0 : -1; - #else - return __pyx_m; - #endif -} -/* #### Code section: pystring_table ### */ - -typedef struct { - const char *s; -#if 1602 <= 65535 - const unsigned short n; -#elif 1602 / 2 < INT_MAX - const unsigned int n; -#elif 1602 / 2 < LONG_MAX - const unsigned long n; -#else - const Py_ssize_t n; -#endif -#if 1 <= 31 - const unsigned int encoding : 5; -#elif 1 <= 255 - const unsigned char encoding; -#elif 1 <= 65535 - const unsigned short encoding; -#else - const Py_ssize_t encoding; -#endif - const unsigned int is_unicode : 1; - const unsigned int intern : 1; -} __Pyx_StringTabEntry; -static const char * const __pyx_string_tab_encodings[] = { 0 }; -static const __Pyx_StringTabEntry __pyx_string_tab[] = { - {__pyx_k_, sizeof(__pyx_k_), 0, 1, 0}, /* PyObject cname: __pyx_kp_u_ */ - {__pyx_k_ApproxNotFoundError, sizeof(__pyx_k_ApproxNotFoundError), 0, 1, 1}, /* PyObject cname: __pyx_n_u_ApproxNotFoundError */ - {__pyx_k_AssertionError, sizeof(__pyx_k_AssertionError), 0, 1, 1}, /* PyObject cname: __pyx_n_u_AssertionError */ - {__pyx_k_AttributeError, sizeof(__pyx_k_AttributeError), 0, 1, 1}, /* PyObject cname: __pyx_n_u_AttributeError */ - {__pyx_k_COMPILED, sizeof(__pyx_k_COMPILED), 0, 1, 1}, /* PyObject cname: __pyx_n_u_COMPILED */ - {__pyx_k_Cu2QuError, sizeof(__pyx_k_Cu2QuError), 0, 1, 1}, /* PyObject cname: __pyx_n_u_Cu2QuError */ - {__pyx_k_Error, sizeof(__pyx_k_Error), 0, 1, 1}, /* PyObject cname: __pyx_n_u_Error */ - {__pyx_k_ImportError, sizeof(__pyx_k_ImportError), 0, 1, 1}, /* PyObject cname: __pyx_n_u_ImportError */ - {__pyx_k_Lib_fontTools_cu2qu_cu2qu_py, sizeof(__pyx_k_Lib_fontTools_cu2qu_cu2qu_py), 0, 1, 0}, /* PyObject cname: __pyx_kp_u_Lib_fontTools_cu2qu_cu2qu_py */ - {__pyx_k_MAX_N, sizeof(__pyx_k_MAX_N), 0, 1, 1}, /* PyObject cname: __pyx_n_u_MAX_N */ - {__pyx_k_NAN, sizeof(__pyx_k_NAN), 0, 1, 1}, /* PyObject cname: __pyx_n_u_NAN */ - {__pyx_k_NaN, sizeof(__pyx_k_NaN), 0, 1, 1}, /* PyObject cname: __pyx_n_u_NaN */ - {__pyx_k_Return_quadratic_Bezier_splines, sizeof(__pyx_k_Return_quadratic_Bezier_splines), 0, 1, 0}, /* PyObject cname: __pyx_kp_u_Return_quadratic_Bezier_splines */ - {__pyx_k_ZeroDivisionError, sizeof(__pyx_k_ZeroDivisionError), 0, 1, 1}, /* PyObject cname: __pyx_n_u_ZeroDivisionError */ - {__pyx_k__2, sizeof(__pyx_k__2), 0, 1, 0}, /* PyObject cname: __pyx_kp_u__2 */ - {__pyx_k_a, sizeof(__pyx_k_a), 0, 1, 1}, /* PyObject cname: __pyx_n_u_a */ - {__pyx_k_a1, sizeof(__pyx_k_a1), 0, 1, 1}, /* PyObject cname: __pyx_n_u_a1 */ - {__pyx_k_all, sizeof(__pyx_k_all), 0, 1, 1}, /* PyObject cname: __pyx_n_u_all */ - {__pyx_k_all_quadratic, sizeof(__pyx_k_all_quadratic), 0, 1, 1}, /* PyObject cname: __pyx_n_u_all_quadratic */ - {__pyx_k_asyncio_coroutines, sizeof(__pyx_k_asyncio_coroutines), 0, 1, 1}, /* PyObject cname: __pyx_n_u_asyncio_coroutines */ - {__pyx_k_b, sizeof(__pyx_k_b), 0, 1, 1}, /* PyObject cname: __pyx_n_u_b */ - {__pyx_k_b1, sizeof(__pyx_k_b1), 0, 1, 1}, /* PyObject cname: __pyx_n_u_b1 */ - {__pyx_k_c, sizeof(__pyx_k_c), 0, 1, 1}, /* PyObject cname: __pyx_n_u_c */ - {__pyx_k_c1, sizeof(__pyx_k_c1), 0, 1, 1}, /* PyObject cname: __pyx_n_u_c1 */ - {__pyx_k_cline_in_traceback, sizeof(__pyx_k_cline_in_traceback), 0, 1, 1}, /* PyObject cname: __pyx_n_u_cline_in_traceback */ - {__pyx_k_close, sizeof(__pyx_k_close), 0, 1, 1}, /* PyObject cname: __pyx_n_u_close */ - {__pyx_k_curve, sizeof(__pyx_k_curve), 0, 1, 1}, /* PyObject cname: __pyx_n_u_curve */ - {__pyx_k_curve_to_quadratic, sizeof(__pyx_k_curve_to_quadratic), 0, 1, 1}, /* PyObject cname: __pyx_n_u_curve_to_quadratic */ - {__pyx_k_curves, sizeof(__pyx_k_curves), 0, 1, 1}, /* PyObject cname: __pyx_n_u_curves */ - {__pyx_k_curves_to_quadratic, sizeof(__pyx_k_curves_to_quadratic), 0, 1, 1}, /* PyObject cname: __pyx_n_u_curves_to_quadratic */ - {__pyx_k_curves_to_quadratic_line_503, sizeof(__pyx_k_curves_to_quadratic_line_503), 0, 1, 0}, /* PyObject cname: __pyx_kp_u_curves_to_quadratic_line_503 */ - {__pyx_k_d, sizeof(__pyx_k_d), 0, 1, 1}, /* PyObject cname: __pyx_n_u_d */ - {__pyx_k_d1, sizeof(__pyx_k_d1), 0, 1, 1}, /* PyObject cname: __pyx_n_u_d1 */ - {__pyx_k_delta_2, sizeof(__pyx_k_delta_2), 0, 1, 1}, /* PyObject cname: __pyx_n_u_delta_2 */ - {__pyx_k_delta_3, sizeof(__pyx_k_delta_3), 0, 1, 1}, /* PyObject cname: __pyx_n_u_delta_3 */ - {__pyx_k_disable, sizeof(__pyx_k_disable), 0, 1, 0}, /* PyObject cname: __pyx_kp_u_disable */ - {__pyx_k_dt, sizeof(__pyx_k_dt), 0, 1, 1}, /* PyObject cname: __pyx_n_u_dt */ - {__pyx_k_enable, sizeof(__pyx_k_enable), 0, 1, 0}, /* PyObject cname: __pyx_kp_u_enable */ - {__pyx_k_errors, sizeof(__pyx_k_errors), 0, 1, 1}, /* PyObject cname: __pyx_n_u_errors */ - {__pyx_k_fontTools_cu2qu_cu2qu, sizeof(__pyx_k_fontTools_cu2qu_cu2qu), 0, 1, 1}, /* PyObject cname: __pyx_n_u_fontTools_cu2qu_cu2qu */ - {__pyx_k_func, sizeof(__pyx_k_func), 0, 1, 1}, /* PyObject cname: __pyx_n_u_func */ - {__pyx_k_gc, sizeof(__pyx_k_gc), 0, 1, 0}, /* PyObject cname: __pyx_kp_u_gc */ - {__pyx_k_i, sizeof(__pyx_k_i), 0, 1, 1}, /* PyObject cname: __pyx_n_u_i */ - {__pyx_k_imag, sizeof(__pyx_k_imag), 0, 1, 1}, /* PyObject cname: __pyx_n_u_imag */ - {__pyx_k_initializing, sizeof(__pyx_k_initializing), 0, 1, 1}, /* PyObject cname: __pyx_n_u_initializing */ - {__pyx_k_is_coroutine, sizeof(__pyx_k_is_coroutine), 0, 1, 1}, /* PyObject cname: __pyx_n_u_is_coroutine */ - {__pyx_k_isenabled, sizeof(__pyx_k_isenabled), 0, 1, 0}, /* PyObject cname: __pyx_kp_u_isenabled */ - {__pyx_k_isnan, sizeof(__pyx_k_isnan), 0, 1, 1}, /* PyObject cname: __pyx_n_u_isnan */ - {__pyx_k_l, sizeof(__pyx_k_l), 0, 1, 1}, /* PyObject cname: __pyx_n_u_l */ - {__pyx_k_last_i, sizeof(__pyx_k_last_i), 0, 1, 1}, /* PyObject cname: __pyx_n_u_last_i */ - {__pyx_k_main, sizeof(__pyx_k_main), 0, 1, 1}, /* PyObject cname: __pyx_n_u_main */ - {__pyx_k_math, sizeof(__pyx_k_math), 0, 1, 1}, /* PyObject cname: __pyx_n_u_math */ - {__pyx_k_max_err, sizeof(__pyx_k_max_err), 0, 1, 1}, /* PyObject cname: __pyx_n_u_max_err */ - {__pyx_k_max_errors, sizeof(__pyx_k_max_errors), 0, 1, 1}, /* PyObject cname: __pyx_n_u_max_errors */ - {__pyx_k_module, sizeof(__pyx_k_module), 0, 1, 1}, /* PyObject cname: __pyx_n_u_module */ - {__pyx_k_n, sizeof(__pyx_k_n), 0, 1, 1}, /* PyObject cname: __pyx_n_u_n */ - {__pyx_k_name, sizeof(__pyx_k_name), 0, 1, 1}, /* PyObject cname: __pyx_n_u_name */ - {__pyx_k_next, sizeof(__pyx_k_next), 0, 1, 1}, /* PyObject cname: __pyx_n_u_next */ - {__pyx_k_p, sizeof(__pyx_k_p), 0, 1, 1}, /* PyObject cname: __pyx_n_u_p */ - {__pyx_k_p0, sizeof(__pyx_k_p0), 0, 1, 1}, /* PyObject cname: __pyx_n_u_p0 */ - {__pyx_k_p1, sizeof(__pyx_k_p1), 0, 1, 1}, /* PyObject cname: __pyx_n_u_p1 */ - {__pyx_k_p2, sizeof(__pyx_k_p2), 0, 1, 1}, /* PyObject cname: __pyx_n_u_p2 */ - {__pyx_k_p3, sizeof(__pyx_k_p3), 0, 1, 1}, /* PyObject cname: __pyx_n_u_p3 */ - {__pyx_k_pop, sizeof(__pyx_k_pop), 0, 1, 1}, /* PyObject cname: __pyx_n_u_pop */ - {__pyx_k_qualname, sizeof(__pyx_k_qualname), 0, 1, 1}, /* PyObject cname: __pyx_n_u_qualname */ - {__pyx_k_range, sizeof(__pyx_k_range), 0, 1, 1}, /* PyObject cname: __pyx_n_u_range */ - {__pyx_k_real, sizeof(__pyx_k_real), 0, 1, 1}, /* PyObject cname: __pyx_n_u_real */ - {__pyx_k_s, sizeof(__pyx_k_s), 0, 1, 1}, /* PyObject cname: __pyx_n_u_s */ - {__pyx_k_send, sizeof(__pyx_k_send), 0, 1, 1}, /* PyObject cname: __pyx_n_u_send */ - {__pyx_k_set_name, sizeof(__pyx_k_set_name), 0, 1, 1}, /* PyObject cname: __pyx_n_u_set_name */ - {__pyx_k_spec, sizeof(__pyx_k_spec), 0, 1, 1}, /* PyObject cname: __pyx_n_u_spec */ - {__pyx_k_spline, sizeof(__pyx_k_spline), 0, 1, 1}, /* PyObject cname: __pyx_n_u_spline */ - {__pyx_k_splines, sizeof(__pyx_k_splines), 0, 1, 1}, /* PyObject cname: __pyx_n_u_splines */ - {__pyx_k_split_cubic_into_n_gen, sizeof(__pyx_k_split_cubic_into_n_gen), 0, 1, 1}, /* PyObject cname: __pyx_n_u_split_cubic_into_n_gen */ - {__pyx_k_t1, sizeof(__pyx_k_t1), 0, 1, 1}, /* PyObject cname: __pyx_n_u_t1 */ - {__pyx_k_t1_2, sizeof(__pyx_k_t1_2), 0, 1, 1}, /* PyObject cname: __pyx_n_u_t1_2 */ - {__pyx_k_test, sizeof(__pyx_k_test), 0, 1, 1}, /* PyObject cname: __pyx_n_u_test */ - {__pyx_k_throw, sizeof(__pyx_k_throw), 0, 1, 1}, /* PyObject cname: __pyx_n_u_throw */ - {__pyx_k_value, sizeof(__pyx_k_value), 0, 1, 1}, /* PyObject cname: __pyx_n_u_value */ - {0, 0, 0, 0, 0} -}; -/* InitStrings.proto */ -static int __Pyx_InitStrings(__Pyx_StringTabEntry const *t, PyObject **target, const char* const* encoding_names); - -/* #### Code section: cached_builtins ### */ - -static int __Pyx_InitCachedBuiltins(__pyx_mstatetype *__pyx_mstate) { - CYTHON_UNUSED_VAR(__pyx_mstate); - __pyx_builtin_AttributeError = __Pyx_GetBuiltinName(__pyx_mstate->__pyx_n_u_AttributeError); if (!__pyx_builtin_AttributeError) __PYX_ERR(0, 20, __pyx_L1_error) - __pyx_builtin_ImportError = __Pyx_GetBuiltinName(__pyx_mstate->__pyx_n_u_ImportError); if (!__pyx_builtin_ImportError) __PYX_ERR(0, 20, __pyx_L1_error) - __pyx_builtin_range = __Pyx_GetBuiltinName(__pyx_mstate->__pyx_n_u_range); if (!__pyx_builtin_range) __PYX_ERR(0, 169, __pyx_L1_error) - __pyx_builtin_ZeroDivisionError = __Pyx_GetBuiltinName(__pyx_mstate->__pyx_n_u_ZeroDivisionError); if (!__pyx_builtin_ZeroDivisionError) __PYX_ERR(0, 301, __pyx_L1_error) - __pyx_builtin_AssertionError = __Pyx_GetBuiltinName(__pyx_mstate->__pyx_n_u_AssertionError); if (!__pyx_builtin_AssertionError) __PYX_ERR(0, 543, __pyx_L1_error) - return 0; - __pyx_L1_error:; - return -1; -} -/* #### Code section: cached_constants ### */ - -static int __Pyx_InitCachedConstants(__pyx_mstatetype *__pyx_mstate) { - __Pyx_RefNannyDeclarations - CYTHON_UNUSED_VAR(__pyx_mstate); - __Pyx_RefNannySetupContext("__Pyx_InitCachedConstants", 0); - __Pyx_RefNannyFinishContext(); - return 0; -} -/* #### Code section: init_constants ### */ - -static int __Pyx_InitConstants(__pyx_mstatetype *__pyx_mstate) { - CYTHON_UNUSED_VAR(__pyx_mstate); - __pyx_mstate->__pyx_umethod_PyDict_Type_pop.type = (PyObject*)&PyDict_Type; - __pyx_mstate->__pyx_umethod_PyDict_Type_pop.method_name = &__pyx_mstate->__pyx_n_u_pop; - if (__Pyx_InitStrings(__pyx_string_tab, __pyx_mstate->__pyx_string_tab, __pyx_string_tab_encodings) < 0) __PYX_ERR(0, 1, __pyx_L1_error); - __pyx_mstate->__pyx_int_1 = PyLong_FromLong(1); if (unlikely(!__pyx_mstate->__pyx_int_1)) __PYX_ERR(0, 1, __pyx_L1_error) - __pyx_mstate->__pyx_int_2 = PyLong_FromLong(2); if (unlikely(!__pyx_mstate->__pyx_int_2)) __PYX_ERR(0, 1, __pyx_L1_error) - __pyx_mstate->__pyx_int_3 = PyLong_FromLong(3); if (unlikely(!__pyx_mstate->__pyx_int_3)) __PYX_ERR(0, 1, __pyx_L1_error) - __pyx_mstate->__pyx_int_4 = PyLong_FromLong(4); if (unlikely(!__pyx_mstate->__pyx_int_4)) __PYX_ERR(0, 1, __pyx_L1_error) - __pyx_mstate->__pyx_int_6 = PyLong_FromLong(6); if (unlikely(!__pyx_mstate->__pyx_int_6)) __PYX_ERR(0, 1, __pyx_L1_error) - __pyx_mstate->__pyx_int_100 = PyLong_FromLong(100); if (unlikely(!__pyx_mstate->__pyx_int_100)) __PYX_ERR(0, 1, __pyx_L1_error) - return 0; - __pyx_L1_error:; - return -1; -} -/* #### Code section: init_codeobjects ### */ -\ - typedef struct { - unsigned int argcount : 3; - unsigned int num_posonly_args : 1; - unsigned int num_kwonly_args : 1; - unsigned int nlocals : 5; - unsigned int flags : 10; - unsigned int first_line : 9; - unsigned int line_table_length : 13; - } __Pyx_PyCode_New_function_description; -/* NewCodeObj.proto */ -static PyObject* __Pyx_PyCode_New( - const __Pyx_PyCode_New_function_description descr, - PyObject * const *varnames, - PyObject *filename, - PyObject *funcname, - const char *line_table, - PyObject *tuple_dedup_map -); - - -static int __Pyx_CreateCodeObjects(__pyx_mstatetype *__pyx_mstate) { - PyObject* tuple_dedup_map = PyDict_New(); - if (unlikely(!tuple_dedup_map)) return -1; - { - const __Pyx_PyCode_New_function_description descr = {5, 0, 0, 19, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS|CO_GENERATOR), 150, 2}; - PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_p0, __pyx_mstate->__pyx_n_u_p1, __pyx_mstate->__pyx_n_u_p2, __pyx_mstate->__pyx_n_u_p3, __pyx_mstate->__pyx_n_u_n, __pyx_mstate->__pyx_n_u_a1, __pyx_mstate->__pyx_n_u_b1, __pyx_mstate->__pyx_n_u_c1, __pyx_mstate->__pyx_n_u_d1, __pyx_mstate->__pyx_n_u_dt, __pyx_mstate->__pyx_n_u_delta_2, __pyx_mstate->__pyx_n_u_delta_3, __pyx_mstate->__pyx_n_u_i, __pyx_mstate->__pyx_n_u_a, __pyx_mstate->__pyx_n_u_b, __pyx_mstate->__pyx_n_u_c, __pyx_mstate->__pyx_n_u_d, __pyx_mstate->__pyx_n_u_t1, __pyx_mstate->__pyx_n_u_t1_2}; - __pyx_mstate_global->__pyx_codeobj_tab[0] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_cu2qu_cu2qu_py, __pyx_mstate->__pyx_n_u_split_cubic_into_n_gen, __pyx_k__3, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[0])) goto bad; - } - { - const __Pyx_PyCode_New_function_description descr = {3, 0, 0, 7, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 468, 97}; - PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_curve, __pyx_mstate->__pyx_n_u_max_err, __pyx_mstate->__pyx_n_u_all_quadratic, __pyx_mstate->__pyx_n_u_n, __pyx_mstate->__pyx_n_u_spline, __pyx_mstate->__pyx_n_u_p, __pyx_mstate->__pyx_n_u_s}; - __pyx_mstate_global->__pyx_codeobj_tab[1] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_cu2qu_cu2qu_py, __pyx_mstate->__pyx_n_u_curve_to_quadratic, __pyx_k_AWBc_U_U_3fBa_AWCy_7_2QgQgT_a_Q, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[1])) goto bad; - } - { - const __Pyx_PyCode_New_function_description descr = {3, 0, 0, 13, (unsigned int)(CO_OPTIMIZED|CO_NEWLOCALS), 503, 211}; - PyObject* const varnames[] = {__pyx_mstate->__pyx_n_u_curves, __pyx_mstate->__pyx_n_u_max_errors, __pyx_mstate->__pyx_n_u_all_quadratic, __pyx_mstate->__pyx_n_u_l, __pyx_mstate->__pyx_n_u_last_i, __pyx_mstate->__pyx_n_u_i, __pyx_mstate->__pyx_n_u_splines, __pyx_mstate->__pyx_n_u_n, __pyx_mstate->__pyx_n_u_spline, __pyx_mstate->__pyx_n_u_curve, __pyx_mstate->__pyx_n_u_p, __pyx_mstate->__pyx_n_u_spline, __pyx_mstate->__pyx_n_u_s}; - __pyx_mstate_global->__pyx_codeobj_tab[2] = __Pyx_PyCode_New(descr, varnames, __pyx_mstate->__pyx_kp_u_Lib_fontTools_cu2qu_cu2qu_py, __pyx_mstate->__pyx_n_u_curves_to_quadratic, __pyx_k_J_Qawb_4uG4y_3a_3c_1A_avRq_T_AV, tuple_dedup_map); if (unlikely(!__pyx_mstate_global->__pyx_codeobj_tab[2])) goto bad; - } - Py_DECREF(tuple_dedup_map); - return 0; - bad: - Py_DECREF(tuple_dedup_map); - return -1; -} -/* #### Code section: init_globals ### */ - -static int __Pyx_InitGlobals(void) { - /* PythonCompatibility.init */ - if (likely(__Pyx_init_co_variables() == 0)); else - -if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 1, __pyx_L1_error) - - /* AssertionsEnabled.init */ - if (likely(__Pyx_init_assertions_enabled() == 0)); else - -if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 1, __pyx_L1_error) - - /* CachedMethodType.init */ - #if CYTHON_COMPILING_IN_LIMITED_API -{ - PyObject *typesModule=NULL; - typesModule = PyImport_ImportModule("types"); - if (typesModule) { - __pyx_mstate_global->__Pyx_CachedMethodType = PyObject_GetAttrString(typesModule, "MethodType"); - Py_DECREF(typesModule); - } -} // error handling follows -#endif - -if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 1, __pyx_L1_error) - - return 0; - __pyx_L1_error:; - return -1; -} -/* #### Code section: cleanup_globals ### */ -/* #### Code section: cleanup_module ### */ -/* #### Code section: main_method ### */ -/* #### Code section: utility_code_pragmas ### */ -#ifdef _MSC_VER -#pragma warning( push ) -/* Warning 4127: conditional expression is constant - * Cython uses constant conditional expressions to allow in inline functions to be optimized at - * compile-time, so this warning is not useful - */ -#pragma warning( disable : 4127 ) -#endif - - - -/* #### Code section: utility_code_def ### */ - -/* --- Runtime support code --- */ -/* Refnanny */ -#if CYTHON_REFNANNY -static __Pyx_RefNannyAPIStruct *__Pyx_RefNannyImportAPI(const char *modname) { - PyObject *m = NULL, *p = NULL; - void *r = NULL; - m = PyImport_ImportModule(modname); - if (!m) goto end; - p = PyObject_GetAttrString(m, "RefNannyAPI"); - if (!p) goto end; - r = PyLong_AsVoidPtr(p); -end: - Py_XDECREF(p); - Py_XDECREF(m); - return (__Pyx_RefNannyAPIStruct *)r; -} -#endif - -/* PyErrExceptionMatches */ -#if CYTHON_FAST_THREAD_STATE -static int __Pyx_PyErr_ExceptionMatchesTuple(PyObject *exc_type, PyObject *tuple) { - Py_ssize_t i, n; - n = PyTuple_GET_SIZE(tuple); - for (i=0; i= 0x030C00A6 - PyObject *current_exception = tstate->current_exception; - if (unlikely(!current_exception)) return 0; - exc_type = (PyObject*) Py_TYPE(current_exception); - if (exc_type == err) return 1; -#else - exc_type = tstate->curexc_type; - if (exc_type == err) return 1; - if (unlikely(!exc_type)) return 0; -#endif - #if CYTHON_AVOID_BORROWED_REFS - Py_INCREF(exc_type); - #endif - if (unlikely(PyTuple_Check(err))) { - result = __Pyx_PyErr_ExceptionMatchesTuple(exc_type, err); - } else { - result = __Pyx_PyErr_GivenExceptionMatches(exc_type, err); - } - #if CYTHON_AVOID_BORROWED_REFS - Py_DECREF(exc_type); - #endif - return result; -} -#endif - -/* PyErrFetchRestore */ -#if CYTHON_FAST_THREAD_STATE -static CYTHON_INLINE void __Pyx_ErrRestoreInState(PyThreadState *tstate, PyObject *type, PyObject *value, PyObject *tb) { -#if PY_VERSION_HEX >= 0x030C00A6 - PyObject *tmp_value; - assert(type == NULL || (value != NULL && type == (PyObject*) Py_TYPE(value))); - if (value) { - #if CYTHON_COMPILING_IN_CPYTHON - if (unlikely(((PyBaseExceptionObject*) value)->traceback != tb)) - #endif - PyException_SetTraceback(value, tb); - } - tmp_value = tstate->current_exception; - tstate->current_exception = value; - Py_XDECREF(tmp_value); - Py_XDECREF(type); - Py_XDECREF(tb); -#else - PyObject *tmp_type, *tmp_value, *tmp_tb; - tmp_type = tstate->curexc_type; - tmp_value = tstate->curexc_value; - tmp_tb = tstate->curexc_traceback; - tstate->curexc_type = type; - tstate->curexc_value = value; - tstate->curexc_traceback = tb; - Py_XDECREF(tmp_type); - Py_XDECREF(tmp_value); - Py_XDECREF(tmp_tb); -#endif -} -static CYTHON_INLINE void __Pyx_ErrFetchInState(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb) { -#if PY_VERSION_HEX >= 0x030C00A6 - PyObject* exc_value; - exc_value = tstate->current_exception; - tstate->current_exception = 0; - *value = exc_value; - *type = NULL; - *tb = NULL; - if (exc_value) { - *type = (PyObject*) Py_TYPE(exc_value); - Py_INCREF(*type); - #if CYTHON_COMPILING_IN_CPYTHON - *tb = ((PyBaseExceptionObject*) exc_value)->traceback; - Py_XINCREF(*tb); - #else - *tb = PyException_GetTraceback(exc_value); - #endif - } -#else - *type = tstate->curexc_type; - *value = tstate->curexc_value; - *tb = tstate->curexc_traceback; - tstate->curexc_type = 0; - tstate->curexc_value = 0; - tstate->curexc_traceback = 0; -#endif -} -#endif - -/* PyObjectGetAttrStr */ -#if CYTHON_USE_TYPE_SLOTS -static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStr(PyObject* obj, PyObject* attr_name) { - PyTypeObject* tp = Py_TYPE(obj); - if (likely(tp->tp_getattro)) - return tp->tp_getattro(obj, attr_name); - return PyObject_GetAttr(obj, attr_name); -} -#endif - -/* PyObjectGetAttrStrNoError */ -#if __PYX_LIMITED_VERSION_HEX < 0x030d0000 -static void __Pyx_PyObject_GetAttrStr_ClearAttributeError(void) { - __Pyx_PyThreadState_declare - __Pyx_PyThreadState_assign - if (likely(__Pyx_PyErr_ExceptionMatches(PyExc_AttributeError))) - __Pyx_PyErr_Clear(); -} -#endif -static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStrNoError(PyObject* obj, PyObject* attr_name) { - PyObject *result; -#if __PYX_LIMITED_VERSION_HEX >= 0x030d0000 - (void) PyObject_GetOptionalAttr(obj, attr_name, &result); - return result; -#else -#if CYTHON_COMPILING_IN_CPYTHON && CYTHON_USE_TYPE_SLOTS - PyTypeObject* tp = Py_TYPE(obj); - if (likely(tp->tp_getattro == PyObject_GenericGetAttr)) { - return _PyObject_GenericGetAttrWithDict(obj, attr_name, NULL, 1); - } -#endif - result = __Pyx_PyObject_GetAttrStr(obj, attr_name); - if (unlikely(!result)) { - __Pyx_PyObject_GetAttrStr_ClearAttributeError(); - } - return result; -#endif -} - -/* GetBuiltinName */ -static PyObject *__Pyx_GetBuiltinName(PyObject *name) { - PyObject* result = __Pyx_PyObject_GetAttrStrNoError(__pyx_mstate_global->__pyx_b, name); - if (unlikely(!result) && !PyErr_Occurred()) { - PyErr_Format(PyExc_NameError, - "name '%U' is not defined", name); - } - return result; -} - -/* PyFunctionFastCall */ -#if CYTHON_FAST_PYCALL && !CYTHON_VECTORCALL -static PyObject* __Pyx_PyFunction_FastCallNoKw(PyCodeObject *co, PyObject *const *args, Py_ssize_t na, - PyObject *globals) { - PyFrameObject *f; - PyThreadState *tstate = __Pyx_PyThreadState_Current; - PyObject **fastlocals; - Py_ssize_t i; - PyObject *result; - assert(globals != NULL); - /* XXX Perhaps we should create a specialized - PyFrame_New() that doesn't take locals, but does - take builtins without sanity checking them. - */ - assert(tstate != NULL); - f = PyFrame_New(tstate, co, globals, NULL); - if (f == NULL) { - return NULL; - } - fastlocals = __Pyx_PyFrame_GetLocalsplus(f); - for (i = 0; i < na; i++) { - Py_INCREF(*args); - fastlocals[i] = *args++; - } - result = PyEval_EvalFrameEx(f,0); - ++tstate->recursion_depth; - Py_DECREF(f); - --tstate->recursion_depth; - return result; -} -static PyObject *__Pyx_PyFunction_FastCallDict(PyObject *func, PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs) { - PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func); - PyObject *globals = PyFunction_GET_GLOBALS(func); - PyObject *argdefs = PyFunction_GET_DEFAULTS(func); - PyObject *closure; - PyObject *kwdefs; - PyObject *kwtuple, **k; - PyObject **d; - Py_ssize_t nd; - Py_ssize_t nk; - PyObject *result; - assert(kwargs == NULL || PyDict_Check(kwargs)); - nk = kwargs ? PyDict_Size(kwargs) : 0; - if (unlikely(Py_EnterRecursiveCall(" while calling a Python object"))) { - return NULL; - } - if ( - co->co_kwonlyargcount == 0 && - likely(kwargs == NULL || nk == 0) && - co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)) { - if (argdefs == NULL && co->co_argcount == nargs) { - result = __Pyx_PyFunction_FastCallNoKw(co, args, nargs, globals); - goto done; - } - else if (nargs == 0 && argdefs != NULL - && co->co_argcount == Py_SIZE(argdefs)) { - /* function called with no arguments, but all parameters have - a default value: use default values as arguments .*/ - args = &PyTuple_GET_ITEM(argdefs, 0); - result =__Pyx_PyFunction_FastCallNoKw(co, args, Py_SIZE(argdefs), globals); - goto done; - } - } - if (kwargs != NULL) { - Py_ssize_t pos, i; - kwtuple = PyTuple_New(2 * nk); - if (kwtuple == NULL) { - result = NULL; - goto done; - } - k = &PyTuple_GET_ITEM(kwtuple, 0); - pos = i = 0; - while (PyDict_Next(kwargs, &pos, &k[i], &k[i+1])) { - Py_INCREF(k[i]); - Py_INCREF(k[i+1]); - i += 2; - } - nk = i / 2; - } - else { - kwtuple = NULL; - k = NULL; - } - closure = PyFunction_GET_CLOSURE(func); - kwdefs = PyFunction_GET_KW_DEFAULTS(func); - if (argdefs != NULL) { - d = &PyTuple_GET_ITEM(argdefs, 0); - nd = Py_SIZE(argdefs); - } - else { - d = NULL; - nd = 0; - } - result = PyEval_EvalCodeEx((PyObject*)co, globals, (PyObject *)NULL, - args, (int)nargs, - k, (int)nk, - d, (int)nd, kwdefs, closure); - Py_XDECREF(kwtuple); -done: - Py_LeaveRecursiveCall(); - return result; -} -#endif - -/* PyObjectCall */ -#if CYTHON_COMPILING_IN_CPYTHON -static CYTHON_INLINE PyObject* __Pyx_PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw) { - PyObject *result; - ternaryfunc call = Py_TYPE(func)->tp_call; - if (unlikely(!call)) - return PyObject_Call(func, arg, kw); - if (unlikely(Py_EnterRecursiveCall(" while calling a Python object"))) - return NULL; - result = (*call)(func, arg, kw); - Py_LeaveRecursiveCall(); - if (unlikely(!result) && unlikely(!PyErr_Occurred())) { - PyErr_SetString( - PyExc_SystemError, - "NULL result without error in PyObject_Call"); - } - return result; -} -#endif - -/* PyObjectCallMethO */ -#if CYTHON_COMPILING_IN_CPYTHON -static CYTHON_INLINE PyObject* __Pyx_PyObject_CallMethO(PyObject *func, PyObject *arg) { - PyObject *self, *result; - PyCFunction cfunc; - cfunc = __Pyx_CyOrPyCFunction_GET_FUNCTION(func); - self = __Pyx_CyOrPyCFunction_GET_SELF(func); - if (unlikely(Py_EnterRecursiveCall(" while calling a Python object"))) - return NULL; - result = cfunc(self, arg); - Py_LeaveRecursiveCall(); - if (unlikely(!result) && unlikely(!PyErr_Occurred())) { - PyErr_SetString( - PyExc_SystemError, - "NULL result without error in PyObject_Call"); - } - return result; -} -#endif - -/* PyObjectFastCall */ -#if PY_VERSION_HEX < 0x03090000 || CYTHON_COMPILING_IN_LIMITED_API -static PyObject* __Pyx_PyObject_FastCall_fallback(PyObject *func, PyObject * const*args, size_t nargs, PyObject *kwargs) { - PyObject *argstuple; - PyObject *result = 0; - size_t i; - argstuple = PyTuple_New((Py_ssize_t)nargs); - if (unlikely(!argstuple)) return NULL; - for (i = 0; i < nargs; i++) { - Py_INCREF(args[i]); - if (__Pyx_PyTuple_SET_ITEM(argstuple, (Py_ssize_t)i, args[i]) != (0)) goto bad; - } - result = __Pyx_PyObject_Call(func, argstuple, kwargs); - bad: - Py_DECREF(argstuple); - return result; -} -#endif -#if CYTHON_VECTORCALL && !CYTHON_COMPILING_IN_LIMITED_API - #if PY_VERSION_HEX < 0x03090000 - #define __Pyx_PyVectorcall_Function(callable) _PyVectorcall_Function(callable) - #elif CYTHON_COMPILING_IN_CPYTHON -static CYTHON_INLINE vectorcallfunc __Pyx_PyVectorcall_Function(PyObject *callable) { - PyTypeObject *tp = Py_TYPE(callable); - #if defined(__Pyx_CyFunction_USED) - if (__Pyx_CyFunction_CheckExact(callable)) { - return __Pyx_CyFunction_func_vectorcall(callable); - } - #endif - if (!PyType_HasFeature(tp, Py_TPFLAGS_HAVE_VECTORCALL)) { - return NULL; - } - assert(PyCallable_Check(callable)); - Py_ssize_t offset = tp->tp_vectorcall_offset; - assert(offset > 0); - vectorcallfunc ptr; - memcpy(&ptr, (char *) callable + offset, sizeof(ptr)); - return ptr; -} - #else - #define __Pyx_PyVectorcall_Function(callable) PyVectorcall_Function(callable) - #endif -#endif -static CYTHON_INLINE PyObject* __Pyx_PyObject_FastCallDict(PyObject *func, PyObject *const *args, size_t _nargs, PyObject *kwargs) { - Py_ssize_t nargs = __Pyx_PyVectorcall_NARGS(_nargs); -#if CYTHON_COMPILING_IN_CPYTHON - if (nargs == 0 && kwargs == NULL) { - if (__Pyx_CyOrPyCFunction_Check(func) && likely( __Pyx_CyOrPyCFunction_GET_FLAGS(func) & METH_NOARGS)) - return __Pyx_PyObject_CallMethO(func, NULL); - } - else if (nargs == 1 && kwargs == NULL) { - if (__Pyx_CyOrPyCFunction_Check(func) && likely( __Pyx_CyOrPyCFunction_GET_FLAGS(func) & METH_O)) - return __Pyx_PyObject_CallMethO(func, args[0]); - } -#endif - #if PY_VERSION_HEX < 0x030800B1 - #if CYTHON_FAST_PYCCALL - if (PyCFunction_Check(func)) { - if (kwargs) { - return _PyCFunction_FastCallDict(func, args, nargs, kwargs); - } else { - return _PyCFunction_FastCallKeywords(func, args, nargs, NULL); - } - } - if (!kwargs && __Pyx_IS_TYPE(func, &PyMethodDescr_Type)) { - return _PyMethodDescr_FastCallKeywords(func, args, nargs, NULL); - } - #endif - #if CYTHON_FAST_PYCALL - if (PyFunction_Check(func)) { - return __Pyx_PyFunction_FastCallDict(func, args, nargs, kwargs); - } - #endif - #endif - if (kwargs == NULL) { - #if CYTHON_VECTORCALL && !CYTHON_COMPILING_IN_LIMITED_API - vectorcallfunc f = __Pyx_PyVectorcall_Function(func); - if (f) { - return f(func, args, _nargs, NULL); - } - #elif defined(__Pyx_CyFunction_USED) && CYTHON_BACKPORT_VECTORCALL - if (__Pyx_CyFunction_CheckExact(func)) { - __pyx_vectorcallfunc f = __Pyx_CyFunction_func_vectorcall(func); - if (f) return f(func, args, _nargs, NULL); - } - #elif CYTHON_COMPILING_IN_LIMITED_API && CYTHON_VECTORCALL - return PyObject_Vectorcall(func, args, _nargs, NULL); - #endif - } - if (nargs == 0) { - return __Pyx_PyObject_Call(func, __pyx_mstate_global->__pyx_empty_tuple, kwargs); - } - #if PY_VERSION_HEX >= 0x03090000 && !CYTHON_COMPILING_IN_LIMITED_API - return PyObject_VectorcallDict(func, args, (size_t)nargs, kwargs); - #else - return __Pyx_PyObject_FastCall_fallback(func, args, (size_t)nargs, kwargs); - #endif -} - -/* PyLongCompare */ -static CYTHON_INLINE int __Pyx_PyLong_BoolEqObjC(PyObject *op1, PyObject *op2, long intval, long inplace) { - CYTHON_MAYBE_UNUSED_VAR(intval); - CYTHON_UNUSED_VAR(inplace); - if (op1 == op2) { - return 1; - } - #if CYTHON_USE_PYLONG_INTERNALS - if (likely(PyLong_CheckExact(op1))) { - int unequal; - unsigned long uintval; - Py_ssize_t size = __Pyx_PyLong_DigitCount(op1); - const digit* digits = __Pyx_PyLong_Digits(op1); - if (intval == 0) { - return (__Pyx_PyLong_IsZero(op1) == 1); - } else if (intval < 0) { - if (__Pyx_PyLong_IsNonNeg(op1)) - return 0; - intval = -intval; - } else { - if (__Pyx_PyLong_IsNeg(op1)) - return 0; - } - uintval = (unsigned long) intval; -#if PyLong_SHIFT * 4 < SIZEOF_LONG*8 - if (uintval >> (PyLong_SHIFT * 4)) { - unequal = (size != 5) || (digits[0] != (uintval & (unsigned long) PyLong_MASK)) - | (digits[1] != ((uintval >> (1 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[2] != ((uintval >> (2 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[3] != ((uintval >> (3 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[4] != ((uintval >> (4 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)); - } else -#endif -#if PyLong_SHIFT * 3 < SIZEOF_LONG*8 - if (uintval >> (PyLong_SHIFT * 3)) { - unequal = (size != 4) || (digits[0] != (uintval & (unsigned long) PyLong_MASK)) - | (digits[1] != ((uintval >> (1 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[2] != ((uintval >> (2 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[3] != ((uintval >> (3 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)); - } else -#endif -#if PyLong_SHIFT * 2 < SIZEOF_LONG*8 - if (uintval >> (PyLong_SHIFT * 2)) { - unequal = (size != 3) || (digits[0] != (uintval & (unsigned long) PyLong_MASK)) - | (digits[1] != ((uintval >> (1 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[2] != ((uintval >> (2 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)); - } else -#endif -#if PyLong_SHIFT * 1 < SIZEOF_LONG*8 - if (uintval >> (PyLong_SHIFT * 1)) { - unequal = (size != 2) || (digits[0] != (uintval & (unsigned long) PyLong_MASK)) - | (digits[1] != ((uintval >> (1 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)); - } else -#endif - unequal = (size != 1) || (((unsigned long) digits[0]) != (uintval & (unsigned long) PyLong_MASK)); - return (unequal == 0); - } - #endif - if (PyFloat_CheckExact(op1)) { - const long b = intval; - double a = __Pyx_PyFloat_AS_DOUBLE(op1); - return ((double)a == (double)b); - } - return __Pyx_PyObject_IsTrueAndDecref( - PyObject_RichCompare(op1, op2, Py_EQ)); -} - -/* RaiseTooManyValuesToUnpack */ -static CYTHON_INLINE void __Pyx_RaiseTooManyValuesError(Py_ssize_t expected) { - PyErr_Format(PyExc_ValueError, - "too many values to unpack (expected %" CYTHON_FORMAT_SSIZE_T "d)", expected); -} - -/* RaiseNeedMoreValuesToUnpack */ -static CYTHON_INLINE void __Pyx_RaiseNeedMoreValuesError(Py_ssize_t index) { - PyErr_Format(PyExc_ValueError, - "need more than %" CYTHON_FORMAT_SSIZE_T "d value%.1s to unpack", - index, (index == 1) ? "" : "s"); -} - -/* IterFinish */ -static CYTHON_INLINE int __Pyx_IterFinish(void) { - PyObject* exc_type; - __Pyx_PyThreadState_declare - __Pyx_PyThreadState_assign - exc_type = __Pyx_PyErr_CurrentExceptionType(); - if (unlikely(exc_type)) { - if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) - return -1; - __Pyx_PyErr_Clear(); - return 0; - } - return 0; -} - -/* UnpackItemEndCheck */ -static int __Pyx_IternextUnpackEndCheck(PyObject *retval, Py_ssize_t expected) { - if (unlikely(retval)) { - Py_DECREF(retval); - __Pyx_RaiseTooManyValuesError(expected); - return -1; - } - return __Pyx_IterFinish(); -} - -/* GetItemInt */ -static PyObject *__Pyx_GetItemInt_Generic(PyObject *o, PyObject* j) { - PyObject *r; - if (unlikely(!j)) return NULL; - r = PyObject_GetItem(o, j); - Py_DECREF(j); - return r; -} -static CYTHON_INLINE PyObject *__Pyx_GetItemInt_List_Fast(PyObject *o, Py_ssize_t i, - CYTHON_NCP_UNUSED int wraparound, - CYTHON_NCP_UNUSED int boundscheck) { -#if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE && !CYTHON_AVOID_BORROWED_REFS && !CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS - Py_ssize_t wrapped_i = i; - if (wraparound & unlikely(i < 0)) { - wrapped_i += PyList_GET_SIZE(o); - } - if ((!boundscheck) || likely(__Pyx_is_valid_index(wrapped_i, PyList_GET_SIZE(o)))) { - PyObject *r = PyList_GET_ITEM(o, wrapped_i); - Py_INCREF(r); - return r; - } - return __Pyx_GetItemInt_Generic(o, PyLong_FromSsize_t(i)); -#else - return PySequence_GetItem(o, i); -#endif -} -static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Tuple_Fast(PyObject *o, Py_ssize_t i, - CYTHON_NCP_UNUSED int wraparound, - CYTHON_NCP_UNUSED int boundscheck) { -#if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE && !CYTHON_AVOID_BORROWED_REFS - Py_ssize_t wrapped_i = i; - if (wraparound & unlikely(i < 0)) { - wrapped_i += PyTuple_GET_SIZE(o); - } - if ((!boundscheck) || likely(__Pyx_is_valid_index(wrapped_i, PyTuple_GET_SIZE(o)))) { - PyObject *r = PyTuple_GET_ITEM(o, wrapped_i); - Py_INCREF(r); - return r; - } - return __Pyx_GetItemInt_Generic(o, PyLong_FromSsize_t(i)); -#else - return PySequence_GetItem(o, i); -#endif -} -static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Fast(PyObject *o, Py_ssize_t i, int is_list, - CYTHON_NCP_UNUSED int wraparound, - CYTHON_NCP_UNUSED int boundscheck) { -#if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE && !CYTHON_AVOID_BORROWED_REFS && CYTHON_USE_TYPE_SLOTS - if (is_list || PyList_CheckExact(o)) { - Py_ssize_t n = ((!wraparound) | likely(i >= 0)) ? i : i + PyList_GET_SIZE(o); - if ((!boundscheck) || (likely(__Pyx_is_valid_index(n, PyList_GET_SIZE(o))))) { - return __Pyx_PyList_GetItemRef(o, n); - } - } - else if (PyTuple_CheckExact(o)) { - Py_ssize_t n = ((!wraparound) | likely(i >= 0)) ? i : i + PyTuple_GET_SIZE(o); - if ((!boundscheck) || likely(__Pyx_is_valid_index(n, PyTuple_GET_SIZE(o)))) { - PyObject *r = PyTuple_GET_ITEM(o, n); - Py_INCREF(r); - return r; - } - } else { - PyMappingMethods *mm = Py_TYPE(o)->tp_as_mapping; - PySequenceMethods *sm = Py_TYPE(o)->tp_as_sequence; - if (mm && mm->mp_subscript) { - PyObject *r, *key = PyLong_FromSsize_t(i); - if (unlikely(!key)) return NULL; - r = mm->mp_subscript(o, key); - Py_DECREF(key); - return r; - } - if (likely(sm && sm->sq_item)) { - if (wraparound && unlikely(i < 0) && likely(sm->sq_length)) { - Py_ssize_t l = sm->sq_length(o); - if (likely(l >= 0)) { - i += l; - } else { - if (!PyErr_ExceptionMatches(PyExc_OverflowError)) - return NULL; - PyErr_Clear(); - } - } - return sm->sq_item(o, i); - } - } -#else - if (is_list || !PyMapping_Check(o)) { - return PySequence_GetItem(o, i); - } -#endif - return __Pyx_GetItemInt_Generic(o, PyLong_FromSsize_t(i)); -} - -/* PyDictVersioning */ -#if CYTHON_USE_DICT_VERSIONS && CYTHON_USE_TYPE_SLOTS -static CYTHON_INLINE PY_UINT64_T __Pyx_get_tp_dict_version(PyObject *obj) { - PyObject *dict = Py_TYPE(obj)->tp_dict; - return likely(dict) ? __PYX_GET_DICT_VERSION(dict) : 0; -} -static CYTHON_INLINE PY_UINT64_T __Pyx_get_object_dict_version(PyObject *obj) { - PyObject **dictptr = NULL; - Py_ssize_t offset = Py_TYPE(obj)->tp_dictoffset; - if (offset) { -#if CYTHON_COMPILING_IN_CPYTHON - dictptr = (likely(offset > 0)) ? (PyObject **) ((char *)obj + offset) : _PyObject_GetDictPtr(obj); -#else - dictptr = _PyObject_GetDictPtr(obj); -#endif - } - return (dictptr && *dictptr) ? __PYX_GET_DICT_VERSION(*dictptr) : 0; -} -static CYTHON_INLINE int __Pyx_object_dict_version_matches(PyObject* obj, PY_UINT64_T tp_dict_version, PY_UINT64_T obj_dict_version) { - PyObject *dict = Py_TYPE(obj)->tp_dict; - if (unlikely(!dict) || unlikely(tp_dict_version != __PYX_GET_DICT_VERSION(dict))) - return 0; - return obj_dict_version == __Pyx_get_object_dict_version(obj); -} -#endif - -/* GetModuleGlobalName */ -#if CYTHON_USE_DICT_VERSIONS -static PyObject *__Pyx__GetModuleGlobalName(PyObject *name, PY_UINT64_T *dict_version, PyObject **dict_cached_value) -#else -static CYTHON_INLINE PyObject *__Pyx__GetModuleGlobalName(PyObject *name) -#endif -{ - PyObject *result; -#if CYTHON_COMPILING_IN_LIMITED_API - if (unlikely(!__pyx_m)) { - if (!PyErr_Occurred()) - PyErr_SetNone(PyExc_NameError); - return NULL; - } - result = PyObject_GetAttr(__pyx_m, name); - if (likely(result)) { - return result; - } - PyErr_Clear(); -#elif CYTHON_AVOID_BORROWED_REFS || CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS - if (unlikely(__Pyx_PyDict_GetItemRef(__pyx_mstate_global->__pyx_d, name, &result) == -1)) PyErr_Clear(); - __PYX_UPDATE_DICT_CACHE(__pyx_mstate_global->__pyx_d, result, *dict_cached_value, *dict_version) - if (likely(result)) { - return result; - } -#else - result = _PyDict_GetItem_KnownHash(__pyx_mstate_global->__pyx_d, name, ((PyASCIIObject *) name)->hash); - __PYX_UPDATE_DICT_CACHE(__pyx_mstate_global->__pyx_d, result, *dict_cached_value, *dict_version) - if (likely(result)) { - return __Pyx_NewRef(result); - } - PyErr_Clear(); -#endif - return __Pyx_GetBuiltinName(name); -} - -/* TupleAndListFromArray */ -#if !CYTHON_COMPILING_IN_CPYTHON && CYTHON_METH_FASTCALL -static CYTHON_INLINE PyObject * -__Pyx_PyTuple_FromArray(PyObject *const *src, Py_ssize_t n) -{ - PyObject *res; - Py_ssize_t i; - if (n <= 0) { - return __Pyx_NewRef(__pyx_mstate_global->__pyx_empty_tuple); - } - res = PyTuple_New(n); - if (unlikely(res == NULL)) return NULL; - for (i = 0; i < n; i++) { - if (unlikely(__Pyx_PyTuple_SET_ITEM(res, i, src[i]) < 0)) { - Py_DECREF(res); - return NULL; - } - Py_INCREF(src[i]); - } - return res; -} -#elif CYTHON_COMPILING_IN_CPYTHON -static CYTHON_INLINE void __Pyx_copy_object_array(PyObject *const *CYTHON_RESTRICT src, PyObject** CYTHON_RESTRICT dest, Py_ssize_t length) { - PyObject *v; - Py_ssize_t i; - for (i = 0; i < length; i++) { - v = dest[i] = src[i]; - Py_INCREF(v); - } -} -static CYTHON_INLINE PyObject * -__Pyx_PyTuple_FromArray(PyObject *const *src, Py_ssize_t n) -{ - PyObject *res; - if (n <= 0) { - return __Pyx_NewRef(__pyx_mstate_global->__pyx_empty_tuple); - } - res = PyTuple_New(n); - if (unlikely(res == NULL)) return NULL; - __Pyx_copy_object_array(src, ((PyTupleObject*)res)->ob_item, n); - return res; -} -static CYTHON_INLINE PyObject * -__Pyx_PyList_FromArray(PyObject *const *src, Py_ssize_t n) -{ - PyObject *res; - if (n <= 0) { - return PyList_New(0); - } - res = PyList_New(n); - if (unlikely(res == NULL)) return NULL; - __Pyx_copy_object_array(src, ((PyListObject*)res)->ob_item, n); - return res; -} -#endif - -/* BytesEquals */ -static CYTHON_INLINE int __Pyx_PyBytes_Equals(PyObject* s1, PyObject* s2, int equals) { -#if CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API || CYTHON_COMPILING_IN_GRAAL ||\ - !(CYTHON_ASSUME_SAFE_SIZE && CYTHON_ASSUME_SAFE_MACROS) - return PyObject_RichCompareBool(s1, s2, equals); -#else - if (s1 == s2) { - return (equals == Py_EQ); - } else if (PyBytes_CheckExact(s1) & PyBytes_CheckExact(s2)) { - const char *ps1, *ps2; - Py_ssize_t length = PyBytes_GET_SIZE(s1); - if (length != PyBytes_GET_SIZE(s2)) - return (equals == Py_NE); - ps1 = PyBytes_AS_STRING(s1); - ps2 = PyBytes_AS_STRING(s2); - if (ps1[0] != ps2[0]) { - return (equals == Py_NE); - } else if (length == 1) { - return (equals == Py_EQ); - } else { - int result; -#if CYTHON_USE_UNICODE_INTERNALS && (PY_VERSION_HEX < 0x030B0000) - Py_hash_t hash1, hash2; - hash1 = ((PyBytesObject*)s1)->ob_shash; - hash2 = ((PyBytesObject*)s2)->ob_shash; - if (hash1 != hash2 && hash1 != -1 && hash2 != -1) { - return (equals == Py_NE); - } -#endif - result = memcmp(ps1, ps2, (size_t)length); - return (equals == Py_EQ) ? (result == 0) : (result != 0); - } - } else if ((s1 == Py_None) & PyBytes_CheckExact(s2)) { - return (equals == Py_NE); - } else if ((s2 == Py_None) & PyBytes_CheckExact(s1)) { - return (equals == Py_NE); - } else { - int result; - PyObject* py_result = PyObject_RichCompare(s1, s2, equals); - if (!py_result) - return -1; - result = __Pyx_PyObject_IsTrue(py_result); - Py_DECREF(py_result); - return result; - } -#endif -} - -/* UnicodeEquals */ -static CYTHON_INLINE int __Pyx_PyUnicode_Equals(PyObject* s1, PyObject* s2, int equals) { -#if CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API || CYTHON_COMPILING_IN_GRAAL - return PyObject_RichCompareBool(s1, s2, equals); -#else - int s1_is_unicode, s2_is_unicode; - if (s1 == s2) { - goto return_eq; - } - s1_is_unicode = PyUnicode_CheckExact(s1); - s2_is_unicode = PyUnicode_CheckExact(s2); - if (s1_is_unicode & s2_is_unicode) { - Py_ssize_t length, length2; - int kind; - void *data1, *data2; - #if !CYTHON_COMPILING_IN_LIMITED_API - if (unlikely(__Pyx_PyUnicode_READY(s1) < 0) || unlikely(__Pyx_PyUnicode_READY(s2) < 0)) - return -1; - #endif - length = __Pyx_PyUnicode_GET_LENGTH(s1); - #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely(length < 0)) return -1; - #endif - length2 = __Pyx_PyUnicode_GET_LENGTH(s2); - #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely(length2 < 0)) return -1; - #endif - if (length != length2) { - goto return_ne; - } -#if CYTHON_USE_UNICODE_INTERNALS - { - Py_hash_t hash1, hash2; - hash1 = ((PyASCIIObject*)s1)->hash; - hash2 = ((PyASCIIObject*)s2)->hash; - if (hash1 != hash2 && hash1 != -1 && hash2 != -1) { - goto return_ne; - } - } -#endif - kind = __Pyx_PyUnicode_KIND(s1); - if (kind != __Pyx_PyUnicode_KIND(s2)) { - goto return_ne; - } - data1 = __Pyx_PyUnicode_DATA(s1); - data2 = __Pyx_PyUnicode_DATA(s2); - if (__Pyx_PyUnicode_READ(kind, data1, 0) != __Pyx_PyUnicode_READ(kind, data2, 0)) { - goto return_ne; - } else if (length == 1) { - goto return_eq; - } else { - int result = memcmp(data1, data2, (size_t)(length * kind)); - return (equals == Py_EQ) ? (result == 0) : (result != 0); - } - } else if ((s1 == Py_None) & s2_is_unicode) { - goto return_ne; - } else if ((s2 == Py_None) & s1_is_unicode) { - goto return_ne; - } else { - int result; - PyObject* py_result = PyObject_RichCompare(s1, s2, equals); - if (!py_result) - return -1; - result = __Pyx_PyObject_IsTrue(py_result); - Py_DECREF(py_result); - return result; - } -return_eq: - return (equals == Py_EQ); -return_ne: - return (equals == Py_NE); -#endif -} - -/* fastcall */ -#if CYTHON_METH_FASTCALL -static CYTHON_INLINE PyObject * __Pyx_GetKwValue_FASTCALL(PyObject *kwnames, PyObject *const *kwvalues, PyObject *s) -{ - Py_ssize_t i, n = __Pyx_PyTuple_GET_SIZE(kwnames); - #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely(n == -1)) return NULL; - #endif - for (i = 0; i < n; i++) - { - PyObject *namei = __Pyx_PyTuple_GET_ITEM(kwnames, i); - #if !CYTHON_ASSUME_SAFE_MACROS - if (unlikely(!namei)) return NULL; - #endif - if (s == namei) return kwvalues[i]; - } - for (i = 0; i < n; i++) - { - PyObject *namei = __Pyx_PyTuple_GET_ITEM(kwnames, i); - #if !CYTHON_ASSUME_SAFE_MACROS - if (unlikely(!namei)) return NULL; - #endif - int eq = __Pyx_PyUnicode_Equals(s, namei, Py_EQ); - if (unlikely(eq != 0)) { - if (unlikely(eq < 0)) return NULL; - return kwvalues[i]; - } - } - return NULL; -} -#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030d0000 || CYTHON_COMPILING_IN_LIMITED_API -CYTHON_UNUSED static PyObject *__Pyx_KwargsAsDict_FASTCALL(PyObject *kwnames, PyObject *const *kwvalues) { - Py_ssize_t i, nkwargs; - PyObject *dict; -#if !CYTHON_ASSUME_SAFE_SIZE - nkwargs = PyTuple_Size(kwnames); - if (unlikely(nkwargs < 0)) return NULL; -#else - nkwargs = PyTuple_GET_SIZE(kwnames); -#endif - dict = PyDict_New(); - if (unlikely(!dict)) - return NULL; - for (i=0; itype, *target->method_name); - if (unlikely(!method)) - return -1; - result = method; -#if CYTHON_COMPILING_IN_CPYTHON - if (likely(__Pyx_TypeCheck(method, &PyMethodDescr_Type))) - { - PyMethodDescrObject *descr = (PyMethodDescrObject*) method; - target->func = descr->d_method->ml_meth; - target->flag = descr->d_method->ml_flags & ~(METH_CLASS | METH_STATIC | METH_COEXIST | METH_STACKLESS); - } else -#endif -#if CYTHON_COMPILING_IN_PYPY -#else - if (PyCFunction_Check(method)) -#endif - { - PyObject *self; - int self_found; -#if CYTHON_COMPILING_IN_LIMITED_API || CYTHON_COMPILING_IN_PYPY - self = PyObject_GetAttrString(method, "__self__"); - if (!self) { - PyErr_Clear(); - } -#else - self = PyCFunction_GET_SELF(method); -#endif - self_found = (self && self != Py_None); -#if CYTHON_COMPILING_IN_LIMITED_API || CYTHON_COMPILING_IN_PYPY - Py_XDECREF(self); -#endif - if (self_found) { - PyObject *unbound_method = PyCFunction_New(&__Pyx_UnboundCMethod_Def, method); - if (unlikely(!unbound_method)) return -1; - Py_DECREF(method); - result = unbound_method; - } - } -#if !CYTHON_COMPILING_IN_CPYTHON_FREETHREADING - if (unlikely(target->method)) { - Py_DECREF(result); - } else -#endif - target->method = result; - return 0; -} - -/* CallUnboundCMethod2 */ -#if CYTHON_COMPILING_IN_CPYTHON -static CYTHON_INLINE PyObject *__Pyx_CallUnboundCMethod2(__Pyx_CachedCFunction *cfunc, PyObject *self, PyObject *arg1, PyObject *arg2) { - int was_initialized = __Pyx_CachedCFunction_GetAndSetInitializing(cfunc); - if (likely(was_initialized == 2 && cfunc->func)) { - PyObject *args[2] = {arg1, arg2}; - if (cfunc->flag == METH_FASTCALL) { - return __Pyx_CallCFunctionFast(cfunc, self, args, 2); - } - if (cfunc->flag == (METH_FASTCALL | METH_KEYWORDS)) - return __Pyx_CallCFunctionFastWithKeywords(cfunc, self, args, 2, NULL); - } -#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING - else if (unlikely(was_initialized == 1)) { - __Pyx_CachedCFunction tmp_cfunc = { -#ifndef __cplusplus - 0 -#endif - }; - tmp_cfunc.type = cfunc->type; - tmp_cfunc.method_name = cfunc->method_name; - return __Pyx__CallUnboundCMethod2(&tmp_cfunc, self, arg1, arg2); - } -#endif - PyObject *result = __Pyx__CallUnboundCMethod2(cfunc, self, arg1, arg2); - __Pyx_CachedCFunction_SetFinishedInitializing(cfunc); - return result; -} -#endif -static PyObject* __Pyx__CallUnboundCMethod2(__Pyx_CachedCFunction* cfunc, PyObject* self, PyObject* arg1, PyObject* arg2){ - if (unlikely(!cfunc->func && !cfunc->method) && unlikely(__Pyx_TryUnpackUnboundCMethod(cfunc) < 0)) return NULL; -#if CYTHON_COMPILING_IN_CPYTHON - if (cfunc->func && (cfunc->flag & METH_VARARGS)) { - PyObject *result = NULL; - PyObject *args = PyTuple_New(2); - if (unlikely(!args)) return NULL; - Py_INCREF(arg1); - PyTuple_SET_ITEM(args, 0, arg1); - Py_INCREF(arg2); - PyTuple_SET_ITEM(args, 1, arg2); - if (cfunc->flag & METH_KEYWORDS) - result = __Pyx_CallCFunctionWithKeywords(cfunc, self, args, NULL); - else - result = __Pyx_CallCFunction(cfunc, self, args); - Py_DECREF(args); - return result; - } -#endif - { - PyObject *args[4] = {NULL, self, arg1, arg2}; - return __Pyx_PyObject_FastCall(cfunc->method, args+1, 3 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET); - } -} - -/* ParseKeywords */ -static int __Pyx_ValidateDuplicatePosArgs( - PyObject *kwds, - PyObject ** const argnames[], - PyObject ** const *first_kw_arg, - const char* function_name) -{ - PyObject ** const *name = argnames; - while (name != first_kw_arg) { - PyObject *key = **name; - int found = PyDict_Contains(kwds, key); - if (unlikely(found)) { - if (found == 1) __Pyx_RaiseDoubleKeywordsError(function_name, key); - goto bad; - } - name++; - } - return 0; -bad: - return -1; -} -#if CYTHON_USE_UNICODE_INTERNALS -static CYTHON_INLINE int __Pyx_UnicodeKeywordsEqual(PyObject *s1, PyObject *s2) { - int kind; - Py_ssize_t len = PyUnicode_GET_LENGTH(s1); - if (len != PyUnicode_GET_LENGTH(s2)) return 0; - kind = PyUnicode_KIND(s1); - if (kind != PyUnicode_KIND(s2)) return 0; - const void *data1 = PyUnicode_DATA(s1); - const void *data2 = PyUnicode_DATA(s2); - return (memcmp(data1, data2, (size_t) len * (size_t) kind) == 0); -} -#endif -static int __Pyx_MatchKeywordArg_str( - PyObject *key, - PyObject ** const argnames[], - PyObject ** const *first_kw_arg, - size_t *index_found, - const char *function_name) -{ - PyObject ** const *name; - #if CYTHON_USE_UNICODE_INTERNALS - Py_hash_t key_hash = ((PyASCIIObject*)key)->hash; - if (unlikely(key_hash == -1)) { - key_hash = PyObject_Hash(key); - if (unlikely(key_hash == -1)) - goto bad; - } - #endif - name = first_kw_arg; - while (*name) { - PyObject *name_str = **name; - #if CYTHON_USE_UNICODE_INTERNALS - if (key_hash == ((PyASCIIObject*)name_str)->hash && __Pyx_UnicodeKeywordsEqual(name_str, key)) { - *index_found = (size_t) (name - argnames); - return 1; - } - #else - #if CYTHON_ASSUME_SAFE_SIZE - if (PyUnicode_GET_LENGTH(name_str) == PyUnicode_GET_LENGTH(key)) - #endif - { - int cmp = PyUnicode_Compare(name_str, key); - if (cmp < 0 && unlikely(PyErr_Occurred())) goto bad; - if (cmp == 0) { - *index_found = (size_t) (name - argnames); - return 1; - } - } - #endif - name++; - } - name = argnames; - while (name != first_kw_arg) { - PyObject *name_str = **name; - #if CYTHON_USE_UNICODE_INTERNALS - if (unlikely(key_hash == ((PyASCIIObject*)name_str)->hash)) { - if (__Pyx_UnicodeKeywordsEqual(name_str, key)) - goto arg_passed_twice; - } - #else - #if CYTHON_ASSUME_SAFE_SIZE - if (PyUnicode_GET_LENGTH(name_str) == PyUnicode_GET_LENGTH(key)) - #endif - { - if (unlikely(name_str == key)) goto arg_passed_twice; - int cmp = PyUnicode_Compare(name_str, key); - if (cmp < 0 && unlikely(PyErr_Occurred())) goto bad; - if (cmp == 0) goto arg_passed_twice; - } - #endif - name++; - } - return 0; -arg_passed_twice: - __Pyx_RaiseDoubleKeywordsError(function_name, key); - goto bad; -bad: - return -1; -} -static int __Pyx_MatchKeywordArg_nostr( - PyObject *key, - PyObject ** const argnames[], - PyObject ** const *first_kw_arg, - size_t *index_found, - const char *function_name) -{ - PyObject ** const *name; - if (unlikely(!PyUnicode_Check(key))) goto invalid_keyword_type; - name = first_kw_arg; - while (*name) { - int cmp = PyObject_RichCompareBool(**name, key, Py_EQ); - if (cmp == 1) { - *index_found = (size_t) (name - argnames); - return 1; - } - if (unlikely(cmp == -1)) goto bad; - name++; - } - name = argnames; - while (name != first_kw_arg) { - int cmp = PyObject_RichCompareBool(**name, key, Py_EQ); - if (unlikely(cmp != 0)) { - if (cmp == 1) goto arg_passed_twice; - else goto bad; - } - name++; - } - return 0; -arg_passed_twice: - __Pyx_RaiseDoubleKeywordsError(function_name, key); - goto bad; -invalid_keyword_type: - PyErr_Format(PyExc_TypeError, - "%.200s() keywords must be strings", function_name); - goto bad; -bad: - return -1; -} -static CYTHON_INLINE int __Pyx_MatchKeywordArg( - PyObject *key, - PyObject ** const argnames[], - PyObject ** const *first_kw_arg, - size_t *index_found, - const char *function_name) -{ - return likely(PyUnicode_CheckExact(key)) ? - __Pyx_MatchKeywordArg_str(key, argnames, first_kw_arg, index_found, function_name) : - __Pyx_MatchKeywordArg_nostr(key, argnames, first_kw_arg, index_found, function_name); -} -static void __Pyx_RejectUnknownKeyword( - PyObject *kwds, - PyObject ** const argnames[], - PyObject ** const *first_kw_arg, - const char *function_name) -{ - Py_ssize_t pos = 0; - PyObject *key = NULL; - __Pyx_BEGIN_CRITICAL_SECTION(kwds); - while (PyDict_Next(kwds, &pos, &key, NULL)) { - PyObject** const *name = first_kw_arg; - while (*name && (**name != key)) name++; - if (!*name) { - #if CYTHON_AVOID_BORROWED_REFS - Py_INCREF(key); - #endif - size_t index_found = 0; - int cmp = __Pyx_MatchKeywordArg(key, argnames, first_kw_arg, &index_found, function_name); - if (cmp != 1) { - if (cmp == 0) { - PyErr_Format(PyExc_TypeError, - "%s() got an unexpected keyword argument '%U'", - function_name, key); - } - #if CYTHON_AVOID_BORROWED_REFS - Py_DECREF(key); - #endif - break; - } - #if CYTHON_AVOID_BORROWED_REFS - Py_DECREF(key); - #endif - } - } - __Pyx_END_CRITICAL_SECTION(); - assert(PyErr_Occurred()); -} -static int __Pyx_ParseKeywordDict( - PyObject *kwds, - PyObject ** const argnames[], - PyObject *values[], - Py_ssize_t num_pos_args, - Py_ssize_t num_kwargs, - const char* function_name, - int ignore_unknown_kwargs) -{ - PyObject** const *name; - PyObject** const *first_kw_arg = argnames + num_pos_args; - Py_ssize_t extracted = 0; -#if !CYTHON_COMPILING_IN_PYPY || defined(PyArg_ValidateKeywordArguments) - if (unlikely(!PyArg_ValidateKeywordArguments(kwds))) return -1; -#endif - name = first_kw_arg; - while (*name && num_kwargs > extracted) { - PyObject * key = **name; - PyObject *value; - int found = 0; - #if __PYX_LIMITED_VERSION_HEX >= 0x030d0000 - found = PyDict_GetItemRef(kwds, key, &value); - #else - value = PyDict_GetItemWithError(kwds, key); - if (value) { - Py_INCREF(value); - found = 1; - } else { - if (unlikely(PyErr_Occurred())) goto bad; - } - #endif - if (found) { - if (unlikely(found < 0)) goto bad; - values[name-argnames] = value; - extracted++; - } - name++; - } - if (num_kwargs > extracted) { - if (ignore_unknown_kwargs) { - if (unlikely(__Pyx_ValidateDuplicatePosArgs(kwds, argnames, first_kw_arg, function_name) == -1)) - goto bad; - } else { - __Pyx_RejectUnknownKeyword(kwds, argnames, first_kw_arg, function_name); - goto bad; - } - } - return 0; -bad: - return -1; -} -static int __Pyx_ParseKeywordDictToDict( - PyObject *kwds, - PyObject ** const argnames[], - PyObject *kwds2, - PyObject *values[], - Py_ssize_t num_pos_args, - const char* function_name) -{ - PyObject** const *name; - PyObject** const *first_kw_arg = argnames + num_pos_args; - Py_ssize_t len; -#if !CYTHON_COMPILING_IN_PYPY || defined(PyArg_ValidateKeywordArguments) - if (unlikely(!PyArg_ValidateKeywordArguments(kwds))) return -1; -#endif - if (PyDict_Update(kwds2, kwds) < 0) goto bad; - name = first_kw_arg; - while (*name) { - PyObject *key = **name; - PyObject *value; -#if !CYTHON_COMPILING_IN_LIMITED_API && (PY_VERSION_HEX >= 0x030d00A2 || defined(PyDict_Pop)) - int found = PyDict_Pop(kwds2, key, &value); - if (found) { - if (unlikely(found < 0)) goto bad; - values[name-argnames] = value; - } -#elif __PYX_LIMITED_VERSION_HEX >= 0x030d0000 - int found = PyDict_GetItemRef(kwds2, key, &value); - if (found) { - if (unlikely(found < 0)) goto bad; - values[name-argnames] = value; - if (unlikely(PyDict_DelItem(kwds2, key) < 0)) goto bad; - } -#else - #if CYTHON_COMPILING_IN_CPYTHON - value = _PyDict_Pop(kwds2, key, kwds2); - #else - value = __Pyx_CallUnboundCMethod2(&__pyx_mstate_global->__pyx_umethod_PyDict_Type_pop, kwds2, key, kwds2); - #endif - if (value == kwds2) { - Py_DECREF(value); - } else { - if (unlikely(!value)) goto bad; - values[name-argnames] = value; - } -#endif - name++; - } - len = PyDict_Size(kwds2); - if (len > 0) { - return __Pyx_ValidateDuplicatePosArgs(kwds, argnames, first_kw_arg, function_name); - } else if (unlikely(len == -1)) { - goto bad; - } - return 0; -bad: - return -1; -} -static int __Pyx_ParseKeywordsTuple( - PyObject *kwds, - PyObject * const *kwvalues, - PyObject ** const argnames[], - PyObject *kwds2, - PyObject *values[], - Py_ssize_t num_pos_args, - Py_ssize_t num_kwargs, - const char* function_name, - int ignore_unknown_kwargs) -{ - PyObject *key = NULL; - PyObject** const * name; - PyObject** const *first_kw_arg = argnames + num_pos_args; - for (Py_ssize_t pos = 0; pos < num_kwargs; pos++) { -#if CYTHON_AVOID_BORROWED_REFS - key = __Pyx_PySequence_ITEM(kwds, pos); -#else - key = __Pyx_PyTuple_GET_ITEM(kwds, pos); -#endif -#if !CYTHON_ASSUME_SAFE_MACROS - if (unlikely(!key)) goto bad; -#endif - name = first_kw_arg; - while (*name && (**name != key)) name++; - if (*name) { - PyObject *value = kwvalues[pos]; - values[name-argnames] = __Pyx_NewRef(value); - } else { - size_t index_found = 0; - int cmp = __Pyx_MatchKeywordArg(key, argnames, first_kw_arg, &index_found, function_name); - if (cmp == 1) { - PyObject *value = kwvalues[pos]; - values[index_found] = __Pyx_NewRef(value); - } else { - if (unlikely(cmp == -1)) goto bad; - if (kwds2) { - PyObject *value = kwvalues[pos]; - if (unlikely(PyDict_SetItem(kwds2, key, value))) goto bad; - } else if (!ignore_unknown_kwargs) { - goto invalid_keyword; - } - } - } - #if CYTHON_AVOID_BORROWED_REFS - Py_DECREF(key); - key = NULL; - #endif - } - return 0; -invalid_keyword: - PyErr_Format(PyExc_TypeError, - "%s() got an unexpected keyword argument '%U'", - function_name, key); - goto bad; -bad: - #if CYTHON_AVOID_BORROWED_REFS - Py_XDECREF(key); - #endif - return -1; -} -static int __Pyx_ParseKeywords( - PyObject *kwds, - PyObject * const *kwvalues, - PyObject ** const argnames[], - PyObject *kwds2, - PyObject *values[], - Py_ssize_t num_pos_args, - Py_ssize_t num_kwargs, - const char* function_name, - int ignore_unknown_kwargs) -{ - if (CYTHON_METH_FASTCALL && likely(PyTuple_Check(kwds))) - return __Pyx_ParseKeywordsTuple(kwds, kwvalues, argnames, kwds2, values, num_pos_args, num_kwargs, function_name, ignore_unknown_kwargs); - else if (kwds2) - return __Pyx_ParseKeywordDictToDict(kwds, argnames, kwds2, values, num_pos_args, function_name); - else - return __Pyx_ParseKeywordDict(kwds, argnames, values, num_pos_args, num_kwargs, function_name, ignore_unknown_kwargs); -} - -/* RaiseArgTupleInvalid */ -static void __Pyx_RaiseArgtupleInvalid( - const char* func_name, - int exact, - Py_ssize_t num_min, - Py_ssize_t num_max, - Py_ssize_t num_found) -{ - Py_ssize_t num_expected; - const char *more_or_less; - if (num_found < num_min) { - num_expected = num_min; - more_or_less = "at least"; - } else { - num_expected = num_max; - more_or_less = "at most"; - } - if (exact) { - more_or_less = "exactly"; - } - PyErr_Format(PyExc_TypeError, - "%.200s() takes %.8s %" CYTHON_FORMAT_SSIZE_T "d positional argument%.1s (%" CYTHON_FORMAT_SSIZE_T "d given)", - func_name, more_or_less, num_expected, - (num_expected == 1) ? "" : "s", num_found); -} - -/* GetException */ -#if CYTHON_FAST_THREAD_STATE -static int __Pyx__GetException(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb) -#else -static int __Pyx_GetException(PyObject **type, PyObject **value, PyObject **tb) -#endif -{ - PyObject *local_type = NULL, *local_value, *local_tb = NULL; -#if CYTHON_FAST_THREAD_STATE - PyObject *tmp_type, *tmp_value, *tmp_tb; - #if PY_VERSION_HEX >= 0x030C0000 - local_value = tstate->current_exception; - tstate->current_exception = 0; - #else - local_type = tstate->curexc_type; - local_value = tstate->curexc_value; - local_tb = tstate->curexc_traceback; - tstate->curexc_type = 0; - tstate->curexc_value = 0; - tstate->curexc_traceback = 0; - #endif -#elif __PYX_LIMITED_VERSION_HEX > 0x030C0000 - local_value = PyErr_GetRaisedException(); -#else - PyErr_Fetch(&local_type, &local_value, &local_tb); -#endif -#if __PYX_LIMITED_VERSION_HEX > 0x030C0000 - if (likely(local_value)) { - local_type = (PyObject*) Py_TYPE(local_value); - Py_INCREF(local_type); - local_tb = PyException_GetTraceback(local_value); - } -#else - PyErr_NormalizeException(&local_type, &local_value, &local_tb); -#if CYTHON_FAST_THREAD_STATE - if (unlikely(tstate->curexc_type)) -#else - if (unlikely(PyErr_Occurred())) -#endif - goto bad; - if (local_tb) { - if (unlikely(PyException_SetTraceback(local_value, local_tb) < 0)) - goto bad; - } -#endif // __PYX_LIMITED_VERSION_HEX > 0x030C0000 - Py_XINCREF(local_tb); - Py_XINCREF(local_type); - Py_XINCREF(local_value); - *type = local_type; - *value = local_value; - *tb = local_tb; -#if CYTHON_FAST_THREAD_STATE - #if CYTHON_USE_EXC_INFO_STACK - { - _PyErr_StackItem *exc_info = tstate->exc_info; - #if PY_VERSION_HEX >= 0x030B00a4 - tmp_value = exc_info->exc_value; - exc_info->exc_value = local_value; - tmp_type = NULL; - tmp_tb = NULL; - Py_XDECREF(local_type); - Py_XDECREF(local_tb); - #else - tmp_type = exc_info->exc_type; - tmp_value = exc_info->exc_value; - tmp_tb = exc_info->exc_traceback; - exc_info->exc_type = local_type; - exc_info->exc_value = local_value; - exc_info->exc_traceback = local_tb; - #endif - } - #else - tmp_type = tstate->exc_type; - tmp_value = tstate->exc_value; - tmp_tb = tstate->exc_traceback; - tstate->exc_type = local_type; - tstate->exc_value = local_value; - tstate->exc_traceback = local_tb; - #endif - Py_XDECREF(tmp_type); - Py_XDECREF(tmp_value); - Py_XDECREF(tmp_tb); -#elif __PYX_LIMITED_VERSION_HEX >= 0x030b0000 - PyErr_SetHandledException(local_value); - Py_XDECREF(local_value); - Py_XDECREF(local_type); - Py_XDECREF(local_tb); -#else - PyErr_SetExcInfo(local_type, local_value, local_tb); -#endif - return 0; -#if __PYX_LIMITED_VERSION_HEX <= 0x030C0000 -bad: - *type = 0; - *value = 0; - *tb = 0; - Py_XDECREF(local_type); - Py_XDECREF(local_value); - Py_XDECREF(local_tb); - return -1; -#endif -} - -/* pep479 */ -static void __Pyx_Generator_Replace_StopIteration(int in_async_gen) { - PyObject *exc, *val, *tb, *cur_exc, *new_exc; - __Pyx_PyThreadState_declare - int is_async_stopiteration = 0; - CYTHON_MAYBE_UNUSED_VAR(in_async_gen); - __Pyx_PyThreadState_assign - cur_exc = __Pyx_PyErr_CurrentExceptionType(); - if (likely(!__Pyx_PyErr_GivenExceptionMatches(cur_exc, PyExc_StopIteration))) { - if (in_async_gen && unlikely(__Pyx_PyErr_GivenExceptionMatches(cur_exc, PyExc_StopAsyncIteration))) { - is_async_stopiteration = 1; - } else { - return; - } - } - __Pyx_GetException(&exc, &val, &tb); - Py_XDECREF(exc); - Py_XDECREF(tb); - new_exc = PyObject_CallFunction(PyExc_RuntimeError, "s", - is_async_stopiteration ? "async generator raised StopAsyncIteration" : - in_async_gen ? "async generator raised StopIteration" : - "generator raised StopIteration"); - if (!new_exc) { - Py_XDECREF(val); - return; - } - PyException_SetCause(new_exc, val); // steals ref to val - PyErr_SetObject(PyExc_RuntimeError, new_exc); -} - -/* GetTopmostException */ -#if CYTHON_USE_EXC_INFO_STACK && CYTHON_FAST_THREAD_STATE -static _PyErr_StackItem * -__Pyx_PyErr_GetTopmostException(PyThreadState *tstate) -{ - _PyErr_StackItem *exc_info = tstate->exc_info; - while ((exc_info->exc_value == NULL || exc_info->exc_value == Py_None) && - exc_info->previous_item != NULL) - { - exc_info = exc_info->previous_item; - } - return exc_info; -} -#endif - -/* SaveResetException */ -#if CYTHON_FAST_THREAD_STATE -static CYTHON_INLINE void __Pyx__ExceptionSave(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb) { - #if CYTHON_USE_EXC_INFO_STACK && PY_VERSION_HEX >= 0x030B00a4 - _PyErr_StackItem *exc_info = __Pyx_PyErr_GetTopmostException(tstate); - PyObject *exc_value = exc_info->exc_value; - if (exc_value == NULL || exc_value == Py_None) { - *value = NULL; - *type = NULL; - *tb = NULL; - } else { - *value = exc_value; - Py_INCREF(*value); - *type = (PyObject*) Py_TYPE(exc_value); - Py_INCREF(*type); - *tb = PyException_GetTraceback(exc_value); - } - #elif CYTHON_USE_EXC_INFO_STACK - _PyErr_StackItem *exc_info = __Pyx_PyErr_GetTopmostException(tstate); - *type = exc_info->exc_type; - *value = exc_info->exc_value; - *tb = exc_info->exc_traceback; - Py_XINCREF(*type); - Py_XINCREF(*value); - Py_XINCREF(*tb); - #else - *type = tstate->exc_type; - *value = tstate->exc_value; - *tb = tstate->exc_traceback; - Py_XINCREF(*type); - Py_XINCREF(*value); - Py_XINCREF(*tb); - #endif -} -static CYTHON_INLINE void __Pyx__ExceptionReset(PyThreadState *tstate, PyObject *type, PyObject *value, PyObject *tb) { - #if CYTHON_USE_EXC_INFO_STACK && PY_VERSION_HEX >= 0x030B00a4 - _PyErr_StackItem *exc_info = tstate->exc_info; - PyObject *tmp_value = exc_info->exc_value; - exc_info->exc_value = value; - Py_XDECREF(tmp_value); - Py_XDECREF(type); - Py_XDECREF(tb); - #else - PyObject *tmp_type, *tmp_value, *tmp_tb; - #if CYTHON_USE_EXC_INFO_STACK - _PyErr_StackItem *exc_info = tstate->exc_info; - tmp_type = exc_info->exc_type; - tmp_value = exc_info->exc_value; - tmp_tb = exc_info->exc_traceback; - exc_info->exc_type = type; - exc_info->exc_value = value; - exc_info->exc_traceback = tb; - #else - tmp_type = tstate->exc_type; - tmp_value = tstate->exc_value; - tmp_tb = tstate->exc_traceback; - tstate->exc_type = type; - tstate->exc_value = value; - tstate->exc_traceback = tb; - #endif - Py_XDECREF(tmp_type); - Py_XDECREF(tmp_value); - Py_XDECREF(tmp_tb); - #endif -} -#endif - -/* IterNextPlain */ -#if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x030A0000 -static PyObject *__Pyx_GetBuiltinNext_LimitedAPI(void) { - if (unlikely(!__pyx_mstate_global->__Pyx_GetBuiltinNext_LimitedAPI_cache)) - __pyx_mstate_global->__Pyx_GetBuiltinNext_LimitedAPI_cache = __Pyx_GetBuiltinName(__pyx_mstate_global->__pyx_n_u_next); - return __pyx_mstate_global->__Pyx_GetBuiltinNext_LimitedAPI_cache; -} -#endif -static CYTHON_INLINE PyObject *__Pyx_PyIter_Next_Plain(PyObject *iterator) { -#if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x030A0000 - PyObject *result; - PyObject *next = __Pyx_GetBuiltinNext_LimitedAPI(); - if (unlikely(!next)) return NULL; - result = PyObject_CallFunctionObjArgs(next, iterator, NULL); - return result; -#else - (void)__Pyx_GetBuiltinName; // only for early limited API - iternextfunc iternext = __Pyx_PyObject_GetIterNextFunc(iterator); - assert(iternext); - return iternext(iterator); -#endif -} - -/* IterNext */ -#if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x03080000 -static PyObject *__Pyx_PyIter_Next2(PyObject *o, PyObject *defval) { - PyObject *result; - PyObject *next = __Pyx_GetBuiltinNext_LimitedAPI(); - if (unlikely(!next)) return NULL; - result = PyObject_CallFunctionObjArgs(next, o, defval, NULL); - return result; -} -#else -static PyObject *__Pyx_PyIter_Next2Default(PyObject* defval) { - PyObject* exc_type; - __Pyx_PyThreadState_declare - __Pyx_PyThreadState_assign - exc_type = __Pyx_PyErr_CurrentExceptionType(); - if (unlikely(exc_type)) { - if (!defval || unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) - return NULL; - __Pyx_PyErr_Clear(); - Py_INCREF(defval); - return defval; - } - if (defval) { - Py_INCREF(defval); - return defval; - } - __Pyx_PyErr_SetNone(PyExc_StopIteration); - return NULL; -} -static void __Pyx_PyIter_Next_ErrorNoIterator(PyObject *iterator) { - __Pyx_TypeName iterator_type_name = __Pyx_PyType_GetFullyQualifiedName(Py_TYPE(iterator)); - PyErr_Format(PyExc_TypeError, - __Pyx_FMT_TYPENAME " object is not an iterator", iterator_type_name); - __Pyx_DECREF_TypeName(iterator_type_name); -} -static CYTHON_INLINE PyObject *__Pyx_PyIter_Next2(PyObject* iterator, PyObject* defval) { - PyObject* next; -#if !CYTHON_COMPILING_IN_LIMITED_API - iternextfunc iternext = __Pyx_PyObject_TryGetSlot(iterator, tp_iternext, iternextfunc); - if (likely(iternext)) { - next = iternext(iterator); - if (likely(next)) - return next; - #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030d0000 - if (unlikely(iternext == &_PyObject_NextNotImplemented)) - return NULL; - #endif - } else if (CYTHON_USE_TYPE_SLOTS) { - __Pyx_PyIter_Next_ErrorNoIterator(iterator); - return NULL; - } else -#endif - if (unlikely(!PyIter_Check(iterator))) { - __Pyx_PyIter_Next_ErrorNoIterator(iterator); - return NULL; - } else { - next = defval ? PyIter_Next(iterator) : __Pyx_PyIter_Next_Plain(iterator); - if (likely(next)) - return next; - } - return __Pyx_PyIter_Next2Default(defval); -} -#endif - -/* PyLongBinop */ -#if !CYTHON_COMPILING_IN_PYPY -static PyObject* __Pyx_Fallback___Pyx_PyLong_AddObjC(PyObject *op1, PyObject *op2, int inplace) { - return (inplace ? PyNumber_InPlaceAdd : PyNumber_Add)(op1, op2); -} -#if CYTHON_USE_PYLONG_INTERNALS -static PyObject* __Pyx_Unpacked___Pyx_PyLong_AddObjC(PyObject *op1, PyObject *op2, long intval, int inplace, int zerodivision_check) { - CYTHON_MAYBE_UNUSED_VAR(inplace); - CYTHON_UNUSED_VAR(zerodivision_check); - const long b = intval; - long a, x; -#ifdef HAVE_LONG_LONG - const PY_LONG_LONG llb = intval; - PY_LONG_LONG lla, llx; -#endif - if (unlikely(__Pyx_PyLong_IsZero(op1))) { - return __Pyx_NewRef(op2); - } - if (likely(__Pyx_PyLong_IsCompact(op1))) { - a = __Pyx_PyLong_CompactValue(op1); - } else { - const digit* digits = __Pyx_PyLong_Digits(op1); - const Py_ssize_t size = __Pyx_PyLong_SignedDigitCount(op1); - switch (size) { - case -2: - if (8 * sizeof(long) - 1 > 2 * PyLong_SHIFT) { - a = -(long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])); - break; - #ifdef HAVE_LONG_LONG - } else if (8 * sizeof(PY_LONG_LONG) - 1 > 2 * PyLong_SHIFT) { - lla = -(PY_LONG_LONG) (((((unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0])); - goto long_long; - #endif - } - CYTHON_FALLTHROUGH; - case 2: - if (8 * sizeof(long) - 1 > 2 * PyLong_SHIFT) { - a = (long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])); - break; - #ifdef HAVE_LONG_LONG - } else if (8 * sizeof(PY_LONG_LONG) - 1 > 2 * PyLong_SHIFT) { - lla = (PY_LONG_LONG) (((((unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0])); - goto long_long; - #endif - } - CYTHON_FALLTHROUGH; - case -3: - if (8 * sizeof(long) - 1 > 3 * PyLong_SHIFT) { - a = -(long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])); - break; - #ifdef HAVE_LONG_LONG - } else if (8 * sizeof(PY_LONG_LONG) - 1 > 3 * PyLong_SHIFT) { - lla = -(PY_LONG_LONG) (((((((unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0])); - goto long_long; - #endif - } - CYTHON_FALLTHROUGH; - case 3: - if (8 * sizeof(long) - 1 > 3 * PyLong_SHIFT) { - a = (long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])); - break; - #ifdef HAVE_LONG_LONG - } else if (8 * sizeof(PY_LONG_LONG) - 1 > 3 * PyLong_SHIFT) { - lla = (PY_LONG_LONG) (((((((unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0])); - goto long_long; - #endif - } - CYTHON_FALLTHROUGH; - case -4: - if (8 * sizeof(long) - 1 > 4 * PyLong_SHIFT) { - a = -(long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])); - break; - #ifdef HAVE_LONG_LONG - } else if (8 * sizeof(PY_LONG_LONG) - 1 > 4 * PyLong_SHIFT) { - lla = -(PY_LONG_LONG) (((((((((unsigned PY_LONG_LONG)digits[3]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0])); - goto long_long; - #endif - } - CYTHON_FALLTHROUGH; - case 4: - if (8 * sizeof(long) - 1 > 4 * PyLong_SHIFT) { - a = (long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])); - break; - #ifdef HAVE_LONG_LONG - } else if (8 * sizeof(PY_LONG_LONG) - 1 > 4 * PyLong_SHIFT) { - lla = (PY_LONG_LONG) (((((((((unsigned PY_LONG_LONG)digits[3]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0])); - goto long_long; - #endif - } - CYTHON_FALLTHROUGH; - default: return PyLong_Type.tp_as_number->nb_add(op1, op2); - } - } - x = a + b; - return PyLong_FromLong(x); -#ifdef HAVE_LONG_LONG - long_long: - llx = lla + llb; - return PyLong_FromLongLong(llx); -#endif - return __Pyx_Fallback___Pyx_PyLong_AddObjC(op1, op2, inplace); - - -} -#endif -static PyObject* __Pyx_Float___Pyx_PyLong_AddObjC(PyObject *float_val, long intval, int zerodivision_check) { - CYTHON_UNUSED_VAR(zerodivision_check); - const long b = intval; - double a = __Pyx_PyFloat_AS_DOUBLE(float_val); - double result; - - result = ((double)a) + (double)b; - return PyFloat_FromDouble(result); -} -static CYTHON_INLINE PyObject* __Pyx_PyLong_AddObjC(PyObject *op1, PyObject *op2, long intval, int inplace, int zerodivision_check) { - CYTHON_MAYBE_UNUSED_VAR(intval); - CYTHON_UNUSED_VAR(zerodivision_check); - #if CYTHON_USE_PYLONG_INTERNALS - if (likely(PyLong_CheckExact(op1))) { - return __Pyx_Unpacked___Pyx_PyLong_AddObjC(op1, op2, intval, inplace, zerodivision_check); - } - #endif - if (PyFloat_CheckExact(op1)) { - return __Pyx_Float___Pyx_PyLong_AddObjC(op1, intval, zerodivision_check); - } - return __Pyx_Fallback___Pyx_PyLong_AddObjC(op1, op2, inplace); -} -#endif - -/* RaiseException */ -static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb, PyObject *cause) { - PyObject* owned_instance = NULL; - if (tb == Py_None) { - tb = 0; - } else if (tb && !PyTraceBack_Check(tb)) { - PyErr_SetString(PyExc_TypeError, - "raise: arg 3 must be a traceback or None"); - goto bad; - } - if (value == Py_None) - value = 0; - if (PyExceptionInstance_Check(type)) { - if (value) { - PyErr_SetString(PyExc_TypeError, - "instance exception may not have a separate value"); - goto bad; - } - value = type; - type = (PyObject*) Py_TYPE(value); - } else if (PyExceptionClass_Check(type)) { - PyObject *instance_class = NULL; - if (value && PyExceptionInstance_Check(value)) { - instance_class = (PyObject*) Py_TYPE(value); - if (instance_class != type) { - int is_subclass = PyObject_IsSubclass(instance_class, type); - if (!is_subclass) { - instance_class = NULL; - } else if (unlikely(is_subclass == -1)) { - goto bad; - } else { - type = instance_class; - } - } - } - if (!instance_class) { - PyObject *args; - if (!value) - args = PyTuple_New(0); - else if (PyTuple_Check(value)) { - Py_INCREF(value); - args = value; - } else - args = PyTuple_Pack(1, value); - if (!args) - goto bad; - owned_instance = PyObject_Call(type, args, NULL); - Py_DECREF(args); - if (!owned_instance) - goto bad; - value = owned_instance; - if (!PyExceptionInstance_Check(value)) { - PyErr_Format(PyExc_TypeError, - "calling %R should have returned an instance of " - "BaseException, not %R", - type, Py_TYPE(value)); - goto bad; - } - } - } else { - PyErr_SetString(PyExc_TypeError, - "raise: exception class must be a subclass of BaseException"); - goto bad; - } - if (cause) { - PyObject *fixed_cause; - if (cause == Py_None) { - fixed_cause = NULL; - } else if (PyExceptionClass_Check(cause)) { - fixed_cause = PyObject_CallObject(cause, NULL); - if (fixed_cause == NULL) - goto bad; - } else if (PyExceptionInstance_Check(cause)) { - fixed_cause = cause; - Py_INCREF(fixed_cause); - } else { - PyErr_SetString(PyExc_TypeError, - "exception causes must derive from " - "BaseException"); - goto bad; - } - PyException_SetCause(value, fixed_cause); - } - PyErr_SetObject(type, value); - if (tb) { -#if PY_VERSION_HEX >= 0x030C00A6 - PyException_SetTraceback(value, tb); -#elif CYTHON_FAST_THREAD_STATE - PyThreadState *tstate = __Pyx_PyThreadState_Current; - PyObject* tmp_tb = tstate->curexc_traceback; - if (tb != tmp_tb) { - Py_INCREF(tb); - tstate->curexc_traceback = tb; - Py_XDECREF(tmp_tb); - } -#else - PyObject *tmp_type, *tmp_value, *tmp_tb; - PyErr_Fetch(&tmp_type, &tmp_value, &tmp_tb); - Py_INCREF(tb); - PyErr_Restore(tmp_type, tmp_value, tb); - Py_XDECREF(tmp_tb); -#endif - } -bad: - Py_XDECREF(owned_instance); - return; -} - -/* SetItemInt */ -static int __Pyx_SetItemInt_Generic(PyObject *o, PyObject *j, PyObject *v) { - int r; - if (unlikely(!j)) return -1; - r = PyObject_SetItem(o, j, v); - Py_DECREF(j); - return r; -} -static CYTHON_INLINE int __Pyx_SetItemInt_Fast(PyObject *o, Py_ssize_t i, PyObject *v, int is_list, - CYTHON_NCP_UNUSED int wraparound, CYTHON_NCP_UNUSED int boundscheck) { -#if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE && !CYTHON_AVOID_BORROWED_REFS && CYTHON_USE_TYPE_SLOTS - if (is_list || PyList_CheckExact(o)) { - Py_ssize_t n = (!wraparound) ? i : ((likely(i >= 0)) ? i : i + PyList_GET_SIZE(o)); - if ((!boundscheck) || likely(__Pyx_is_valid_index(n, PyList_GET_SIZE(o)))) { - Py_INCREF(v); -#if CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS - PyList_SetItem(o, n, v); -#else - PyObject* old = PyList_GET_ITEM(o, n); - PyList_SET_ITEM(o, n, v); - Py_DECREF(old); -#endif - return 1; - } - } else { - PyMappingMethods *mm = Py_TYPE(o)->tp_as_mapping; - PySequenceMethods *sm = Py_TYPE(o)->tp_as_sequence; - if (mm && mm->mp_ass_subscript) { - int r; - PyObject *key = PyLong_FromSsize_t(i); - if (unlikely(!key)) return -1; - r = mm->mp_ass_subscript(o, key, v); - Py_DECREF(key); - return r; - } - if (likely(sm && sm->sq_ass_item)) { - if (wraparound && unlikely(i < 0) && likely(sm->sq_length)) { - Py_ssize_t l = sm->sq_length(o); - if (likely(l >= 0)) { - i += l; - } else { - if (!PyErr_ExceptionMatches(PyExc_OverflowError)) - return -1; - PyErr_Clear(); - } - } - return sm->sq_ass_item(o, i, v); - } - } -#else - if (is_list || !PyMapping_Check(o)) - { - return PySequence_SetItem(o, i, v); - } -#endif - return __Pyx_SetItemInt_Generic(o, PyLong_FromSsize_t(i), v); -} - -/* ModInt[long] */ -static CYTHON_INLINE long __Pyx_mod_long(long a, long b, int b_is_constant) { - long r = a % b; - long adapt_python = (b_is_constant ? - ((r != 0) & ((r < 0) ^ (b < 0))) : - ((r != 0) & ((r ^ b) < 0)) - ); - return r + adapt_python * b; -} - -/* LimitedApiGetTypeDict */ -#if CYTHON_COMPILING_IN_LIMITED_API -static Py_ssize_t __Pyx_GetTypeDictOffset(void) { - PyObject *tp_dictoffset_o; - Py_ssize_t tp_dictoffset; - tp_dictoffset_o = PyObject_GetAttrString((PyObject*)(&PyType_Type), "__dictoffset__"); - if (unlikely(!tp_dictoffset_o)) return -1; - tp_dictoffset = PyLong_AsSsize_t(tp_dictoffset_o); - Py_DECREF(tp_dictoffset_o); - if (unlikely(tp_dictoffset == 0)) { - PyErr_SetString( - PyExc_TypeError, - "'type' doesn't have a dictoffset"); - return -1; - } else if (unlikely(tp_dictoffset < 0)) { - PyErr_SetString( - PyExc_TypeError, - "'type' has an unexpected negative dictoffset. " - "Please report this as Cython bug"); - return -1; - } - return tp_dictoffset; -} -static PyObject *__Pyx_GetTypeDict(PyTypeObject *tp) { - static Py_ssize_t tp_dictoffset = 0; - if (unlikely(tp_dictoffset == 0)) { - tp_dictoffset = __Pyx_GetTypeDictOffset(); - if (unlikely(tp_dictoffset == -1 && PyErr_Occurred())) { - tp_dictoffset = 0; // try again next time? - return NULL; - } - } - return *(PyObject**)((char*)tp + tp_dictoffset); -} -#endif - -/* SetItemOnTypeDict */ -static int __Pyx__SetItemOnTypeDict(PyTypeObject *tp, PyObject *k, PyObject *v) { - int result; - PyObject *tp_dict; -#if CYTHON_COMPILING_IN_LIMITED_API - tp_dict = __Pyx_GetTypeDict(tp); - if (unlikely(!tp_dict)) return -1; -#else - tp_dict = tp->tp_dict; -#endif - result = PyDict_SetItem(tp_dict, k, v); - if (likely(!result)) { - PyType_Modified(tp); - if (unlikely(PyObject_HasAttr(v, __pyx_mstate_global->__pyx_n_u_set_name))) { - PyObject *setNameResult = PyObject_CallMethodObjArgs(v, __pyx_mstate_global->__pyx_n_u_set_name, (PyObject *) tp, k, NULL); - if (!setNameResult) return -1; - Py_DECREF(setNameResult); - } - } - return result; -} - -/* FixUpExtensionType */ -static int __Pyx_fix_up_extension_type_from_spec(PyType_Spec *spec, PyTypeObject *type) { -#if __PYX_LIMITED_VERSION_HEX > 0x030900B1 - CYTHON_UNUSED_VAR(spec); - CYTHON_UNUSED_VAR(type); - CYTHON_UNUSED_VAR(__Pyx__SetItemOnTypeDict); -#else - const PyType_Slot *slot = spec->slots; - int changed = 0; -#if !CYTHON_COMPILING_IN_LIMITED_API - while (slot && slot->slot && slot->slot != Py_tp_members) - slot++; - if (slot && slot->slot == Py_tp_members) { -#if !CYTHON_COMPILING_IN_CPYTHON - const -#endif // !CYTHON_COMPILING_IN_CPYTHON) - PyMemberDef *memb = (PyMemberDef*) slot->pfunc; - while (memb && memb->name) { - if (memb->name[0] == '_' && memb->name[1] == '_') { - if (strcmp(memb->name, "__weaklistoffset__") == 0) { - assert(memb->type == T_PYSSIZET); - assert(memb->flags == READONLY); - type->tp_weaklistoffset = memb->offset; - changed = 1; - } - else if (strcmp(memb->name, "__dictoffset__") == 0) { - assert(memb->type == T_PYSSIZET); - assert(memb->flags == READONLY); - type->tp_dictoffset = memb->offset; - changed = 1; - } -#if CYTHON_METH_FASTCALL - else if (strcmp(memb->name, "__vectorcalloffset__") == 0) { - assert(memb->type == T_PYSSIZET); - assert(memb->flags == READONLY); -#if PY_VERSION_HEX >= 0x030800b4 - type->tp_vectorcall_offset = memb->offset; -#else - type->tp_print = (printfunc) memb->offset; -#endif - changed = 1; - } -#endif // CYTHON_METH_FASTCALL -#if !CYTHON_COMPILING_IN_PYPY - else if (strcmp(memb->name, "__module__") == 0) { - PyObject *descr; - assert(memb->type == T_OBJECT); - assert(memb->flags == 0 || memb->flags == READONLY); - descr = PyDescr_NewMember(type, memb); - if (unlikely(!descr)) - return -1; - int set_item_result = PyDict_SetItem(type->tp_dict, PyDescr_NAME(descr), descr); - Py_DECREF(descr); - if (unlikely(set_item_result < 0)) { - return -1; - } - changed = 1; - } -#endif // !CYTHON_COMPILING_IN_PYPY - } - memb++; - } - } -#endif // !CYTHON_COMPILING_IN_LIMITED_API -#if !CYTHON_COMPILING_IN_PYPY - slot = spec->slots; - while (slot && slot->slot && slot->slot != Py_tp_getset) - slot++; - if (slot && slot->slot == Py_tp_getset) { - PyGetSetDef *getset = (PyGetSetDef*) slot->pfunc; - while (getset && getset->name) { - if (getset->name[0] == '_' && getset->name[1] == '_' && strcmp(getset->name, "__module__") == 0) { - PyObject *descr = PyDescr_NewGetSet(type, getset); - if (unlikely(!descr)) - return -1; - #if CYTHON_COMPILING_IN_LIMITED_API - PyObject *pyname = PyUnicode_FromString(getset->name); - if (unlikely(!pyname)) { - Py_DECREF(descr); - return -1; - } - int set_item_result = __Pyx_SetItemOnTypeDict(type, pyname, descr); - Py_DECREF(pyname); - #else - CYTHON_UNUSED_VAR(__Pyx__SetItemOnTypeDict); - int set_item_result = PyDict_SetItem(type->tp_dict, PyDescr_NAME(descr), descr); - #endif - Py_DECREF(descr); - if (unlikely(set_item_result < 0)) { - return -1; - } - changed = 1; - } - ++getset; - } - } -#endif // !CYTHON_COMPILING_IN_PYPY - if (changed) - PyType_Modified(type); -#endif // PY_VERSION_HEX > 0x030900B1 - return 0; -} - -/* PyObjectCallNoArg */ -static CYTHON_INLINE PyObject* __Pyx_PyObject_CallNoArg(PyObject *func) { - PyObject *arg[2] = {NULL, NULL}; - return __Pyx_PyObject_FastCall(func, arg + 1, 0 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET); -} - -/* PyObjectCallOneArg */ -static CYTHON_INLINE PyObject* __Pyx_PyObject_CallOneArg(PyObject *func, PyObject *arg) { - PyObject *args[2] = {NULL, arg}; - return __Pyx_PyObject_FastCall(func, args+1, 1 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET); -} - -/* PyObjectGetMethod */ -static int __Pyx_PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method) { - PyObject *attr; -#if CYTHON_UNPACK_METHODS && CYTHON_COMPILING_IN_CPYTHON && CYTHON_USE_PYTYPE_LOOKUP - __Pyx_TypeName type_name; - PyTypeObject *tp = Py_TYPE(obj); - PyObject *descr; - descrgetfunc f = NULL; - PyObject **dictptr, *dict; - int meth_found = 0; - assert (*method == NULL); - if (unlikely(tp->tp_getattro != PyObject_GenericGetAttr)) { - attr = __Pyx_PyObject_GetAttrStr(obj, name); - goto try_unpack; - } - if (unlikely(tp->tp_dict == NULL) && unlikely(PyType_Ready(tp) < 0)) { - return 0; - } - descr = _PyType_Lookup(tp, name); - if (likely(descr != NULL)) { - Py_INCREF(descr); -#if defined(Py_TPFLAGS_METHOD_DESCRIPTOR) && Py_TPFLAGS_METHOD_DESCRIPTOR - if (__Pyx_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)) -#else - #ifdef __Pyx_CyFunction_USED - if (likely(PyFunction_Check(descr) || __Pyx_IS_TYPE(descr, &PyMethodDescr_Type) || __Pyx_CyFunction_Check(descr))) - #else - if (likely(PyFunction_Check(descr) || __Pyx_IS_TYPE(descr, &PyMethodDescr_Type))) - #endif -#endif - { - meth_found = 1; - } else { - f = Py_TYPE(descr)->tp_descr_get; - if (f != NULL && PyDescr_IsData(descr)) { - attr = f(descr, obj, (PyObject *)Py_TYPE(obj)); - Py_DECREF(descr); - goto try_unpack; - } - } - } - dictptr = _PyObject_GetDictPtr(obj); - if (dictptr != NULL && (dict = *dictptr) != NULL) { - Py_INCREF(dict); - attr = __Pyx_PyDict_GetItemStr(dict, name); - if (attr != NULL) { - Py_INCREF(attr); - Py_DECREF(dict); - Py_XDECREF(descr); - goto try_unpack; - } - Py_DECREF(dict); - } - if (meth_found) { - *method = descr; - return 1; - } - if (f != NULL) { - attr = f(descr, obj, (PyObject *)Py_TYPE(obj)); - Py_DECREF(descr); - goto try_unpack; - } - if (likely(descr != NULL)) { - *method = descr; - return 0; - } - type_name = __Pyx_PyType_GetFullyQualifiedName(tp); - PyErr_Format(PyExc_AttributeError, - "'" __Pyx_FMT_TYPENAME "' object has no attribute '%U'", - type_name, name); - __Pyx_DECREF_TypeName(type_name); - return 0; -#else - attr = __Pyx_PyObject_GetAttrStr(obj, name); - goto try_unpack; -#endif -try_unpack: -#if CYTHON_UNPACK_METHODS - if (likely(attr) && PyMethod_Check(attr) && likely(PyMethod_GET_SELF(attr) == obj)) { - PyObject *function = PyMethod_GET_FUNCTION(attr); - Py_INCREF(function); - Py_DECREF(attr); - *method = function; - return 1; - } -#endif - *method = attr; - return 0; -} - -/* PyObjectCallMethod0 */ -static PyObject* __Pyx_PyObject_CallMethod0(PyObject* obj, PyObject* method_name) { -#if CYTHON_VECTORCALL && (__PYX_LIMITED_VERSION_HEX >= 0x030C0000 || (!CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x03090000)) - PyObject *args[1] = {obj}; - (void) __Pyx_PyObject_GetMethod; - (void) __Pyx_PyObject_CallOneArg; - (void) __Pyx_PyObject_CallNoArg; - return PyObject_VectorcallMethod(method_name, args, 1 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); -#else - PyObject *method = NULL, *result = NULL; - int is_method = __Pyx_PyObject_GetMethod(obj, method_name, &method); - if (likely(is_method)) { - result = __Pyx_PyObject_CallOneArg(method, obj); - Py_DECREF(method); - return result; - } - if (unlikely(!method)) goto bad; - result = __Pyx_PyObject_CallNoArg(method); - Py_DECREF(method); -bad: - return result; -#endif -} - -/* ValidateBasesTuple */ -#if CYTHON_COMPILING_IN_CPYTHON || CYTHON_COMPILING_IN_LIMITED_API || CYTHON_USE_TYPE_SPECS -static int __Pyx_validate_bases_tuple(const char *type_name, Py_ssize_t dictoffset, PyObject *bases) { - Py_ssize_t i, n; -#if CYTHON_ASSUME_SAFE_SIZE - n = PyTuple_GET_SIZE(bases); -#else - n = PyTuple_Size(bases); - if (unlikely(n < 0)) return -1; -#endif - for (i = 1; i < n; i++) - { - PyTypeObject *b; -#if CYTHON_AVOID_BORROWED_REFS - PyObject *b0 = PySequence_GetItem(bases, i); - if (!b0) return -1; -#elif CYTHON_ASSUME_SAFE_MACROS - PyObject *b0 = PyTuple_GET_ITEM(bases, i); -#else - PyObject *b0 = PyTuple_GetItem(bases, i); - if (!b0) return -1; -#endif - b = (PyTypeObject*) b0; - if (!__Pyx_PyType_HasFeature(b, Py_TPFLAGS_HEAPTYPE)) - { - __Pyx_TypeName b_name = __Pyx_PyType_GetFullyQualifiedName(b); - PyErr_Format(PyExc_TypeError, - "base class '" __Pyx_FMT_TYPENAME "' is not a heap type", b_name); - __Pyx_DECREF_TypeName(b_name); -#if CYTHON_AVOID_BORROWED_REFS - Py_DECREF(b0); -#endif - return -1; - } - if (dictoffset == 0) - { - Py_ssize_t b_dictoffset = 0; -#if CYTHON_USE_TYPE_SLOTS - b_dictoffset = b->tp_dictoffset; -#else - PyObject *py_b_dictoffset = PyObject_GetAttrString((PyObject*)b, "__dictoffset__"); - if (!py_b_dictoffset) goto dictoffset_return; - b_dictoffset = PyLong_AsSsize_t(py_b_dictoffset); - Py_DECREF(py_b_dictoffset); - if (b_dictoffset == -1 && PyErr_Occurred()) goto dictoffset_return; -#endif - if (b_dictoffset) { - { - __Pyx_TypeName b_name = __Pyx_PyType_GetFullyQualifiedName(b); - PyErr_Format(PyExc_TypeError, - "extension type '%.200s' has no __dict__ slot, " - "but base type '" __Pyx_FMT_TYPENAME "' has: " - "either add 'cdef dict __dict__' to the extension type " - "or add '__slots__ = [...]' to the base type", - type_name, b_name); - __Pyx_DECREF_TypeName(b_name); - } -#if !CYTHON_USE_TYPE_SLOTS - dictoffset_return: -#endif -#if CYTHON_AVOID_BORROWED_REFS - Py_DECREF(b0); -#endif - return -1; - } - } -#if CYTHON_AVOID_BORROWED_REFS - Py_DECREF(b0); -#endif - } - return 0; -} -#endif - -/* PyType_Ready */ -CYTHON_UNUSED static int __Pyx_PyType_HasMultipleInheritance(PyTypeObject *t) { - while (t) { - PyObject *bases = __Pyx_PyType_GetSlot(t, tp_bases, PyObject*); - if (bases) { - return 1; - } - t = __Pyx_PyType_GetSlot(t, tp_base, PyTypeObject*); - } - return 0; -} -static int __Pyx_PyType_Ready(PyTypeObject *t) { -#if CYTHON_USE_TYPE_SPECS || !CYTHON_COMPILING_IN_CPYTHON || defined(PYSTON_MAJOR_VERSION) - (void)__Pyx_PyObject_CallMethod0; -#if CYTHON_USE_TYPE_SPECS - (void)__Pyx_validate_bases_tuple; -#endif - return PyType_Ready(t); -#else - int r; - if (!__Pyx_PyType_HasMultipleInheritance(t)) { - return PyType_Ready(t); - } - PyObject *bases = __Pyx_PyType_GetSlot(t, tp_bases, PyObject*); - if (bases && unlikely(__Pyx_validate_bases_tuple(t->tp_name, t->tp_dictoffset, bases) == -1)) - return -1; -#if !defined(PYSTON_MAJOR_VERSION) - { - int gc_was_enabled; - #if PY_VERSION_HEX >= 0x030A00b1 - gc_was_enabled = PyGC_Disable(); - (void)__Pyx_PyObject_CallMethod0; - #else - PyObject *ret, *py_status; - PyObject *gc = NULL; - #if (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM+0 >= 0x07030400) &&\ - !CYTHON_COMPILING_IN_GRAAL - gc = PyImport_GetModule(__pyx_mstate_global->__pyx_kp_u_gc); - #endif - if (unlikely(!gc)) gc = PyImport_Import(__pyx_mstate_global->__pyx_kp_u_gc); - if (unlikely(!gc)) return -1; - py_status = __Pyx_PyObject_CallMethod0(gc, __pyx_mstate_global->__pyx_kp_u_isenabled); - if (unlikely(!py_status)) { - Py_DECREF(gc); - return -1; - } - gc_was_enabled = __Pyx_PyObject_IsTrue(py_status); - Py_DECREF(py_status); - if (gc_was_enabled > 0) { - ret = __Pyx_PyObject_CallMethod0(gc, __pyx_mstate_global->__pyx_kp_u_disable); - if (unlikely(!ret)) { - Py_DECREF(gc); - return -1; - } - Py_DECREF(ret); - } else if (unlikely(gc_was_enabled == -1)) { - Py_DECREF(gc); - return -1; - } - #endif - t->tp_flags |= Py_TPFLAGS_HEAPTYPE; -#if PY_VERSION_HEX >= 0x030A0000 - t->tp_flags |= Py_TPFLAGS_IMMUTABLETYPE; -#endif -#else - (void)__Pyx_PyObject_CallMethod0; -#endif - r = PyType_Ready(t); -#if !defined(PYSTON_MAJOR_VERSION) - t->tp_flags &= ~Py_TPFLAGS_HEAPTYPE; - #if PY_VERSION_HEX >= 0x030A00b1 - if (gc_was_enabled) - PyGC_Enable(); - #else - if (gc_was_enabled) { - PyObject *tp, *v, *tb; - PyErr_Fetch(&tp, &v, &tb); - ret = __Pyx_PyObject_CallMethod0(gc, __pyx_mstate_global->__pyx_kp_u_enable); - if (likely(ret || r == -1)) { - Py_XDECREF(ret); - PyErr_Restore(tp, v, tb); - } else { - Py_XDECREF(tp); - Py_XDECREF(v); - Py_XDECREF(tb); - r = -1; - } - } - Py_DECREF(gc); - #endif - } -#endif - return r; -#endif -} - -/* Import */ -static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list, int level) { - PyObject *module = 0; - PyObject *empty_dict = 0; - PyObject *empty_list = 0; - empty_dict = PyDict_New(); - if (unlikely(!empty_dict)) - goto bad; - if (level == -1) { - const char* package_sep = strchr(__Pyx_MODULE_NAME, '.'); - if (package_sep != (0)) { - module = PyImport_ImportModuleLevelObject( - name, __pyx_mstate_global->__pyx_d, empty_dict, from_list, 1); - if (unlikely(!module)) { - if (unlikely(!PyErr_ExceptionMatches(PyExc_ImportError))) - goto bad; - PyErr_Clear(); - } - } - level = 0; - } - if (!module) { - module = PyImport_ImportModuleLevelObject( - name, __pyx_mstate_global->__pyx_d, empty_dict, from_list, level); - } -bad: - Py_XDECREF(empty_dict); - Py_XDECREF(empty_list); - return module; -} - -/* ImportDottedModule */ -static PyObject *__Pyx__ImportDottedModule_Error(PyObject *name, PyObject *parts_tuple, Py_ssize_t count) { - PyObject *partial_name = NULL, *slice = NULL, *sep = NULL; - Py_ssize_t size; - if (unlikely(PyErr_Occurred())) { - PyErr_Clear(); - } -#if CYTHON_ASSUME_SAFE_SIZE - size = PyTuple_GET_SIZE(parts_tuple); -#else - size = PyTuple_Size(parts_tuple); - if (size < 0) goto bad; -#endif - if (likely(size == count)) { - partial_name = name; - } else { - slice = PySequence_GetSlice(parts_tuple, 0, count); - if (unlikely(!slice)) - goto bad; - sep = PyUnicode_FromStringAndSize(".", 1); - if (unlikely(!sep)) - goto bad; - partial_name = PyUnicode_Join(sep, slice); - } - PyErr_Format( - PyExc_ModuleNotFoundError, - "No module named '%U'", partial_name); -bad: - Py_XDECREF(sep); - Py_XDECREF(slice); - Py_XDECREF(partial_name); - return NULL; -} -static PyObject *__Pyx__ImportDottedModule_Lookup(PyObject *name) { - PyObject *imported_module; -#if (CYTHON_COMPILING_IN_PYPY && PYPY_VERSION_NUM < 0x07030400) ||\ - CYTHON_COMPILING_IN_GRAAL - PyObject *modules = PyImport_GetModuleDict(); - if (unlikely(!modules)) - return NULL; - imported_module = __Pyx_PyDict_GetItemStr(modules, name); - Py_XINCREF(imported_module); -#else - imported_module = PyImport_GetModule(name); -#endif - return imported_module; -} -static PyObject *__Pyx_ImportDottedModule_WalkParts(PyObject *module, PyObject *name, PyObject *parts_tuple) { - Py_ssize_t i, nparts; -#if CYTHON_ASSUME_SAFE_SIZE - nparts = PyTuple_GET_SIZE(parts_tuple); -#else - nparts = PyTuple_Size(parts_tuple); - if (nparts < 0) return NULL; -#endif - for (i=1; i < nparts && module; i++) { - PyObject *part, *submodule; -#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - part = PyTuple_GET_ITEM(parts_tuple, i); -#else - part = __Pyx_PySequence_ITEM(parts_tuple, i); - if (!part) return NULL; -#endif - submodule = __Pyx_PyObject_GetAttrStrNoError(module, part); -#if !(CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS) - Py_DECREF(part); -#endif - Py_DECREF(module); - module = submodule; - } - if (unlikely(!module)) { - return __Pyx__ImportDottedModule_Error(name, parts_tuple, i); - } - return module; -} -static PyObject *__Pyx__ImportDottedModule(PyObject *name, PyObject *parts_tuple) { - PyObject *imported_module; - PyObject *module = __Pyx_Import(name, NULL, 0); - if (!parts_tuple || unlikely(!module)) - return module; - imported_module = __Pyx__ImportDottedModule_Lookup(name); - if (likely(imported_module)) { - Py_DECREF(module); - return imported_module; - } - PyErr_Clear(); - return __Pyx_ImportDottedModule_WalkParts(module, name, parts_tuple); -} -static PyObject *__Pyx_ImportDottedModule(PyObject *name, PyObject *parts_tuple) { -#if CYTHON_COMPILING_IN_CPYTHON - PyObject *module = __Pyx__ImportDottedModule_Lookup(name); - if (likely(module)) { - PyObject *spec = __Pyx_PyObject_GetAttrStrNoError(module, __pyx_mstate_global->__pyx_n_u_spec); - if (likely(spec)) { - PyObject *unsafe = __Pyx_PyObject_GetAttrStrNoError(spec, __pyx_mstate_global->__pyx_n_u_initializing); - if (likely(!unsafe || !__Pyx_PyObject_IsTrue(unsafe))) { - Py_DECREF(spec); - spec = NULL; - } - Py_XDECREF(unsafe); - } - if (likely(!spec)) { - PyErr_Clear(); - return module; - } - Py_DECREF(spec); - Py_DECREF(module); - } else if (PyErr_Occurred()) { - PyErr_Clear(); - } -#endif - return __Pyx__ImportDottedModule(name, parts_tuple); -} - -/* ListPack */ -static PyObject *__Pyx_PyList_Pack(Py_ssize_t n, ...) { - va_list va; - PyObject *l = PyList_New(n); - va_start(va, n); - if (unlikely(!l)) goto end; - for (Py_ssize_t i=0; i__pyx_kp_u_); - if (unlikely(!module_dot)) { goto modbad; } - full_name = PyUnicode_Concat(module_dot, name); - if (unlikely(!full_name)) { goto modbad; } - #if (CYTHON_COMPILING_IN_PYPY && PYPY_VERSION_NUM < 0x07030400) ||\ - CYTHON_COMPILING_IN_GRAAL - { - PyObject *modules = PyImport_GetModuleDict(); - if (unlikely(!modules)) - goto modbad; - value = PyObject_GetItem(modules, full_name); - } - #else - value = PyImport_GetModule(full_name); - #endif - modbad: - Py_XDECREF(full_name); - Py_XDECREF(module_dot); - Py_XDECREF(module_name); - } - if (unlikely(!value)) { - PyErr_Format(PyExc_ImportError, "cannot import name %S", name); - } - return value; -} - -/* pybytes_as_double */ -static double __Pyx_SlowPyString_AsDouble(PyObject *obj) { - PyObject *float_value = PyFloat_FromString(obj); - if (likely(float_value)) { - double value = __Pyx_PyFloat_AS_DOUBLE(float_value); - Py_DECREF(float_value); - return value; - } - return (double)-1; -} -static const char* __Pyx__PyBytes_AsDouble_Copy(const char* start, char* buffer, Py_ssize_t length) { - int last_was_punctuation = 1; - int parse_error_found = 0; - Py_ssize_t i; - for (i=0; i < length; i++) { - char chr = start[i]; - int is_punctuation = (chr == '_') | (chr == '.') | (chr == 'e') | (chr == 'E'); - *buffer = chr; - buffer += (chr != '_'); - parse_error_found |= last_was_punctuation & is_punctuation; - last_was_punctuation = is_punctuation; - } - parse_error_found |= last_was_punctuation; - *buffer = '\0'; - return unlikely(parse_error_found) ? NULL : buffer; -} -static double __Pyx__PyBytes_AsDouble_inf_nan(const char* start, Py_ssize_t length) { - int matches = 1; - char sign = start[0]; - int is_signed = (sign == '+') | (sign == '-'); - start += is_signed; - length -= is_signed; - switch (start[0]) { - #ifdef Py_NAN - case 'n': - case 'N': - if (unlikely(length != 3)) goto parse_failure; - matches &= (start[1] == 'a' || start[1] == 'A'); - matches &= (start[2] == 'n' || start[2] == 'N'); - if (unlikely(!matches)) goto parse_failure; - return (sign == '-') ? -Py_NAN : Py_NAN; - #endif - case 'i': - case 'I': - if (unlikely(length < 3)) goto parse_failure; - matches &= (start[1] == 'n' || start[1] == 'N'); - matches &= (start[2] == 'f' || start[2] == 'F'); - if (likely(length == 3 && matches)) - return (sign == '-') ? -Py_HUGE_VAL : Py_HUGE_VAL; - if (unlikely(length != 8)) goto parse_failure; - matches &= (start[3] == 'i' || start[3] == 'I'); - matches &= (start[4] == 'n' || start[4] == 'N'); - matches &= (start[5] == 'i' || start[5] == 'I'); - matches &= (start[6] == 't' || start[6] == 'T'); - matches &= (start[7] == 'y' || start[7] == 'Y'); - if (unlikely(!matches)) goto parse_failure; - return (sign == '-') ? -Py_HUGE_VAL : Py_HUGE_VAL; - case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - break; - default: - goto parse_failure; - } - return 0.0; -parse_failure: - return -1.0; -} -static CYTHON_INLINE int __Pyx__PyBytes_AsDouble_IsSpace(char ch) { - return (ch == 0x20) | !((ch < 0x9) | (ch > 0xd)); -} -CYTHON_UNUSED static double __Pyx__PyBytes_AsDouble(PyObject *obj, const char* start, Py_ssize_t length) { - double value; - Py_ssize_t i, digits; - const char *last = start + length; - char *end; - while (__Pyx__PyBytes_AsDouble_IsSpace(*start)) - start++; - while (start < last - 1 && __Pyx__PyBytes_AsDouble_IsSpace(last[-1])) - last--; - length = last - start; - if (unlikely(length <= 0)) goto fallback; - value = __Pyx__PyBytes_AsDouble_inf_nan(start, length); - if (unlikely(value == -1.0)) goto fallback; - if (value != 0.0) return value; - digits = 0; - for (i=0; i < length; digits += start[i++] != '_'); - if (likely(digits == length)) { - value = PyOS_string_to_double(start, &end, NULL); - } else if (digits < 40) { - char number[40]; - last = __Pyx__PyBytes_AsDouble_Copy(start, number, length); - if (unlikely(!last)) goto fallback; - value = PyOS_string_to_double(number, &end, NULL); - } else { - char *number = (char*) PyMem_Malloc((digits + 1) * sizeof(char)); - if (unlikely(!number)) goto fallback; - last = __Pyx__PyBytes_AsDouble_Copy(start, number, length); - if (unlikely(!last)) { - PyMem_Free(number); - goto fallback; - } - value = PyOS_string_to_double(number, &end, NULL); - PyMem_Free(number); - } - if (likely(end == last) || (value == (double)-1 && PyErr_Occurred())) { - return value; - } -fallback: - return __Pyx_SlowPyString_AsDouble(obj); -} - -/* FetchSharedCythonModule */ -static PyObject *__Pyx_FetchSharedCythonABIModule(void) { - return __Pyx_PyImport_AddModuleRef(__PYX_ABI_MODULE_NAME); -} - -/* dict_setdefault */ -static CYTHON_INLINE PyObject *__Pyx_PyDict_SetDefault(PyObject *d, PyObject *key, PyObject *default_value, - int is_safe_type) { - PyObject* value; - CYTHON_MAYBE_UNUSED_VAR(is_safe_type); -#if CYTHON_COMPILING_IN_LIMITED_API - value = PyObject_CallMethod(d, "setdefault", "OO", key, default_value); -#elif PY_VERSION_HEX >= 0x030d0000 - PyDict_SetDefaultRef(d, key, default_value, &value); -#else - value = PyDict_SetDefault(d, key, default_value); - if (unlikely(!value)) return NULL; - Py_INCREF(value); -#endif - return value; -} - -/* FetchCommonType */ -#if __PYX_LIMITED_VERSION_HEX < 0x030C0000 -static PyObject* __Pyx_PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module, PyType_Spec *spec, PyObject *bases) { - PyObject *result = __Pyx_PyType_FromModuleAndSpec(module, spec, bases); - if (result && metaclass) { - PyObject *old_tp = (PyObject*)Py_TYPE(result); - Py_INCREF((PyObject*)metaclass); -#if __PYX_LIMITED_VERSION_HEX >= 0x03090000 - Py_SET_TYPE(result, metaclass); -#else - result->ob_type = metaclass; -#endif - Py_DECREF(old_tp); - } - return result; -} -#else -#define __Pyx_PyType_FromMetaclass(me, mo, s, b) PyType_FromMetaclass(me, mo, s, b) -#endif -static int __Pyx_VerifyCachedType(PyObject *cached_type, - const char *name, - Py_ssize_t expected_basicsize) { - Py_ssize_t basicsize; - if (!PyType_Check(cached_type)) { - PyErr_Format(PyExc_TypeError, - "Shared Cython type %.200s is not a type object", name); - return -1; - } - if (expected_basicsize == 0) { - return 0; // size is inherited, nothing useful to check - } -#if CYTHON_COMPILING_IN_LIMITED_API - PyObject *py_basicsize; - py_basicsize = PyObject_GetAttrString(cached_type, "__basicsize__"); - if (unlikely(!py_basicsize)) return -1; - basicsize = PyLong_AsSsize_t(py_basicsize); - Py_DECREF(py_basicsize); - py_basicsize = NULL; - if (unlikely(basicsize == (Py_ssize_t)-1) && PyErr_Occurred()) return -1; -#else - basicsize = ((PyTypeObject*) cached_type)->tp_basicsize; -#endif - if (basicsize != expected_basicsize) { - PyErr_Format(PyExc_TypeError, - "Shared Cython type %.200s has the wrong size, try recompiling", - name); - return -1; - } - return 0; -} -static PyTypeObject *__Pyx_FetchCommonTypeFromSpec(PyTypeObject *metaclass, PyObject *module, PyType_Spec *spec, PyObject *bases) { - PyObject *abi_module = NULL, *cached_type = NULL, *abi_module_dict, *new_cached_type, *py_object_name; - int get_item_ref_result; - const char* object_name = strrchr(spec->name, '.'); - object_name = object_name ? object_name+1 : spec->name; - py_object_name = PyUnicode_FromString(object_name); - if (!py_object_name) return NULL; - abi_module = __Pyx_FetchSharedCythonABIModule(); - if (!abi_module) goto done; - abi_module_dict = PyModule_GetDict(abi_module); - if (!abi_module_dict) goto done; - get_item_ref_result = __Pyx_PyDict_GetItemRef(abi_module_dict, py_object_name, &cached_type); - if (get_item_ref_result == 1) { - if (__Pyx_VerifyCachedType( - cached_type, - object_name, - spec->basicsize) < 0) { - goto bad; - } - goto done; - } else if (unlikely(get_item_ref_result == -1)) { - goto bad; - } - CYTHON_UNUSED_VAR(module); - cached_type = __Pyx_PyType_FromMetaclass(metaclass, abi_module, spec, bases); - if (unlikely(!cached_type)) goto bad; - if (unlikely(__Pyx_fix_up_extension_type_from_spec(spec, (PyTypeObject *) cached_type) < 0)) goto bad; - new_cached_type = __Pyx_PyDict_SetDefault(abi_module_dict, py_object_name, cached_type, 1); - if (unlikely(new_cached_type != cached_type)) { - if (unlikely(!new_cached_type)) goto bad; - Py_DECREF(cached_type); - cached_type = new_cached_type; - if (__Pyx_VerifyCachedType( - cached_type, - object_name, - spec->basicsize) < 0) { - goto bad; - } - goto done; - } else { - Py_DECREF(new_cached_type); - } -done: - Py_XDECREF(abi_module); - Py_DECREF(py_object_name); - assert(cached_type == NULL || PyType_Check(cached_type)); - return (PyTypeObject *) cached_type; -bad: - Py_XDECREF(cached_type); - cached_type = NULL; - goto done; -} - -/* CommonTypesMetaclass */ -static PyObject* __pyx_CommonTypesMetaclass_get_module(CYTHON_UNUSED PyObject *self, CYTHON_UNUSED void* context) { - return PyUnicode_FromString(__PYX_ABI_MODULE_NAME); -} -static PyGetSetDef __pyx_CommonTypesMetaclass_getset[] = { - {"__module__", __pyx_CommonTypesMetaclass_get_module, NULL, NULL, NULL}, - {0, 0, 0, 0, 0} -}; -static PyType_Slot __pyx_CommonTypesMetaclass_slots[] = { - {Py_tp_getset, (void *)__pyx_CommonTypesMetaclass_getset}, - {0, 0} -}; -static PyType_Spec __pyx_CommonTypesMetaclass_spec = { - __PYX_TYPE_MODULE_PREFIX "_common_types_metatype", - 0, - 0, -#if PY_VERSION_HEX >= 0x030A0000 - Py_TPFLAGS_IMMUTABLETYPE | - Py_TPFLAGS_DISALLOW_INSTANTIATION | -#endif - Py_TPFLAGS_DEFAULT, - __pyx_CommonTypesMetaclass_slots -}; -static int __pyx_CommonTypesMetaclass_init(PyObject *module) { - __pyx_mstatetype *mstate = __Pyx_PyModule_GetState(module); - PyObject *bases = PyTuple_Pack(1, &PyType_Type); - if (unlikely(!bases)) { - return -1; - } - mstate->__pyx_CommonTypesMetaclassType = __Pyx_FetchCommonTypeFromSpec(NULL, module, &__pyx_CommonTypesMetaclass_spec, bases); - Py_DECREF(bases); - if (unlikely(mstate->__pyx_CommonTypesMetaclassType == NULL)) { - return -1; - } - return 0; -} - -/* CallTypeTraverse */ -#if !CYTHON_USE_TYPE_SPECS || (!CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x03090000) -#else -static int __Pyx_call_type_traverse(PyObject *o, int always_call, visitproc visit, void *arg) { - #if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x03090000 - if (__Pyx_get_runtime_version() < 0x03090000) return 0; - #endif - if (!always_call) { - PyTypeObject *base = __Pyx_PyObject_GetSlot(o, tp_base, PyTypeObject*); - unsigned long flags = PyType_GetFlags(base); - if (flags & Py_TPFLAGS_HEAPTYPE) { - return 0; - } - } - Py_VISIT((PyObject*)Py_TYPE(o)); - return 0; -} -#endif - -/* PyMethodNew */ -#if CYTHON_COMPILING_IN_LIMITED_API -static PyObject *__Pyx_PyMethod_New(PyObject *func, PyObject *self, PyObject *typ) { - PyObject *result; - CYTHON_UNUSED_VAR(typ); - if (!self) - return __Pyx_NewRef(func); - #if __PYX_LIMITED_VERSION_HEX >= 0x030C0000 - { - PyObject *args[] = {func, self}; - result = PyObject_Vectorcall(__pyx_mstate_global->__Pyx_CachedMethodType, args, 2, NULL); - } - #else - result = PyObject_CallFunctionObjArgs(__pyx_mstate_global->__Pyx_CachedMethodType, func, self, NULL); - #endif - return result; -} -#else -static PyObject *__Pyx_PyMethod_New(PyObject *func, PyObject *self, PyObject *typ) { - CYTHON_UNUSED_VAR(typ); - if (!self) - return __Pyx_NewRef(func); - return PyMethod_New(func, self); -} -#endif - -/* PyVectorcallFastCallDict */ -#if CYTHON_METH_FASTCALL && (CYTHON_VECTORCALL || CYTHON_BACKPORT_VECTORCALL) -static PyObject *__Pyx_PyVectorcall_FastCallDict_kw(PyObject *func, __pyx_vectorcallfunc vc, PyObject *const *args, size_t nargs, PyObject *kw) -{ - PyObject *res = NULL; - PyObject *kwnames; - PyObject **newargs; - PyObject **kwvalues; - Py_ssize_t i, pos; - size_t j; - PyObject *key, *value; - unsigned long keys_are_strings; - #if !CYTHON_ASSUME_SAFE_SIZE - Py_ssize_t nkw = PyDict_Size(kw); - if (unlikely(nkw == -1)) return NULL; - #else - Py_ssize_t nkw = PyDict_GET_SIZE(kw); - #endif - newargs = (PyObject **)PyMem_Malloc((nargs + (size_t)nkw) * sizeof(args[0])); - if (unlikely(newargs == NULL)) { - PyErr_NoMemory(); - return NULL; - } - for (j = 0; j < nargs; j++) newargs[j] = args[j]; - kwnames = PyTuple_New(nkw); - if (unlikely(kwnames == NULL)) { - PyMem_Free(newargs); - return NULL; - } - kwvalues = newargs + nargs; - pos = i = 0; - keys_are_strings = Py_TPFLAGS_UNICODE_SUBCLASS; - while (PyDict_Next(kw, &pos, &key, &value)) { - keys_are_strings &= - #if CYTHON_COMPILING_IN_LIMITED_API - PyType_GetFlags(Py_TYPE(key)); - #else - Py_TYPE(key)->tp_flags; - #endif - Py_INCREF(key); - Py_INCREF(value); - #if !CYTHON_ASSUME_SAFE_MACROS - if (unlikely(PyTuple_SetItem(kwnames, i, key) < 0)) goto cleanup; - #else - PyTuple_SET_ITEM(kwnames, i, key); - #endif - kwvalues[i] = value; - i++; - } - if (unlikely(!keys_are_strings)) { - PyErr_SetString(PyExc_TypeError, "keywords must be strings"); - goto cleanup; - } - res = vc(func, newargs, nargs, kwnames); -cleanup: - Py_DECREF(kwnames); - for (i = 0; i < nkw; i++) - Py_DECREF(kwvalues[i]); - PyMem_Free(newargs); - return res; -} -static CYTHON_INLINE PyObject *__Pyx_PyVectorcall_FastCallDict(PyObject *func, __pyx_vectorcallfunc vc, PyObject *const *args, size_t nargs, PyObject *kw) -{ - Py_ssize_t kw_size = - likely(kw == NULL) ? - 0 : -#if !CYTHON_ASSUME_SAFE_SIZE - PyDict_Size(kw); -#else - PyDict_GET_SIZE(kw); -#endif - if (kw_size == 0) { - return vc(func, args, nargs, NULL); - } -#if !CYTHON_ASSUME_SAFE_SIZE - else if (unlikely(kw_size == -1)) { - return NULL; - } -#endif - return __Pyx_PyVectorcall_FastCallDict_kw(func, vc, args, nargs, kw); -} -#endif - -/* CythonFunctionShared */ -#if CYTHON_COMPILING_IN_LIMITED_API -static CYTHON_INLINE int __Pyx__IsSameCyOrCFunctionNoMethod(PyObject *func, void (*cfunc)(void)) { - if (__Pyx_CyFunction_Check(func)) { - return PyCFunction_GetFunction(((__pyx_CyFunctionObject*)func)->func) == (PyCFunction) cfunc; - } else if (PyCFunction_Check(func)) { - return PyCFunction_GetFunction(func) == (PyCFunction) cfunc; - } - return 0; -} -static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(PyObject *func, void (*cfunc)(void)) { - if ((PyObject*)Py_TYPE(func) == __pyx_mstate_global->__Pyx_CachedMethodType) { - int result; - PyObject *newFunc = PyObject_GetAttr(func, __pyx_mstate_global->__pyx_n_u_func); - if (unlikely(!newFunc)) { - PyErr_Clear(); // It's only an optimization, so don't throw an error - return 0; - } - result = __Pyx__IsSameCyOrCFunctionNoMethod(newFunc, cfunc); - Py_DECREF(newFunc); - return result; - } - return __Pyx__IsSameCyOrCFunctionNoMethod(func, cfunc); -} -#else -static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(PyObject *func, void (*cfunc)(void)) { - if (PyMethod_Check(func)) { - func = PyMethod_GET_FUNCTION(func); - } - return __Pyx_CyOrPyCFunction_Check(func) && __Pyx_CyOrPyCFunction_GET_FUNCTION(func) == (PyCFunction) cfunc; -} -#endif -static CYTHON_INLINE void __Pyx__CyFunction_SetClassObj(__pyx_CyFunctionObject* f, PyObject* classobj) { -#if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API - __Pyx_Py_XDECREF_SET( - __Pyx_CyFunction_GetClassObj(f), - ((classobj) ? __Pyx_NewRef(classobj) : NULL)); -#else - __Pyx_Py_XDECREF_SET( - ((PyCMethodObject *) (f))->mm_class, - (PyTypeObject*)((classobj) ? __Pyx_NewRef(classobj) : NULL)); -#endif -} -static PyObject * -__Pyx_CyFunction_get_doc_locked(__pyx_CyFunctionObject *op) -{ - if (unlikely(op->func_doc == NULL)) { -#if CYTHON_COMPILING_IN_LIMITED_API - op->func_doc = PyObject_GetAttrString(op->func, "__doc__"); - if (unlikely(!op->func_doc)) return NULL; -#else - if (((PyCFunctionObject*)op)->m_ml->ml_doc) { - op->func_doc = PyUnicode_FromString(((PyCFunctionObject*)op)->m_ml->ml_doc); - if (unlikely(op->func_doc == NULL)) - return NULL; - } else { - Py_INCREF(Py_None); - return Py_None; - } -#endif - } - Py_INCREF(op->func_doc); - return op->func_doc; -} -static PyObject * -__Pyx_CyFunction_get_doc(__pyx_CyFunctionObject *op, void *closure) { - PyObject *result; - CYTHON_UNUSED_VAR(closure); - __Pyx_BEGIN_CRITICAL_SECTION(op); - result = __Pyx_CyFunction_get_doc_locked(op); - __Pyx_END_CRITICAL_SECTION(); - return result; -} -static int -__Pyx_CyFunction_set_doc(__pyx_CyFunctionObject *op, PyObject *value, void *context) -{ - CYTHON_UNUSED_VAR(context); - if (value == NULL) { - value = Py_None; - } - Py_INCREF(value); - __Pyx_BEGIN_CRITICAL_SECTION(op); - __Pyx_Py_XDECREF_SET(op->func_doc, value); - __Pyx_END_CRITICAL_SECTION(); - return 0; -} -static PyObject * -__Pyx_CyFunction_get_name_locked(__pyx_CyFunctionObject *op) -{ - if (unlikely(op->func_name == NULL)) { -#if CYTHON_COMPILING_IN_LIMITED_API - op->func_name = PyObject_GetAttrString(op->func, "__name__"); -#else - op->func_name = PyUnicode_InternFromString(((PyCFunctionObject*)op)->m_ml->ml_name); -#endif - if (unlikely(op->func_name == NULL)) - return NULL; - } - Py_INCREF(op->func_name); - return op->func_name; -} -static PyObject * -__Pyx_CyFunction_get_name(__pyx_CyFunctionObject *op, void *context) -{ - PyObject *result = NULL; - CYTHON_UNUSED_VAR(context); - __Pyx_BEGIN_CRITICAL_SECTION(op); - result = __Pyx_CyFunction_get_name_locked(op); - __Pyx_END_CRITICAL_SECTION(); - return result; -} -static int -__Pyx_CyFunction_set_name(__pyx_CyFunctionObject *op, PyObject *value, void *context) -{ - CYTHON_UNUSED_VAR(context); - if (unlikely(value == NULL || !PyUnicode_Check(value))) { - PyErr_SetString(PyExc_TypeError, - "__name__ must be set to a string object"); - return -1; - } - Py_INCREF(value); - __Pyx_BEGIN_CRITICAL_SECTION(op); - __Pyx_Py_XDECREF_SET(op->func_name, value); - __Pyx_END_CRITICAL_SECTION(); - return 0; -} -static PyObject * -__Pyx_CyFunction_get_qualname(__pyx_CyFunctionObject *op, void *context) -{ - CYTHON_UNUSED_VAR(context); - PyObject *result; - __Pyx_BEGIN_CRITICAL_SECTION(op); - Py_INCREF(op->func_qualname); - result = op->func_qualname; - __Pyx_END_CRITICAL_SECTION(); - return result; -} -static int -__Pyx_CyFunction_set_qualname(__pyx_CyFunctionObject *op, PyObject *value, void *context) -{ - CYTHON_UNUSED_VAR(context); - if (unlikely(value == NULL || !PyUnicode_Check(value))) { - PyErr_SetString(PyExc_TypeError, - "__qualname__ must be set to a string object"); - return -1; - } - Py_INCREF(value); - __Pyx_BEGIN_CRITICAL_SECTION(op); - __Pyx_Py_XDECREF_SET(op->func_qualname, value); - __Pyx_END_CRITICAL_SECTION(); - return 0; -} -static PyObject * -__Pyx_CyFunction_get_dict_locked(__pyx_CyFunctionObject *op) -{ - if (unlikely(op->func_dict == NULL)) { - op->func_dict = PyDict_New(); - if (unlikely(op->func_dict == NULL)) - return NULL; - } - Py_INCREF(op->func_dict); - return op->func_dict; -} -static PyObject * -__Pyx_CyFunction_get_dict(__pyx_CyFunctionObject *op, void *context) -{ - CYTHON_UNUSED_VAR(context); - PyObject *result; - __Pyx_BEGIN_CRITICAL_SECTION(op); - result = __Pyx_CyFunction_get_dict_locked(op); - __Pyx_END_CRITICAL_SECTION(); - return result; -} -static int -__Pyx_CyFunction_set_dict(__pyx_CyFunctionObject *op, PyObject *value, void *context) -{ - CYTHON_UNUSED_VAR(context); - if (unlikely(value == NULL)) { - PyErr_SetString(PyExc_TypeError, - "function's dictionary may not be deleted"); - return -1; - } - if (unlikely(!PyDict_Check(value))) { - PyErr_SetString(PyExc_TypeError, - "setting function's dictionary to a non-dict"); - return -1; - } - Py_INCREF(value); - __Pyx_BEGIN_CRITICAL_SECTION(op); - __Pyx_Py_XDECREF_SET(op->func_dict, value); - __Pyx_END_CRITICAL_SECTION(); - return 0; -} -static PyObject * -__Pyx_CyFunction_get_globals(__pyx_CyFunctionObject *op, void *context) -{ - CYTHON_UNUSED_VAR(context); - Py_INCREF(op->func_globals); - return op->func_globals; -} -static PyObject * -__Pyx_CyFunction_get_closure(__pyx_CyFunctionObject *op, void *context) -{ - CYTHON_UNUSED_VAR(op); - CYTHON_UNUSED_VAR(context); - Py_INCREF(Py_None); - return Py_None; -} -static PyObject * -__Pyx_CyFunction_get_code(__pyx_CyFunctionObject *op, void *context) -{ - PyObject* result = (op->func_code) ? op->func_code : Py_None; - CYTHON_UNUSED_VAR(context); - Py_INCREF(result); - return result; -} -static int -__Pyx_CyFunction_init_defaults(__pyx_CyFunctionObject *op) { - int result = 0; - PyObject *res = op->defaults_getter((PyObject *) op); - if (unlikely(!res)) - return -1; - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - op->defaults_tuple = PyTuple_GET_ITEM(res, 0); - Py_INCREF(op->defaults_tuple); - op->defaults_kwdict = PyTuple_GET_ITEM(res, 1); - Py_INCREF(op->defaults_kwdict); - #else - op->defaults_tuple = __Pyx_PySequence_ITEM(res, 0); - if (unlikely(!op->defaults_tuple)) result = -1; - else { - op->defaults_kwdict = __Pyx_PySequence_ITEM(res, 1); - if (unlikely(!op->defaults_kwdict)) result = -1; - } - #endif - Py_DECREF(res); - return result; -} -static int -__Pyx_CyFunction_set_defaults(__pyx_CyFunctionObject *op, PyObject* value, void *context) { - CYTHON_UNUSED_VAR(context); - if (!value) { - value = Py_None; - } else if (unlikely(value != Py_None && !PyTuple_Check(value))) { - PyErr_SetString(PyExc_TypeError, - "__defaults__ must be set to a tuple object"); - return -1; - } - PyErr_WarnEx(PyExc_RuntimeWarning, "changes to cyfunction.__defaults__ will not " - "currently affect the values used in function calls", 1); - Py_INCREF(value); - __Pyx_BEGIN_CRITICAL_SECTION(op); - __Pyx_Py_XDECREF_SET(op->defaults_tuple, value); - __Pyx_END_CRITICAL_SECTION(); - return 0; -} -static PyObject * -__Pyx_CyFunction_get_defaults_locked(__pyx_CyFunctionObject *op) { - PyObject* result = op->defaults_tuple; - if (unlikely(!result)) { - if (op->defaults_getter) { - if (unlikely(__Pyx_CyFunction_init_defaults(op) < 0)) return NULL; - result = op->defaults_tuple; - } else { - result = Py_None; - } - } - Py_INCREF(result); - return result; -} -static PyObject * -__Pyx_CyFunction_get_defaults(__pyx_CyFunctionObject *op, void *context) { - PyObject* result = NULL; - CYTHON_UNUSED_VAR(context); - __Pyx_BEGIN_CRITICAL_SECTION(op); - result = __Pyx_CyFunction_get_defaults_locked(op); - __Pyx_END_CRITICAL_SECTION(); - return result; -} -static int -__Pyx_CyFunction_set_kwdefaults(__pyx_CyFunctionObject *op, PyObject* value, void *context) { - CYTHON_UNUSED_VAR(context); - if (!value) { - value = Py_None; - } else if (unlikely(value != Py_None && !PyDict_Check(value))) { - PyErr_SetString(PyExc_TypeError, - "__kwdefaults__ must be set to a dict object"); - return -1; - } - PyErr_WarnEx(PyExc_RuntimeWarning, "changes to cyfunction.__kwdefaults__ will not " - "currently affect the values used in function calls", 1); - Py_INCREF(value); - __Pyx_BEGIN_CRITICAL_SECTION(op); - __Pyx_Py_XDECREF_SET(op->defaults_kwdict, value); - __Pyx_END_CRITICAL_SECTION(); - return 0; -} -static PyObject * -__Pyx_CyFunction_get_kwdefaults_locked(__pyx_CyFunctionObject *op) { - PyObject* result = op->defaults_kwdict; - if (unlikely(!result)) { - if (op->defaults_getter) { - if (unlikely(__Pyx_CyFunction_init_defaults(op) < 0)) return NULL; - result = op->defaults_kwdict; - } else { - result = Py_None; - } - } - Py_INCREF(result); - return result; -} -static PyObject * -__Pyx_CyFunction_get_kwdefaults(__pyx_CyFunctionObject *op, void *context) { - PyObject* result; - CYTHON_UNUSED_VAR(context); - __Pyx_BEGIN_CRITICAL_SECTION(op); - result = __Pyx_CyFunction_get_kwdefaults_locked(op); - __Pyx_END_CRITICAL_SECTION(); - return result; -} -static int -__Pyx_CyFunction_set_annotations(__pyx_CyFunctionObject *op, PyObject* value, void *context) { - CYTHON_UNUSED_VAR(context); - if (!value || value == Py_None) { - value = NULL; - } else if (unlikely(!PyDict_Check(value))) { - PyErr_SetString(PyExc_TypeError, - "__annotations__ must be set to a dict object"); - return -1; - } - Py_XINCREF(value); - __Pyx_BEGIN_CRITICAL_SECTION(op); - __Pyx_Py_XDECREF_SET(op->func_annotations, value); - __Pyx_END_CRITICAL_SECTION(); - return 0; -} -static PyObject * -__Pyx_CyFunction_get_annotations_locked(__pyx_CyFunctionObject *op) { - PyObject* result = op->func_annotations; - if (unlikely(!result)) { - result = PyDict_New(); - if (unlikely(!result)) return NULL; - op->func_annotations = result; - } - Py_INCREF(result); - return result; -} -static PyObject * -__Pyx_CyFunction_get_annotations(__pyx_CyFunctionObject *op, void *context) { - PyObject *result; - CYTHON_UNUSED_VAR(context); - __Pyx_BEGIN_CRITICAL_SECTION(op); - result = __Pyx_CyFunction_get_annotations_locked(op); - __Pyx_END_CRITICAL_SECTION(); - return result; -} -static PyObject * -__Pyx_CyFunction_get_is_coroutine_value(__pyx_CyFunctionObject *op) { - int is_coroutine = op->flags & __Pyx_CYFUNCTION_COROUTINE; - if (is_coroutine) { - PyObject *is_coroutine_value, *module, *fromlist, *marker = __pyx_mstate_global->__pyx_n_u_is_coroutine; - fromlist = PyList_New(1); - if (unlikely(!fromlist)) return NULL; - Py_INCREF(marker); -#if CYTHON_ASSUME_SAFE_MACROS - PyList_SET_ITEM(fromlist, 0, marker); -#else - if (unlikely(PyList_SetItem(fromlist, 0, marker) < 0)) { - Py_DECREF(marker); - Py_DECREF(fromlist); - return NULL; - } -#endif - module = PyImport_ImportModuleLevelObject(__pyx_mstate_global->__pyx_n_u_asyncio_coroutines, NULL, NULL, fromlist, 0); - Py_DECREF(fromlist); - if (unlikely(!module)) goto ignore; - is_coroutine_value = __Pyx_PyObject_GetAttrStr(module, marker); - Py_DECREF(module); - if (likely(is_coroutine_value)) { - return is_coroutine_value; - } -ignore: - PyErr_Clear(); - } - return __Pyx_PyBool_FromLong(is_coroutine); -} -static PyObject * -__Pyx_CyFunction_get_is_coroutine(__pyx_CyFunctionObject *op, void *context) { - PyObject *result; - CYTHON_UNUSED_VAR(context); - if (op->func_is_coroutine) { - return __Pyx_NewRef(op->func_is_coroutine); - } - result = __Pyx_CyFunction_get_is_coroutine_value(op); - if (unlikely(!result)) - return NULL; - __Pyx_BEGIN_CRITICAL_SECTION(op); - if (op->func_is_coroutine) { - Py_DECREF(result); - result = __Pyx_NewRef(op->func_is_coroutine); - } else { - op->func_is_coroutine = __Pyx_NewRef(result); - } - __Pyx_END_CRITICAL_SECTION(); - return result; -} -static void __Pyx_CyFunction_raise_argument_count_error(__pyx_CyFunctionObject *func, const char* message, Py_ssize_t size) { -#if CYTHON_COMPILING_IN_LIMITED_API - PyObject *py_name = __Pyx_CyFunction_get_name(func, NULL); - if (!py_name) return; - PyErr_Format(PyExc_TypeError, - "%.200S() %s (%" CYTHON_FORMAT_SSIZE_T "d given)", - py_name, message, size); - Py_DECREF(py_name); -#else - const char* name = ((PyCFunctionObject*)func)->m_ml->ml_name; - PyErr_Format(PyExc_TypeError, - "%.200s() %s (%" CYTHON_FORMAT_SSIZE_T "d given)", - name, message, size); -#endif -} -static void __Pyx_CyFunction_raise_type_error(__pyx_CyFunctionObject *func, const char* message) { -#if CYTHON_COMPILING_IN_LIMITED_API - PyObject *py_name = __Pyx_CyFunction_get_name(func, NULL); - if (!py_name) return; - PyErr_Format(PyExc_TypeError, - "%.200S() %s", - py_name, message); - Py_DECREF(py_name); -#else - const char* name = ((PyCFunctionObject*)func)->m_ml->ml_name; - PyErr_Format(PyExc_TypeError, - "%.200s() %s", - name, message); -#endif -} -#if CYTHON_COMPILING_IN_LIMITED_API -static PyObject * -__Pyx_CyFunction_get_module(__pyx_CyFunctionObject *op, void *context) { - CYTHON_UNUSED_VAR(context); - return PyObject_GetAttrString(op->func, "__module__"); -} -static int -__Pyx_CyFunction_set_module(__pyx_CyFunctionObject *op, PyObject* value, void *context) { - CYTHON_UNUSED_VAR(context); - return PyObject_SetAttrString(op->func, "__module__", value); -} -#endif -static PyGetSetDef __pyx_CyFunction_getsets[] = { - {"func_doc", (getter)__Pyx_CyFunction_get_doc, (setter)__Pyx_CyFunction_set_doc, 0, 0}, - {"__doc__", (getter)__Pyx_CyFunction_get_doc, (setter)__Pyx_CyFunction_set_doc, 0, 0}, - {"func_name", (getter)__Pyx_CyFunction_get_name, (setter)__Pyx_CyFunction_set_name, 0, 0}, - {"__name__", (getter)__Pyx_CyFunction_get_name, (setter)__Pyx_CyFunction_set_name, 0, 0}, - {"__qualname__", (getter)__Pyx_CyFunction_get_qualname, (setter)__Pyx_CyFunction_set_qualname, 0, 0}, - {"func_dict", (getter)__Pyx_CyFunction_get_dict, (setter)__Pyx_CyFunction_set_dict, 0, 0}, - {"__dict__", (getter)__Pyx_CyFunction_get_dict, (setter)__Pyx_CyFunction_set_dict, 0, 0}, - {"func_globals", (getter)__Pyx_CyFunction_get_globals, 0, 0, 0}, - {"__globals__", (getter)__Pyx_CyFunction_get_globals, 0, 0, 0}, - {"func_closure", (getter)__Pyx_CyFunction_get_closure, 0, 0, 0}, - {"__closure__", (getter)__Pyx_CyFunction_get_closure, 0, 0, 0}, - {"func_code", (getter)__Pyx_CyFunction_get_code, 0, 0, 0}, - {"__code__", (getter)__Pyx_CyFunction_get_code, 0, 0, 0}, - {"func_defaults", (getter)__Pyx_CyFunction_get_defaults, (setter)__Pyx_CyFunction_set_defaults, 0, 0}, - {"__defaults__", (getter)__Pyx_CyFunction_get_defaults, (setter)__Pyx_CyFunction_set_defaults, 0, 0}, - {"__kwdefaults__", (getter)__Pyx_CyFunction_get_kwdefaults, (setter)__Pyx_CyFunction_set_kwdefaults, 0, 0}, - {"__annotations__", (getter)__Pyx_CyFunction_get_annotations, (setter)__Pyx_CyFunction_set_annotations, 0, 0}, - {"_is_coroutine", (getter)__Pyx_CyFunction_get_is_coroutine, 0, 0, 0}, -#if CYTHON_COMPILING_IN_LIMITED_API - {"__module__", (getter)__Pyx_CyFunction_get_module, (setter)__Pyx_CyFunction_set_module, 0, 0}, -#endif - {0, 0, 0, 0, 0} -}; -static PyMemberDef __pyx_CyFunction_members[] = { -#if !CYTHON_COMPILING_IN_LIMITED_API - {"__module__", T_OBJECT, offsetof(PyCFunctionObject, m_module), 0, 0}, -#endif - {"__dictoffset__", T_PYSSIZET, offsetof(__pyx_CyFunctionObject, func_dict), READONLY, 0}, -#if CYTHON_METH_FASTCALL -#if CYTHON_BACKPORT_VECTORCALL || CYTHON_COMPILING_IN_LIMITED_API - {"__vectorcalloffset__", T_PYSSIZET, offsetof(__pyx_CyFunctionObject, func_vectorcall), READONLY, 0}, -#else - {"__vectorcalloffset__", T_PYSSIZET, offsetof(PyCFunctionObject, vectorcall), READONLY, 0}, -#endif -#if CYTHON_COMPILING_IN_LIMITED_API - {"__weaklistoffset__", T_PYSSIZET, offsetof(__pyx_CyFunctionObject, func_weakreflist), READONLY, 0}, -#else - {"__weaklistoffset__", T_PYSSIZET, offsetof(PyCFunctionObject, m_weakreflist), READONLY, 0}, -#endif -#endif - {0, 0, 0, 0, 0} -}; -static PyObject * -__Pyx_CyFunction_reduce(__pyx_CyFunctionObject *m, PyObject *args) -{ - PyObject *result = NULL; - CYTHON_UNUSED_VAR(args); - __Pyx_BEGIN_CRITICAL_SECTION(m); - Py_INCREF(m->func_qualname); - result = m->func_qualname; - __Pyx_END_CRITICAL_SECTION(); - return result; -} -static PyMethodDef __pyx_CyFunction_methods[] = { - {"__reduce__", (PyCFunction)__Pyx_CyFunction_reduce, METH_VARARGS, 0}, - {0, 0, 0, 0} -}; -#if CYTHON_COMPILING_IN_LIMITED_API -#define __Pyx_CyFunction_weakreflist(cyfunc) ((cyfunc)->func_weakreflist) -#else -#define __Pyx_CyFunction_weakreflist(cyfunc) (((PyCFunctionObject*)cyfunc)->m_weakreflist) -#endif -static PyObject *__Pyx_CyFunction_Init(__pyx_CyFunctionObject *op, PyMethodDef *ml, int flags, PyObject* qualname, - PyObject *closure, PyObject *module, PyObject* globals, PyObject* code) { -#if !CYTHON_COMPILING_IN_LIMITED_API - PyCFunctionObject *cf = (PyCFunctionObject*) op; -#endif - if (unlikely(op == NULL)) - return NULL; -#if CYTHON_COMPILING_IN_LIMITED_API - op->func = PyCFunction_NewEx(ml, (PyObject*)op, module); - if (unlikely(!op->func)) return NULL; -#endif - op->flags = flags; - __Pyx_CyFunction_weakreflist(op) = NULL; -#if !CYTHON_COMPILING_IN_LIMITED_API - cf->m_ml = ml; - cf->m_self = (PyObject *) op; -#endif - Py_XINCREF(closure); - op->func_closure = closure; -#if !CYTHON_COMPILING_IN_LIMITED_API - Py_XINCREF(module); - cf->m_module = module; -#endif - op->func_dict = NULL; - op->func_name = NULL; - Py_INCREF(qualname); - op->func_qualname = qualname; - op->func_doc = NULL; -#if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API - op->func_classobj = NULL; -#else - ((PyCMethodObject*)op)->mm_class = NULL; -#endif - op->func_globals = globals; - Py_INCREF(op->func_globals); - Py_XINCREF(code); - op->func_code = code; - op->defaults = NULL; - op->defaults_tuple = NULL; - op->defaults_kwdict = NULL; - op->defaults_getter = NULL; - op->func_annotations = NULL; - op->func_is_coroutine = NULL; -#if CYTHON_METH_FASTCALL - switch (ml->ml_flags & (METH_VARARGS | METH_FASTCALL | METH_NOARGS | METH_O | METH_KEYWORDS | METH_METHOD)) { - case METH_NOARGS: - __Pyx_CyFunction_func_vectorcall(op) = __Pyx_CyFunction_Vectorcall_NOARGS; - break; - case METH_O: - __Pyx_CyFunction_func_vectorcall(op) = __Pyx_CyFunction_Vectorcall_O; - break; - case METH_METHOD | METH_FASTCALL | METH_KEYWORDS: - __Pyx_CyFunction_func_vectorcall(op) = __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS_METHOD; - break; - case METH_FASTCALL | METH_KEYWORDS: - __Pyx_CyFunction_func_vectorcall(op) = __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS; - break; - case METH_VARARGS | METH_KEYWORDS: - __Pyx_CyFunction_func_vectorcall(op) = NULL; - break; - default: - PyErr_SetString(PyExc_SystemError, "Bad call flags for CyFunction"); - Py_DECREF(op); - return NULL; - } -#endif - return (PyObject *) op; -} -static int -__Pyx_CyFunction_clear(__pyx_CyFunctionObject *m) -{ - Py_CLEAR(m->func_closure); -#if CYTHON_COMPILING_IN_LIMITED_API - Py_CLEAR(m->func); -#else - Py_CLEAR(((PyCFunctionObject*)m)->m_module); -#endif - Py_CLEAR(m->func_dict); - Py_CLEAR(m->func_name); - Py_CLEAR(m->func_qualname); - Py_CLEAR(m->func_doc); - Py_CLEAR(m->func_globals); - Py_CLEAR(m->func_code); -#if !CYTHON_COMPILING_IN_LIMITED_API -#if PY_VERSION_HEX < 0x030900B1 - Py_CLEAR(__Pyx_CyFunction_GetClassObj(m)); -#else - { - PyObject *cls = (PyObject*) ((PyCMethodObject *) (m))->mm_class; - ((PyCMethodObject *) (m))->mm_class = NULL; - Py_XDECREF(cls); - } -#endif -#endif - Py_CLEAR(m->defaults_tuple); - Py_CLEAR(m->defaults_kwdict); - Py_CLEAR(m->func_annotations); - Py_CLEAR(m->func_is_coroutine); - Py_CLEAR(m->defaults); - return 0; -} -static void __Pyx__CyFunction_dealloc(__pyx_CyFunctionObject *m) -{ - if (__Pyx_CyFunction_weakreflist(m) != NULL) - PyObject_ClearWeakRefs((PyObject *) m); - __Pyx_CyFunction_clear(m); - __Pyx_PyHeapTypeObject_GC_Del(m); -} -static void __Pyx_CyFunction_dealloc(__pyx_CyFunctionObject *m) -{ - PyObject_GC_UnTrack(m); - __Pyx__CyFunction_dealloc(m); -} -static int __Pyx_CyFunction_traverse(__pyx_CyFunctionObject *m, visitproc visit, void *arg) -{ - { - int e = __Pyx_call_type_traverse((PyObject*)m, 1, visit, arg); - if (e) return e; - } - Py_VISIT(m->func_closure); -#if CYTHON_COMPILING_IN_LIMITED_API - Py_VISIT(m->func); -#else - Py_VISIT(((PyCFunctionObject*)m)->m_module); -#endif - Py_VISIT(m->func_dict); - __Pyx_VISIT_CONST(m->func_name); - __Pyx_VISIT_CONST(m->func_qualname); - Py_VISIT(m->func_doc); - Py_VISIT(m->func_globals); - __Pyx_VISIT_CONST(m->func_code); -#if !CYTHON_COMPILING_IN_LIMITED_API - Py_VISIT(__Pyx_CyFunction_GetClassObj(m)); -#endif - Py_VISIT(m->defaults_tuple); - Py_VISIT(m->defaults_kwdict); - Py_VISIT(m->func_is_coroutine); - Py_VISIT(m->defaults); - return 0; -} -static PyObject* -__Pyx_CyFunction_repr(__pyx_CyFunctionObject *op) -{ - PyObject *repr; - __Pyx_BEGIN_CRITICAL_SECTION(op); - repr = PyUnicode_FromFormat("", - op->func_qualname, (void *)op); - __Pyx_END_CRITICAL_SECTION(); - return repr; -} -static PyObject * __Pyx_CyFunction_CallMethod(PyObject *func, PyObject *self, PyObject *arg, PyObject *kw) { -#if CYTHON_COMPILING_IN_LIMITED_API - PyObject *f = ((__pyx_CyFunctionObject*)func)->func; - PyCFunction meth; - int flags; - meth = PyCFunction_GetFunction(f); - if (unlikely(!meth)) return NULL; - flags = PyCFunction_GetFlags(f); - if (unlikely(flags < 0)) return NULL; -#else - PyCFunctionObject* f = (PyCFunctionObject*)func; - PyCFunction meth = f->m_ml->ml_meth; - int flags = f->m_ml->ml_flags; -#endif - Py_ssize_t size; - switch (flags & (METH_VARARGS | METH_KEYWORDS | METH_NOARGS | METH_O)) { - case METH_VARARGS: - if (likely(kw == NULL || PyDict_Size(kw) == 0)) - return (*meth)(self, arg); - break; - case METH_VARARGS | METH_KEYWORDS: - return (*(PyCFunctionWithKeywords)(void(*)(void))meth)(self, arg, kw); - case METH_NOARGS: - if (likely(kw == NULL || PyDict_Size(kw) == 0)) { -#if CYTHON_ASSUME_SAFE_SIZE - size = PyTuple_GET_SIZE(arg); -#else - size = PyTuple_Size(arg); - if (unlikely(size < 0)) return NULL; -#endif - if (likely(size == 0)) - return (*meth)(self, NULL); - __Pyx_CyFunction_raise_argument_count_error( - (__pyx_CyFunctionObject*)func, - "takes no arguments", size); - return NULL; - } - break; - case METH_O: - if (likely(kw == NULL || PyDict_Size(kw) == 0)) { -#if CYTHON_ASSUME_SAFE_SIZE - size = PyTuple_GET_SIZE(arg); -#else - size = PyTuple_Size(arg); - if (unlikely(size < 0)) return NULL; -#endif - if (likely(size == 1)) { - PyObject *result, *arg0; - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - arg0 = PyTuple_GET_ITEM(arg, 0); - #else - arg0 = __Pyx_PySequence_ITEM(arg, 0); if (unlikely(!arg0)) return NULL; - #endif - result = (*meth)(self, arg0); - #if !(CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS) - Py_DECREF(arg0); - #endif - return result; - } - __Pyx_CyFunction_raise_argument_count_error( - (__pyx_CyFunctionObject*)func, - "takes exactly one argument", size); - return NULL; - } - break; - default: - PyErr_SetString(PyExc_SystemError, "Bad call flags for CyFunction"); - return NULL; - } - __Pyx_CyFunction_raise_type_error( - (__pyx_CyFunctionObject*)func, "takes no keyword arguments"); - return NULL; -} -static CYTHON_INLINE PyObject *__Pyx_CyFunction_Call(PyObject *func, PyObject *arg, PyObject *kw) { - PyObject *self, *result; -#if CYTHON_COMPILING_IN_LIMITED_API - self = PyCFunction_GetSelf(((__pyx_CyFunctionObject*)func)->func); - if (unlikely(!self) && PyErr_Occurred()) return NULL; -#else - self = ((PyCFunctionObject*)func)->m_self; -#endif - result = __Pyx_CyFunction_CallMethod(func, self, arg, kw); - return result; -} -static PyObject *__Pyx_CyFunction_CallAsMethod(PyObject *func, PyObject *args, PyObject *kw) { - PyObject *result; - __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *) func; -#if CYTHON_METH_FASTCALL && (CYTHON_VECTORCALL || CYTHON_BACKPORT_VECTORCALL) - __pyx_vectorcallfunc vc = __Pyx_CyFunction_func_vectorcall(cyfunc); - if (vc) { -#if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE - return __Pyx_PyVectorcall_FastCallDict(func, vc, &PyTuple_GET_ITEM(args, 0), (size_t)PyTuple_GET_SIZE(args), kw); -#else - (void) &__Pyx_PyVectorcall_FastCallDict; - return PyVectorcall_Call(func, args, kw); -#endif - } -#endif - if ((cyfunc->flags & __Pyx_CYFUNCTION_CCLASS) && !(cyfunc->flags & __Pyx_CYFUNCTION_STATICMETHOD)) { - Py_ssize_t argc; - PyObject *new_args; - PyObject *self; -#if CYTHON_ASSUME_SAFE_SIZE - argc = PyTuple_GET_SIZE(args); -#else - argc = PyTuple_Size(args); - if (unlikely(argc < 0)) return NULL; -#endif - new_args = PyTuple_GetSlice(args, 1, argc); - if (unlikely(!new_args)) - return NULL; - self = PyTuple_GetItem(args, 0); - if (unlikely(!self)) { - Py_DECREF(new_args); - PyErr_Format(PyExc_TypeError, - "unbound method %.200S() needs an argument", - cyfunc->func_qualname); - return NULL; - } - result = __Pyx_CyFunction_CallMethod(func, self, new_args, kw); - Py_DECREF(new_args); - } else { - result = __Pyx_CyFunction_Call(func, args, kw); - } - return result; -} -#if CYTHON_METH_FASTCALL && (CYTHON_VECTORCALL || CYTHON_BACKPORT_VECTORCALL) -static CYTHON_INLINE int __Pyx_CyFunction_Vectorcall_CheckArgs(__pyx_CyFunctionObject *cyfunc, Py_ssize_t nargs, PyObject *kwnames) -{ - int ret = 0; - if ((cyfunc->flags & __Pyx_CYFUNCTION_CCLASS) && !(cyfunc->flags & __Pyx_CYFUNCTION_STATICMETHOD)) { - if (unlikely(nargs < 1)) { - __Pyx_CyFunction_raise_type_error( - cyfunc, "needs an argument"); - return -1; - } - ret = 1; - } - if (unlikely(kwnames) && unlikely(__Pyx_PyTuple_GET_SIZE(kwnames))) { - __Pyx_CyFunction_raise_type_error( - cyfunc, "takes no keyword arguments"); - return -1; - } - return ret; -} -static PyObject * __Pyx_CyFunction_Vectorcall_NOARGS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) -{ - __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *)func; -#if CYTHON_BACKPORT_VECTORCALL - Py_ssize_t nargs = (Py_ssize_t)nargsf; -#else - Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); -#endif - PyObject *self; -#if CYTHON_COMPILING_IN_LIMITED_API - PyCFunction meth = PyCFunction_GetFunction(cyfunc->func); - if (unlikely(!meth)) return NULL; -#else - PyCFunction meth = ((PyCFunctionObject*)cyfunc)->m_ml->ml_meth; -#endif - switch (__Pyx_CyFunction_Vectorcall_CheckArgs(cyfunc, nargs, kwnames)) { - case 1: - self = args[0]; - args += 1; - nargs -= 1; - break; - case 0: -#if CYTHON_COMPILING_IN_LIMITED_API - self = PyCFunction_GetSelf(((__pyx_CyFunctionObject*)cyfunc)->func); - if (unlikely(!self) && PyErr_Occurred()) return NULL; -#else - self = ((PyCFunctionObject*)cyfunc)->m_self; -#endif - break; - default: - return NULL; - } - if (unlikely(nargs != 0)) { - __Pyx_CyFunction_raise_argument_count_error( - cyfunc, "takes no arguments", nargs); - return NULL; - } - return meth(self, NULL); -} -static PyObject * __Pyx_CyFunction_Vectorcall_O(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) -{ - __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *)func; -#if CYTHON_BACKPORT_VECTORCALL - Py_ssize_t nargs = (Py_ssize_t)nargsf; -#else - Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); -#endif - PyObject *self; -#if CYTHON_COMPILING_IN_LIMITED_API - PyCFunction meth = PyCFunction_GetFunction(cyfunc->func); - if (unlikely(!meth)) return NULL; -#else - PyCFunction meth = ((PyCFunctionObject*)cyfunc)->m_ml->ml_meth; -#endif - switch (__Pyx_CyFunction_Vectorcall_CheckArgs(cyfunc, nargs, kwnames)) { - case 1: - self = args[0]; - args += 1; - nargs -= 1; - break; - case 0: -#if CYTHON_COMPILING_IN_LIMITED_API - self = PyCFunction_GetSelf(((__pyx_CyFunctionObject*)cyfunc)->func); - if (unlikely(!self) && PyErr_Occurred()) return NULL; -#else - self = ((PyCFunctionObject*)cyfunc)->m_self; -#endif - break; - default: - return NULL; - } - if (unlikely(nargs != 1)) { - __Pyx_CyFunction_raise_argument_count_error( - cyfunc, "takes exactly one argument", nargs); - return NULL; - } - return meth(self, args[0]); -} -static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) -{ - __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *)func; -#if CYTHON_BACKPORT_VECTORCALL - Py_ssize_t nargs = (Py_ssize_t)nargsf; -#else - Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); -#endif - PyObject *self; -#if CYTHON_COMPILING_IN_LIMITED_API - PyCFunction meth = PyCFunction_GetFunction(cyfunc->func); - if (unlikely(!meth)) return NULL; -#else - PyCFunction meth = ((PyCFunctionObject*)cyfunc)->m_ml->ml_meth; -#endif - switch (__Pyx_CyFunction_Vectorcall_CheckArgs(cyfunc, nargs, NULL)) { - case 1: - self = args[0]; - args += 1; - nargs -= 1; - break; - case 0: -#if CYTHON_COMPILING_IN_LIMITED_API - self = PyCFunction_GetSelf(((__pyx_CyFunctionObject*)cyfunc)->func); - if (unlikely(!self) && PyErr_Occurred()) return NULL; -#else - self = ((PyCFunctionObject*)cyfunc)->m_self; -#endif - break; - default: - return NULL; - } - return ((__Pyx_PyCFunctionFastWithKeywords)(void(*)(void))meth)(self, args, nargs, kwnames); -} -static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS_METHOD(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) -{ - __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *)func; - PyTypeObject *cls = (PyTypeObject *) __Pyx_CyFunction_GetClassObj(cyfunc); -#if CYTHON_BACKPORT_VECTORCALL - Py_ssize_t nargs = (Py_ssize_t)nargsf; -#else - Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); -#endif - PyObject *self; -#if CYTHON_COMPILING_IN_LIMITED_API - PyCFunction meth = PyCFunction_GetFunction(cyfunc->func); - if (unlikely(!meth)) return NULL; -#else - PyCFunction meth = ((PyCFunctionObject*)cyfunc)->m_ml->ml_meth; -#endif - switch (__Pyx_CyFunction_Vectorcall_CheckArgs(cyfunc, nargs, NULL)) { - case 1: - self = args[0]; - args += 1; - nargs -= 1; - break; - case 0: -#if CYTHON_COMPILING_IN_LIMITED_API - self = PyCFunction_GetSelf(((__pyx_CyFunctionObject*)cyfunc)->func); - if (unlikely(!self) && PyErr_Occurred()) return NULL; -#else - self = ((PyCFunctionObject*)cyfunc)->m_self; -#endif - break; - default: - return NULL; - } - return ((__Pyx_PyCMethod)(void(*)(void))meth)(self, cls, args, (size_t)nargs, kwnames); -} -#endif -static PyType_Slot __pyx_CyFunctionType_slots[] = { - {Py_tp_dealloc, (void *)__Pyx_CyFunction_dealloc}, - {Py_tp_repr, (void *)__Pyx_CyFunction_repr}, - {Py_tp_call, (void *)__Pyx_CyFunction_CallAsMethod}, - {Py_tp_traverse, (void *)__Pyx_CyFunction_traverse}, - {Py_tp_clear, (void *)__Pyx_CyFunction_clear}, - {Py_tp_methods, (void *)__pyx_CyFunction_methods}, - {Py_tp_members, (void *)__pyx_CyFunction_members}, - {Py_tp_getset, (void *)__pyx_CyFunction_getsets}, - {Py_tp_descr_get, (void *)__Pyx_PyMethod_New}, - {0, 0}, -}; -static PyType_Spec __pyx_CyFunctionType_spec = { - __PYX_TYPE_MODULE_PREFIX "cython_function_or_method", - sizeof(__pyx_CyFunctionObject), - 0, -#ifdef Py_TPFLAGS_METHOD_DESCRIPTOR - Py_TPFLAGS_METHOD_DESCRIPTOR | -#endif -#if CYTHON_METH_FASTCALL -#if defined(Py_TPFLAGS_HAVE_VECTORCALL) - Py_TPFLAGS_HAVE_VECTORCALL | -#elif defined(_Py_TPFLAGS_HAVE_VECTORCALL) - _Py_TPFLAGS_HAVE_VECTORCALL | -#endif -#endif // CYTHON_METH_FASTCALL -#if PY_VERSION_HEX >= 0x030A0000 - Py_TPFLAGS_IMMUTABLETYPE | -#endif - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, - __pyx_CyFunctionType_slots -}; -static int __pyx_CyFunction_init(PyObject *module) { - __pyx_mstatetype *mstate = __Pyx_PyModule_GetState(module); - mstate->__pyx_CyFunctionType = __Pyx_FetchCommonTypeFromSpec( - mstate->__pyx_CommonTypesMetaclassType, module, &__pyx_CyFunctionType_spec, NULL); - if (unlikely(mstate->__pyx_CyFunctionType == NULL)) { - return -1; - } - return 0; -} -static CYTHON_INLINE PyObject *__Pyx_CyFunction_InitDefaults(PyObject *func, PyTypeObject *defaults_type) { - __pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) func; - m->defaults = PyObject_CallObject((PyObject*)defaults_type, NULL); // _PyObject_New(defaults_type); - if (unlikely(!m->defaults)) - return NULL; - return m->defaults; -} -static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsTuple(PyObject *func, PyObject *tuple) { - __pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) func; - m->defaults_tuple = tuple; - Py_INCREF(tuple); -} -static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsKwDict(PyObject *func, PyObject *dict) { - __pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) func; - m->defaults_kwdict = dict; - Py_INCREF(dict); -} -static CYTHON_INLINE void __Pyx_CyFunction_SetAnnotationsDict(PyObject *func, PyObject *dict) { - __pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) func; - m->func_annotations = dict; - Py_INCREF(dict); -} - -/* CythonFunction */ -static PyObject *__Pyx_CyFunction_New(PyMethodDef *ml, int flags, PyObject* qualname, - PyObject *closure, PyObject *module, PyObject* globals, PyObject* code) { - PyObject *op = __Pyx_CyFunction_Init( - PyObject_GC_New(__pyx_CyFunctionObject, __pyx_mstate_global->__pyx_CyFunctionType), - ml, flags, qualname, closure, module, globals, code - ); - if (likely(op)) { - PyObject_GC_Track(op); - } - return op; -} - -/* CLineInTraceback */ -#if CYTHON_CLINE_IN_TRACEBACK && CYTHON_CLINE_IN_TRACEBACK_RUNTIME -static int __Pyx_CLineForTraceback(PyThreadState *tstate, int c_line) { - PyObject *use_cline; - PyObject *ptype, *pvalue, *ptraceback; -#if CYTHON_COMPILING_IN_CPYTHON - PyObject **cython_runtime_dict; -#endif - CYTHON_MAYBE_UNUSED_VAR(tstate); - if (unlikely(!__pyx_mstate_global->__pyx_cython_runtime)) { - return c_line; - } - __Pyx_ErrFetchInState(tstate, &ptype, &pvalue, &ptraceback); -#if CYTHON_COMPILING_IN_CPYTHON - cython_runtime_dict = _PyObject_GetDictPtr(__pyx_mstate_global->__pyx_cython_runtime); - if (likely(cython_runtime_dict)) { - __Pyx_BEGIN_CRITICAL_SECTION(*cython_runtime_dict); - __PYX_PY_DICT_LOOKUP_IF_MODIFIED( - use_cline, *cython_runtime_dict, - __Pyx_PyDict_GetItemStr(*cython_runtime_dict, __pyx_mstate_global->__pyx_n_u_cline_in_traceback)) - Py_XINCREF(use_cline); - __Pyx_END_CRITICAL_SECTION(); - } else -#endif - { - PyObject *use_cline_obj = __Pyx_PyObject_GetAttrStrNoError(__pyx_mstate_global->__pyx_cython_runtime, __pyx_mstate_global->__pyx_n_u_cline_in_traceback); - if (use_cline_obj) { - use_cline = PyObject_Not(use_cline_obj) ? Py_False : Py_True; - Py_INCREF(use_cline); - Py_DECREF(use_cline_obj); - } else { - PyErr_Clear(); - use_cline = NULL; - } - } - if (!use_cline) { - c_line = 0; - (void) PyObject_SetAttr(__pyx_mstate_global->__pyx_cython_runtime, __pyx_mstate_global->__pyx_n_u_cline_in_traceback, Py_False); - } - else if (use_cline == Py_False || (use_cline != Py_True && PyObject_Not(use_cline) != 0)) { - c_line = 0; - } - Py_XDECREF(use_cline); - __Pyx_ErrRestoreInState(tstate, ptype, pvalue, ptraceback); - return c_line; -} -#endif - -/* CodeObjectCache */ -static int __pyx_bisect_code_objects(__Pyx_CodeObjectCacheEntry* entries, int count, int code_line) { - int start = 0, mid = 0, end = count - 1; - if (end >= 0 && code_line > entries[end].code_line) { - return count; - } - while (start < end) { - mid = start + (end - start) / 2; - if (code_line < entries[mid].code_line) { - end = mid; - } else if (code_line > entries[mid].code_line) { - start = mid + 1; - } else { - return mid; - } - } - if (code_line <= entries[mid].code_line) { - return mid; - } else { - return mid + 1; - } -} -static __Pyx_CachedCodeObjectType *__pyx__find_code_object(struct __Pyx_CodeObjectCache *code_cache, int code_line) { - __Pyx_CachedCodeObjectType* code_object; - int pos; - if (unlikely(!code_line) || unlikely(!code_cache->entries)) { - return NULL; - } - pos = __pyx_bisect_code_objects(code_cache->entries, code_cache->count, code_line); - if (unlikely(pos >= code_cache->count) || unlikely(code_cache->entries[pos].code_line != code_line)) { - return NULL; - } - code_object = code_cache->entries[pos].code_object; - Py_INCREF(code_object); - return code_object; -} -static __Pyx_CachedCodeObjectType *__pyx_find_code_object(int code_line) { -#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING && !CYTHON_ATOMICS - (void)__pyx__find_code_object; - return NULL; // Most implementation should have atomics. But otherwise, don't make it thread-safe, just miss. -#else - struct __Pyx_CodeObjectCache *code_cache = &__pyx_mstate_global->__pyx_code_cache; -#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING - __pyx_nonatomic_int_type old_count = __pyx_atomic_incr_acq_rel(&code_cache->accessor_count); - if (old_count < 0) { - __pyx_atomic_decr_acq_rel(&code_cache->accessor_count); - return NULL; - } -#endif - __Pyx_CachedCodeObjectType *result = __pyx__find_code_object(code_cache, code_line); -#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING - __pyx_atomic_decr_acq_rel(&code_cache->accessor_count); -#endif - return result; -#endif -} -static void __pyx__insert_code_object(struct __Pyx_CodeObjectCache *code_cache, int code_line, __Pyx_CachedCodeObjectType* code_object) -{ - int pos, i; - __Pyx_CodeObjectCacheEntry* entries = code_cache->entries; - if (unlikely(!code_line)) { - return; - } - if (unlikely(!entries)) { - entries = (__Pyx_CodeObjectCacheEntry*)PyMem_Malloc(64*sizeof(__Pyx_CodeObjectCacheEntry)); - if (likely(entries)) { - code_cache->entries = entries; - code_cache->max_count = 64; - code_cache->count = 1; - entries[0].code_line = code_line; - entries[0].code_object = code_object; - Py_INCREF(code_object); - } - return; - } - pos = __pyx_bisect_code_objects(code_cache->entries, code_cache->count, code_line); - if ((pos < code_cache->count) && unlikely(code_cache->entries[pos].code_line == code_line)) { - __Pyx_CachedCodeObjectType* tmp = entries[pos].code_object; - entries[pos].code_object = code_object; - Py_INCREF(code_object); - Py_DECREF(tmp); - return; - } - if (code_cache->count == code_cache->max_count) { - int new_max = code_cache->max_count + 64; - entries = (__Pyx_CodeObjectCacheEntry*)PyMem_Realloc( - code_cache->entries, ((size_t)new_max) * sizeof(__Pyx_CodeObjectCacheEntry)); - if (unlikely(!entries)) { - return; - } - code_cache->entries = entries; - code_cache->max_count = new_max; - } - for (i=code_cache->count; i>pos; i--) { - entries[i] = entries[i-1]; - } - entries[pos].code_line = code_line; - entries[pos].code_object = code_object; - code_cache->count++; - Py_INCREF(code_object); -} -static void __pyx_insert_code_object(int code_line, __Pyx_CachedCodeObjectType* code_object) { -#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING && !CYTHON_ATOMICS - (void)__pyx__insert_code_object; - return; // Most implementation should have atomics. But otherwise, don't make it thread-safe, just fail. -#else - struct __Pyx_CodeObjectCache *code_cache = &__pyx_mstate_global->__pyx_code_cache; -#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING - __pyx_nonatomic_int_type expected = 0; - if (!__pyx_atomic_int_cmp_exchange(&code_cache->accessor_count, &expected, INT_MIN)) { - return; - } -#endif - __pyx__insert_code_object(code_cache, code_line, code_object); -#if CYTHON_COMPILING_IN_CPYTHON_FREETHREADING - __pyx_atomic_sub(&code_cache->accessor_count, INT_MIN); -#endif -#endif -} - -/* AddTraceback */ -#include "compile.h" -#include "frameobject.h" -#include "traceback.h" -#if PY_VERSION_HEX >= 0x030b00a6 && !CYTHON_COMPILING_IN_LIMITED_API && !defined(PYPY_VERSION) - #ifndef Py_BUILD_CORE - #define Py_BUILD_CORE 1 - #endif - #include "internal/pycore_frame.h" -#endif -#if CYTHON_COMPILING_IN_LIMITED_API -static PyObject *__Pyx_PyCode_Replace_For_AddTraceback(PyObject *code, PyObject *scratch_dict, - PyObject *firstlineno, PyObject *name) { - PyObject *replace = NULL; - if (unlikely(PyDict_SetItemString(scratch_dict, "co_firstlineno", firstlineno))) return NULL; - if (unlikely(PyDict_SetItemString(scratch_dict, "co_name", name))) return NULL; - replace = PyObject_GetAttrString(code, "replace"); - if (likely(replace)) { - PyObject *result = PyObject_Call(replace, __pyx_mstate_global->__pyx_empty_tuple, scratch_dict); - Py_DECREF(replace); - return result; - } - PyErr_Clear(); - return NULL; -} -static void __Pyx_AddTraceback(const char *funcname, int c_line, - int py_line, const char *filename) { - PyObject *code_object = NULL, *py_py_line = NULL, *py_funcname = NULL, *dict = NULL; - PyObject *replace = NULL, *getframe = NULL, *frame = NULL; - PyObject *exc_type, *exc_value, *exc_traceback; - int success = 0; - if (c_line) { - (void) __pyx_cfilenm; - (void) __Pyx_CLineForTraceback(__Pyx_PyThreadState_Current, c_line); - } - PyErr_Fetch(&exc_type, &exc_value, &exc_traceback); - code_object = __pyx_find_code_object(c_line ? -c_line : py_line); - if (!code_object) { - code_object = Py_CompileString("_getframe()", filename, Py_eval_input); - if (unlikely(!code_object)) goto bad; - py_py_line = PyLong_FromLong(py_line); - if (unlikely(!py_py_line)) goto bad; - py_funcname = PyUnicode_FromString(funcname); - if (unlikely(!py_funcname)) goto bad; - dict = PyDict_New(); - if (unlikely(!dict)) goto bad; - { - PyObject *old_code_object = code_object; - code_object = __Pyx_PyCode_Replace_For_AddTraceback(code_object, dict, py_py_line, py_funcname); - Py_DECREF(old_code_object); - } - if (unlikely(!code_object)) goto bad; - __pyx_insert_code_object(c_line ? -c_line : py_line, code_object); - } else { - dict = PyDict_New(); - } - getframe = PySys_GetObject("_getframe"); - if (unlikely(!getframe)) goto bad; - if (unlikely(PyDict_SetItemString(dict, "_getframe", getframe))) goto bad; - frame = PyEval_EvalCode(code_object, dict, dict); - if (unlikely(!frame) || frame == Py_None) goto bad; - success = 1; - bad: - PyErr_Restore(exc_type, exc_value, exc_traceback); - Py_XDECREF(code_object); - Py_XDECREF(py_py_line); - Py_XDECREF(py_funcname); - Py_XDECREF(dict); - Py_XDECREF(replace); - if (success) { - PyTraceBack_Here( - (struct _frame*)frame); - } - Py_XDECREF(frame); -} -#else -static PyCodeObject* __Pyx_CreateCodeObjectForTraceback( - const char *funcname, int c_line, - int py_line, const char *filename) { - PyCodeObject *py_code = NULL; - PyObject *py_funcname = NULL; - if (c_line) { - py_funcname = PyUnicode_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, c_line); - if (!py_funcname) goto bad; - funcname = PyUnicode_AsUTF8(py_funcname); - if (!funcname) goto bad; - } - py_code = PyCode_NewEmpty(filename, funcname, py_line); - Py_XDECREF(py_funcname); - return py_code; -bad: - Py_XDECREF(py_funcname); - return NULL; -} -static void __Pyx_AddTraceback(const char *funcname, int c_line, - int py_line, const char *filename) { - PyCodeObject *py_code = 0; - PyFrameObject *py_frame = 0; - PyThreadState *tstate = __Pyx_PyThreadState_Current; - PyObject *ptype, *pvalue, *ptraceback; - if (c_line) { - c_line = __Pyx_CLineForTraceback(tstate, c_line); - } - py_code = __pyx_find_code_object(c_line ? -c_line : py_line); - if (!py_code) { - __Pyx_ErrFetchInState(tstate, &ptype, &pvalue, &ptraceback); - py_code = __Pyx_CreateCodeObjectForTraceback( - funcname, c_line, py_line, filename); - if (!py_code) { - /* If the code object creation fails, then we should clear the - fetched exception references and propagate the new exception */ - Py_XDECREF(ptype); - Py_XDECREF(pvalue); - Py_XDECREF(ptraceback); - goto bad; - } - __Pyx_ErrRestoreInState(tstate, ptype, pvalue, ptraceback); - __pyx_insert_code_object(c_line ? -c_line : py_line, py_code); - } - py_frame = PyFrame_New( - tstate, /*PyThreadState *tstate,*/ - py_code, /*PyCodeObject *code,*/ - __pyx_mstate_global->__pyx_d, /*PyObject *globals,*/ - 0 /*PyObject *locals*/ - ); - if (!py_frame) goto bad; - __Pyx_PyFrame_SetLineNumber(py_frame, py_line); - PyTraceBack_Here(py_frame); -bad: - Py_XDECREF(py_code); - Py_XDECREF(py_frame); -} -#endif - -/* Declarations */ -#if CYTHON_CCOMPLEX && (1) && (!0 || __cplusplus) - #ifdef __cplusplus - static CYTHON_INLINE __pyx_t_double_complex __pyx_t_double_complex_from_parts(double x, double y) { - return ::std::complex< double >(x, y); - } - #else - static CYTHON_INLINE __pyx_t_double_complex __pyx_t_double_complex_from_parts(double x, double y) { - return x + y*(__pyx_t_double_complex)_Complex_I; - } - #endif -#else - static CYTHON_INLINE __pyx_t_double_complex __pyx_t_double_complex_from_parts(double x, double y) { - __pyx_t_double_complex z; - z.real = x; - z.imag = y; - return z; - } -#endif - -/* Arithmetic */ -#if CYTHON_CCOMPLEX && (1) && (!0 || __cplusplus) -#else - static CYTHON_INLINE int __Pyx_c_eq_double(__pyx_t_double_complex a, __pyx_t_double_complex b) { - return (a.real == b.real) && (a.imag == b.imag); - } - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_sum_double(__pyx_t_double_complex a, __pyx_t_double_complex b) { - __pyx_t_double_complex z; - z.real = a.real + b.real; - z.imag = a.imag + b.imag; - return z; - } - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_diff_double(__pyx_t_double_complex a, __pyx_t_double_complex b) { - __pyx_t_double_complex z; - z.real = a.real - b.real; - z.imag = a.imag - b.imag; - return z; - } - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_prod_double(__pyx_t_double_complex a, __pyx_t_double_complex b) { - __pyx_t_double_complex z; - z.real = a.real * b.real - a.imag * b.imag; - z.imag = a.real * b.imag + a.imag * b.real; - return z; - } - #if 1 - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_quot_double(__pyx_t_double_complex a, __pyx_t_double_complex b) { - if (b.imag == 0) { - return __pyx_t_double_complex_from_parts(a.real / b.real, a.imag / b.real); - } else if (fabs(b.real) >= fabs(b.imag)) { - if (b.real == 0 && b.imag == 0) { - return __pyx_t_double_complex_from_parts(a.real / b.real, a.imag / b.imag); - } else { - double r = b.imag / b.real; - double s = (double)(1.0) / (b.real + b.imag * r); - return __pyx_t_double_complex_from_parts( - (a.real + a.imag * r) * s, (a.imag - a.real * r) * s); - } - } else { - double r = b.real / b.imag; - double s = (double)(1.0) / (b.imag + b.real * r); - return __pyx_t_double_complex_from_parts( - (a.real * r + a.imag) * s, (a.imag * r - a.real) * s); - } - } - #else - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_quot_double(__pyx_t_double_complex a, __pyx_t_double_complex b) { - if (b.imag == 0) { - return __pyx_t_double_complex_from_parts(a.real / b.real, a.imag / b.real); - } else { - double denom = b.real * b.real + b.imag * b.imag; - return __pyx_t_double_complex_from_parts( - (a.real * b.real + a.imag * b.imag) / denom, - (a.imag * b.real - a.real * b.imag) / denom); - } - } - #endif - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_neg_double(__pyx_t_double_complex a) { - __pyx_t_double_complex z; - z.real = -a.real; - z.imag = -a.imag; - return z; - } - static CYTHON_INLINE int __Pyx_c_is_zero_double(__pyx_t_double_complex a) { - return (a.real == 0) && (a.imag == 0); - } - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_conj_double(__pyx_t_double_complex a) { - __pyx_t_double_complex z; - z.real = a.real; - z.imag = -a.imag; - return z; - } - #if 1 - static CYTHON_INLINE double __Pyx_c_abs_double(__pyx_t_double_complex z) { - #if !defined(HAVE_HYPOT) || defined(_MSC_VER) - return sqrt(z.real*z.real + z.imag*z.imag); - #else - return hypot(z.real, z.imag); - #endif - } - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_pow_double(__pyx_t_double_complex a, __pyx_t_double_complex b) { - __pyx_t_double_complex z; - double r, lnr, theta, z_r, z_theta; - if (b.imag == 0 && b.real == (int)b.real) { - if (b.real < 0) { - double denom = a.real * a.real + a.imag * a.imag; - a.real = a.real / denom; - a.imag = -a.imag / denom; - b.real = -b.real; - } - switch ((int)b.real) { - case 0: - z.real = 1; - z.imag = 0; - return z; - case 1: - return a; - case 2: - return __Pyx_c_prod_double(a, a); - case 3: - z = __Pyx_c_prod_double(a, a); - return __Pyx_c_prod_double(z, a); - case 4: - z = __Pyx_c_prod_double(a, a); - return __Pyx_c_prod_double(z, z); - } - } - if (a.imag == 0) { - if (a.real == 0) { - return a; - } else if ((b.imag == 0) && (a.real >= 0)) { - z.real = pow(a.real, b.real); - z.imag = 0; - return z; - } else if (a.real > 0) { - r = a.real; - theta = 0; - } else { - r = -a.real; - theta = atan2(0.0, -1.0); - } - } else { - r = __Pyx_c_abs_double(a); - theta = atan2(a.imag, a.real); - } - lnr = log(r); - z_r = exp(lnr * b.real - theta * b.imag); - z_theta = theta * b.real + lnr * b.imag; - z.real = z_r * cos(z_theta); - z.imag = z_r * sin(z_theta); - return z; - } - #endif -#endif - -/* FromPy */ -static __pyx_t_double_complex __Pyx_PyComplex_As___pyx_t_double_complex(PyObject* o) { -#if CYTHON_COMPILING_IN_LIMITED_API - double real=-1.0, imag=-1.0; - real = PyComplex_RealAsDouble(o); - if (unlikely(real == -1.0 && PyErr_Occurred())) goto end; - imag = PyComplex_ImagAsDouble(o); - end: - return __pyx_t_double_complex_from_parts( - (double)real, (double)imag - ); -#else - Py_complex cval; -#if !CYTHON_COMPILING_IN_PYPY && !CYTHON_COMPILING_IN_GRAAL - if (PyComplex_CheckExact(o)) - cval = ((PyComplexObject *)o)->cval; - else -#endif - cval = PyComplex_AsCComplex(o); - return __pyx_t_double_complex_from_parts( - (double)cval.real, - (double)cval.imag); -#endif -} - -/* CIntFromPyVerify */ -#define __PYX_VERIFY_RETURN_INT(target_type, func_type, func_value)\ - __PYX__VERIFY_RETURN_INT(target_type, func_type, func_value, 0) -#define __PYX_VERIFY_RETURN_INT_EXC(target_type, func_type, func_value)\ - __PYX__VERIFY_RETURN_INT(target_type, func_type, func_value, 1) -#define __PYX__VERIFY_RETURN_INT(target_type, func_type, func_value, exc)\ - {\ - func_type value = func_value;\ - if (sizeof(target_type) < sizeof(func_type)) {\ - if (unlikely(value != (func_type) (target_type) value)) {\ - func_type zero = 0;\ - if (exc && unlikely(value == (func_type)-1 && PyErr_Occurred()))\ - return (target_type) -1;\ - if (is_unsigned && unlikely(value < zero))\ - goto raise_neg_overflow;\ - else\ - goto raise_overflow;\ - }\ - }\ - return (target_type) value;\ - } - -/* CIntFromPy */ -static CYTHON_INLINE int __Pyx_PyLong_As_int(PyObject *x) { -#ifdef __Pyx_HAS_GCC_DIAGNOSTIC -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wconversion" -#endif - const int neg_one = (int) -1, const_zero = (int) 0; -#ifdef __Pyx_HAS_GCC_DIAGNOSTIC -#pragma GCC diagnostic pop -#endif - const int is_unsigned = neg_one > const_zero; - if (unlikely(!PyLong_Check(x))) { - int val; - PyObject *tmp = __Pyx_PyNumber_Long(x); - if (!tmp) return (int) -1; - val = __Pyx_PyLong_As_int(tmp); - Py_DECREF(tmp); - return val; - } - if (is_unsigned) { -#if CYTHON_USE_PYLONG_INTERNALS - if (unlikely(__Pyx_PyLong_IsNeg(x))) { - goto raise_neg_overflow; - } else if (__Pyx_PyLong_IsCompact(x)) { - __PYX_VERIFY_RETURN_INT(int, __Pyx_compact_upylong, __Pyx_PyLong_CompactValueUnsigned(x)) - } else { - const digit* digits = __Pyx_PyLong_Digits(x); - assert(__Pyx_PyLong_DigitCount(x) > 1); - switch (__Pyx_PyLong_DigitCount(x)) { - case 2: - if ((8 * sizeof(int) > 1 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(int) >= 2 * PyLong_SHIFT)) { - return (int) (((((int)digits[1]) << PyLong_SHIFT) | (int)digits[0])); - } - } - break; - case 3: - if ((8 * sizeof(int) > 2 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(int) >= 3 * PyLong_SHIFT)) { - return (int) (((((((int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0])); - } - } - break; - case 4: - if ((8 * sizeof(int) > 3 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(int) >= 4 * PyLong_SHIFT)) { - return (int) (((((((((int)digits[3]) << PyLong_SHIFT) | (int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0])); - } - } - break; - } - } -#endif -#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030C00A7 - if (unlikely(Py_SIZE(x) < 0)) { - goto raise_neg_overflow; - } -#else - { - int result = PyObject_RichCompareBool(x, Py_False, Py_LT); - if (unlikely(result < 0)) - return (int) -1; - if (unlikely(result == 1)) - goto raise_neg_overflow; - } -#endif - if ((sizeof(int) <= sizeof(unsigned long))) { - __PYX_VERIFY_RETURN_INT_EXC(int, unsigned long, PyLong_AsUnsignedLong(x)) -#ifdef HAVE_LONG_LONG - } else if ((sizeof(int) <= sizeof(unsigned PY_LONG_LONG))) { - __PYX_VERIFY_RETURN_INT_EXC(int, unsigned PY_LONG_LONG, PyLong_AsUnsignedLongLong(x)) -#endif - } - } else { -#if CYTHON_USE_PYLONG_INTERNALS - if (__Pyx_PyLong_IsCompact(x)) { - __PYX_VERIFY_RETURN_INT(int, __Pyx_compact_pylong, __Pyx_PyLong_CompactValue(x)) - } else { - const digit* digits = __Pyx_PyLong_Digits(x); - assert(__Pyx_PyLong_DigitCount(x) > 1); - switch (__Pyx_PyLong_SignedDigitCount(x)) { - case -2: - if ((8 * sizeof(int) - 1 > 1 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(int, long, -(long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(int) - 1 > 2 * PyLong_SHIFT)) { - return (int) (((int)-1)*(((((int)digits[1]) << PyLong_SHIFT) | (int)digits[0]))); - } - } - break; - case 2: - if ((8 * sizeof(int) > 1 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(int) - 1 > 2 * PyLong_SHIFT)) { - return (int) ((((((int)digits[1]) << PyLong_SHIFT) | (int)digits[0]))); - } - } - break; - case -3: - if ((8 * sizeof(int) - 1 > 2 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(int, long, -(long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(int) - 1 > 3 * PyLong_SHIFT)) { - return (int) (((int)-1)*(((((((int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0]))); - } - } - break; - case 3: - if ((8 * sizeof(int) > 2 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(int) - 1 > 3 * PyLong_SHIFT)) { - return (int) ((((((((int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0]))); - } - } - break; - case -4: - if ((8 * sizeof(int) - 1 > 3 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(int, long, -(long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(int) - 1 > 4 * PyLong_SHIFT)) { - return (int) (((int)-1)*(((((((((int)digits[3]) << PyLong_SHIFT) | (int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0]))); - } - } - break; - case 4: - if ((8 * sizeof(int) > 3 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(int) - 1 > 4 * PyLong_SHIFT)) { - return (int) ((((((((((int)digits[3]) << PyLong_SHIFT) | (int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0]))); - } - } - break; - } - } -#endif - if ((sizeof(int) <= sizeof(long))) { - __PYX_VERIFY_RETURN_INT_EXC(int, long, PyLong_AsLong(x)) -#ifdef HAVE_LONG_LONG - } else if ((sizeof(int) <= sizeof(PY_LONG_LONG))) { - __PYX_VERIFY_RETURN_INT_EXC(int, PY_LONG_LONG, PyLong_AsLongLong(x)) -#endif - } - } - { - int val; - int ret = -1; -#if PY_VERSION_HEX >= 0x030d00A6 && !CYTHON_COMPILING_IN_LIMITED_API - Py_ssize_t bytes_copied = PyLong_AsNativeBytes( - x, &val, sizeof(val), Py_ASNATIVEBYTES_NATIVE_ENDIAN | (is_unsigned ? Py_ASNATIVEBYTES_UNSIGNED_BUFFER | Py_ASNATIVEBYTES_REJECT_NEGATIVE : 0)); - if (unlikely(bytes_copied == -1)) { - } else if (unlikely(bytes_copied > (Py_ssize_t) sizeof(val))) { - goto raise_overflow; - } else { - ret = 0; - } -#elif PY_VERSION_HEX < 0x030d0000 && !(CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API) || defined(_PyLong_AsByteArray) - int one = 1; int is_little = (int)*(unsigned char *)&one; - unsigned char *bytes = (unsigned char *)&val; - ret = _PyLong_AsByteArray((PyLongObject *)x, - bytes, sizeof(val), - is_little, !is_unsigned); -#else - PyObject *v; - PyObject *stepval = NULL, *mask = NULL, *shift = NULL; - int bits, remaining_bits, is_negative = 0; - int chunk_size = (sizeof(long) < 8) ? 30 : 62; - if (likely(PyLong_CheckExact(x))) { - v = __Pyx_NewRef(x); - } else { - v = PyNumber_Long(x); - if (unlikely(!v)) return (int) -1; - assert(PyLong_CheckExact(v)); - } - { - int result = PyObject_RichCompareBool(v, Py_False, Py_LT); - if (unlikely(result < 0)) { - Py_DECREF(v); - return (int) -1; - } - is_negative = result == 1; - } - if (is_unsigned && unlikely(is_negative)) { - Py_DECREF(v); - goto raise_neg_overflow; - } else if (is_negative) { - stepval = PyNumber_Invert(v); - Py_DECREF(v); - if (unlikely(!stepval)) - return (int) -1; - } else { - stepval = v; - } - v = NULL; - val = (int) 0; - mask = PyLong_FromLong((1L << chunk_size) - 1); if (unlikely(!mask)) goto done; - shift = PyLong_FromLong(chunk_size); if (unlikely(!shift)) goto done; - for (bits = 0; bits < (int) sizeof(int) * 8 - chunk_size; bits += chunk_size) { - PyObject *tmp, *digit; - long idigit; - digit = PyNumber_And(stepval, mask); - if (unlikely(!digit)) goto done; - idigit = PyLong_AsLong(digit); - Py_DECREF(digit); - if (unlikely(idigit < 0)) goto done; - val |= ((int) idigit) << bits; - tmp = PyNumber_Rshift(stepval, shift); - if (unlikely(!tmp)) goto done; - Py_DECREF(stepval); stepval = tmp; - } - Py_DECREF(shift); shift = NULL; - Py_DECREF(mask); mask = NULL; - { - long idigit = PyLong_AsLong(stepval); - if (unlikely(idigit < 0)) goto done; - remaining_bits = ((int) sizeof(int) * 8) - bits - (is_unsigned ? 0 : 1); - if (unlikely(idigit >= (1L << remaining_bits))) - goto raise_overflow; - val |= ((int) idigit) << bits; - } - if (!is_unsigned) { - if (unlikely(val & (((int) 1) << (sizeof(int) * 8 - 1)))) - goto raise_overflow; - if (is_negative) - val = ~val; - } - ret = 0; - done: - Py_XDECREF(shift); - Py_XDECREF(mask); - Py_XDECREF(stepval); -#endif - if (unlikely(ret)) - return (int) -1; - return val; - } -raise_overflow: - PyErr_SetString(PyExc_OverflowError, - "value too large to convert to int"); - return (int) -1; -raise_neg_overflow: - PyErr_SetString(PyExc_OverflowError, - "can't convert negative value to int"); - return (int) -1; -} - -/* PyObjectVectorCallKwBuilder */ -#if CYTHON_VECTORCALL -static int __Pyx_VectorcallBuilder_AddArg(PyObject *key, PyObject *value, PyObject *builder, PyObject **args, int n) { - (void)__Pyx_PyObject_FastCallDict; - if (__Pyx_PyTuple_SET_ITEM(builder, n, key) != (0)) return -1; - Py_INCREF(key); - args[n] = value; - return 0; -} -CYTHON_UNUSED static int __Pyx_VectorcallBuilder_AddArg_Check(PyObject *key, PyObject *value, PyObject *builder, PyObject **args, int n) { - (void)__Pyx_VectorcallBuilder_AddArgStr; - if (unlikely(!PyUnicode_Check(key))) { - PyErr_SetString(PyExc_TypeError, "keywords must be strings"); - return -1; - } - return __Pyx_VectorcallBuilder_AddArg(key, value, builder, args, n); -} -static int __Pyx_VectorcallBuilder_AddArgStr(const char *key, PyObject *value, PyObject *builder, PyObject **args, int n) { - PyObject *pyKey = PyUnicode_FromString(key); - if (!pyKey) return -1; - return __Pyx_VectorcallBuilder_AddArg(pyKey, value, builder, args, n); -} -#else // CYTHON_VECTORCALL -CYTHON_UNUSED static int __Pyx_VectorcallBuilder_AddArg_Check(PyObject *key, PyObject *value, PyObject *builder, CYTHON_UNUSED PyObject **args, CYTHON_UNUSED int n) { - if (unlikely(!PyUnicode_Check(key))) { - PyErr_SetString(PyExc_TypeError, "keywords must be strings"); - return -1; - } - return PyDict_SetItem(builder, key, value); -} -#endif - -/* CIntToPy */ -static CYTHON_INLINE PyObject* __Pyx_PyLong_From_long(long value) { -#ifdef __Pyx_HAS_GCC_DIAGNOSTIC -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wconversion" -#endif - const long neg_one = (long) -1, const_zero = (long) 0; -#ifdef __Pyx_HAS_GCC_DIAGNOSTIC -#pragma GCC diagnostic pop -#endif - const int is_unsigned = neg_one > const_zero; - if (is_unsigned) { - if (sizeof(long) < sizeof(long)) { - return PyLong_FromLong((long) value); - } else if (sizeof(long) <= sizeof(unsigned long)) { - return PyLong_FromUnsignedLong((unsigned long) value); -#if defined(HAVE_LONG_LONG) && !CYTHON_COMPILING_IN_PYPY - } else if (sizeof(long) <= sizeof(unsigned PY_LONG_LONG)) { - return PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG) value); -#endif - } - } else { - if (sizeof(long) <= sizeof(long)) { - return PyLong_FromLong((long) value); -#ifdef HAVE_LONG_LONG - } else if (sizeof(long) <= sizeof(PY_LONG_LONG)) { - return PyLong_FromLongLong((PY_LONG_LONG) value); -#endif - } - } - { - unsigned char *bytes = (unsigned char *)&value; -#if !CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x030d00A4 - if (is_unsigned) { - return PyLong_FromUnsignedNativeBytes(bytes, sizeof(value), -1); - } else { - return PyLong_FromNativeBytes(bytes, sizeof(value), -1); - } -#elif !CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x030d0000 - int one = 1; int little = (int)*(unsigned char *)&one; - return _PyLong_FromByteArray(bytes, sizeof(long), - little, !is_unsigned); -#else - int one = 1; int little = (int)*(unsigned char *)&one; - PyObject *from_bytes, *result = NULL, *kwds = NULL; - PyObject *py_bytes = NULL, *order_str = NULL; - from_bytes = PyObject_GetAttrString((PyObject*)&PyLong_Type, "from_bytes"); - if (!from_bytes) return NULL; - py_bytes = PyBytes_FromStringAndSize((char*)bytes, sizeof(long)); - if (!py_bytes) goto limited_bad; - order_str = PyUnicode_FromString(little ? "little" : "big"); - if (!order_str) goto limited_bad; - { - PyObject *args[3+(CYTHON_VECTORCALL ? 1 : 0)] = { NULL, py_bytes, order_str }; - if (!is_unsigned) { - kwds = __Pyx_MakeVectorcallBuilderKwds(1); - if (!kwds) goto limited_bad; - if (__Pyx_VectorcallBuilder_AddArgStr("signed", __Pyx_NewRef(Py_True), kwds, args+3, 0) < 0) goto limited_bad; - } - result = __Pyx_Object_Vectorcall_CallFromBuilder(from_bytes, args+1, 2 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET, kwds); - } - limited_bad: - Py_XDECREF(kwds); - Py_XDECREF(order_str); - Py_XDECREF(py_bytes); - Py_XDECREF(from_bytes); - return result; -#endif - } -} - -/* CIntToPy */ -static CYTHON_INLINE PyObject* __Pyx_PyLong_From_int(int value) { -#ifdef __Pyx_HAS_GCC_DIAGNOSTIC -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wconversion" -#endif - const int neg_one = (int) -1, const_zero = (int) 0; -#ifdef __Pyx_HAS_GCC_DIAGNOSTIC -#pragma GCC diagnostic pop -#endif - const int is_unsigned = neg_one > const_zero; - if (is_unsigned) { - if (sizeof(int) < sizeof(long)) { - return PyLong_FromLong((long) value); - } else if (sizeof(int) <= sizeof(unsigned long)) { - return PyLong_FromUnsignedLong((unsigned long) value); -#if defined(HAVE_LONG_LONG) && !CYTHON_COMPILING_IN_PYPY - } else if (sizeof(int) <= sizeof(unsigned PY_LONG_LONG)) { - return PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG) value); -#endif - } - } else { - if (sizeof(int) <= sizeof(long)) { - return PyLong_FromLong((long) value); -#ifdef HAVE_LONG_LONG - } else if (sizeof(int) <= sizeof(PY_LONG_LONG)) { - return PyLong_FromLongLong((PY_LONG_LONG) value); -#endif - } - } - { - unsigned char *bytes = (unsigned char *)&value; -#if !CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x030d00A4 - if (is_unsigned) { - return PyLong_FromUnsignedNativeBytes(bytes, sizeof(value), -1); - } else { - return PyLong_FromNativeBytes(bytes, sizeof(value), -1); - } -#elif !CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x030d0000 - int one = 1; int little = (int)*(unsigned char *)&one; - return _PyLong_FromByteArray(bytes, sizeof(int), - little, !is_unsigned); -#else - int one = 1; int little = (int)*(unsigned char *)&one; - PyObject *from_bytes, *result = NULL, *kwds = NULL; - PyObject *py_bytes = NULL, *order_str = NULL; - from_bytes = PyObject_GetAttrString((PyObject*)&PyLong_Type, "from_bytes"); - if (!from_bytes) return NULL; - py_bytes = PyBytes_FromStringAndSize((char*)bytes, sizeof(int)); - if (!py_bytes) goto limited_bad; - order_str = PyUnicode_FromString(little ? "little" : "big"); - if (!order_str) goto limited_bad; - { - PyObject *args[3+(CYTHON_VECTORCALL ? 1 : 0)] = { NULL, py_bytes, order_str }; - if (!is_unsigned) { - kwds = __Pyx_MakeVectorcallBuilderKwds(1); - if (!kwds) goto limited_bad; - if (__Pyx_VectorcallBuilder_AddArgStr("signed", __Pyx_NewRef(Py_True), kwds, args+3, 0) < 0) goto limited_bad; - } - result = __Pyx_Object_Vectorcall_CallFromBuilder(from_bytes, args+1, 2 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET, kwds); - } - limited_bad: - Py_XDECREF(kwds); - Py_XDECREF(order_str); - Py_XDECREF(py_bytes); - Py_XDECREF(from_bytes); - return result; -#endif - } -} - -/* FormatTypeName */ -#if CYTHON_COMPILING_IN_LIMITED_API && __PYX_LIMITED_VERSION_HEX < 0x030d0000 -static __Pyx_TypeName -__Pyx_PyType_GetFullyQualifiedName(PyTypeObject* tp) -{ - PyObject *module = NULL, *name = NULL, *result = NULL; - #if __PYX_LIMITED_VERSION_HEX < 0x030b0000 - name = __Pyx_PyObject_GetAttrStr((PyObject *)tp, - __pyx_mstate_global->__pyx_n_u_qualname); - #else - name = PyType_GetQualName(tp); - #endif - if (unlikely(name == NULL) || unlikely(!PyUnicode_Check(name))) goto bad; - module = __Pyx_PyObject_GetAttrStr((PyObject *)tp, - __pyx_mstate_global->__pyx_n_u_module); - if (unlikely(module == NULL) || unlikely(!PyUnicode_Check(module))) goto bad; - if (PyUnicode_CompareWithASCIIString(module, "builtins") == 0) { - result = name; - name = NULL; - goto done; - } - result = PyUnicode_FromFormat("%U.%U", module, name); - if (unlikely(result == NULL)) goto bad; - done: - Py_XDECREF(name); - Py_XDECREF(module); - return result; - bad: - PyErr_Clear(); - if (name) { - result = name; - name = NULL; - } else { - result = __Pyx_NewRef(__pyx_mstate_global->__pyx_kp_u__2); - } - goto done; -} -#endif - -/* CIntFromPy */ -static CYTHON_INLINE long __Pyx_PyLong_As_long(PyObject *x) { -#ifdef __Pyx_HAS_GCC_DIAGNOSTIC -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wconversion" -#endif - const long neg_one = (long) -1, const_zero = (long) 0; -#ifdef __Pyx_HAS_GCC_DIAGNOSTIC -#pragma GCC diagnostic pop -#endif - const int is_unsigned = neg_one > const_zero; - if (unlikely(!PyLong_Check(x))) { - long val; - PyObject *tmp = __Pyx_PyNumber_Long(x); - if (!tmp) return (long) -1; - val = __Pyx_PyLong_As_long(tmp); - Py_DECREF(tmp); - return val; - } - if (is_unsigned) { -#if CYTHON_USE_PYLONG_INTERNALS - if (unlikely(__Pyx_PyLong_IsNeg(x))) { - goto raise_neg_overflow; - } else if (__Pyx_PyLong_IsCompact(x)) { - __PYX_VERIFY_RETURN_INT(long, __Pyx_compact_upylong, __Pyx_PyLong_CompactValueUnsigned(x)) - } else { - const digit* digits = __Pyx_PyLong_Digits(x); - assert(__Pyx_PyLong_DigitCount(x) > 1); - switch (__Pyx_PyLong_DigitCount(x)) { - case 2: - if ((8 * sizeof(long) > 1 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(long) >= 2 * PyLong_SHIFT)) { - return (long) (((((long)digits[1]) << PyLong_SHIFT) | (long)digits[0])); - } - } - break; - case 3: - if ((8 * sizeof(long) > 2 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(long) >= 3 * PyLong_SHIFT)) { - return (long) (((((((long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0])); - } - } - break; - case 4: - if ((8 * sizeof(long) > 3 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(long) >= 4 * PyLong_SHIFT)) { - return (long) (((((((((long)digits[3]) << PyLong_SHIFT) | (long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0])); - } - } - break; - } - } -#endif -#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030C00A7 - if (unlikely(Py_SIZE(x) < 0)) { - goto raise_neg_overflow; - } -#else - { - int result = PyObject_RichCompareBool(x, Py_False, Py_LT); - if (unlikely(result < 0)) - return (long) -1; - if (unlikely(result == 1)) - goto raise_neg_overflow; - } -#endif - if ((sizeof(long) <= sizeof(unsigned long))) { - __PYX_VERIFY_RETURN_INT_EXC(long, unsigned long, PyLong_AsUnsignedLong(x)) -#ifdef HAVE_LONG_LONG - } else if ((sizeof(long) <= sizeof(unsigned PY_LONG_LONG))) { - __PYX_VERIFY_RETURN_INT_EXC(long, unsigned PY_LONG_LONG, PyLong_AsUnsignedLongLong(x)) -#endif - } - } else { -#if CYTHON_USE_PYLONG_INTERNALS - if (__Pyx_PyLong_IsCompact(x)) { - __PYX_VERIFY_RETURN_INT(long, __Pyx_compact_pylong, __Pyx_PyLong_CompactValue(x)) - } else { - const digit* digits = __Pyx_PyLong_Digits(x); - assert(__Pyx_PyLong_DigitCount(x) > 1); - switch (__Pyx_PyLong_SignedDigitCount(x)) { - case -2: - if ((8 * sizeof(long) - 1 > 1 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(long, long, -(long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(long) - 1 > 2 * PyLong_SHIFT)) { - return (long) (((long)-1)*(((((long)digits[1]) << PyLong_SHIFT) | (long)digits[0]))); - } - } - break; - case 2: - if ((8 * sizeof(long) > 1 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(long) - 1 > 2 * PyLong_SHIFT)) { - return (long) ((((((long)digits[1]) << PyLong_SHIFT) | (long)digits[0]))); - } - } - break; - case -3: - if ((8 * sizeof(long) - 1 > 2 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(long, long, -(long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(long) - 1 > 3 * PyLong_SHIFT)) { - return (long) (((long)-1)*(((((((long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0]))); - } - } - break; - case 3: - if ((8 * sizeof(long) > 2 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(long) - 1 > 3 * PyLong_SHIFT)) { - return (long) ((((((((long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0]))); - } - } - break; - case -4: - if ((8 * sizeof(long) - 1 > 3 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(long, long, -(long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(long) - 1 > 4 * PyLong_SHIFT)) { - return (long) (((long)-1)*(((((((((long)digits[3]) << PyLong_SHIFT) | (long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0]))); - } - } - break; - case 4: - if ((8 * sizeof(long) > 3 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(long) - 1 > 4 * PyLong_SHIFT)) { - return (long) ((((((((((long)digits[3]) << PyLong_SHIFT) | (long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0]))); - } - } - break; - } - } -#endif - if ((sizeof(long) <= sizeof(long))) { - __PYX_VERIFY_RETURN_INT_EXC(long, long, PyLong_AsLong(x)) -#ifdef HAVE_LONG_LONG - } else if ((sizeof(long) <= sizeof(PY_LONG_LONG))) { - __PYX_VERIFY_RETURN_INT_EXC(long, PY_LONG_LONG, PyLong_AsLongLong(x)) -#endif - } - } - { - long val; - int ret = -1; -#if PY_VERSION_HEX >= 0x030d00A6 && !CYTHON_COMPILING_IN_LIMITED_API - Py_ssize_t bytes_copied = PyLong_AsNativeBytes( - x, &val, sizeof(val), Py_ASNATIVEBYTES_NATIVE_ENDIAN | (is_unsigned ? Py_ASNATIVEBYTES_UNSIGNED_BUFFER | Py_ASNATIVEBYTES_REJECT_NEGATIVE : 0)); - if (unlikely(bytes_copied == -1)) { - } else if (unlikely(bytes_copied > (Py_ssize_t) sizeof(val))) { - goto raise_overflow; - } else { - ret = 0; - } -#elif PY_VERSION_HEX < 0x030d0000 && !(CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API) || defined(_PyLong_AsByteArray) - int one = 1; int is_little = (int)*(unsigned char *)&one; - unsigned char *bytes = (unsigned char *)&val; - ret = _PyLong_AsByteArray((PyLongObject *)x, - bytes, sizeof(val), - is_little, !is_unsigned); -#else - PyObject *v; - PyObject *stepval = NULL, *mask = NULL, *shift = NULL; - int bits, remaining_bits, is_negative = 0; - int chunk_size = (sizeof(long) < 8) ? 30 : 62; - if (likely(PyLong_CheckExact(x))) { - v = __Pyx_NewRef(x); - } else { - v = PyNumber_Long(x); - if (unlikely(!v)) return (long) -1; - assert(PyLong_CheckExact(v)); - } - { - int result = PyObject_RichCompareBool(v, Py_False, Py_LT); - if (unlikely(result < 0)) { - Py_DECREF(v); - return (long) -1; - } - is_negative = result == 1; - } - if (is_unsigned && unlikely(is_negative)) { - Py_DECREF(v); - goto raise_neg_overflow; - } else if (is_negative) { - stepval = PyNumber_Invert(v); - Py_DECREF(v); - if (unlikely(!stepval)) - return (long) -1; - } else { - stepval = v; - } - v = NULL; - val = (long) 0; - mask = PyLong_FromLong((1L << chunk_size) - 1); if (unlikely(!mask)) goto done; - shift = PyLong_FromLong(chunk_size); if (unlikely(!shift)) goto done; - for (bits = 0; bits < (int) sizeof(long) * 8 - chunk_size; bits += chunk_size) { - PyObject *tmp, *digit; - long idigit; - digit = PyNumber_And(stepval, mask); - if (unlikely(!digit)) goto done; - idigit = PyLong_AsLong(digit); - Py_DECREF(digit); - if (unlikely(idigit < 0)) goto done; - val |= ((long) idigit) << bits; - tmp = PyNumber_Rshift(stepval, shift); - if (unlikely(!tmp)) goto done; - Py_DECREF(stepval); stepval = tmp; - } - Py_DECREF(shift); shift = NULL; - Py_DECREF(mask); mask = NULL; - { - long idigit = PyLong_AsLong(stepval); - if (unlikely(idigit < 0)) goto done; - remaining_bits = ((int) sizeof(long) * 8) - bits - (is_unsigned ? 0 : 1); - if (unlikely(idigit >= (1L << remaining_bits))) - goto raise_overflow; - val |= ((long) idigit) << bits; - } - if (!is_unsigned) { - if (unlikely(val & (((long) 1) << (sizeof(long) * 8 - 1)))) - goto raise_overflow; - if (is_negative) - val = ~val; - } - ret = 0; - done: - Py_XDECREF(shift); - Py_XDECREF(mask); - Py_XDECREF(stepval); -#endif - if (unlikely(ret)) - return (long) -1; - return val; - } -raise_overflow: - PyErr_SetString(PyExc_OverflowError, - "value too large to convert to long"); - return (long) -1; -raise_neg_overflow: - PyErr_SetString(PyExc_OverflowError, - "can't convert negative value to long"); - return (long) -1; -} - -/* FastTypeChecks */ -#if CYTHON_COMPILING_IN_CPYTHON -static int __Pyx_InBases(PyTypeObject *a, PyTypeObject *b) { - while (a) { - a = __Pyx_PyType_GetSlot(a, tp_base, PyTypeObject*); - if (a == b) - return 1; - } - return b == &PyBaseObject_Type; -} -static CYTHON_INLINE int __Pyx_IsSubtype(PyTypeObject *a, PyTypeObject *b) { - PyObject *mro; - if (a == b) return 1; - mro = a->tp_mro; - if (likely(mro)) { - Py_ssize_t i, n; - n = PyTuple_GET_SIZE(mro); - for (i = 0; i < n; i++) { - if (PyTuple_GET_ITEM(mro, i) == (PyObject *)b) - return 1; - } - return 0; - } - return __Pyx_InBases(a, b); -} -static CYTHON_INLINE int __Pyx_IsAnySubtype2(PyTypeObject *cls, PyTypeObject *a, PyTypeObject *b) { - PyObject *mro; - if (cls == a || cls == b) return 1; - mro = cls->tp_mro; - if (likely(mro)) { - Py_ssize_t i, n; - n = PyTuple_GET_SIZE(mro); - for (i = 0; i < n; i++) { - PyObject *base = PyTuple_GET_ITEM(mro, i); - if (base == (PyObject *)a || base == (PyObject *)b) - return 1; - } - return 0; - } - return __Pyx_InBases(cls, a) || __Pyx_InBases(cls, b); -} -static CYTHON_INLINE int __Pyx_inner_PyErr_GivenExceptionMatches2(PyObject *err, PyObject* exc_type1, PyObject *exc_type2) { - if (exc_type1) { - return __Pyx_IsAnySubtype2((PyTypeObject*)err, (PyTypeObject*)exc_type1, (PyTypeObject*)exc_type2); - } else { - return __Pyx_IsSubtype((PyTypeObject*)err, (PyTypeObject*)exc_type2); - } -} -static int __Pyx_PyErr_GivenExceptionMatchesTuple(PyObject *exc_type, PyObject *tuple) { - Py_ssize_t i, n; - assert(PyExceptionClass_Check(exc_type)); - n = PyTuple_GET_SIZE(tuple); - for (i=0; i= 0x030B00a4 - _PyErr_StackItem *exc_info = tstate->exc_info; - tmp_value = exc_info->exc_value; - exc_info->exc_value = *value; - if (tmp_value == NULL || tmp_value == Py_None) { - Py_XDECREF(tmp_value); - tmp_value = NULL; - tmp_type = NULL; - tmp_tb = NULL; - } else { - tmp_type = (PyObject*) Py_TYPE(tmp_value); - Py_INCREF(tmp_type); - #if CYTHON_COMPILING_IN_CPYTHON - tmp_tb = ((PyBaseExceptionObject*) tmp_value)->traceback; - Py_XINCREF(tmp_tb); - #else - tmp_tb = PyException_GetTraceback(tmp_value); - #endif - } - #elif CYTHON_USE_EXC_INFO_STACK - _PyErr_StackItem *exc_info = tstate->exc_info; - tmp_type = exc_info->exc_type; - tmp_value = exc_info->exc_value; - tmp_tb = exc_info->exc_traceback; - exc_info->exc_type = *type; - exc_info->exc_value = *value; - exc_info->exc_traceback = *tb; - #else - tmp_type = tstate->exc_type; - tmp_value = tstate->exc_value; - tmp_tb = tstate->exc_traceback; - tstate->exc_type = *type; - tstate->exc_value = *value; - tstate->exc_traceback = *tb; - #endif - *type = tmp_type; - *value = tmp_value; - *tb = tmp_tb; -} -#else -static CYTHON_INLINE void __Pyx_ExceptionSwap(PyObject **type, PyObject **value, PyObject **tb) { - PyObject *tmp_type, *tmp_value, *tmp_tb; - PyErr_GetExcInfo(&tmp_type, &tmp_value, &tmp_tb); - PyErr_SetExcInfo(*type, *value, *tb); - *type = tmp_type; - *value = tmp_value; - *tb = tmp_tb; -} -#endif - -/* PyObjectCall2Args */ -static CYTHON_INLINE PyObject* __Pyx_PyObject_Call2Args(PyObject* function, PyObject* arg1, PyObject* arg2) { - PyObject *args[3] = {NULL, arg1, arg2}; - return __Pyx_PyObject_FastCall(function, args+1, 2 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET); -} - -/* PyObjectCallMethod1 */ -#if !(CYTHON_VECTORCALL && (__PYX_LIMITED_VERSION_HEX >= 0x030C0000 || (!CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x03090000))) -static PyObject* __Pyx__PyObject_CallMethod1(PyObject* method, PyObject* arg) { - PyObject *result = __Pyx_PyObject_CallOneArg(method, arg); - Py_DECREF(method); - return result; -} -#endif -static PyObject* __Pyx_PyObject_CallMethod1(PyObject* obj, PyObject* method_name, PyObject* arg) { -#if CYTHON_VECTORCALL && (__PYX_LIMITED_VERSION_HEX >= 0x030C0000 || (!CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x03090000)) - PyObject *args[2] = {obj, arg}; - (void) __Pyx_PyObject_GetMethod; - (void) __Pyx_PyObject_CallOneArg; - (void) __Pyx_PyObject_Call2Args; - return PyObject_VectorcallMethod(method_name, args, 2 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); -#else - PyObject *method = NULL, *result; - int is_method = __Pyx_PyObject_GetMethod(obj, method_name, &method); - if (likely(is_method)) { - result = __Pyx_PyObject_Call2Args(method, obj, arg); - Py_DECREF(method); - return result; - } - if (unlikely(!method)) return NULL; - return __Pyx__PyObject_CallMethod1(method, arg); -#endif -} - -/* ReturnWithStopIteration */ -static void __Pyx__ReturnWithStopIteration(PyObject* value, int async); -static CYTHON_INLINE void __Pyx_ReturnWithStopIteration(PyObject* value, int async, int iternext) { - if (value == Py_None) { - if (async || !iternext) - PyErr_SetNone(async ? PyExc_StopAsyncIteration : PyExc_StopIteration); - return; - } - __Pyx__ReturnWithStopIteration(value, async); -} -static void __Pyx__ReturnWithStopIteration(PyObject* value, int async) { -#if CYTHON_COMPILING_IN_CPYTHON - __Pyx_PyThreadState_declare -#endif - PyObject *exc; - PyObject *exc_type = async ? PyExc_StopAsyncIteration : PyExc_StopIteration; -#if CYTHON_COMPILING_IN_CPYTHON - if ((PY_VERSION_HEX >= (0x030C00A6)) || unlikely(PyTuple_Check(value) || PyExceptionInstance_Check(value))) { - if (PY_VERSION_HEX >= (0x030e00A1)) { - exc = __Pyx_PyObject_CallOneArg(exc_type, value); - } else { - PyObject *args_tuple = PyTuple_New(1); - if (unlikely(!args_tuple)) return; - Py_INCREF(value); - PyTuple_SET_ITEM(args_tuple, 0, value); - exc = PyObject_Call(exc_type, args_tuple, NULL); - Py_DECREF(args_tuple); - } - if (unlikely(!exc)) return; - } else { - Py_INCREF(value); - exc = value; - } - #if CYTHON_FAST_THREAD_STATE - __Pyx_PyThreadState_assign - #if CYTHON_USE_EXC_INFO_STACK - if (!__pyx_tstate->exc_info->exc_value) - #else - if (!__pyx_tstate->exc_type) - #endif - { - Py_INCREF(exc_type); - __Pyx_ErrRestore(exc_type, exc, NULL); - return; - } - #endif -#else - exc = __Pyx_PyObject_CallOneArg(exc_type, value); - if (unlikely(!exc)) return; -#endif - PyErr_SetObject(exc_type, exc); - Py_DECREF(exc); -} - -/* CoroutineBase */ -#if !CYTHON_COMPILING_IN_LIMITED_API -#include -#if PY_VERSION_HEX >= 0x030b00a6 && !defined(PYPY_VERSION) - #ifndef Py_BUILD_CORE - #define Py_BUILD_CORE 1 - #endif - #include "internal/pycore_frame.h" -#endif -#endif // CYTHON_COMPILING_IN_LIMITED_API -static CYTHON_INLINE void -__Pyx_Coroutine_Undelegate(__pyx_CoroutineObject *gen) { -#if CYTHON_USE_AM_SEND - gen->yieldfrom_am_send = NULL; -#endif - Py_CLEAR(gen->yieldfrom); -} -static int __Pyx_PyGen__FetchStopIterationValue(PyThreadState *__pyx_tstate, PyObject **pvalue) { - PyObject *et, *ev, *tb; - PyObject *value = NULL; - CYTHON_UNUSED_VAR(__pyx_tstate); - __Pyx_ErrFetch(&et, &ev, &tb); - if (!et) { - Py_XDECREF(tb); - Py_XDECREF(ev); - Py_INCREF(Py_None); - *pvalue = Py_None; - return 0; - } - if (likely(et == PyExc_StopIteration)) { - if (!ev) { - Py_INCREF(Py_None); - value = Py_None; - } - else if (likely(__Pyx_IS_TYPE(ev, (PyTypeObject*)PyExc_StopIteration))) { - #if CYTHON_COMPILING_IN_LIMITED_API || CYTHON_COMPILING_IN_GRAAL - value = PyObject_GetAttr(ev, __pyx_mstate_global->__pyx_n_u_value); - if (unlikely(!value)) goto limited_api_failure; - #else - value = ((PyStopIterationObject *)ev)->value; - Py_INCREF(value); - #endif - Py_DECREF(ev); - } - else if (unlikely(PyTuple_Check(ev))) { - Py_ssize_t tuple_size = __Pyx_PyTuple_GET_SIZE(ev); - #if !CYTHON_ASSUME_SAFE_SIZE - if (unlikely(tuple_size < 0)) { - Py_XDECREF(tb); - Py_DECREF(ev); - Py_DECREF(et); - return -1; - } - #endif - if (tuple_size >= 1) { -#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - value = PyTuple_GET_ITEM(ev, 0); - Py_INCREF(value); -#elif CYTHON_ASSUME_SAFE_MACROS - value = PySequence_ITEM(ev, 0); -#else - value = PySequence_GetItem(ev, 0); - if (!value) goto limited_api_failure; -#endif - } else { - Py_INCREF(Py_None); - value = Py_None; - } - Py_DECREF(ev); - } - else if (!__Pyx_TypeCheck(ev, (PyTypeObject*)PyExc_StopIteration)) { - value = ev; - } - if (likely(value)) { - Py_XDECREF(tb); - Py_DECREF(et); - *pvalue = value; - return 0; - } - } else if (!__Pyx_PyErr_GivenExceptionMatches(et, PyExc_StopIteration)) { - __Pyx_ErrRestore(et, ev, tb); - return -1; - } - PyErr_NormalizeException(&et, &ev, &tb); - if (unlikely(!PyObject_TypeCheck(ev, (PyTypeObject*)PyExc_StopIteration))) { - __Pyx_ErrRestore(et, ev, tb); - return -1; - } - Py_XDECREF(tb); - Py_DECREF(et); -#if CYTHON_COMPILING_IN_LIMITED_API - value = PyObject_GetAttr(ev, __pyx_mstate_global->__pyx_n_u_value); -#else - value = ((PyStopIterationObject *)ev)->value; - Py_INCREF(value); -#endif - Py_DECREF(ev); -#if CYTHON_COMPILING_IN_LIMITED_API - if (unlikely(!value)) return -1; -#endif - *pvalue = value; - return 0; -#if CYTHON_COMPILING_IN_LIMITED_API || CYTHON_COMPILING_IN_GRAAL || !CYTHON_ASSUME_SAFE_MACROS - limited_api_failure: - Py_XDECREF(et); - Py_XDECREF(tb); - Py_XDECREF(ev); - return -1; -#endif -} -static CYTHON_INLINE -__Pyx_PySendResult __Pyx_Coroutine_status_from_result(PyObject **retval) { - if (*retval) { - return PYGEN_NEXT; - } else if (likely(__Pyx_PyGen__FetchStopIterationValue(__Pyx_PyThreadState_Current, retval) == 0)) { - return PYGEN_RETURN; - } else { - return PYGEN_ERROR; - } -} -static CYTHON_INLINE -void __Pyx_Coroutine_ExceptionClear(__Pyx_ExcInfoStruct *exc_state) { -#if PY_VERSION_HEX >= 0x030B00a4 - Py_CLEAR(exc_state->exc_value); -#else - PyObject *t, *v, *tb; - t = exc_state->exc_type; - v = exc_state->exc_value; - tb = exc_state->exc_traceback; - exc_state->exc_type = NULL; - exc_state->exc_value = NULL; - exc_state->exc_traceback = NULL; - Py_XDECREF(t); - Py_XDECREF(v); - Py_XDECREF(tb); -#endif -} -#define __Pyx_Coroutine_AlreadyRunningError(gen) (__Pyx__Coroutine_AlreadyRunningError(gen), (PyObject*)NULL) -static void __Pyx__Coroutine_AlreadyRunningError(__pyx_CoroutineObject *gen) { - const char *msg; - CYTHON_MAYBE_UNUSED_VAR(gen); - if ((0)) { - #ifdef __Pyx_Coroutine_USED - } else if (__Pyx_Coroutine_Check((PyObject*)gen)) { - msg = "coroutine already executing"; - #endif - #ifdef __Pyx_AsyncGen_USED - } else if (__Pyx_AsyncGen_CheckExact((PyObject*)gen)) { - msg = "async generator already executing"; - #endif - } else { - msg = "generator already executing"; - } - PyErr_SetString(PyExc_ValueError, msg); -} -static void __Pyx_Coroutine_AlreadyTerminatedError(PyObject *gen, PyObject *value, int closing) { - CYTHON_MAYBE_UNUSED_VAR(gen); - CYTHON_MAYBE_UNUSED_VAR(closing); - #ifdef __Pyx_Coroutine_USED - if (!closing && __Pyx_Coroutine_Check(gen)) { - PyErr_SetString(PyExc_RuntimeError, "cannot reuse already awaited coroutine"); - } else - #endif - if (value) { - #ifdef __Pyx_AsyncGen_USED - if (__Pyx_AsyncGen_CheckExact(gen)) - PyErr_SetNone(PyExc_StopAsyncIteration); - else - #endif - PyErr_SetNone(PyExc_StopIteration); - } -} -static -__Pyx_PySendResult __Pyx_Coroutine_SendEx(__pyx_CoroutineObject *self, PyObject *value, PyObject **result, int closing) { - __Pyx_PyThreadState_declare - PyThreadState *tstate; - __Pyx_ExcInfoStruct *exc_state; - PyObject *retval; - assert(__Pyx_Coroutine_get_is_running(self)); // Callers should ensure is_running - if (unlikely(self->resume_label == -1)) { - __Pyx_Coroutine_AlreadyTerminatedError((PyObject*)self, value, closing); - return PYGEN_ERROR; - } -#if CYTHON_FAST_THREAD_STATE - __Pyx_PyThreadState_assign - tstate = __pyx_tstate; -#else - tstate = __Pyx_PyThreadState_Current; -#endif - exc_state = &self->gi_exc_state; - if (exc_state->exc_value) { - #if CYTHON_COMPILING_IN_LIMITED_API || CYTHON_COMPILING_IN_PYPY - #else - PyObject *exc_tb; - #if PY_VERSION_HEX >= 0x030B00a4 && !CYTHON_COMPILING_IN_CPYTHON - exc_tb = PyException_GetTraceback(exc_state->exc_value); - #elif PY_VERSION_HEX >= 0x030B00a4 - exc_tb = ((PyBaseExceptionObject*) exc_state->exc_value)->traceback; - #else - exc_tb = exc_state->exc_traceback; - #endif - if (exc_tb) { - PyTracebackObject *tb = (PyTracebackObject *) exc_tb; - PyFrameObject *f = tb->tb_frame; - assert(f->f_back == NULL); - #if PY_VERSION_HEX >= 0x030B00A1 - f->f_back = PyThreadState_GetFrame(tstate); - #else - Py_XINCREF(tstate->frame); - f->f_back = tstate->frame; - #endif - #if PY_VERSION_HEX >= 0x030B00a4 && !CYTHON_COMPILING_IN_CPYTHON - Py_DECREF(exc_tb); - #endif - } - #endif - } -#if CYTHON_USE_EXC_INFO_STACK - exc_state->previous_item = tstate->exc_info; - tstate->exc_info = exc_state; -#else - if (exc_state->exc_type) { - __Pyx_ExceptionSwap(&exc_state->exc_type, &exc_state->exc_value, &exc_state->exc_traceback); - } else { - __Pyx_Coroutine_ExceptionClear(exc_state); - __Pyx_ExceptionSave(&exc_state->exc_type, &exc_state->exc_value, &exc_state->exc_traceback); - } -#endif - retval = self->body(self, tstate, value); -#if CYTHON_USE_EXC_INFO_STACK - exc_state = &self->gi_exc_state; - tstate->exc_info = exc_state->previous_item; - exc_state->previous_item = NULL; - __Pyx_Coroutine_ResetFrameBackpointer(exc_state); -#endif - *result = retval; - if (self->resume_label == -1) { - return likely(retval) ? PYGEN_RETURN : PYGEN_ERROR; - } - return PYGEN_NEXT; -} -static CYTHON_INLINE void __Pyx_Coroutine_ResetFrameBackpointer(__Pyx_ExcInfoStruct *exc_state) { -#if CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API - CYTHON_UNUSED_VAR(exc_state); -#else - PyObject *exc_tb; - #if PY_VERSION_HEX >= 0x030B00a4 - if (!exc_state->exc_value) return; - exc_tb = PyException_GetTraceback(exc_state->exc_value); - #else - exc_tb = exc_state->exc_traceback; - #endif - if (likely(exc_tb)) { - PyTracebackObject *tb = (PyTracebackObject *) exc_tb; - PyFrameObject *f = tb->tb_frame; - Py_CLEAR(f->f_back); - #if PY_VERSION_HEX >= 0x030B00a4 - Py_DECREF(exc_tb); - #endif - } -#endif -} -#define __Pyx_Coroutine_MethodReturnFromResult(gen, result, retval, iternext)\ - ((result) == PYGEN_NEXT ? (retval) : __Pyx__Coroutine_MethodReturnFromResult(gen, result, retval, iternext)) -static PyObject * -__Pyx__Coroutine_MethodReturnFromResult(PyObject* gen, __Pyx_PySendResult result, PyObject *retval, int iternext) { - CYTHON_MAYBE_UNUSED_VAR(gen); - if (likely(result == PYGEN_RETURN)) { - int is_async = 0; - #ifdef __Pyx_AsyncGen_USED - is_async = __Pyx_AsyncGen_CheckExact(gen); - #endif - __Pyx_ReturnWithStopIteration(retval, is_async, iternext); - Py_XDECREF(retval); - } - return NULL; -} -#if CYTHON_COMPILING_IN_CPYTHON -static CYTHON_INLINE -PyObject *__Pyx_PyGen_Send(PyGenObject *gen, PyObject *arg) { -#if PY_VERSION_HEX <= 0x030A00A1 - return _PyGen_Send(gen, arg); -#else - PyObject *result; - if (PyIter_Send((PyObject*)gen, arg ? arg : Py_None, &result) == PYGEN_RETURN) { - if (PyAsyncGen_CheckExact(gen)) { - assert(result == Py_None); - PyErr_SetNone(PyExc_StopAsyncIteration); - } - else if (result == Py_None) { - PyErr_SetNone(PyExc_StopIteration); - } - else { -#if PY_VERSION_HEX < 0x030d00A1 - _PyGen_SetStopIterationValue(result); -#else - if (!PyTuple_Check(result) && !PyExceptionInstance_Check(result)) { - PyErr_SetObject(PyExc_StopIteration, result); - } else { - PyObject *exc = __Pyx_PyObject_CallOneArg(PyExc_StopIteration, result); - if (likely(exc != NULL)) { - PyErr_SetObject(PyExc_StopIteration, exc); - Py_DECREF(exc); - } - } -#endif - } - Py_DECREF(result); - result = NULL; - } - return result; -#endif -} -#endif -static CYTHON_INLINE __Pyx_PySendResult -__Pyx_Coroutine_FinishDelegation(__pyx_CoroutineObject *gen, PyObject** retval) { - __Pyx_PySendResult result; - PyObject *val = NULL; - assert(__Pyx_Coroutine_get_is_running(gen)); - __Pyx_Coroutine_Undelegate(gen); - __Pyx_PyGen__FetchStopIterationValue(__Pyx_PyThreadState_Current, &val); - result = __Pyx_Coroutine_SendEx(gen, val, retval, 0); - Py_XDECREF(val); - return result; -} -#if CYTHON_USE_AM_SEND -static __Pyx_PySendResult -__Pyx_Coroutine_SendToDelegate(__pyx_CoroutineObject *gen, __Pyx_pyiter_sendfunc gen_am_send, PyObject *value, PyObject **retval) { - PyObject *ret = NULL; - __Pyx_PySendResult delegate_result, result; - assert(__Pyx_Coroutine_get_is_running(gen)); - delegate_result = gen_am_send(gen->yieldfrom, value, &ret); - if (delegate_result == PYGEN_NEXT) { - assert (ret != NULL); - *retval = ret; - return PYGEN_NEXT; - } - assert (delegate_result != PYGEN_ERROR || ret == NULL); - __Pyx_Coroutine_Undelegate(gen); - result = __Pyx_Coroutine_SendEx(gen, ret, retval, 0); - Py_XDECREF(ret); - return result; -} -#endif -static PyObject *__Pyx_Coroutine_Send(PyObject *self, PyObject *value) { - PyObject *retval = NULL; - __Pyx_PySendResult result = __Pyx_Coroutine_AmSend(self, value, &retval); - return __Pyx_Coroutine_MethodReturnFromResult(self, result, retval, 0); -} -static __Pyx_PySendResult -__Pyx_Coroutine_AmSend(PyObject *self, PyObject *value, PyObject **retval) { - __Pyx_PySendResult result; - __pyx_CoroutineObject *gen = (__pyx_CoroutineObject*) self; - if (unlikely(__Pyx_Coroutine_test_and_set_is_running(gen))) { - *retval = __Pyx_Coroutine_AlreadyRunningError(gen); - return PYGEN_ERROR; - } - #if CYTHON_USE_AM_SEND - if (gen->yieldfrom_am_send) { - result = __Pyx_Coroutine_SendToDelegate(gen, gen->yieldfrom_am_send, value, retval); - } else - #endif - if (gen->yieldfrom) { - PyObject *yf = gen->yieldfrom; - PyObject *ret; - #if !CYTHON_USE_AM_SEND - #ifdef __Pyx_Generator_USED - if (__Pyx_Generator_CheckExact(yf)) { - ret = __Pyx_Coroutine_Send(yf, value); - } else - #endif - #ifdef __Pyx_Coroutine_USED - if (__Pyx_Coroutine_Check(yf)) { - ret = __Pyx_Coroutine_Send(yf, value); - } else - #endif - #ifdef __Pyx_AsyncGen_USED - if (__pyx_PyAsyncGenASend_CheckExact(yf)) { - ret = __Pyx_async_gen_asend_send(yf, value); - } else - #endif - #if CYTHON_COMPILING_IN_CPYTHON - if (PyGen_CheckExact(yf)) { - ret = __Pyx_PyGen_Send((PyGenObject*)yf, value == Py_None ? NULL : value); - } else - if (PyCoro_CheckExact(yf)) { - ret = __Pyx_PyGen_Send((PyGenObject*)yf, value == Py_None ? NULL : value); - } else - #endif - #endif - { - #if !CYTHON_COMPILING_IN_LIMITED_API || __PYX_LIMITED_VERSION_HEX >= 0x03080000 - if (value == Py_None && PyIter_Check(yf)) - ret = __Pyx_PyIter_Next_Plain(yf); - else - #endif - ret = __Pyx_PyObject_CallMethod1(yf, __pyx_mstate_global->__pyx_n_u_send, value); - } - if (likely(ret)) { - __Pyx_Coroutine_unset_is_running(gen); - *retval = ret; - return PYGEN_NEXT; - } - result = __Pyx_Coroutine_FinishDelegation(gen, retval); - } else { - result = __Pyx_Coroutine_SendEx(gen, value, retval, 0); - } - __Pyx_Coroutine_unset_is_running(gen); - return result; -} -static int __Pyx_Coroutine_CloseIter(__pyx_CoroutineObject *gen, PyObject *yf) { - __Pyx_PySendResult result; - PyObject *retval = NULL; - CYTHON_UNUSED_VAR(gen); - assert(__Pyx_Coroutine_get_is_running(gen)); - #ifdef __Pyx_Generator_USED - if (__Pyx_Generator_CheckExact(yf)) { - result = __Pyx_Coroutine_Close(yf, &retval); - } else - #endif - #ifdef __Pyx_Coroutine_USED - if (__Pyx_Coroutine_Check(yf)) { - result = __Pyx_Coroutine_Close(yf, &retval); - } else - if (__Pyx_CoroutineAwait_CheckExact(yf)) { - result = __Pyx_CoroutineAwait_Close((__pyx_CoroutineAwaitObject*)yf); - } else - #endif - #ifdef __Pyx_AsyncGen_USED - if (__pyx_PyAsyncGenASend_CheckExact(yf)) { - retval = __Pyx_async_gen_asend_close(yf, NULL); - result = PYGEN_RETURN; - } else - if (__pyx_PyAsyncGenAThrow_CheckExact(yf)) { - retval = __Pyx_async_gen_athrow_close(yf, NULL); - result = PYGEN_RETURN; - } else - #endif - { - PyObject *meth; - result = PYGEN_RETURN; - meth = __Pyx_PyObject_GetAttrStrNoError(yf, __pyx_mstate_global->__pyx_n_u_close); - if (unlikely(!meth)) { - if (unlikely(PyErr_Occurred())) { - PyErr_WriteUnraisable(yf); - } - } else { - retval = __Pyx_PyObject_CallNoArg(meth); - Py_DECREF(meth); - if (unlikely(!retval)) { - result = PYGEN_ERROR; - } - } - } - Py_XDECREF(retval); - return result == PYGEN_ERROR ? -1 : 0; -} -static PyObject *__Pyx_Generator_Next(PyObject *self) { - __Pyx_PySendResult result; - PyObject *retval = NULL; - __pyx_CoroutineObject *gen = (__pyx_CoroutineObject*) self; - if (unlikely(__Pyx_Coroutine_test_and_set_is_running(gen))) { - return __Pyx_Coroutine_AlreadyRunningError(gen); - } - #if CYTHON_USE_AM_SEND - if (gen->yieldfrom_am_send) { - result = __Pyx_Coroutine_SendToDelegate(gen, gen->yieldfrom_am_send, Py_None, &retval); - } else - #endif - if (gen->yieldfrom) { - PyObject *yf = gen->yieldfrom; - PyObject *ret; - #ifdef __Pyx_Generator_USED - if (__Pyx_Generator_CheckExact(yf)) { - ret = __Pyx_Generator_Next(yf); - } else - #endif - #ifdef __Pyx_Coroutine_USED - if (__Pyx_Coroutine_CheckExact(yf)) { - ret = __Pyx_Coroutine_Send(yf, Py_None); - } else - #endif - #if CYTHON_COMPILING_IN_CPYTHON && (PY_VERSION_HEX < 0x030A00A3 || !CYTHON_USE_AM_SEND) - if (PyGen_CheckExact(yf)) { - ret = __Pyx_PyGen_Send((PyGenObject*)yf, NULL); - } else - #endif - ret = __Pyx_PyIter_Next_Plain(yf); - if (likely(ret)) { - __Pyx_Coroutine_unset_is_running(gen); - return ret; - } - result = __Pyx_Coroutine_FinishDelegation(gen, &retval); - } else { - result = __Pyx_Coroutine_SendEx(gen, Py_None, &retval, 0); - } - __Pyx_Coroutine_unset_is_running(gen); - return __Pyx_Coroutine_MethodReturnFromResult(self, result, retval, 1); -} -static PyObject *__Pyx_Coroutine_Close_Method(PyObject *self, PyObject *arg) { - PyObject *retval = NULL; - __Pyx_PySendResult result; - CYTHON_UNUSED_VAR(arg); - result = __Pyx_Coroutine_Close(self, &retval); - if (unlikely(result == PYGEN_ERROR)) - return NULL; - Py_XDECREF(retval); - Py_RETURN_NONE; -} -static __Pyx_PySendResult -__Pyx_Coroutine_Close(PyObject *self, PyObject **retval) { - __pyx_CoroutineObject *gen = (__pyx_CoroutineObject *) self; - __Pyx_PySendResult result; - PyObject *yf; - int err = 0; - if (unlikely(__Pyx_Coroutine_test_and_set_is_running(gen))) { - *retval = __Pyx_Coroutine_AlreadyRunningError(gen); - return PYGEN_ERROR; - } - yf = gen->yieldfrom; - if (yf) { - Py_INCREF(yf); - err = __Pyx_Coroutine_CloseIter(gen, yf); - __Pyx_Coroutine_Undelegate(gen); - Py_DECREF(yf); - } - if (err == 0) - PyErr_SetNone(PyExc_GeneratorExit); - result = __Pyx_Coroutine_SendEx(gen, NULL, retval, 1); - if (result == PYGEN_ERROR) { - __Pyx_PyThreadState_declare - __Pyx_PyThreadState_assign - __Pyx_Coroutine_unset_is_running(gen); - if (!__Pyx_PyErr_Occurred()) { - return PYGEN_RETURN; - } else if (likely(__Pyx_PyErr_ExceptionMatches2(PyExc_GeneratorExit, PyExc_StopIteration))) { - __Pyx_PyErr_Clear(); - return PYGEN_RETURN; - } - return PYGEN_ERROR; - } else if (likely(result == PYGEN_RETURN && *retval == Py_None)) { - __Pyx_Coroutine_unset_is_running(gen); - return PYGEN_RETURN; - } else { - const char *msg; - Py_DECREF(*retval); - *retval = NULL; - if ((0)) { - #ifdef __Pyx_Coroutine_USED - } else if (__Pyx_Coroutine_Check(self)) { - msg = "coroutine ignored GeneratorExit"; - #endif - #ifdef __Pyx_AsyncGen_USED - } else if (__Pyx_AsyncGen_CheckExact(self)) { - msg = "async generator ignored GeneratorExit"; - #endif - } else { - msg = "generator ignored GeneratorExit"; - } - PyErr_SetString(PyExc_RuntimeError, msg); - __Pyx_Coroutine_unset_is_running(gen); - return PYGEN_ERROR; - } -} -static PyObject *__Pyx__Coroutine_Throw(PyObject *self, PyObject *typ, PyObject *val, PyObject *tb, - PyObject *args, int close_on_genexit) { - __pyx_CoroutineObject *gen = (__pyx_CoroutineObject *) self; - PyObject *yf; - if (unlikely(__Pyx_Coroutine_test_and_set_is_running(gen))) - return __Pyx_Coroutine_AlreadyRunningError(gen); - yf = gen->yieldfrom; - if (yf) { - __Pyx_PySendResult result; - PyObject *ret; - Py_INCREF(yf); - if (__Pyx_PyErr_GivenExceptionMatches(typ, PyExc_GeneratorExit) && close_on_genexit) { - int err = __Pyx_Coroutine_CloseIter(gen, yf); - Py_DECREF(yf); - __Pyx_Coroutine_Undelegate(gen); - if (err < 0) - goto propagate_exception; - goto throw_here; - } - if (0 - #ifdef __Pyx_Generator_USED - || __Pyx_Generator_CheckExact(yf) - #endif - #ifdef __Pyx_Coroutine_USED - || __Pyx_Coroutine_Check(yf) - #endif - ) { - ret = __Pyx__Coroutine_Throw(yf, typ, val, tb, args, close_on_genexit); - #ifdef __Pyx_Coroutine_USED - } else if (__Pyx_CoroutineAwait_CheckExact(yf)) { - ret = __Pyx__Coroutine_Throw(((__pyx_CoroutineAwaitObject*)yf)->coroutine, typ, val, tb, args, close_on_genexit); - #endif - } else { - PyObject *meth = __Pyx_PyObject_GetAttrStrNoError(yf, __pyx_mstate_global->__pyx_n_u_throw); - if (unlikely(!meth)) { - Py_DECREF(yf); - if (unlikely(PyErr_Occurred())) { - __Pyx_Coroutine_unset_is_running(gen); - return NULL; - } - __Pyx_Coroutine_Undelegate(gen); - goto throw_here; - } - if (likely(args)) { - ret = __Pyx_PyObject_Call(meth, args, NULL); - } else { - PyObject *cargs[4] = {NULL, typ, val, tb}; - ret = __Pyx_PyObject_FastCall(meth, cargs+1, 3 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET); - } - Py_DECREF(meth); - } - Py_DECREF(yf); - if (ret) { - __Pyx_Coroutine_unset_is_running(gen); - return ret; - } - result = __Pyx_Coroutine_FinishDelegation(gen, &ret); - __Pyx_Coroutine_unset_is_running(gen); - return __Pyx_Coroutine_MethodReturnFromResult(self, result, ret, 0); - } -throw_here: - __Pyx_Raise(typ, val, tb, NULL); -propagate_exception: - { - PyObject *retval = NULL; - __Pyx_PySendResult result = __Pyx_Coroutine_SendEx(gen, NULL, &retval, 0); - __Pyx_Coroutine_unset_is_running(gen); - return __Pyx_Coroutine_MethodReturnFromResult(self, result, retval, 0); - } -} -static PyObject *__Pyx_Coroutine_Throw(PyObject *self, PyObject *args) { - PyObject *typ; - PyObject *val = NULL; - PyObject *tb = NULL; - if (unlikely(!PyArg_UnpackTuple(args, "throw", 1, 3, &typ, &val, &tb))) - return NULL; - return __Pyx__Coroutine_Throw(self, typ, val, tb, args, 1); -} -static CYTHON_INLINE int __Pyx_Coroutine_traverse_excstate(__Pyx_ExcInfoStruct *exc_state, visitproc visit, void *arg) { -#if PY_VERSION_HEX >= 0x030B00a4 - Py_VISIT(exc_state->exc_value); -#else - Py_VISIT(exc_state->exc_type); - Py_VISIT(exc_state->exc_value); - Py_VISIT(exc_state->exc_traceback); -#endif - return 0; -} -static int __Pyx_Coroutine_traverse(__pyx_CoroutineObject *gen, visitproc visit, void *arg) { - { - int e = __Pyx_call_type_traverse((PyObject*)gen, 1, visit, arg); - if (e) return e; - } - Py_VISIT(gen->closure); - Py_VISIT(gen->classobj); - Py_VISIT(gen->yieldfrom); - return __Pyx_Coroutine_traverse_excstate(&gen->gi_exc_state, visit, arg); -} -static int __Pyx_Coroutine_clear(PyObject *self) { - __pyx_CoroutineObject *gen = (__pyx_CoroutineObject *) self; - Py_CLEAR(gen->closure); - Py_CLEAR(gen->classobj); - __Pyx_Coroutine_Undelegate(gen); - __Pyx_Coroutine_ExceptionClear(&gen->gi_exc_state); -#ifdef __Pyx_AsyncGen_USED - if (__Pyx_AsyncGen_CheckExact(self)) { - Py_CLEAR(((__pyx_PyAsyncGenObject*)gen)->ag_finalizer); - } -#endif - Py_CLEAR(gen->gi_code); - Py_CLEAR(gen->gi_frame); - Py_CLEAR(gen->gi_name); - Py_CLEAR(gen->gi_qualname); - Py_CLEAR(gen->gi_modulename); - return 0; -} -static void __Pyx_Coroutine_dealloc(PyObject *self) { - __pyx_CoroutineObject *gen = (__pyx_CoroutineObject *) self; - PyObject_GC_UnTrack(gen); - if (gen->gi_weakreflist != NULL) - PyObject_ClearWeakRefs(self); - if (gen->resume_label >= 0) { - PyObject_GC_Track(self); -#if CYTHON_USE_TP_FINALIZE - if (unlikely(PyObject_CallFinalizerFromDealloc(self))) -#else - { - destructor del = __Pyx_PyObject_GetSlot(gen, tp_del, destructor); - if (del) del(self); - } - if (unlikely(Py_REFCNT(self) > 0)) -#endif - { - return; - } - PyObject_GC_UnTrack(self); - } -#ifdef __Pyx_AsyncGen_USED - if (__Pyx_AsyncGen_CheckExact(self)) { - /* We have to handle this case for asynchronous generators - right here, because this code has to be between UNTRACK - and GC_Del. */ - Py_CLEAR(((__pyx_PyAsyncGenObject*)self)->ag_finalizer); - } -#endif - __Pyx_Coroutine_clear(self); - __Pyx_PyHeapTypeObject_GC_Del(gen); -} -#if CYTHON_USE_TP_FINALIZE -static void __Pyx_Coroutine_del(PyObject *self) { - PyObject *error_type, *error_value, *error_traceback; - __pyx_CoroutineObject *gen = (__pyx_CoroutineObject *) self; - __Pyx_PyThreadState_declare - if (gen->resume_label < 0) { - return; - } - __Pyx_PyThreadState_assign - __Pyx_ErrFetch(&error_type, &error_value, &error_traceback); -#ifdef __Pyx_AsyncGen_USED - if (__Pyx_AsyncGen_CheckExact(self)) { - __pyx_PyAsyncGenObject *agen = (__pyx_PyAsyncGenObject*)self; - PyObject *finalizer = agen->ag_finalizer; - if (finalizer && !agen->ag_closed) { - PyObject *res = __Pyx_PyObject_CallOneArg(finalizer, self); - if (unlikely(!res)) { - PyErr_WriteUnraisable(self); - } else { - Py_DECREF(res); - } - __Pyx_ErrRestore(error_type, error_value, error_traceback); - return; - } - } -#endif - if (unlikely(gen->resume_label == 0 && !error_value)) { -#ifdef __Pyx_Coroutine_USED -#ifdef __Pyx_Generator_USED - if (!__Pyx_Generator_CheckExact(self)) -#endif - { - PyObject_GC_UnTrack(self); - if (unlikely(PyErr_WarnFormat(PyExc_RuntimeWarning, 1, "coroutine '%.50S' was never awaited", gen->gi_qualname) < 0)) - PyErr_WriteUnraisable(self); - PyObject_GC_Track(self); - } -#endif - } else { - PyObject *retval = NULL; - __Pyx_PySendResult result = __Pyx_Coroutine_Close(self, &retval); - if (result == PYGEN_ERROR) { - PyErr_WriteUnraisable(self); - } else { - Py_XDECREF(retval); - } - } - __Pyx_ErrRestore(error_type, error_value, error_traceback); -} -#endif -static PyObject * -__Pyx_Coroutine_get_name(__pyx_CoroutineObject *self, void *context) -{ - PyObject *name = self->gi_name; - CYTHON_UNUSED_VAR(context); - if (unlikely(!name)) name = Py_None; - Py_INCREF(name); - return name; -} -static int -__Pyx_Coroutine_set_name(__pyx_CoroutineObject *self, PyObject *value, void *context) -{ - CYTHON_UNUSED_VAR(context); - if (unlikely(value == NULL || !PyUnicode_Check(value))) { - PyErr_SetString(PyExc_TypeError, - "__name__ must be set to a string object"); - return -1; - } - Py_INCREF(value); - __Pyx_Py_XDECREF_SET(self->gi_name, value); - return 0; -} -static PyObject * -__Pyx_Coroutine_get_qualname(__pyx_CoroutineObject *self, void *context) -{ - PyObject *name = self->gi_qualname; - CYTHON_UNUSED_VAR(context); - if (unlikely(!name)) name = Py_None; - Py_INCREF(name); - return name; -} -static int -__Pyx_Coroutine_set_qualname(__pyx_CoroutineObject *self, PyObject *value, void *context) -{ - CYTHON_UNUSED_VAR(context); - if (unlikely(value == NULL || !PyUnicode_Check(value))) { - PyErr_SetString(PyExc_TypeError, - "__qualname__ must be set to a string object"); - return -1; - } - Py_INCREF(value); - __Pyx_Py_XDECREF_SET(self->gi_qualname, value); - return 0; -} -static PyObject * -__Pyx__Coroutine_get_frame(__pyx_CoroutineObject *self) -{ -#if !CYTHON_COMPILING_IN_LIMITED_API - PyObject *frame; - #if PY_VERSION_HEX >= 0x030d0000 - Py_BEGIN_CRITICAL_SECTION(self); - #endif - frame = self->gi_frame; - if (!frame) { - if (unlikely(!self->gi_code)) { - Py_RETURN_NONE; - } - PyObject *globals = PyDict_New(); - if (unlikely(!globals)) return NULL; - frame = (PyObject *) PyFrame_New( - PyThreadState_Get(), /*PyThreadState *tstate,*/ - (PyCodeObject*) self->gi_code, /*PyCodeObject *code,*/ - globals, /*PyObject *globals,*/ - 0 /*PyObject *locals*/ - ); - Py_DECREF(globals); - if (unlikely(!frame)) - return NULL; - if (unlikely(self->gi_frame)) { - Py_DECREF(frame); - frame = self->gi_frame; - } else { - self->gi_frame = frame; - } - } - Py_INCREF(frame); - #if PY_VERSION_HEX >= 0x030d0000 - Py_END_CRITICAL_SECTION(); - #endif - return frame; -#else - CYTHON_UNUSED_VAR(self); - Py_RETURN_NONE; -#endif -} -static PyObject * -__Pyx_Coroutine_get_frame(__pyx_CoroutineObject *self, void *context) { - CYTHON_UNUSED_VAR(context); - PyObject *frame = self->gi_frame; - if (frame) - return __Pyx_NewRef(frame); - return __Pyx__Coroutine_get_frame(self); -} -static __pyx_CoroutineObject *__Pyx__Coroutine_New( - PyTypeObject* type, __pyx_coroutine_body_t body, PyObject *code, PyObject *closure, - PyObject *name, PyObject *qualname, PyObject *module_name) { - __pyx_CoroutineObject *gen = PyObject_GC_New(__pyx_CoroutineObject, type); - if (unlikely(!gen)) - return NULL; - return __Pyx__Coroutine_NewInit(gen, body, code, closure, name, qualname, module_name); -} -static __pyx_CoroutineObject *__Pyx__Coroutine_NewInit( - __pyx_CoroutineObject *gen, __pyx_coroutine_body_t body, PyObject *code, PyObject *closure, - PyObject *name, PyObject *qualname, PyObject *module_name) { - gen->body = body; - gen->closure = closure; - Py_XINCREF(closure); - gen->is_running = 0; - gen->resume_label = 0; - gen->classobj = NULL; - gen->yieldfrom = NULL; - gen->yieldfrom_am_send = NULL; - #if PY_VERSION_HEX >= 0x030B00a4 && !CYTHON_COMPILING_IN_LIMITED_API - gen->gi_exc_state.exc_value = NULL; - #else - gen->gi_exc_state.exc_type = NULL; - gen->gi_exc_state.exc_value = NULL; - gen->gi_exc_state.exc_traceback = NULL; - #endif -#if CYTHON_USE_EXC_INFO_STACK - gen->gi_exc_state.previous_item = NULL; -#endif - gen->gi_weakreflist = NULL; - Py_XINCREF(qualname); - gen->gi_qualname = qualname; - Py_XINCREF(name); - gen->gi_name = name; - Py_XINCREF(module_name); - gen->gi_modulename = module_name; - Py_XINCREF(code); - gen->gi_code = code; - gen->gi_frame = NULL; - PyObject_GC_Track(gen); - return gen; -} -static char __Pyx_Coroutine_test_and_set_is_running(__pyx_CoroutineObject *gen) { - char result; - #if PY_VERSION_HEX >= 0x030d0000 && !CYTHON_COMPILING_IN_LIMITED_API - Py_BEGIN_CRITICAL_SECTION(gen); - #endif - result = gen->is_running; - gen->is_running = 1; - #if PY_VERSION_HEX >= 0x030d0000 && !CYTHON_COMPILING_IN_LIMITED_API - Py_END_CRITICAL_SECTION(); - #endif - return result; -} -static void __Pyx_Coroutine_unset_is_running(__pyx_CoroutineObject *gen) { - #if PY_VERSION_HEX >= 0x030d0000 && !CYTHON_COMPILING_IN_LIMITED_API - Py_BEGIN_CRITICAL_SECTION(gen); - #endif - assert(gen->is_running); - gen->is_running = 0; - #if PY_VERSION_HEX >= 0x030d0000 && !CYTHON_COMPILING_IN_LIMITED_API - Py_END_CRITICAL_SECTION(); - #endif -} -static char __Pyx_Coroutine_get_is_running(__pyx_CoroutineObject *gen) { - char result; - #if PY_VERSION_HEX >= 0x030d0000 && !CYTHON_COMPILING_IN_LIMITED_API - Py_BEGIN_CRITICAL_SECTION(gen); - #endif - result = gen->is_running; - #if PY_VERSION_HEX >= 0x030d0000 && !CYTHON_COMPILING_IN_LIMITED_API - Py_END_CRITICAL_SECTION(); - #endif - return result; -} -static PyObject *__Pyx_Coroutine_get_is_running_getter(PyObject *gen, void *closure) { - CYTHON_UNUSED_VAR(closure); - char result = __Pyx_Coroutine_get_is_running((__pyx_CoroutineObject*)gen); - if (result) Py_RETURN_TRUE; - else Py_RETURN_FALSE; -} -#if __PYX_HAS_PY_AM_SEND == 2 -static void __Pyx_SetBackportTypeAmSend(PyTypeObject *type, __Pyx_PyAsyncMethodsStruct *static_amsend_methods, __Pyx_pyiter_sendfunc am_send) { - Py_ssize_t ptr_offset = (char*)(type->tp_as_async) - (char*)type; - if (ptr_offset < 0 || ptr_offset > type->tp_basicsize) { - return; - } - memcpy((void*)static_amsend_methods, (void*)(type->tp_as_async), sizeof(*type->tp_as_async)); - static_amsend_methods->am_send = am_send; - type->tp_as_async = __Pyx_SlotTpAsAsync(static_amsend_methods); -} -#endif -static PyObject *__Pyx_Coroutine_fail_reduce_ex(PyObject *self, PyObject *arg) { - CYTHON_UNUSED_VAR(arg); - __Pyx_TypeName self_type_name = __Pyx_PyType_GetFullyQualifiedName(Py_TYPE((PyObject*)self)); - PyErr_Format(PyExc_TypeError, "cannot pickle '" __Pyx_FMT_TYPENAME "' object", - self_type_name); - __Pyx_DECREF_TypeName(self_type_name); - return NULL; -} - -/* Generator */ -static PyMethodDef __pyx_Generator_methods[] = { - {"send", (PyCFunction) __Pyx_Coroutine_Send, METH_O, - PyDoc_STR("send(arg) -> send 'arg' into generator,\nreturn next yielded value or raise StopIteration.")}, - {"throw", (PyCFunction) __Pyx_Coroutine_Throw, METH_VARARGS, - PyDoc_STR("throw(typ[,val[,tb]]) -> raise exception in generator,\nreturn next yielded value or raise StopIteration.")}, - {"close", (PyCFunction) __Pyx_Coroutine_Close_Method, METH_NOARGS, - PyDoc_STR("close() -> raise GeneratorExit inside generator.")}, - {"__reduce_ex__", (PyCFunction) __Pyx_Coroutine_fail_reduce_ex, METH_O, 0}, - {"__reduce__", (PyCFunction) __Pyx_Coroutine_fail_reduce_ex, METH_NOARGS, 0}, - {0, 0, 0, 0} -}; -static PyMemberDef __pyx_Generator_memberlist[] = { - {"gi_yieldfrom", T_OBJECT, offsetof(__pyx_CoroutineObject, yieldfrom), READONLY, - PyDoc_STR("object being iterated by 'yield from', or None")}, - {"gi_code", T_OBJECT, offsetof(__pyx_CoroutineObject, gi_code), READONLY, NULL}, - {"__module__", T_OBJECT, offsetof(__pyx_CoroutineObject, gi_modulename), 0, 0}, - {"__weaklistoffset__", T_PYSSIZET, offsetof(__pyx_CoroutineObject, gi_weakreflist), READONLY, 0}, - {0, 0, 0, 0, 0} -}; -static PyGetSetDef __pyx_Generator_getsets[] = { - {"__name__", (getter)__Pyx_Coroutine_get_name, (setter)__Pyx_Coroutine_set_name, - PyDoc_STR("name of the generator"), 0}, - {"__qualname__", (getter)__Pyx_Coroutine_get_qualname, (setter)__Pyx_Coroutine_set_qualname, - PyDoc_STR("qualified name of the generator"), 0}, - {"gi_frame", (getter)__Pyx_Coroutine_get_frame, NULL, - PyDoc_STR("Frame of the generator"), 0}, - {"gi_running", __Pyx_Coroutine_get_is_running_getter, NULL, NULL, NULL}, - {0, 0, 0, 0, 0} -}; -static PyType_Slot __pyx_GeneratorType_slots[] = { - {Py_tp_dealloc, (void *)__Pyx_Coroutine_dealloc}, - {Py_tp_traverse, (void *)__Pyx_Coroutine_traverse}, - {Py_tp_iter, (void *)PyObject_SelfIter}, - {Py_tp_iternext, (void *)__Pyx_Generator_Next}, - {Py_tp_methods, (void *)__pyx_Generator_methods}, - {Py_tp_members, (void *)__pyx_Generator_memberlist}, - {Py_tp_getset, (void *)__pyx_Generator_getsets}, - {Py_tp_getattro, (void *) PyObject_GenericGetAttr}, -#if CYTHON_USE_TP_FINALIZE - {Py_tp_finalize, (void *)__Pyx_Coroutine_del}, -#endif -#if __PYX_HAS_PY_AM_SEND == 1 - {Py_am_send, (void *)__Pyx_Coroutine_AmSend}, -#endif - {0, 0}, -}; -static PyType_Spec __pyx_GeneratorType_spec = { - __PYX_TYPE_MODULE_PREFIX "generator", - sizeof(__pyx_CoroutineObject), - 0, -#if PY_VERSION_HEX >= 0x030A0000 - Py_TPFLAGS_IMMUTABLETYPE | -#endif - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE | __Pyx_TPFLAGS_HAVE_AM_SEND, - __pyx_GeneratorType_slots -}; -#if __PYX_HAS_PY_AM_SEND == 2 -static __Pyx_PyAsyncMethodsStruct __pyx_Generator_as_async; -#endif -static int __pyx_Generator_init(PyObject *module) { - __pyx_mstatetype *mstate = __Pyx_PyModule_GetState(module); - mstate->__pyx_GeneratorType = __Pyx_FetchCommonTypeFromSpec( - mstate->__pyx_CommonTypesMetaclassType, module, &__pyx_GeneratorType_spec, NULL); - if (unlikely(!mstate->__pyx_GeneratorType)) { - return -1; - } -#if __PYX_HAS_PY_AM_SEND == 2 - __Pyx_SetBackportTypeAmSend(mstate->__pyx_GeneratorType, &__pyx_Generator_as_async, &__Pyx_Coroutine_AmSend); -#endif - return 0; -} -static PyObject *__Pyx_Generator_GetInlinedResult(PyObject *self) { - __pyx_CoroutineObject *gen = (__pyx_CoroutineObject*) self; - PyObject *retval = NULL; - if (unlikely(__Pyx_Coroutine_test_and_set_is_running(gen))) { - return __Pyx_Coroutine_AlreadyRunningError(gen); - } - __Pyx_PySendResult result = __Pyx_Coroutine_SendEx(gen, Py_None, &retval, 0); - __Pyx_Coroutine_unset_is_running(gen); - (void) result; - assert (result == PYGEN_RETURN || result == PYGEN_ERROR); - assert ((result == PYGEN_RETURN && retval != NULL) || (result == PYGEN_ERROR && retval == NULL)); - return retval; -} - -/* GetRuntimeVersion */ -static unsigned long __Pyx_get_runtime_version(void) { -#if __PYX_LIMITED_VERSION_HEX >= 0x030b0000 - return Py_Version & ~0xFFUL; -#else - static unsigned long __Pyx_cached_runtime_version = 0; - if (__Pyx_cached_runtime_version == 0) { - const char* rt_version = Py_GetVersion(); - unsigned long version = 0; - unsigned long factor = 0x01000000UL; - unsigned int digit = 0; - int i = 0; - while (factor) { - while ('0' <= rt_version[i] && rt_version[i] <= '9') { - digit = digit * 10 + (unsigned int) (rt_version[i] - '0'); - ++i; - } - version += factor * digit; - if (rt_version[i] != '.') - break; - digit = 0; - factor >>= 8; - ++i; - } - __Pyx_cached_runtime_version = version; - } - return __Pyx_cached_runtime_version; -#endif -} - -/* CheckBinaryVersion */ -static int __Pyx_check_binary_version(unsigned long ct_version, unsigned long rt_version, int allow_newer) { - const unsigned long MAJOR_MINOR = 0xFFFF0000UL; - if ((rt_version & MAJOR_MINOR) == (ct_version & MAJOR_MINOR)) - return 0; - if (likely(allow_newer && (rt_version & MAJOR_MINOR) > (ct_version & MAJOR_MINOR))) - return 1; - { - char message[200]; - PyOS_snprintf(message, sizeof(message), - "compile time Python version %d.%d " - "of module '%.100s' " - "%s " - "runtime version %d.%d", - (int) (ct_version >> 24), (int) ((ct_version >> 16) & 0xFF), - __Pyx_MODULE_NAME, - (allow_newer) ? "was newer than" : "does not match", - (int) (rt_version >> 24), (int) ((rt_version >> 16) & 0xFF) - ); - return PyErr_WarnEx(NULL, message, 1); - } -} - -/* NewCodeObj */ -#if CYTHON_COMPILING_IN_LIMITED_API - static PyObject* __Pyx__PyCode_New(int a, int p, int k, int l, int s, int f, - PyObject *code, PyObject *c, PyObject* n, PyObject *v, - PyObject *fv, PyObject *cell, PyObject* fn, - PyObject *name, int fline, PyObject *lnos) { - PyObject *exception_table = NULL; - PyObject *types_module=NULL, *code_type=NULL, *result=NULL; - #if __PYX_LIMITED_VERSION_HEX < 0x030b0000 - PyObject *version_info; - PyObject *py_minor_version = NULL; - #endif - long minor_version = 0; - PyObject *type, *value, *traceback; - PyErr_Fetch(&type, &value, &traceback); - #if __PYX_LIMITED_VERSION_HEX >= 0x030b0000 - minor_version = 11; - #else - if (!(version_info = PySys_GetObject("version_info"))) goto end; - if (!(py_minor_version = PySequence_GetItem(version_info, 1))) goto end; - minor_version = PyLong_AsLong(py_minor_version); - Py_DECREF(py_minor_version); - if (minor_version == -1 && PyErr_Occurred()) goto end; - #endif - if (!(types_module = PyImport_ImportModule("types"))) goto end; - if (!(code_type = PyObject_GetAttrString(types_module, "CodeType"))) goto end; - if (minor_version <= 7) { - (void)p; - result = PyObject_CallFunction(code_type, "iiiiiOOOOOOiOOO", a, k, l, s, f, code, - c, n, v, fn, name, fline, lnos, fv, cell); - } else if (minor_version <= 10) { - result = PyObject_CallFunction(code_type, "iiiiiiOOOOOOiOOO", a,p, k, l, s, f, code, - c, n, v, fn, name, fline, lnos, fv, cell); - } else { - if (!(exception_table = PyBytes_FromStringAndSize(NULL, 0))) goto end; - result = PyObject_CallFunction(code_type, "iiiiiiOOOOOOOiOOOO", a,p, k, l, s, f, code, - c, n, v, fn, name, name, fline, lnos, exception_table, fv, cell); - } - end: - Py_XDECREF(code_type); - Py_XDECREF(exception_table); - Py_XDECREF(types_module); - if (type) { - PyErr_Restore(type, value, traceback); - } - return result; - } -#elif PY_VERSION_HEX >= 0x030B0000 - static PyCodeObject* __Pyx__PyCode_New(int a, int p, int k, int l, int s, int f, - PyObject *code, PyObject *c, PyObject* n, PyObject *v, - PyObject *fv, PyObject *cell, PyObject* fn, - PyObject *name, int fline, PyObject *lnos) { - PyCodeObject *result; - result = - #if PY_VERSION_HEX >= 0x030C0000 - PyUnstable_Code_NewWithPosOnlyArgs - #else - PyCode_NewWithPosOnlyArgs - #endif - (a, p, k, l, s, f, code, c, n, v, fv, cell, fn, name, name, fline, lnos, __pyx_mstate_global->__pyx_empty_bytes); - #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030c00A1 - if (likely(result)) - result->_co_firsttraceable = 0; - #endif - return result; - } -#elif PY_VERSION_HEX >= 0x030800B2 && !CYTHON_COMPILING_IN_PYPY - #define __Pyx__PyCode_New(a, p, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)\ - PyCode_NewWithPosOnlyArgs(a, p, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) -#else - #define __Pyx__PyCode_New(a, p, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)\ - PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) -#endif -static PyObject* __Pyx_PyCode_New( - const __Pyx_PyCode_New_function_description descr, - PyObject * const *varnames, - PyObject *filename, - PyObject *funcname, - const char *line_table, - PyObject *tuple_dedup_map -) { - PyObject *code_obj = NULL, *varnames_tuple_dedup = NULL, *code_bytes = NULL, *line_table_bytes = NULL; - Py_ssize_t var_count = (Py_ssize_t) descr.nlocals; - PyObject *varnames_tuple = PyTuple_New(var_count); - if (unlikely(!varnames_tuple)) return NULL; - for (Py_ssize_t i=0; i < var_count; i++) { - Py_INCREF(varnames[i]); - if (__Pyx_PyTuple_SET_ITEM(varnames_tuple, i, varnames[i]) != (0)) goto done; - } - #if CYTHON_COMPILING_IN_LIMITED_API - varnames_tuple_dedup = PyDict_GetItem(tuple_dedup_map, varnames_tuple); - if (!varnames_tuple_dedup) { - if (unlikely(PyDict_SetItem(tuple_dedup_map, varnames_tuple, varnames_tuple) < 0)) goto done; - varnames_tuple_dedup = varnames_tuple; - } - #else - varnames_tuple_dedup = PyDict_SetDefault(tuple_dedup_map, varnames_tuple, varnames_tuple); - if (unlikely(!varnames_tuple_dedup)) goto done; - #endif - #if CYTHON_AVOID_BORROWED_REFS - Py_INCREF(varnames_tuple_dedup); - #endif - if (__PYX_LIMITED_VERSION_HEX >= (0x030b0000) && line_table != NULL - && !CYTHON_COMPILING_IN_GRAAL) { - line_table_bytes = PyBytes_FromStringAndSize(line_table, descr.line_table_length); - if (unlikely(!line_table_bytes)) goto done; - Py_ssize_t code_len = (descr.line_table_length * 2 + 4) & ~3; - code_bytes = PyBytes_FromStringAndSize(NULL, code_len); - if (unlikely(!code_bytes)) goto done; - char* c_code_bytes = PyBytes_AsString(code_bytes); - if (unlikely(!c_code_bytes)) goto done; - memset(c_code_bytes, 0, (size_t) code_len); - } - code_obj = (PyObject*) __Pyx__PyCode_New( - (int) descr.argcount, - (int) descr.num_posonly_args, - (int) descr.num_kwonly_args, - (int) descr.nlocals, - 0, - (int) descr.flags, - code_bytes ? code_bytes : __pyx_mstate_global->__pyx_empty_bytes, - __pyx_mstate_global->__pyx_empty_tuple, - __pyx_mstate_global->__pyx_empty_tuple, - varnames_tuple_dedup, - __pyx_mstate_global->__pyx_empty_tuple, - __pyx_mstate_global->__pyx_empty_tuple, - filename, - funcname, - (int) descr.first_line, - (__PYX_LIMITED_VERSION_HEX >= (0x030b0000) && line_table_bytes) ? line_table_bytes : __pyx_mstate_global->__pyx_empty_bytes - ); -done: - Py_XDECREF(code_bytes); - Py_XDECREF(line_table_bytes); - #if CYTHON_AVOID_BORROWED_REFS - Py_XDECREF(varnames_tuple_dedup); - #endif - Py_DECREF(varnames_tuple); - return code_obj; -} - -/* InitStrings */ -static int __Pyx_InitStrings(__Pyx_StringTabEntry const *t, PyObject **target, const char* const* encoding_names) { - while (t->s) { - PyObject *str; - if (t->is_unicode) { - if (t->intern) { - str = PyUnicode_InternFromString(t->s); - } else if (t->encoding) { - str = PyUnicode_Decode(t->s, t->n - 1, encoding_names[t->encoding], NULL); - } else { - str = PyUnicode_FromStringAndSize(t->s, t->n - 1); - } - } else { - str = PyBytes_FromStringAndSize(t->s, t->n - 1); - } - if (!str) - return -1; - *target = str; - if (PyObject_Hash(str) == -1) - return -1; - ++t; - ++target; - } - return 0; -} - -#include -static CYTHON_INLINE Py_ssize_t __Pyx_ssize_strlen(const char *s) { - size_t len = strlen(s); - if (unlikely(len > (size_t) PY_SSIZE_T_MAX)) { - PyErr_SetString(PyExc_OverflowError, "byte string is too long"); - return -1; - } - return (Py_ssize_t) len; -} -static CYTHON_INLINE PyObject* __Pyx_PyUnicode_FromString(const char* c_str) { - Py_ssize_t len = __Pyx_ssize_strlen(c_str); - if (unlikely(len < 0)) return NULL; - return __Pyx_PyUnicode_FromStringAndSize(c_str, len); -} -static CYTHON_INLINE PyObject* __Pyx_PyByteArray_FromString(const char* c_str) { - Py_ssize_t len = __Pyx_ssize_strlen(c_str); - if (unlikely(len < 0)) return NULL; - return PyByteArray_FromStringAndSize(c_str, len); -} -static CYTHON_INLINE const char* __Pyx_PyObject_AsString(PyObject* o) { - Py_ssize_t ignore; - return __Pyx_PyObject_AsStringAndSize(o, &ignore); -} -#if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII || __PYX_DEFAULT_STRING_ENCODING_IS_UTF8 -static CYTHON_INLINE const char* __Pyx_PyUnicode_AsStringAndSize(PyObject* o, Py_ssize_t *length) { - if (unlikely(__Pyx_PyUnicode_READY(o) == -1)) return NULL; -#if CYTHON_COMPILING_IN_LIMITED_API - { - const char* result; - Py_ssize_t unicode_length; - CYTHON_MAYBE_UNUSED_VAR(unicode_length); // only for __PYX_DEFAULT_STRING_ENCODING_IS_ASCII - #if __PYX_LIMITED_VERSION_HEX < 0x030A0000 - if (unlikely(PyArg_Parse(o, "s#", &result, length) < 0)) return NULL; - #else - result = PyUnicode_AsUTF8AndSize(o, length); - #endif - #if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII - unicode_length = PyUnicode_GetLength(o); - if (unlikely(unicode_length < 0)) return NULL; - if (unlikely(unicode_length != *length)) { - PyUnicode_AsASCIIString(o); - return NULL; - } - #endif - return result; - } -#else -#if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII - if (likely(PyUnicode_IS_ASCII(o))) { - *length = PyUnicode_GET_LENGTH(o); - return PyUnicode_AsUTF8(o); - } else { - PyUnicode_AsASCIIString(o); - return NULL; - } -#else - return PyUnicode_AsUTF8AndSize(o, length); -#endif -#endif -} -#endif -static CYTHON_INLINE const char* __Pyx_PyObject_AsStringAndSize(PyObject* o, Py_ssize_t *length) { -#if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII || __PYX_DEFAULT_STRING_ENCODING_IS_UTF8 - if (PyUnicode_Check(o)) { - return __Pyx_PyUnicode_AsStringAndSize(o, length); - } else -#endif - if (PyByteArray_Check(o)) { -#if (CYTHON_ASSUME_SAFE_SIZE && CYTHON_ASSUME_SAFE_MACROS) || (CYTHON_COMPILING_IN_PYPY && (defined(PyByteArray_AS_STRING) && defined(PyByteArray_GET_SIZE))) - *length = PyByteArray_GET_SIZE(o); - return PyByteArray_AS_STRING(o); -#else - *length = PyByteArray_Size(o); - if (*length == -1) return NULL; - return PyByteArray_AsString(o); -#endif - } else - { - char* result; - int r = PyBytes_AsStringAndSize(o, &result, length); - if (unlikely(r < 0)) { - return NULL; - } else { - return result; - } - } -} -static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject* x) { - int is_true = x == Py_True; - if (is_true | (x == Py_False) | (x == Py_None)) return is_true; - else return PyObject_IsTrue(x); -} -static CYTHON_INLINE int __Pyx_PyObject_IsTrueAndDecref(PyObject* x) { - int retval; - if (unlikely(!x)) return -1; - retval = __Pyx_PyObject_IsTrue(x); - Py_DECREF(x); - return retval; -} -static PyObject* __Pyx_PyNumber_LongWrongResultType(PyObject* result) { - __Pyx_TypeName result_type_name = __Pyx_PyType_GetFullyQualifiedName(Py_TYPE(result)); - if (PyLong_Check(result)) { - if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, - "__int__ returned non-int (type " __Pyx_FMT_TYPENAME "). " - "The ability to return an instance of a strict subclass of int is deprecated, " - "and may be removed in a future version of Python.", - result_type_name)) { - __Pyx_DECREF_TypeName(result_type_name); - Py_DECREF(result); - return NULL; - } - __Pyx_DECREF_TypeName(result_type_name); - return result; - } - PyErr_Format(PyExc_TypeError, - "__int__ returned non-int (type " __Pyx_FMT_TYPENAME ")", - result_type_name); - __Pyx_DECREF_TypeName(result_type_name); - Py_DECREF(result); - return NULL; -} -static CYTHON_INLINE PyObject* __Pyx_PyNumber_Long(PyObject* x) { -#if CYTHON_USE_TYPE_SLOTS - PyNumberMethods *m; -#endif - PyObject *res = NULL; - if (likely(PyLong_Check(x))) - return __Pyx_NewRef(x); -#if CYTHON_USE_TYPE_SLOTS - m = Py_TYPE(x)->tp_as_number; - if (likely(m && m->nb_int)) { - res = m->nb_int(x); - } -#else - if (!PyBytes_CheckExact(x) && !PyUnicode_CheckExact(x)) { - res = PyNumber_Long(x); - } -#endif - if (likely(res)) { - if (unlikely(!PyLong_CheckExact(res))) { - return __Pyx_PyNumber_LongWrongResultType(res); - } - } - else if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_TypeError, - "an integer is required"); - } - return res; -} -static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject* b) { - Py_ssize_t ival; - PyObject *x; - if (likely(PyLong_CheckExact(b))) { - #if CYTHON_USE_PYLONG_INTERNALS - if (likely(__Pyx_PyLong_IsCompact(b))) { - return __Pyx_PyLong_CompactValue(b); - } else { - const digit* digits = __Pyx_PyLong_Digits(b); - const Py_ssize_t size = __Pyx_PyLong_SignedDigitCount(b); - switch (size) { - case 2: - if (8 * sizeof(Py_ssize_t) > 2 * PyLong_SHIFT) { - return (Py_ssize_t) (((((size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0])); - } - break; - case -2: - if (8 * sizeof(Py_ssize_t) > 2 * PyLong_SHIFT) { - return -(Py_ssize_t) (((((size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0])); - } - break; - case 3: - if (8 * sizeof(Py_ssize_t) > 3 * PyLong_SHIFT) { - return (Py_ssize_t) (((((((size_t)digits[2]) << PyLong_SHIFT) | (size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0])); - } - break; - case -3: - if (8 * sizeof(Py_ssize_t) > 3 * PyLong_SHIFT) { - return -(Py_ssize_t) (((((((size_t)digits[2]) << PyLong_SHIFT) | (size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0])); - } - break; - case 4: - if (8 * sizeof(Py_ssize_t) > 4 * PyLong_SHIFT) { - return (Py_ssize_t) (((((((((size_t)digits[3]) << PyLong_SHIFT) | (size_t)digits[2]) << PyLong_SHIFT) | (size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0])); - } - break; - case -4: - if (8 * sizeof(Py_ssize_t) > 4 * PyLong_SHIFT) { - return -(Py_ssize_t) (((((((((size_t)digits[3]) << PyLong_SHIFT) | (size_t)digits[2]) << PyLong_SHIFT) | (size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0])); - } - break; - } - } - #endif - return PyLong_AsSsize_t(b); - } - x = PyNumber_Index(b); - if (!x) return -1; - ival = PyLong_AsSsize_t(x); - Py_DECREF(x); - return ival; -} -static CYTHON_INLINE Py_hash_t __Pyx_PyIndex_AsHash_t(PyObject* o) { - if (sizeof(Py_hash_t) == sizeof(Py_ssize_t)) { - return (Py_hash_t) __Pyx_PyIndex_AsSsize_t(o); - } else { - Py_ssize_t ival; - PyObject *x; - x = PyNumber_Index(o); - if (!x) return -1; - ival = PyLong_AsLong(x); - Py_DECREF(x); - return ival; - } -} -static CYTHON_INLINE PyObject *__Pyx_Owned_Py_None(int b) { - CYTHON_UNUSED_VAR(b); - return __Pyx_NewRef(Py_None); -} -static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b) { - return b ? __Pyx_NewRef(Py_True) : __Pyx_NewRef(Py_False); -} -static CYTHON_INLINE PyObject * __Pyx_PyLong_FromSize_t(size_t ival) { - return PyLong_FromSize_t(ival); -} - - -/* MultiPhaseInitModuleState */ -#if CYTHON_PEP489_MULTI_PHASE_INIT && CYTHON_USE_MODULE_STATE -#ifndef CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE -#if (CYTHON_COMPILING_IN_LIMITED_API || PY_VERSION_HEX >= 0x030C0000) - #define CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE 1 -#else - #define CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE 0 -#endif -#endif -#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE && !CYTHON_ATOMICS -#error "Module state with PEP489 requires atomics. Currently that's one of\ - C11, C++11, gcc atomic intrinsics or MSVC atomic intrinsics" -#endif -#if !CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE -#define __Pyx_ModuleStateLookup_Lock() -#define __Pyx_ModuleStateLookup_Unlock() -#elif !CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x030d0000 -static PyMutex __Pyx_ModuleStateLookup_mutex = {0}; -#define __Pyx_ModuleStateLookup_Lock() PyMutex_Lock(&__Pyx_ModuleStateLookup_mutex) -#define __Pyx_ModuleStateLookup_Unlock() PyMutex_Unlock(&__Pyx_ModuleStateLookup_mutex) -#elif defined(__cplusplus) && __cplusplus >= 201103L -#include -static std::mutex __Pyx_ModuleStateLookup_mutex; -#define __Pyx_ModuleStateLookup_Lock() __Pyx_ModuleStateLookup_mutex.lock() -#define __Pyx_ModuleStateLookup_Unlock() __Pyx_ModuleStateLookup_mutex.unlock() -#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ > 201112L) && !defined(__STDC_NO_THREADS__) -#include -static mtx_t __Pyx_ModuleStateLookup_mutex; -static once_flag __Pyx_ModuleStateLookup_mutex_once_flag = ONCE_FLAG_INIT; -static void __Pyx_ModuleStateLookup_initialize_mutex(void) { - mtx_init(&__Pyx_ModuleStateLookup_mutex, mtx_plain); -} -#define __Pyx_ModuleStateLookup_Lock()\ - call_once(&__Pyx_ModuleStateLookup_mutex_once_flag, __Pyx_ModuleStateLookup_initialize_mutex);\ - mtx_lock(&__Pyx_ModuleStateLookup_mutex) -#define __Pyx_ModuleStateLookup_Unlock() mtx_unlock(&__Pyx_ModuleStateLookup_mutex) -#elif defined(HAVE_PTHREAD_H) -#include -static pthread_mutex_t __Pyx_ModuleStateLookup_mutex = PTHREAD_MUTEX_INITIALIZER; -#define __Pyx_ModuleStateLookup_Lock() pthread_mutex_lock(&__Pyx_ModuleStateLookup_mutex) -#define __Pyx_ModuleStateLookup_Unlock() pthread_mutex_unlock(&__Pyx_ModuleStateLookup_mutex) -#elif defined(_WIN32) -#include // synchapi.h on its own doesn't work -static SRWLOCK __Pyx_ModuleStateLookup_mutex = SRWLOCK_INIT; -#define __Pyx_ModuleStateLookup_Lock() AcquireSRWLockExclusive(&__Pyx_ModuleStateLookup_mutex) -#define __Pyx_ModuleStateLookup_Unlock() ReleaseSRWLockExclusive(&__Pyx_ModuleStateLookup_mutex) -#else -#error "No suitable lock available for CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE.\ - Requires C standard >= C11, or C++ standard >= C++11,\ - or pthreads, or the Windows 32 API, or Python >= 3.13." -#endif -typedef struct { - int64_t id; - PyObject *module; -} __Pyx_InterpreterIdAndModule; -typedef struct { - char interpreter_id_as_index; - Py_ssize_t count; - Py_ssize_t allocated; - __Pyx_InterpreterIdAndModule table[1]; -} __Pyx_ModuleStateLookupData; -#define __PYX_MODULE_STATE_LOOKUP_SMALL_SIZE 32 -#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE -static __pyx_atomic_int_type __Pyx_ModuleStateLookup_read_counter = 0; -#endif -#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE -static __pyx_atomic_ptr_type __Pyx_ModuleStateLookup_data = 0; -#else -static __Pyx_ModuleStateLookupData* __Pyx_ModuleStateLookup_data = NULL; -#endif -static __Pyx_InterpreterIdAndModule* __Pyx_State_FindModuleStateLookupTableLowerBound( - __Pyx_InterpreterIdAndModule* table, - Py_ssize_t count, - int64_t interpreterId) { - __Pyx_InterpreterIdAndModule* begin = table; - __Pyx_InterpreterIdAndModule* end = begin + count; - if (begin->id == interpreterId) { - return begin; - } - while ((end - begin) > __PYX_MODULE_STATE_LOOKUP_SMALL_SIZE) { - __Pyx_InterpreterIdAndModule* halfway = begin + (end - begin)/2; - if (halfway->id == interpreterId) { - return halfway; - } - if (halfway->id < interpreterId) { - begin = halfway; - } else { - end = halfway; - } - } - for (; begin < end; ++begin) { - if (begin->id >= interpreterId) return begin; - } - return begin; -} -static PyObject *__Pyx_State_FindModule(CYTHON_UNUSED void* dummy) { - int64_t interpreter_id = PyInterpreterState_GetID(__Pyx_PyInterpreterState_Get()); - if (interpreter_id == -1) return NULL; -#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE - __Pyx_ModuleStateLookupData* data = (__Pyx_ModuleStateLookupData*)__pyx_atomic_pointer_load_relaxed(&__Pyx_ModuleStateLookup_data); - { - __pyx_atomic_incr_acq_rel(&__Pyx_ModuleStateLookup_read_counter); - if (likely(data)) { - __Pyx_ModuleStateLookupData* new_data = (__Pyx_ModuleStateLookupData*)__pyx_atomic_pointer_load_acquire(&__Pyx_ModuleStateLookup_data); - if (likely(data == new_data)) { - goto read_finished; - } - } - __pyx_atomic_decr_acq_rel(&__Pyx_ModuleStateLookup_read_counter); - __Pyx_ModuleStateLookup_Lock(); - __pyx_atomic_incr_relaxed(&__Pyx_ModuleStateLookup_read_counter); - data = (__Pyx_ModuleStateLookupData*)__pyx_atomic_pointer_load_relaxed(&__Pyx_ModuleStateLookup_data); - __Pyx_ModuleStateLookup_Unlock(); - } - read_finished:; -#else - __Pyx_ModuleStateLookupData* data = __Pyx_ModuleStateLookup_data; -#endif - __Pyx_InterpreterIdAndModule* found = NULL; - if (unlikely(!data)) goto end; - if (data->interpreter_id_as_index) { - if (interpreter_id < data->count) { - found = data->table+interpreter_id; - } - } else { - found = __Pyx_State_FindModuleStateLookupTableLowerBound( - data->table, data->count, interpreter_id); - } - end: - { - PyObject *result=NULL; - if (found && found->id == interpreter_id) { - result = found->module; - } -#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE - __pyx_atomic_decr_acq_rel(&__Pyx_ModuleStateLookup_read_counter); -#endif - return result; - } -} -#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE -static void __Pyx_ModuleStateLookup_wait_until_no_readers(void) { - while (__pyx_atomic_load(&__Pyx_ModuleStateLookup_read_counter) != 0); -} -#else -#define __Pyx_ModuleStateLookup_wait_until_no_readers() -#endif -static int __Pyx_State_AddModuleInterpIdAsIndex(__Pyx_ModuleStateLookupData **old_data, PyObject* module, int64_t interpreter_id) { - Py_ssize_t to_allocate = (*old_data)->allocated; - while (to_allocate <= interpreter_id) { - if (to_allocate == 0) to_allocate = 1; - else to_allocate *= 2; - } - __Pyx_ModuleStateLookupData *new_data = *old_data; - if (to_allocate != (*old_data)->allocated) { - new_data = (__Pyx_ModuleStateLookupData *)realloc( - *old_data, - sizeof(__Pyx_ModuleStateLookupData)+(to_allocate-1)*sizeof(__Pyx_InterpreterIdAndModule)); - if (!new_data) { - PyErr_NoMemory(); - return -1; - } - for (Py_ssize_t i = new_data->allocated; i < to_allocate; ++i) { - new_data->table[i].id = i; - new_data->table[i].module = NULL; - } - new_data->allocated = to_allocate; - } - new_data->table[interpreter_id].module = module; - if (new_data->count < interpreter_id+1) { - new_data->count = interpreter_id+1; - } - *old_data = new_data; - return 0; -} -static void __Pyx_State_ConvertFromInterpIdAsIndex(__Pyx_ModuleStateLookupData *data) { - __Pyx_InterpreterIdAndModule *read = data->table; - __Pyx_InterpreterIdAndModule *write = data->table; - __Pyx_InterpreterIdAndModule *end = read + data->count; - for (; readmodule) { - write->id = read->id; - write->module = read->module; - ++write; - } - } - data->count = write - data->table; - for (; writeid = 0; - write->module = NULL; - } - data->interpreter_id_as_index = 0; -} -static int __Pyx_State_AddModule(PyObject* module, CYTHON_UNUSED void* dummy) { - int64_t interpreter_id = PyInterpreterState_GetID(__Pyx_PyInterpreterState_Get()); - if (interpreter_id == -1) return -1; - int result = 0; - __Pyx_ModuleStateLookup_Lock(); -#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE - __Pyx_ModuleStateLookupData *old_data = (__Pyx_ModuleStateLookupData *) - __pyx_atomic_pointer_exchange(&__Pyx_ModuleStateLookup_data, 0); -#else - __Pyx_ModuleStateLookupData *old_data = __Pyx_ModuleStateLookup_data; -#endif - __Pyx_ModuleStateLookupData *new_data = old_data; - if (!new_data) { - new_data = (__Pyx_ModuleStateLookupData *)calloc(1, sizeof(__Pyx_ModuleStateLookupData)); - if (!new_data) { - result = -1; - PyErr_NoMemory(); - goto end; - } - new_data->allocated = 1; - new_data->interpreter_id_as_index = 1; - } - __Pyx_ModuleStateLookup_wait_until_no_readers(); - if (new_data->interpreter_id_as_index) { - if (interpreter_id < __PYX_MODULE_STATE_LOOKUP_SMALL_SIZE) { - result = __Pyx_State_AddModuleInterpIdAsIndex(&new_data, module, interpreter_id); - goto end; - } - __Pyx_State_ConvertFromInterpIdAsIndex(new_data); - } - { - Py_ssize_t insert_at = 0; - { - __Pyx_InterpreterIdAndModule* lower_bound = __Pyx_State_FindModuleStateLookupTableLowerBound( - new_data->table, new_data->count, interpreter_id); - assert(lower_bound); - insert_at = lower_bound - new_data->table; - if (unlikely(insert_at < new_data->count && lower_bound->id == interpreter_id)) { - lower_bound->module = module; - goto end; // already in table, nothing more to do - } - } - if (new_data->count+1 >= new_data->allocated) { - Py_ssize_t to_allocate = (new_data->count+1)*2; - new_data = - (__Pyx_ModuleStateLookupData*)realloc( - new_data, - sizeof(__Pyx_ModuleStateLookupData) + - (to_allocate-1)*sizeof(__Pyx_InterpreterIdAndModule)); - if (!new_data) { - result = -1; - new_data = old_data; - PyErr_NoMemory(); - goto end; - } - new_data->allocated = to_allocate; - } - ++new_data->count; - int64_t last_id = interpreter_id; - PyObject *last_module = module; - for (Py_ssize_t i=insert_at; icount; ++i) { - int64_t current_id = new_data->table[i].id; - new_data->table[i].id = last_id; - last_id = current_id; - PyObject *current_module = new_data->table[i].module; - new_data->table[i].module = last_module; - last_module = current_module; - } - } - end: -#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE - __pyx_atomic_pointer_exchange(&__Pyx_ModuleStateLookup_data, new_data); -#else - __Pyx_ModuleStateLookup_data = new_data; -#endif - __Pyx_ModuleStateLookup_Unlock(); - return result; -} -static int __Pyx_State_RemoveModule(CYTHON_UNUSED void* dummy) { - int64_t interpreter_id = PyInterpreterState_GetID(__Pyx_PyInterpreterState_Get()); - if (interpreter_id == -1) return -1; - __Pyx_ModuleStateLookup_Lock(); -#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE - __Pyx_ModuleStateLookupData *data = (__Pyx_ModuleStateLookupData *) - __pyx_atomic_pointer_exchange(&__Pyx_ModuleStateLookup_data, 0); -#else - __Pyx_ModuleStateLookupData *data = __Pyx_ModuleStateLookup_data; -#endif - if (data->interpreter_id_as_index) { - if (interpreter_id < data->count) { - data->table[interpreter_id].module = NULL; - } - goto done; - } - { - __Pyx_ModuleStateLookup_wait_until_no_readers(); - __Pyx_InterpreterIdAndModule* lower_bound = __Pyx_State_FindModuleStateLookupTableLowerBound( - data->table, data->count, interpreter_id); - if (!lower_bound) goto done; - if (lower_bound->id != interpreter_id) goto done; - __Pyx_InterpreterIdAndModule *end = data->table+data->count; - for (;lower_boundid = (lower_bound+1)->id; - lower_bound->module = (lower_bound+1)->module; - } - } - --data->count; - if (data->count == 0) { - free(data); - data = NULL; - } - done: -#if CYTHON_MODULE_STATE_LOOKUP_THREAD_SAFE - __pyx_atomic_pointer_exchange(&__Pyx_ModuleStateLookup_data, data); -#else - __Pyx_ModuleStateLookup_data = data; -#endif - __Pyx_ModuleStateLookup_Unlock(); - return 0; -} -#endif - -/* #### Code section: utility_code_pragmas_end ### */ -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - - - -/* #### Code section: end ### */ -#endif /* Py_PYTHON_H */ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/cu2qu.cpython-312-x86_64-linux-gnu.so b/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/cu2qu.cpython-312-x86_64-linux-gnu.so deleted file mode 100755 index a5b19957..00000000 Binary files a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/cu2qu.cpython-312-x86_64-linux-gnu.so and /dev/null differ diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/cu2qu.py b/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/cu2qu.py deleted file mode 100644 index 150c03fb..00000000 --- a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/cu2qu.py +++ /dev/null @@ -1,563 +0,0 @@ -# cython: language_level=3 -# distutils: define_macros=CYTHON_TRACE_NOGIL=1 - -# Copyright 2015 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -try: - import cython -except (AttributeError, ImportError): - # if cython not installed, use mock module with no-op decorators and types - from fontTools.misc import cython -COMPILED = cython.compiled - -import math - -from .errors import Error as Cu2QuError, ApproxNotFoundError - - -__all__ = ["curve_to_quadratic", "curves_to_quadratic"] - -MAX_N = 100 - -NAN = float("NaN") - - -@cython.cfunc -@cython.inline -@cython.returns(cython.double) -@cython.locals(v1=cython.complex, v2=cython.complex, result=cython.double) -def dot(v1, v2): - """Return the dot product of two vectors. - - Args: - v1 (complex): First vector. - v2 (complex): Second vector. - - Returns: - double: Dot product. - """ - result = (v1 * v2.conjugate()).real - # When vectors are perpendicular (i.e. dot product is 0), the above expression may - # yield slightly different results when running in pure Python vs C/Cython, - # both of which are correct within IEEE-754 floating-point precision. - # It's probably due to the different order of operations and roundings in each - # implementation. Because we are using the result in a denominator and catching - # ZeroDivisionError (see `calc_intersect`), it's best to normalize the result here. - if abs(result) < 1e-15: - result = 0.0 - return result - - -@cython.cfunc -@cython.locals(z=cython.complex, den=cython.double) -@cython.locals(zr=cython.double, zi=cython.double) -def _complex_div_by_real(z, den): - """Divide complex by real using Python's method (two separate divisions). - - This ensures bit-exact compatibility with Python's complex division, - avoiding C's multiply-by-reciprocal optimization that can cause 1 ULP differences - on some platforms/compilers (e.g. clang on macOS arm64). - - https://github.com/fonttools/fonttools/issues/3928 - """ - zr = z.real - zi = z.imag - return complex(zr / den, zi / den) - - -@cython.cfunc -@cython.inline -@cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex) -@cython.locals( - _1=cython.complex, _2=cython.complex, _3=cython.complex, _4=cython.complex -) -def calc_cubic_points(a, b, c, d): - _1 = d - _2 = _complex_div_by_real(c, 3.0) + d - _3 = _complex_div_by_real(b + c, 3.0) + _2 - _4 = a + d + c + b - return _1, _2, _3, _4 - - -@cython.cfunc -@cython.inline -@cython.locals( - p0=cython.complex, p1=cython.complex, p2=cython.complex, p3=cython.complex -) -@cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex) -def calc_cubic_parameters(p0, p1, p2, p3): - c = (p1 - p0) * 3.0 - b = (p2 - p1) * 3.0 - c - d = p0 - a = p3 - d - c - b - return a, b, c, d - - -@cython.cfunc -@cython.inline -@cython.locals( - p0=cython.complex, p1=cython.complex, p2=cython.complex, p3=cython.complex -) -def split_cubic_into_n_iter(p0, p1, p2, p3, n): - """Split a cubic Bezier into n equal parts. - - Splits the curve into `n` equal parts by curve time. - (t=0..1/n, t=1/n..2/n, ...) - - Args: - p0 (complex): Start point of curve. - p1 (complex): First handle of curve. - p2 (complex): Second handle of curve. - p3 (complex): End point of curve. - - Returns: - An iterator yielding the control points (four complex values) of the - subcurves. - """ - # Hand-coded special-cases - if n == 2: - return iter(split_cubic_into_two(p0, p1, p2, p3)) - if n == 3: - return iter(split_cubic_into_three(p0, p1, p2, p3)) - if n == 4: - a, b = split_cubic_into_two(p0, p1, p2, p3) - return iter( - split_cubic_into_two(a[0], a[1], a[2], a[3]) - + split_cubic_into_two(b[0], b[1], b[2], b[3]) - ) - if n == 6: - a, b = split_cubic_into_two(p0, p1, p2, p3) - return iter( - split_cubic_into_three(a[0], a[1], a[2], a[3]) - + split_cubic_into_three(b[0], b[1], b[2], b[3]) - ) - - return _split_cubic_into_n_gen(p0, p1, p2, p3, n) - - -@cython.locals( - p0=cython.complex, - p1=cython.complex, - p2=cython.complex, - p3=cython.complex, - n=cython.int, -) -@cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex) -@cython.locals( - dt=cython.double, delta_2=cython.double, delta_3=cython.double, i=cython.int -) -@cython.locals( - a1=cython.complex, b1=cython.complex, c1=cython.complex, d1=cython.complex -) -def _split_cubic_into_n_gen(p0, p1, p2, p3, n): - a, b, c, d = calc_cubic_parameters(p0, p1, p2, p3) - dt = 1 / n - delta_2 = dt * dt - delta_3 = dt * delta_2 - for i in range(n): - t1 = i * dt - t1_2 = t1 * t1 - # calc new a, b, c and d - a1 = a * delta_3 - b1 = (3 * a * t1 + b) * delta_2 - c1 = (2 * b * t1 + c + 3 * a * t1_2) * dt - d1 = a * t1 * t1_2 + b * t1_2 + c * t1 + d - yield calc_cubic_points(a1, b1, c1, d1) - - -@cython.cfunc -@cython.inline -@cython.locals( - p0=cython.complex, p1=cython.complex, p2=cython.complex, p3=cython.complex -) -@cython.locals(mid=cython.complex, deriv3=cython.complex) -def split_cubic_into_two(p0, p1, p2, p3): - """Split a cubic Bezier into two equal parts. - - Splits the curve into two equal parts at t = 0.5 - - Args: - p0 (complex): Start point of curve. - p1 (complex): First handle of curve. - p2 (complex): Second handle of curve. - p3 (complex): End point of curve. - - Returns: - tuple: Two cubic Beziers (each expressed as a tuple of four complex - values). - """ - mid = (p0 + 3 * (p1 + p2) + p3) * 0.125 - deriv3 = (p3 + p2 - p1 - p0) * 0.125 - return ( - (p0, (p0 + p1) * 0.5, mid - deriv3, mid), - (mid, mid + deriv3, (p2 + p3) * 0.5, p3), - ) - - -@cython.cfunc -@cython.inline -@cython.locals( - p0=cython.complex, - p1=cython.complex, - p2=cython.complex, - p3=cython.complex, -) -@cython.locals( - mid1=cython.complex, - deriv1=cython.complex, - mid2=cython.complex, - deriv2=cython.complex, -) -def split_cubic_into_three(p0, p1, p2, p3): - """Split a cubic Bezier into three equal parts. - - Splits the curve into three equal parts at t = 1/3 and t = 2/3 - - Args: - p0 (complex): Start point of curve. - p1 (complex): First handle of curve. - p2 (complex): Second handle of curve. - p3 (complex): End point of curve. - - Returns: - tuple: Three cubic Beziers (each expressed as a tuple of four complex - values). - """ - mid1 = (8 * p0 + 12 * p1 + 6 * p2 + p3) * (1 / 27) - deriv1 = (p3 + 3 * p2 - 4 * p0) * (1 / 27) - mid2 = (p0 + 6 * p1 + 12 * p2 + 8 * p3) * (1 / 27) - deriv2 = (4 * p3 - 3 * p1 - p0) * (1 / 27) - return ( - (p0, (2 * p0 + p1) / 3.0, mid1 - deriv1, mid1), - (mid1, mid1 + deriv1, mid2 - deriv2, mid2), - (mid2, mid2 + deriv2, (p2 + 2 * p3) / 3.0, p3), - ) - - -@cython.cfunc -@cython.inline -@cython.returns(cython.complex) -@cython.locals( - t=cython.double, - p0=cython.complex, - p1=cython.complex, - p2=cython.complex, - p3=cython.complex, -) -@cython.locals(_p1=cython.complex, _p2=cython.complex) -def cubic_approx_control(t, p0, p1, p2, p3): - """Approximate a cubic Bezier using a quadratic one. - - Args: - t (double): Position of control point. - p0 (complex): Start point of curve. - p1 (complex): First handle of curve. - p2 (complex): Second handle of curve. - p3 (complex): End point of curve. - - Returns: - complex: Location of candidate control point on quadratic curve. - """ - _p1 = p0 + (p1 - p0) * 1.5 - _p2 = p3 + (p2 - p3) * 1.5 - return _p1 + (_p2 - _p1) * t - - -@cython.cfunc -@cython.inline -@cython.returns(cython.complex) -@cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex) -@cython.locals(ab=cython.complex, cd=cython.complex, p=cython.complex, h=cython.double) -def calc_intersect(a, b, c, d): - """Calculate the intersection of two lines. - - Args: - a (complex): Start point of first line. - b (complex): End point of first line. - c (complex): Start point of second line. - d (complex): End point of second line. - - Returns: - complex: Location of intersection if one present, ``complex(NaN,NaN)`` - if no intersection was found. - """ - ab = b - a - cd = d - c - p = ab * 1j - try: - h = dot(p, a - c) / dot(p, cd) - except ZeroDivisionError: - # if 3 or 4 points are equal, we do have an intersection despite the zero-div: - # return one of the off-curves so that the algorithm can attempt a one-curve - # solution if it's within tolerance: - # https://github.com/linebender/kurbo/pull/484 - if b == c and (a == b or c == d): - return b - return complex(NAN, NAN) - return c + cd * h - - -@cython.cfunc -@cython.returns(cython.int) -@cython.locals( - tolerance=cython.double, - p0=cython.complex, - p1=cython.complex, - p2=cython.complex, - p3=cython.complex, -) -@cython.locals(mid=cython.complex, deriv3=cython.complex) -def cubic_farthest_fit_inside(p0, p1, p2, p3, tolerance): - """Check if a cubic Bezier lies within a given distance of the origin. - - "Origin" means *the* origin (0,0), not the start of the curve. Note that no - checks are made on the start and end positions of the curve; this function - only checks the inside of the curve. - - Args: - p0 (complex): Start point of curve. - p1 (complex): First handle of curve. - p2 (complex): Second handle of curve. - p3 (complex): End point of curve. - tolerance (double): Distance from origin. - - Returns: - bool: True if the cubic Bezier ``p`` entirely lies within a distance - ``tolerance`` of the origin, False otherwise. - """ - # First check p2 then p1, as p2 has higher error early on. - if abs(p2) <= tolerance and abs(p1) <= tolerance: - return True - - # Split. - mid = (p0 + 3 * (p1 + p2) + p3) * 0.125 - if abs(mid) > tolerance: - return False - deriv3 = (p3 + p2 - p1 - p0) * 0.125 - return cubic_farthest_fit_inside( - p0, (p0 + p1) * 0.5, mid - deriv3, mid, tolerance - ) and cubic_farthest_fit_inside(mid, mid + deriv3, (p2 + p3) * 0.5, p3, tolerance) - - -@cython.cfunc -@cython.inline -@cython.locals(tolerance=cython.double) -@cython.locals( - q1=cython.complex, - c0=cython.complex, - c1=cython.complex, - c2=cython.complex, - c3=cython.complex, -) -def cubic_approx_quadratic(cubic, tolerance): - """Approximate a cubic Bezier with a single quadratic within a given tolerance. - - Args: - cubic (sequence): Four complex numbers representing control points of - the cubic Bezier curve. - tolerance (double): Permitted deviation from the original curve. - - Returns: - Three complex numbers representing control points of the quadratic - curve if it fits within the given tolerance, or ``None`` if no suitable - curve could be calculated. - """ - - q1 = calc_intersect(cubic[0], cubic[1], cubic[2], cubic[3]) - if math.isnan(q1.imag): - return None - c0 = cubic[0] - c3 = cubic[3] - c1 = c0 + (q1 - c0) * (2 / 3) - c2 = c3 + (q1 - c3) * (2 / 3) - if not cubic_farthest_fit_inside(0, c1 - cubic[1], c2 - cubic[2], 0, tolerance): - return None - return c0, q1, c3 - - -@cython.cfunc -@cython.locals(n=cython.int, tolerance=cython.double) -@cython.locals(i=cython.int) -@cython.locals(all_quadratic=cython.int) -@cython.locals( - c0=cython.complex, c1=cython.complex, c2=cython.complex, c3=cython.complex -) -@cython.locals( - q0=cython.complex, - q1=cython.complex, - next_q1=cython.complex, - q2=cython.complex, - d1=cython.complex, -) -def cubic_approx_spline(cubic, n, tolerance, all_quadratic): - """Approximate a cubic Bezier curve with a spline of n quadratics. - - Args: - cubic (sequence): Four complex numbers representing control points of - the cubic Bezier curve. - n (int): Number of quadratic Bezier curves in the spline. - tolerance (double): Permitted deviation from the original curve. - - Returns: - A list of ``n+2`` complex numbers, representing control points of the - quadratic spline if it fits within the given tolerance, or ``None`` if - no suitable spline could be calculated. - """ - - if n == 1: - return cubic_approx_quadratic(cubic, tolerance) - if n == 2 and all_quadratic == False: - return cubic - - cubics = split_cubic_into_n_iter(cubic[0], cubic[1], cubic[2], cubic[3], n) - - # calculate the spline of quadratics and check errors at the same time. - next_cubic = next(cubics) - next_q1 = cubic_approx_control( - 0, next_cubic[0], next_cubic[1], next_cubic[2], next_cubic[3] - ) - q2 = cubic[0] - d1 = 0j - spline = [cubic[0], next_q1] - for i in range(1, n + 1): - # Current cubic to convert - c0, c1, c2, c3 = next_cubic - - # Current quadratic approximation of current cubic - q0 = q2 - q1 = next_q1 - if i < n: - next_cubic = next(cubics) - next_q1 = cubic_approx_control( - i / (n - 1), next_cubic[0], next_cubic[1], next_cubic[2], next_cubic[3] - ) - spline.append(next_q1) - q2 = (q1 + next_q1) * 0.5 - else: - q2 = c3 - - # End-point deltas - d0 = d1 - d1 = q2 - c3 - - if abs(d1) > tolerance or not cubic_farthest_fit_inside( - d0, - q0 + (q1 - q0) * (2 / 3) - c1, - q2 + (q1 - q2) * (2 / 3) - c2, - d1, - tolerance, - ): - return None - spline.append(cubic[3]) - - return spline - - -@cython.locals(max_err=cython.double) -@cython.locals(n=cython.int) -@cython.locals(all_quadratic=cython.int) -def curve_to_quadratic(curve, max_err, all_quadratic=True): - """Approximate a cubic Bezier curve with a spline of n quadratics. - - Args: - cubic (sequence): Four 2D tuples representing control points of - the cubic Bezier curve. - max_err (double): Permitted deviation from the original curve. - all_quadratic (bool): If True (default) returned value is a - quadratic spline. If False, it's either a single quadratic - curve or a single cubic curve. - - Returns: - If all_quadratic is True: A list of 2D tuples, representing - control points of the quadratic spline if it fits within the - given tolerance, or ``None`` if no suitable spline could be - calculated. - - If all_quadratic is False: Either a quadratic curve (if length - of output is 3), or a cubic curve (if length of output is 4). - """ - - curve = [complex(*p) for p in curve] - - for n in range(1, MAX_N + 1): - spline = cubic_approx_spline(curve, n, max_err, all_quadratic) - if spline is not None: - # done. go home - return [(s.real, s.imag) for s in spline] - - raise ApproxNotFoundError(curve) - - -@cython.locals(l=cython.int, last_i=cython.int, i=cython.int) -@cython.locals(all_quadratic=cython.int) -def curves_to_quadratic(curves, max_errors, all_quadratic=True): - """Return quadratic Bezier splines approximating the input cubic Beziers. - - Args: - curves: A sequence of *n* curves, each curve being a sequence of four - 2D tuples. - max_errors: A sequence of *n* floats representing the maximum permissible - deviation from each of the cubic Bezier curves. - all_quadratic (bool): If True (default) returned values are a - quadratic spline. If False, they are either a single quadratic - curve or a single cubic curve. - - Example:: - - >>> curves_to_quadratic( [ - ... [ (50,50), (100,100), (150,100), (200,50) ], - ... [ (75,50), (120,100), (150,75), (200,60) ] - ... ], [1,1] ) - [[(50.0, 50.0), (75.0, 75.0), (125.0, 91.66666666666666), (175.0, 75.0), (200.0, 50.0)], [(75.0, 50.0), (97.5, 75.0), (135.41666666666666, 82.08333333333333), (175.0, 67.5), (200.0, 60.0)]] - - The returned splines have "implied oncurve points" suitable for use in - TrueType ``glif`` outlines - i.e. in the first spline returned above, - the first quadratic segment runs from (50,50) to - ( (75 + 125)/2 , (120 + 91.666..)/2 ) = (100, 83.333...). - - Returns: - If all_quadratic is True, a list of splines, each spline being a list - of 2D tuples. - - If all_quadratic is False, a list of curves, each curve being a quadratic - (length 3), or cubic (length 4). - - Raises: - fontTools.cu2qu.Errors.ApproxNotFoundError: if no suitable approximation - can be found for all curves with the given parameters. - """ - - curves = [[complex(*p) for p in curve] for curve in curves] - assert len(max_errors) == len(curves) - - l = len(curves) - splines = [None] * l - last_i = i = 0 - n = 1 - while True: - spline = cubic_approx_spline(curves[i], n, max_errors[i], all_quadratic) - if spline is None: - if n == MAX_N: - break - n += 1 - last_i = i - continue - splines[i] = spline - i = (i + 1) % l - if i == last_i: - # done. go home - return [[(s.real, s.imag) for s in spline] for spline in splines] - - raise ApproxNotFoundError(curves) diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/errors.py b/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/errors.py deleted file mode 100644 index fa3dc429..00000000 --- a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/errors.py +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright 2016 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -class Error(Exception): - """Base Cu2Qu exception class for all other errors.""" - - -class ApproxNotFoundError(Error): - def __init__(self, curve): - message = "no approximation found: %s" % curve - super().__init__(message) - self.curve = curve - - -class UnequalZipLengthsError(Error): - pass - - -class IncompatibleGlyphsError(Error): - def __init__(self, glyphs): - assert len(glyphs) > 1 - self.glyphs = glyphs - names = set(repr(g.name) for g in glyphs) - if len(names) > 1: - self.combined_name = "{%s}" % ", ".join(sorted(names)) - else: - self.combined_name = names.pop() - - def __repr__(self): - return "<%s %s>" % (type(self).__name__, self.combined_name) - - -class IncompatibleSegmentNumberError(IncompatibleGlyphsError): - def __str__(self): - return "Glyphs named %s have different number of segments" % ( - self.combined_name - ) - - -class IncompatibleSegmentTypesError(IncompatibleGlyphsError): - def __init__(self, glyphs, segments): - IncompatibleGlyphsError.__init__(self, glyphs) - self.segments = segments - - def __str__(self): - lines = [] - ndigits = len(str(max(self.segments))) - for i, tags in sorted(self.segments.items()): - lines.append( - "%s: (%s)" % (str(i).rjust(ndigits), ", ".join(repr(t) for t in tags)) - ) - return "Glyphs named %s have incompatible segment types:\n %s" % ( - self.combined_name, - "\n ".join(lines), - ) - - -class IncompatibleFontsError(Error): - def __init__(self, glyph_errors): - self.glyph_errors = glyph_errors - - def __str__(self): - return "fonts contains incompatible glyphs: %s" % ( - ", ".join(repr(g) for g in sorted(self.glyph_errors.keys())) - ) diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/ufo.py b/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/ufo.py deleted file mode 100644 index 7a6dbc67..00000000 --- a/.venv-docs/lib/python3.12/site-packages/fontTools/cu2qu/ufo.py +++ /dev/null @@ -1,349 +0,0 @@ -# Copyright 2015 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -"""Converts cubic bezier curves to quadratic splines. - -Conversion is performed such that the quadratic splines keep the same end-curve -tangents as the original cubics. The approach is iterative, increasing the -number of segments for a spline until the error gets below a bound. - -Respective curves from multiple fonts will be converted at once to ensure that -the resulting splines are interpolation-compatible. -""" - -import logging -from fontTools.pens.basePen import AbstractPen -from fontTools.pens.pointPen import PointToSegmentPen -from fontTools.pens.reverseContourPen import ReverseContourPen - -from . import curves_to_quadratic -from .errors import ( - UnequalZipLengthsError, - IncompatibleSegmentNumberError, - IncompatibleSegmentTypesError, - IncompatibleGlyphsError, - IncompatibleFontsError, -) - - -__all__ = ["fonts_to_quadratic", "font_to_quadratic"] - -# The default approximation error below is a relative value (1/1000 of the EM square). -# Later on, we convert it to absolute font units by multiplying it by a font's UPEM -# (see fonts_to_quadratic). -DEFAULT_MAX_ERR = 0.001 -CURVE_TYPE_LIB_KEY = "com.github.googlei18n.cu2qu.curve_type" - -logger = logging.getLogger(__name__) - - -_zip = zip - - -def zip(*args): - """Ensure each argument to zip has the same length. Also make sure a list is - returned for python 2/3 compatibility. - """ - - if len(set(len(a) for a in args)) != 1: - raise UnequalZipLengthsError(*args) - return list(_zip(*args)) - - -class GetSegmentsPen(AbstractPen): - """Pen to collect segments into lists of points for conversion. - - Curves always include their initial on-curve point, so some points are - duplicated between segments. - """ - - def __init__(self): - self._last_pt = None - self.segments = [] - - def _add_segment(self, tag, *args): - if tag in ["move", "line", "qcurve", "curve"]: - self._last_pt = args[-1] - self.segments.append((tag, args)) - - def moveTo(self, pt): - self._add_segment("move", pt) - - def lineTo(self, pt): - self._add_segment("line", pt) - - def qCurveTo(self, *points): - self._add_segment("qcurve", self._last_pt, *points) - - def curveTo(self, *points): - self._add_segment("curve", self._last_pt, *points) - - def closePath(self): - self._add_segment("close") - - def endPath(self): - self._add_segment("end") - - def addComponent(self, glyphName, transformation): - pass - - -def _get_segments(glyph): - """Get a glyph's segments as extracted by GetSegmentsPen.""" - - pen = GetSegmentsPen() - # glyph.draw(pen) - # We can't simply draw the glyph with the pen, but we must initialize the - # PointToSegmentPen explicitly with outputImpliedClosingLine=True. - # By default PointToSegmentPen does not outputImpliedClosingLine -- unless - # last and first point on closed contour are duplicated. Because we are - # converting multiple glyphs at the same time, we want to make sure - # this function returns the same number of segments, whether or not - # the last and first point overlap. - # https://github.com/googlefonts/fontmake/issues/572 - # https://github.com/fonttools/fonttools/pull/1720 - pointPen = PointToSegmentPen(pen, outputImpliedClosingLine=True) - glyph.drawPoints(pointPen) - return pen.segments - - -def _set_segments(glyph, segments, reverse_direction): - """Draw segments as extracted by GetSegmentsPen back to a glyph.""" - - glyph.clearContours() - pen = glyph.getPen() - if reverse_direction: - pen = ReverseContourPen(pen) - for tag, args in segments: - if tag == "move": - pen.moveTo(*args) - elif tag == "line": - pen.lineTo(*args) - elif tag == "curve": - pen.curveTo(*args[1:]) - elif tag == "qcurve": - pen.qCurveTo(*args[1:]) - elif tag == "close": - pen.closePath() - elif tag == "end": - pen.endPath() - else: - raise AssertionError('Unhandled segment type "%s"' % tag) - - -def _segments_to_quadratic(segments, max_err, stats, all_quadratic=True): - """Return quadratic approximations of cubic segments.""" - - assert all(s[0] == "curve" for s in segments), "Non-cubic given to convert" - - new_points = curves_to_quadratic([s[1] for s in segments], max_err, all_quadratic) - n = len(new_points[0]) - assert all(len(s) == n for s in new_points[1:]), "Converted incompatibly" - - spline_length = str(n - 2) - stats[spline_length] = stats.get(spline_length, 0) + 1 - - if all_quadratic or n == 3: - return [("qcurve", p) for p in new_points] - else: - return [("curve", p) for p in new_points] - - -def _glyphs_to_quadratic(glyphs, max_err, reverse_direction, stats, all_quadratic=True): - """Do the actual conversion of a set of compatible glyphs, after arguments - have been set up. - - Return True if the glyphs were modified, else return False. - """ - - try: - segments_by_location = zip(*[_get_segments(g) for g in glyphs]) - except UnequalZipLengthsError: - raise IncompatibleSegmentNumberError(glyphs) - if not any(segments_by_location): - return False - - # always modify input glyphs if reverse_direction is True - glyphs_modified = reverse_direction - - new_segments_by_location = [] - incompatible = {} - for i, segments in enumerate(segments_by_location): - tag = segments[0][0] - if not all(s[0] == tag for s in segments[1:]): - incompatible[i] = [s[0] for s in segments] - elif tag == "curve": - new_segments = _segments_to_quadratic( - segments, max_err, stats, all_quadratic - ) - if all_quadratic or new_segments != segments: - glyphs_modified = True - segments = new_segments - new_segments_by_location.append(segments) - - if glyphs_modified: - new_segments_by_glyph = zip(*new_segments_by_location) - for glyph, new_segments in zip(glyphs, new_segments_by_glyph): - _set_segments(glyph, new_segments, reverse_direction) - - if incompatible: - raise IncompatibleSegmentTypesError(glyphs, segments=incompatible) - return glyphs_modified - - -def glyphs_to_quadratic( - glyphs, max_err=None, reverse_direction=False, stats=None, all_quadratic=True -): - """Convert the curves of a set of compatible of glyphs to quadratic. - - All curves will be converted to quadratic at once, ensuring interpolation - compatibility. If this is not required, calling glyphs_to_quadratic with one - glyph at a time may yield slightly more optimized results. - - Return True if glyphs were modified, else return False. - - Raises IncompatibleGlyphsError if glyphs have non-interpolatable outlines. - """ - if stats is None: - stats = {} - - if not max_err: - # assume 1000 is the default UPEM - max_err = DEFAULT_MAX_ERR * 1000 - - if isinstance(max_err, (list, tuple)): - max_errors = max_err - else: - max_errors = [max_err] * len(glyphs) - assert len(max_errors) == len(glyphs) - - return _glyphs_to_quadratic( - glyphs, max_errors, reverse_direction, stats, all_quadratic - ) - - -def fonts_to_quadratic( - fonts, - max_err_em=None, - max_err=None, - reverse_direction=False, - stats=None, - dump_stats=False, - remember_curve_type=True, - all_quadratic=True, -): - """Convert the curves of a collection of fonts to quadratic. - - All curves will be converted to quadratic at once, ensuring interpolation - compatibility. If this is not required, calling fonts_to_quadratic with one - font at a time may yield slightly more optimized results. - - Return the set of modified glyph names if any, else return an empty set. - - By default, cu2qu stores the curve type in the fonts' lib, under a private - key "com.github.googlei18n.cu2qu.curve_type", and will not try to convert - them again if the curve type is already set to "quadratic". - Setting 'remember_curve_type' to False disables this optimization. - - Raises IncompatibleFontsError if same-named glyphs from different fonts - have non-interpolatable outlines. - """ - - if remember_curve_type: - curve_types = {f.lib.get(CURVE_TYPE_LIB_KEY, "cubic") for f in fonts} - if len(curve_types) == 1: - curve_type = next(iter(curve_types)) - if curve_type in ("quadratic", "mixed"): - logger.info("Curves already converted to quadratic") - return False - elif curve_type == "cubic": - pass # keep converting - else: - raise NotImplementedError(curve_type) - elif len(curve_types) > 1: - # going to crash later if they do differ - logger.warning("fonts may contain different curve types") - - if stats is None: - stats = {} - - if max_err_em and max_err: - raise TypeError("Only one of max_err and max_err_em can be specified.") - if not (max_err_em or max_err): - max_err_em = DEFAULT_MAX_ERR - - if isinstance(max_err, (list, tuple)): - assert len(max_err) == len(fonts) - max_errors = max_err - elif max_err: - max_errors = [max_err] * len(fonts) - - if isinstance(max_err_em, (list, tuple)): - assert len(fonts) == len(max_err_em) - max_errors = [f.info.unitsPerEm * e for f, e in zip(fonts, max_err_em)] - elif max_err_em: - max_errors = [f.info.unitsPerEm * max_err_em for f in fonts] - - modified = set() - glyph_errors = {} - for name in set().union(*(f.keys() for f in fonts)): - glyphs = [] - cur_max_errors = [] - for font, error in zip(fonts, max_errors): - if name in font: - glyphs.append(font[name]) - cur_max_errors.append(error) - try: - if _glyphs_to_quadratic( - glyphs, cur_max_errors, reverse_direction, stats, all_quadratic - ): - modified.add(name) - except IncompatibleGlyphsError as exc: - logger.error(exc) - glyph_errors[name] = exc - - if glyph_errors: - raise IncompatibleFontsError(glyph_errors) - - if modified and dump_stats: - spline_lengths = sorted(stats.keys()) - logger.info( - "New spline lengths: %s" - % (", ".join("%s: %d" % (l, stats[l]) for l in spline_lengths)) - ) - - if remember_curve_type: - for font in fonts: - curve_type = font.lib.get(CURVE_TYPE_LIB_KEY, "cubic") - new_curve_type = "quadratic" if all_quadratic else "mixed" - if curve_type != new_curve_type: - font.lib[CURVE_TYPE_LIB_KEY] = new_curve_type - return modified - - -def glyph_to_quadratic(glyph, **kwargs): - """Convenience wrapper around glyphs_to_quadratic, for just one glyph. - Return True if the glyph was modified, else return False. - """ - - return glyphs_to_quadratic([glyph], **kwargs) - - -def font_to_quadratic(font, **kwargs): - """Convenience wrapper around fonts_to_quadratic, for just one font. - Return the set of modified glyph names if any, else return empty set. - """ - - return fonts_to_quadratic([font], **kwargs) diff --git a/.venv-docs/lib/python3.12/site-packages/fontTools/designspaceLib/__init__.py b/.venv-docs/lib/python3.12/site-packages/fontTools/designspaceLib/__init__.py deleted file mode 100644 index 661f3405..00000000 --- a/.venv-docs/lib/python3.12/site-packages/fontTools/designspaceLib/__init__.py +++ /dev/null @@ -1,3338 +0,0 @@ -""" - designSpaceDocument - - - Read and write designspace files -""" - -from __future__ import annotations - -import collections -import copy -import itertools -import math -import os -import posixpath -from io import BytesIO, StringIO -from textwrap import indent -from typing import Any, Dict, List, MutableMapping, Optional, Tuple, Union, cast - -from fontTools.misc import etree as ET -from fontTools.misc import plistlib -from fontTools.misc.loggingTools import LogMixin -from fontTools.misc.textTools import tobytes, tostr - - -__all__ = [ - "AxisDescriptor", - "AxisLabelDescriptor", - "AxisMappingDescriptor", - "BaseDocReader", - "BaseDocWriter", - "DesignSpaceDocument", - "DesignSpaceDocumentError", - "DiscreteAxisDescriptor", - "InstanceDescriptor", - "LocationLabelDescriptor", - "RangeAxisSubsetDescriptor", - "RuleDescriptor", - "SourceDescriptor", - "ValueAxisSubsetDescriptor", - "VariableFontDescriptor", -] - -# ElementTree allows to find namespace-prefixed elements, but not attributes -# so we have to do it ourselves for 'xml:lang' -XML_NS = "{http://www.w3.org/XML/1998/namespace}" -XML_LANG = XML_NS + "lang" - - -def posix(path): - """Normalize paths using forward slash to work also on Windows.""" - new_path = posixpath.join(*path.split(os.path.sep)) - if path.startswith("/"): - # The above transformation loses absolute paths - new_path = "/" + new_path - elif path.startswith(r"\\"): - # The above transformation loses leading slashes of UNC path mounts - new_path = "//" + new_path - return new_path - - -def posixpath_property(private_name): - """Generate a propery that holds a path always using forward slashes.""" - - def getter(self): - # Normal getter - return getattr(self, private_name) - - def setter(self, value): - # The setter rewrites paths using forward slashes - if value is not None: - value = posix(value) - setattr(self, private_name, value) - - return property(getter, setter) - - -class DesignSpaceDocumentError(Exception): - def __init__(self, msg, obj=None): - self.msg = msg - self.obj = obj - - def __str__(self): - return str(self.msg) + (": %r" % self.obj if self.obj is not None else "") - - -class AsDictMixin(object): - def asdict(self): - d = {} - for attr, value in self.__dict__.items(): - if attr.startswith("_"): - continue - if hasattr(value, "asdict"): - value = value.asdict() - elif isinstance(value, list): - value = [v.asdict() if hasattr(v, "asdict") else v for v in value] - d[attr] = value - return d - - -class SimpleDescriptor(AsDictMixin): - """Containers for a bunch of attributes""" - - # XXX this is ugly. The 'print' is inappropriate here, and instead of - # assert, it should simply return True/False - def compare(self, other): - # test if this object contains the same data as the other - for attr in self._attrs: - try: - assert getattr(self, attr) == getattr(other, attr) - except AssertionError: - print( - "failed attribute", - attr, - getattr(self, attr), - "!=", - getattr(other, attr), - ) - - def __repr__(self): - attrs = [f"{a}={repr(getattr(self, a))}," for a in self._attrs] - attrs = indent("\n".join(attrs), " ") - return f"{self.__class__.__name__}(\n{attrs}\n)" - - -class SourceDescriptor(SimpleDescriptor): - """Simple container for data related to the source - - .. code:: python - - doc = DesignSpaceDocument() - s1 = SourceDescriptor() - s1.path = masterPath1 - s1.name = "master.ufo1" - s1.font = defcon.Font("master.ufo1") - s1.location = dict(weight=0) - s1.familyName = "MasterFamilyName" - s1.styleName = "MasterStyleNameOne" - s1.localisedFamilyName = dict(fr="Caractère") - s1.mutedGlyphNames.append("A") - s1.mutedGlyphNames.append("Z") - doc.addSource(s1) - - """ - - flavor = "source" - _attrs = [ - "filename", - "path", - "name", - "layerName", - "location", - "copyLib", - "copyGroups", - "copyFeatures", - "muteKerning", - "muteInfo", - "mutedGlyphNames", - "familyName", - "styleName", - "localisedFamilyName", - ] - - filename = posixpath_property("_filename") - path = posixpath_property("_path") - - def __init__( - self, - *, - filename=None, - path=None, - font=None, - name=None, - location=None, - designLocation=None, - layerName=None, - familyName=None, - styleName=None, - localisedFamilyName=None, - copyLib=False, - copyInfo=False, - copyGroups=False, - copyFeatures=False, - muteKerning=False, - muteInfo=False, - mutedGlyphNames=None, - ): - self.filename = filename - """string. A relative path to the source file, **as it is in the document**. - - MutatorMath + VarLib. - """ - self.path = path - """The absolute path, calculated from filename.""" - - self.font = font - """Any Python object. Optional. Points to a representation of this - source font that is loaded in memory, as a Python object (e.g. a - ``defcon.Font`` or a ``fontTools.ttFont.TTFont``). - - The default document reader will not fill-in this attribute, and the - default writer will not use this attribute. It is up to the user of - ``designspaceLib`` to either load the resource identified by - ``filename`` and store it in this field, or write the contents of - this field to the disk and make ```filename`` point to that. - """ - - self.name = name - """string. Optional. Unique identifier name for this source. - - MutatorMath + varLib. - """ - - self.designLocation = ( - designLocation if designLocation is not None else location or {} - ) - """dict. Axis values for this source, in design space coordinates. - - MutatorMath + varLib. - - This may be only part of the full design location. - See :meth:`getFullDesignLocation()` - - .. versionadded:: 5.0 - """ - - self.layerName = layerName - """string. The name of the layer in the source to look for - outline data. Default ``None`` which means ``foreground``. - """ - self.familyName = familyName - """string. Family name of this source. Though this data - can be extracted from the font, it can be efficient to have it right - here. - - varLib. - """ - self.styleName = styleName - """string. Style name of this source. Though this data - can be extracted from the font, it can be efficient to have it right - here. - - varLib. - """ - self.localisedFamilyName = localisedFamilyName or {} - """dict. A dictionary of localised family name strings, keyed by - language code. - - If present, will be used to build localized names for all instances. - - .. versionadded:: 5.0 - """ - - self.copyLib = copyLib - """bool. Indicates if the contents of the font.lib need to - be copied to the instances. - - MutatorMath. - - .. deprecated:: 5.0 - """ - self.copyInfo = copyInfo - """bool. Indicates if the non-interpolating font.info needs - to be copied to the instances. - - MutatorMath. - - .. deprecated:: 5.0 - """ - self.copyGroups = copyGroups - """bool. Indicates if the groups need to be copied to the - instances. - - MutatorMath. - - .. deprecated:: 5.0 - """ - self.copyFeatures = copyFeatures - """bool. Indicates if the feature text needs to be - copied to the instances. - - MutatorMath. - - .. deprecated:: 5.0 - """ - self.muteKerning = muteKerning - """bool. Indicates if the kerning data from this source - needs to be muted (i.e. not be part of the calculations). - - MutatorMath only. - """ - self.muteInfo = muteInfo - """bool. Indicated if the interpolating font.info data for - this source needs to be muted. - - MutatorMath only. - """ - self.mutedGlyphNames = mutedGlyphNames or [] - """list. Glyphnames that need to be muted in the - instances. - - MutatorMath only. - """ - - @property - def location(self): - """dict. Axis values for this source, in design space coordinates. - - MutatorMath + varLib. - - .. deprecated:: 5.0 - Use the more explicit alias for this property :attr:`designLocation`. - """ - return self.designLocation - - @location.setter - def location(self, location: Optional[SimpleLocationDict]): - self.designLocation = location or {} - - def setFamilyName(self, familyName, languageCode="en"): - """Setter for :attr:`localisedFamilyName` - - .. versionadded:: 5.0 - """ - self.localisedFamilyName[languageCode] = tostr(familyName) - - def getFamilyName(self, languageCode="en"): - """Getter for :attr:`localisedFamilyName` - - .. versionadded:: 5.0 - """ - return self.localisedFamilyName.get(languageCode) - - def getFullDesignLocation(self, doc: "DesignSpaceDocument") -> SimpleLocationDict: - """Get the complete design location of this source, from its - :attr:`designLocation` and the document's axis defaults. - - .. versionadded:: 5.0 - """ - result: SimpleLocationDict = {} - for axis in doc.axes: - if axis.name in self.designLocation: - result[axis.name] = self.designLocation[axis.name] - else: - result[axis.name] = axis.map_forward(axis.default) - return result - - -class RuleDescriptor(SimpleDescriptor): - """Represents the rule descriptor element: a set of glyph substitutions to - trigger conditionally in some parts of the designspace. - - .. code:: python - - r1 = RuleDescriptor() - r1.name = "unique.rule.name" - r1.conditionSets.append([dict(name="weight", minimum=-10, maximum=10), dict(...)]) - r1.conditionSets.append([dict(...), dict(...)]) - r1.subs.append(("a", "a.alt")) - - .. code:: xml - - - - - - - - - - - - - - """ - - _attrs = ["name", "conditionSets", "subs"] # what do we need here - - def __init__(self, *, name=None, conditionSets=None, subs=None): - self.name = name - """string. Unique name for this rule. Can be used to reference this rule data.""" - # list of lists of dict(name='aaaa', minimum=0, maximum=1000) - self.conditionSets = conditionSets or [] - """a list of conditionsets. - - - Each conditionset is a list of conditions. - - Each condition is a dict with ``name``, ``minimum`` and ``maximum`` keys. - """ - # list of substitutions stored as tuples of glyphnames ("a", "a.alt") - self.subs = subs or [] - """list of substitutions. - - - Each substitution is stored as tuples of glyphnames, e.g. ("a", "a.alt"). - - Note: By default, rules are applied first, before other text - shaping/OpenType layout, as they are part of the - `Required Variation Alternates OpenType feature `_. - See ref:`rules-element` § Attributes. - """ - - -def evaluateRule(rule, location): - """Return True if any of the rule's conditionsets matches the given location.""" - return any(evaluateConditions(c, location) for c in rule.conditionSets) - - -def evaluateConditions(conditions, location): - """Return True if all the conditions matches the given location. - - - If a condition has no minimum, check for < maximum. - - If a condition has no maximum, check for > minimum. - """ - for cd in conditions: - value = location[cd["name"]] - if cd.get("minimum") is None: - if value > cd["maximum"]: - return False - elif cd.get("maximum") is None: - if cd["minimum"] > value: - return False - elif not cd["minimum"] <= value <= cd["maximum"]: - return False - return True - - -def processRules(rules, location, glyphNames): - """Apply these rules at this location to these glyphnames. - - Return a new list of glyphNames with substitutions applied. - - - rule order matters - """ - newNames = [] - for rule in rules: - if evaluateRule(rule, location): - for name in glyphNames: - swap = False - for a, b in rule.subs: - if name == a: - swap = True - break - if swap: - newNames.append(b) - else: - newNames.append(name) - glyphNames = newNames - newNames = [] - return glyphNames - - -AnisotropicLocationDict = Dict[str, Union[float, Tuple[float, float]]] -SimpleLocationDict = Dict[str, float] - - -class AxisMappingDescriptor(SimpleDescriptor): - """Represents the axis mapping element: mapping an input location - to an output location in the designspace. - - .. code:: python - - m1 = AxisMappingDescriptor() - m1.inputLocation = {"weight": 900, "width": 150} - m1.outputLocation = {"weight": 870} - - .. code:: xml - - - - - - - - - - - - - """ - - _attrs = ["inputLocation", "outputLocation"] - - def __init__( - self, - *, - inputLocation=None, - outputLocation=None, - description=None, - groupDescription=None, - ): - self.inputLocation: SimpleLocationDict = inputLocation or {} - """dict. Axis values for the input of the mapping, in design space coordinates. - - varLib. - - .. versionadded:: 5.1 - """ - self.outputLocation: SimpleLocationDict = outputLocation or {} - """dict. Axis values for the output of the mapping, in design space coordinates. - - varLib. - - .. versionadded:: 5.1 - """ - self.description = description - """string. A description of the mapping. - - varLib. - - .. versionadded:: 5.2 - """ - self.groupDescription = groupDescription - """string. A description of the group of mappings. - - varLib. - - .. versionadded:: 5.2 - """ - - -class InstanceDescriptor(SimpleDescriptor): - """Simple container for data related to the instance - - - .. code:: python - - i2 = InstanceDescriptor() - i2.path = instancePath2 - i2.familyName = "InstanceFamilyName" - i2.styleName = "InstanceStyleName" - i2.name = "instance.ufo2" - # anisotropic location - i2.designLocation = dict(weight=500, width=(400,300)) - i2.postScriptFontName = "InstancePostscriptName" - i2.styleMapFamilyName = "InstanceStyleMapFamilyName" - i2.styleMapStyleName = "InstanceStyleMapStyleName" - i2.lib['com.coolDesignspaceApp.specimenText'] = 'Hamburgerwhatever' - doc.addInstance(i2) - """ - - flavor = "instance" - _defaultLanguageCode = "en" - _attrs = [ - "filename", - "path", - "name", - "locationLabel", - "designLocation", - "userLocation", - "familyName", - "styleName", - "postScriptFontName", - "styleMapFamilyName", - "styleMapStyleName", - "localisedFamilyName", - "localisedStyleName", - "localisedStyleMapFamilyName", - "localisedStyleMapStyleName", - "glyphs", - "kerning", - "info", - "lib", - ] - - filename = posixpath_property("_filename") - path = posixpath_property("_path") - - def __init__( - self, - *, - filename=None, - path=None, - font=None, - name=None, - location=None, - locationLabel=None, - designLocation=None, - userLocation=None, - familyName=None, - styleName=None, - postScriptFontName=None, - styleMapFamilyName=None, - styleMapStyleName=None, - localisedFamilyName=None, - localisedStyleName=None, - localisedStyleMapFamilyName=None, - localisedStyleMapStyleName=None, - glyphs=None, - kerning=True, - info=True, - lib=None, - ): - self.filename = filename - """string. Relative path to the instance file, **as it is - in the document**. The file may or may not exist. - - MutatorMath + VarLib. - """ - self.path = path - """string. Absolute path to the instance file, calculated from - the document path and the string in the filename attr. The file may - or may not exist. - - MutatorMath. - """ - self.font = font - """Same as :attr:`SourceDescriptor.font` - - .. seealso:: :attr:`SourceDescriptor.font` - """ - self.name = name - """string. Unique identifier name of the instance, used to - identify it if it needs to be referenced from elsewhere in the - document. - """ - self.locationLabel = locationLabel - """Name of a :class:`LocationLabelDescriptor`. If - provided, the instance should have the same location as the - LocationLabel. - - .. seealso:: - :meth:`getFullDesignLocation` - :meth:`getFullUserLocation` - - .. versionadded:: 5.0 - """ - self.designLocation: AnisotropicLocationDict = ( - designLocation if designLocation is not None else (location or {}) - ) - """dict. Axis values for this instance, in design space coordinates. - - MutatorMath + varLib. - - .. seealso:: This may be only part of the full location. See: - :meth:`getFullDesignLocation` - :meth:`getFullUserLocation` - - .. versionadded:: 5.0 - """ - self.userLocation: SimpleLocationDict = userLocation or {} - """dict. Axis values for this instance, in user space coordinates. - - MutatorMath + varLib. - - .. seealso:: This may be only part of the full location. See: - :meth:`getFullDesignLocation` - :meth:`getFullUserLocation` - - .. versionadded:: 5.0 - """ - self.familyName = familyName - """string. Family name of this instance. - - MutatorMath + varLib. - """ - self.styleName = styleName - """string. Style name of this instance. - - MutatorMath + varLib. - """ - self.postScriptFontName = postScriptFontName - """string. Postscript fontname for this instance. - - MutatorMath + varLib. - """ - self.styleMapFamilyName = styleMapFamilyName - """string. StyleMap familyname for this instance. - - MutatorMath + varLib. - """ - self.styleMapStyleName = styleMapStyleName - """string. StyleMap stylename for this instance. - - MutatorMath + varLib. - """ - self.localisedFamilyName = localisedFamilyName or {} - """dict. A dictionary of localised family name - strings, keyed by language code. - """ - self.localisedStyleName = localisedStyleName or {} - """dict. A dictionary of localised stylename - strings, keyed by language code. - """ - self.localisedStyleMapFamilyName = localisedStyleMapFamilyName or {} - """A dictionary of localised style map - familyname strings, keyed by language code. - """ - self.localisedStyleMapStyleName = localisedStyleMapStyleName or {} - """A dictionary of localised style map - stylename strings, keyed by language code. - """ - self.glyphs = glyphs or {} - """dict for special master definitions for glyphs. If glyphs - need special masters (to record the results of executed rules for - example). - - MutatorMath. - - .. deprecated:: 5.0 - Use rules or sparse sources instead. - """ - self.kerning = kerning - """ bool. Indicates if this instance needs its kerning - calculated. - - MutatorMath. - - .. deprecated:: 5.0 - """ - self.info = info - """bool. Indicated if this instance needs the interpolating - font.info calculated. - - .. deprecated:: 5.0 - """ - - self.lib = lib or {} - """Custom data associated with this instance.""" - - @property - def location(self): - """dict. Axis values for this instance. - - MutatorMath + varLib. - - .. deprecated:: 5.0 - Use the more explicit alias for this property :attr:`designLocation`. - """ - return self.designLocation - - @location.setter - def location(self, location: Optional[AnisotropicLocationDict]): - self.designLocation = location or {} - - def setStyleName(self, styleName, languageCode="en"): - """These methods give easier access to the localised names.""" - self.localisedStyleName[languageCode] = tostr(styleName) - - def getStyleName(self, languageCode="en"): - return self.localisedStyleName.get(languageCode) - - def setFamilyName(self, familyName, languageCode="en"): - self.localisedFamilyName[languageCode] = tostr(familyName) - - def getFamilyName(self, languageCode="en"): - return self.localisedFamilyName.get(languageCode) - - def setStyleMapStyleName(self, styleMapStyleName, languageCode="en"): - self.localisedStyleMapStyleName[languageCode] = tostr(styleMapStyleName) - - def getStyleMapStyleName(self, languageCode="en"): - return self.localisedStyleMapStyleName.get(languageCode) - - def setStyleMapFamilyName(self, styleMapFamilyName, languageCode="en"): - self.localisedStyleMapFamilyName[languageCode] = tostr(styleMapFamilyName) - - def getStyleMapFamilyName(self, languageCode="en"): - return self.localisedStyleMapFamilyName.get(languageCode) - - def clearLocation(self, axisName: Optional[str] = None): - """Clear all location-related fields. Ensures that - :attr:``designLocation`` and :attr:``userLocation`` are dictionaries - (possibly empty if clearing everything). - - In order to update the location of this instance wholesale, a user - should first clear all the fields, then change the field(s) for which - they have data. - - .. code:: python - - instance.clearLocation() - instance.designLocation = {'Weight': (34, 36.5), 'Width': 100} - instance.userLocation = {'Opsz': 16} - - In order to update a single axis location, the user should only clear - that axis, then edit the values: - - .. code:: python - - instance.clearLocation('Weight') - instance.designLocation['Weight'] = (34, 36.5) - - Args: - axisName: if provided, only clear the location for that axis. - - .. versionadded:: 5.0 - """ - self.locationLabel = None - if axisName is None: - self.designLocation = {} - self.userLocation = {} - else: - if self.designLocation is None: - self.designLocation = {} - if axisName in self.designLocation: - del self.designLocation[axisName] - if self.userLocation is None: - self.userLocation = {} - if axisName in self.userLocation: - del self.userLocation[axisName] - - def getLocationLabelDescriptor( - self, doc: "DesignSpaceDocument" - ) -> Optional[LocationLabelDescriptor]: - """Get the :class:`LocationLabelDescriptor` instance that matches - this instances's :attr:`locationLabel`. - - Raises if the named label can't be found. - - .. versionadded:: 5.0 - """ - if self.locationLabel is None: - return None - label = doc.getLocationLabel(self.locationLabel) - if label is None: - raise DesignSpaceDocumentError( - "InstanceDescriptor.getLocationLabelDescriptor(): " - f"unknown location label `{self.locationLabel}` in instance `{self.name}`." - ) - return label - - def getFullDesignLocation( - self, doc: "DesignSpaceDocument" - ) -> AnisotropicLocationDict: - """Get the complete design location of this instance, by combining data - from the various location fields, default axis values and mappings, and - top-level location labels. - - The source of truth for this instance's location is determined for each - axis independently by taking the first not-None field in this list: - - - ``locationLabel``: the location along this axis is the same as the - matching STAT format 4 label. No anisotropy. - - ``designLocation[axisName]``: the explicit design location along this - axis, possibly anisotropic. - - ``userLocation[axisName]``: the explicit user location along this - axis. No anisotropy. - - ``axis.default``: default axis value. No anisotropy. - - .. versionadded:: 5.0 - """ - label = self.getLocationLabelDescriptor(doc) - if label is not None: - return doc.map_forward(label.userLocation) # type: ignore - result: AnisotropicLocationDict = {} - for axis in doc.axes: - if axis.name in self.designLocation: - result[axis.name] = self.designLocation[axis.name] - elif axis.name in self.userLocation: - result[axis.name] = axis.map_forward(self.userLocation[axis.name]) - else: - result[axis.name] = axis.map_forward(axis.default) - return result - - def getFullUserLocation(self, doc: "DesignSpaceDocument") -> SimpleLocationDict: - """Get the complete user location for this instance. - - .. seealso:: :meth:`getFullDesignLocation` - - .. versionadded:: 5.0 - """ - return doc.map_backward(self.getFullDesignLocation(doc)) - - -def tagForAxisName(name): - # try to find or make a tag name for this axis name - names = { - "weight": ("wght", dict(en="Weight")), - "width": ("wdth", dict(en="Width")), - "optical": ("opsz", dict(en="Optical Size")), - "slant": ("slnt", dict(en="Slant")), - "italic": ("ital", dict(en="Italic")), - } - if name.lower() in names: - return names[name.lower()] - if len(name) < 4: - tag = name + "*" * (4 - len(name)) - else: - tag = name[:4] - return tag, dict(en=name) - - -class AbstractAxisDescriptor(SimpleDescriptor): - flavor = "axis" - - def __init__( - self, - *, - tag=None, - name=None, - labelNames=None, - hidden=False, - map=None, - axisOrdering=None, - axisLabels=None, - ): - # opentype tag for this axis - self.tag = tag - """string. Four letter tag for this axis. Some might be - registered at the `OpenType - specification `__. - Privately-defined axis tags must begin with an uppercase letter and - use only uppercase letters or digits. - """ - # name of the axis used in locations - self.name = name - """string. Name of the axis as it is used in the location dicts. - - MutatorMath + varLib. - """ - # names for UI purposes, if this is not a standard axis, - self.labelNames = labelNames or {} - """dict. When defining a non-registered axis, it will be - necessary to define user-facing readable names for the axis. Keyed by - xml:lang code. Values are required to be ``unicode`` strings, even if - they only contain ASCII characters. - """ - self.hidden = hidden - """bool. Whether this axis should be hidden in user interfaces. - """ - self.map = map or [] - """list of input / output values that can describe a warp of user space - to design space coordinates. If no map values are present, it is assumed - user space is the same as design space, as in [(minimum, minimum), - (maximum, maximum)]. - - varLib. - """ - self.axisOrdering = axisOrdering - """STAT table field ``axisOrdering``. - - See: `OTSpec STAT Axis Record `_ - - .. versionadded:: 5.0 - """ - self.axisLabels: List[AxisLabelDescriptor] = axisLabels or [] - """STAT table entries for Axis Value Tables format 1, 2, 3. - - See: `OTSpec STAT Axis Value Tables `_ - - .. versionadded:: 5.0 - """ - - -class AxisDescriptor(AbstractAxisDescriptor): - """Simple container for the axis data. - - Add more localisations? - - .. code:: python - - a1 = AxisDescriptor() - a1.minimum = 1 - a1.maximum = 1000 - a1.default = 400 - a1.name = "weight" - a1.tag = "wght" - a1.labelNames['fa-IR'] = "قطر" - a1.labelNames['en'] = "Wéíght" - a1.map = [(1.0, 10.0), (400.0, 66.0), (1000.0, 990.0)] - a1.axisOrdering = 1 - a1.axisLabels = [ - AxisLabelDescriptor(name="Regular", userValue=400, elidable=True) - ] - doc.addAxis(a1) - """ - - _attrs = [ - "tag", - "name", - "maximum", - "minimum", - "default", - "map", - "axisOrdering", - "axisLabels", - ] - - def __init__( - self, - *, - tag=None, - name=None, - labelNames=None, - minimum=None, - default=None, - maximum=None, - hidden=False, - map=None, - axisOrdering=None, - axisLabels=None, - ): - super().__init__( - tag=tag, - name=name, - labelNames=labelNames, - hidden=hidden, - map=map, - axisOrdering=axisOrdering, - axisLabels=axisLabels, - ) - self.minimum = minimum - """number. The minimum value for this axis in user space. - - MutatorMath + varLib. - """ - self.maximum = maximum - """number. The maximum value for this axis in user space. - - MutatorMath + varLib. - """ - self.default = default - """number. The default value for this axis, i.e. when a new location is - created, this is the value this axis will get in user space. - - MutatorMath + varLib. - """ - - def serialize(self): - # output to a dict, used in testing - return dict( - tag=self.tag, - name=self.name, - labelNames=self.labelNames, - maximum=self.maximum, - minimum=self.minimum, - default=self.default, - hidden=self.hidden, - map=self.map, - axisOrdering=self.axisOrdering, - axisLabels=self.axisLabels, - ) - - def map_forward(self, v): - """Maps value from axis mapping's input (user) to output (design).""" - from fontTools.varLib.models import piecewiseLinearMap - - if not self.map: - return v - return piecewiseLinearMap(v, {k: v for k, v in self.map}) - - def map_backward(self, v): - """Maps value from axis mapping's output (design) to input (user).""" - from fontTools.varLib.models import piecewiseLinearMap - - if isinstance(v, tuple): - v = v[0] - if not self.map: - return v - return piecewiseLinearMap(v, {v: k for k, v in self.map}) - - -class DiscreteAxisDescriptor(AbstractAxisDescriptor): - """Container for discrete axis data. - - Use this for axes that do not interpolate. The main difference from a - continuous axis is that a continuous axis has a ``minimum`` and ``maximum``, - while a discrete axis has a list of ``values``. - - Example: an Italic axis with 2 stops, Roman and Italic, that are not - compatible. The axis still allows to bind together the full font family, - which is useful for the STAT table, however it can't become a variation - axis in a VF. - - .. code:: python - - a2 = DiscreteAxisDescriptor() - a2.values = [0, 1] - a2.default = 0 - a2.name = "Italic" - a2.tag = "ITAL" - a2.labelNames['fr'] = "Italique" - a2.map = [(0, 0), (1, -11)] - a2.axisOrdering = 2 - a2.axisLabels = [ - AxisLabelDescriptor(name="Roman", userValue=0, elidable=True) - ] - doc.addAxis(a2) - - .. versionadded:: 5.0 - """ - - flavor = "axis" - _attrs = ("tag", "name", "values", "default", "map", "axisOrdering", "axisLabels") - - def __init__( - self, - *, - tag=None, - name=None, - labelNames=None, - values=None, - default=None, - hidden=False, - map=None, - axisOrdering=None, - axisLabels=None, - ): - super().__init__( - tag=tag, - name=name, - labelNames=labelNames, - hidden=hidden, - map=map, - axisOrdering=axisOrdering, - axisLabels=axisLabels, - ) - self.default: float = default - """The default value for this axis, i.e. when a new location is - created, this is the value this axis will get in user space. - - However, this default value is less important than in continuous axes: - - - it doesn't define the "neutral" version of outlines from which - deltas would apply, as this axis does not interpolate. - - it doesn't provide the reference glyph set for the designspace, as - fonts at each value can have different glyph sets. - """ - self.values: List[float] = values or [] - """List of possible values for this axis. Contrary to continuous axes, - only the values in this list can be taken by the axis, nothing in-between. - """ - - def map_forward(self, value): - """Maps value from axis mapping's input to output. - - Returns value unchanged if no mapping entry is found. - - Note: for discrete axes, each value must have its mapping entry, if - you intend that value to be mapped. - """ - return next((v for k, v in self.map if k == value), value) - - def map_backward(self, value): - """Maps value from axis mapping's output to input. - - Returns value unchanged if no mapping entry is found. - - Note: for discrete axes, each value must have its mapping entry, if - you intend that value to be mapped. - """ - if isinstance(value, tuple): - value = value[0] - return next((k for k, v in self.map if v == value), value) - - -class AxisLabelDescriptor(SimpleDescriptor): - """Container for axis label data. - - Analogue of OpenType's STAT data for a single axis (formats 1, 2 and 3). - All values are user values. - See: `OTSpec STAT Axis value table, format 1, 2, 3 `_ - - The STAT format of the Axis value depends on which field are filled-in, - see :meth:`getFormat` - - .. versionadded:: 5.0 - """ - - flavor = "label" - _attrs = ( - "userMinimum", - "userValue", - "userMaximum", - "name", - "elidable", - "olderSibling", - "linkedUserValue", - "labelNames", - ) - - def __init__( - self, - *, - name, - userValue, - userMinimum=None, - userMaximum=None, - elidable=False, - olderSibling=False, - linkedUserValue=None, - labelNames=None, - ): - self.userMinimum: Optional[float] = userMinimum - """STAT field ``rangeMinValue`` (format 2).""" - self.userValue: float = userValue - """STAT field ``value`` (format 1, 3) or ``nominalValue`` (format 2).""" - self.userMaximum: Optional[float] = userMaximum - """STAT field ``rangeMaxValue`` (format 2).""" - self.name: str = name - """Label for this axis location, STAT field ``valueNameID``.""" - self.elidable: bool = elidable - """STAT flag ``ELIDABLE_AXIS_VALUE_NAME``. - - See: `OTSpec STAT Flags `_ - """ - self.olderSibling: bool = olderSibling - """STAT flag ``OLDER_SIBLING_FONT_ATTRIBUTE``. - - See: `OTSpec STAT Flags `_ - """ - self.linkedUserValue: Optional[float] = linkedUserValue - """STAT field ``linkedValue`` (format 3).""" - self.labelNames: MutableMapping[str, str] = labelNames or {} - """User-facing translations of this location's label. Keyed by - ``xml:lang`` code. - """ - - def getFormat(self) -> int: - """Determine which format of STAT Axis value to use to encode this label. - - =========== ========= =========== =========== =============== - STAT Format userValue userMinimum userMaximum linkedUserValue - =========== ========= =========== =========== =============== - 1 ✅ ❌ ❌ ❌ - 2 ✅ ✅ ✅ ❌ - 3 ✅ ❌ ❌ ✅ - =========== ========= =========== =========== =============== - """ - if self.linkedUserValue is not None: - return 3 - if self.userMinimum is not None or self.userMaximum is not None: - return 2 - return 1 - - @property - def defaultName(self) -> str: - """Return the English name from :attr:`labelNames` or the :attr:`name`.""" - return self.labelNames.get("en") or self.name - - -class LocationLabelDescriptor(SimpleDescriptor): - """Container for location label data. - - Analogue of OpenType's STAT data for a free-floating location (format 4). - All values are user values. - - See: `OTSpec STAT Axis value table, format 4 `_ - - .. versionadded:: 5.0 - """ - - flavor = "label" - _attrs = ("name", "elidable", "olderSibling", "userLocation", "labelNames") - - def __init__( - self, - *, - name, - userLocation, - elidable=False, - olderSibling=False, - labelNames=None, - ): - self.name: str = name - """Label for this named location, STAT field ``valueNameID``.""" - self.userLocation: SimpleLocationDict = userLocation or {} - """Location in user coordinates along each axis. - - If an axis is not mentioned, it is assumed to be at its default location. - - .. seealso:: This may be only part of the full location. See: - :meth:`getFullUserLocation` - """ - self.elidable: bool = elidable - """STAT flag ``ELIDABLE_AXIS_VALUE_NAME``. - - See: `OTSpec STAT Flags `_ - """ - self.olderSibling: bool = olderSibling - """STAT flag ``OLDER_SIBLING_FONT_ATTRIBUTE``. - - See: `OTSpec STAT Flags `_ - """ - self.labelNames: Dict[str, str] = labelNames or {} - """User-facing translations of this location's label. Keyed by - xml:lang code. - """ - - @property - def defaultName(self) -> str: - """Return the English name from :attr:`labelNames` or the :attr:`name`.""" - return self.labelNames.get("en") or self.name - - def getFullUserLocation(self, doc: "DesignSpaceDocument") -> SimpleLocationDict: - """Get the complete user location of this label, by combining data - from the explicit user location and default axis values. - - .. versionadded:: 5.0 - """ - return { - axis.name: self.userLocation.get(axis.name, axis.default) - for axis in doc.axes - } - - -class VariableFontDescriptor(SimpleDescriptor): - """Container for variable fonts, sub-spaces of the Designspace. - - Use-cases: - - - From a single DesignSpace with discrete axes, define 1 variable font - per value on the discrete axes. Before version 5, you would have needed - 1 DesignSpace per such variable font, and a lot of data duplication. - - From a big variable font with many axes, define subsets of that variable - font that only include some axes and freeze other axes at a given location. - - .. versionadded:: 5.0 - """ - - flavor = "variable-font" - _attrs = ("filename", "axisSubsets", "lib") - - filename = posixpath_property("_filename") - - def __init__(self, *, name, filename=None, axisSubsets=None, lib=None): - self.name: str = name - """string, required. Name of this variable to identify it during the - build process and from other parts of the document, and also as a - filename in case the filename property is empty. - - VarLib. - """ - self.filename: str = filename - """string, optional. Relative path to the variable font file, **as it is - in the document**. The file may or may not exist. - - If not specified, the :attr:`name` will be used as a basename for the file. - """ - self.axisSubsets: List[ - Union[RangeAxisSubsetDescriptor, ValueAxisSubsetDescriptor] - ] = (axisSubsets or []) - """Axis subsets to include in this variable font. - - If an axis is not mentioned, assume that we only want the default - location of that axis (same as a :class:`ValueAxisSubsetDescriptor`). - """ - self.lib: MutableMapping[str, Any] = lib or {} - """Custom data associated with this variable font.""" - - -class RangeAxisSubsetDescriptor(SimpleDescriptor): - """Subset of a continuous axis to include in a variable font. - - .. versionadded:: 5.0 - """ - - flavor = "axis-subset" - _attrs = ("name", "userMinimum", "userDefault", "userMaximum") - - def __init__( - self, *, name, userMinimum=-math.inf, userDefault=None, userMaximum=math.inf - ): - self.name: str = name - """Name of the :class:`AxisDescriptor` to subset.""" - self.userMinimum: float = userMinimum - """New minimum value of the axis in the target variable font. - If not specified, assume the same minimum value as the full axis. - (default = ``-math.inf``) - """ - self.userDefault: Optional[float] = userDefault - """New default value of the axis in the target variable font. - If not specified, assume the same default value as the full axis. - (default = ``None``) - """ - self.userMaximum: float = userMaximum - """New maximum value of the axis in the target variable font. - If not specified, assume the same maximum value as the full axis. - (default = ``math.inf``) - """ - - -class ValueAxisSubsetDescriptor(SimpleDescriptor): - """Single value of a discrete or continuous axis to use in a variable font. - - .. versionadded:: 5.0 - """ - - flavor = "axis-subset" - _attrs = ("name", "userValue") - - def __init__(self, *, name, userValue): - self.name: str = name - """Name of the :class:`AxisDescriptor` or :class:`DiscreteAxisDescriptor` - to "snapshot" or "freeze". - """ - self.userValue: float = userValue - """Value in user coordinates at which to freeze the given axis.""" - - -class BaseDocWriter(object): - _whiteSpace = " " - axisDescriptorClass = AxisDescriptor - discreteAxisDescriptorClass = DiscreteAxisDescriptor - axisLabelDescriptorClass = AxisLabelDescriptor - axisMappingDescriptorClass = AxisMappingDescriptor - locationLabelDescriptorClass = LocationLabelDescriptor - ruleDescriptorClass = RuleDescriptor - sourceDescriptorClass = SourceDescriptor - variableFontDescriptorClass = VariableFontDescriptor - valueAxisSubsetDescriptorClass = ValueAxisSubsetDescriptor - rangeAxisSubsetDescriptorClass = RangeAxisSubsetDescriptor - instanceDescriptorClass = InstanceDescriptor - - @classmethod - def getAxisDecriptor(cls): - return cls.axisDescriptorClass() - - @classmethod - def getAxisMappingDescriptor(cls): - return cls.axisMappingDescriptorClass() - - @classmethod - def getSourceDescriptor(cls): - return cls.sourceDescriptorClass() - - @classmethod - def getInstanceDescriptor(cls): - return cls.instanceDescriptorClass() - - @classmethod - def getRuleDescriptor(cls): - return cls.ruleDescriptorClass() - - def __init__(self, documentPath, documentObject: DesignSpaceDocument): - self.path = documentPath - self.documentObject = documentObject - self.effectiveFormatTuple = self._getEffectiveFormatTuple() - self.root = ET.Element("designspace") - - def write(self, pretty=True, encoding="UTF-8", xml_declaration=True): - self.root.attrib["format"] = ".".join(str(i) for i in self.effectiveFormatTuple) - - if ( - self.documentObject.axes - or self.documentObject.axisMappings - or self.documentObject.elidedFallbackName is not None - ): - axesElement = ET.Element("axes") - if self.documentObject.elidedFallbackName is not None: - axesElement.attrib["elidedfallbackname"] = ( - self.documentObject.elidedFallbackName - ) - self.root.append(axesElement) - for axisObject in self.documentObject.axes: - self._addAxis(axisObject) - - if self.documentObject.axisMappings: - mappingsElement = None - lastGroup = object() - for mappingObject in self.documentObject.axisMappings: - if getattr(mappingObject, "groupDescription", None) != lastGroup: - if mappingsElement is not None: - self.root.findall(".axes")[0].append(mappingsElement) - lastGroup = getattr(mappingObject, "groupDescription", None) - mappingsElement = ET.Element("mappings") - if lastGroup is not None: - mappingsElement.attrib["description"] = lastGroup - self._addAxisMapping(mappingsElement, mappingObject) - if mappingsElement is not None: - self.root.findall(".axes")[0].append(mappingsElement) - - if self.documentObject.locationLabels: - labelsElement = ET.Element("labels") - for labelObject in self.documentObject.locationLabels: - self._addLocationLabel(labelsElement, labelObject) - self.root.append(labelsElement) - - if self.documentObject.rules: - if getattr(self.documentObject, "rulesProcessingLast", False): - attributes = {"processing": "last"} - else: - attributes = {} - self.root.append(ET.Element("rules", attributes)) - for ruleObject in self.documentObject.rules: - self._addRule(ruleObject) - - if self.documentObject.sources: - self.root.append(ET.Element("sources")) - for sourceObject in self.documentObject.sources: - self._addSource(sourceObject) - - if self.documentObject.variableFonts: - variableFontsElement = ET.Element("variable-fonts") - for variableFont in self.documentObject.variableFonts: - self._addVariableFont(variableFontsElement, variableFont) - self.root.append(variableFontsElement) - - if self.documentObject.instances: - self.root.append(ET.Element("instances")) - for instanceObject in self.documentObject.instances: - self._addInstance(instanceObject) - - if self.documentObject.lib: - self._addLib(self.root, self.documentObject.lib, 2) - - tree = ET.ElementTree(self.root) - tree.write( - self.path, - encoding=encoding, - method="xml", - xml_declaration=xml_declaration, - pretty_print=pretty, - ) - - def _getEffectiveFormatTuple(self): - """Try to use the version specified in the document, or a sufficiently - recent version to be able to encode what the document contains. - """ - minVersion = self.documentObject.formatTuple - if ( - any( - hasattr(axis, "values") - or axis.axisOrdering is not None - or axis.axisLabels - for axis in self.documentObject.axes - ) - or self.documentObject.locationLabels - or any(source.localisedFamilyName for source in self.documentObject.sources) - or self.documentObject.variableFonts - or any( - instance.locationLabel or instance.userLocation - for instance in self.documentObject.instances - ) - ): - if minVersion < (5, 0): - minVersion = (5, 0) - if self.documentObject.axisMappings: - if minVersion < (5, 1): - minVersion = (5, 1) - return minVersion - - def _makeLocationElement(self, locationObject, name=None): - """Convert Location dict to a locationElement.""" - locElement = ET.Element("location") - if name is not None: - locElement.attrib["name"] = name - validatedLocation = self.documentObject.newDefaultLocation() - for axisName, axisValue in locationObject.items(): - if axisName in validatedLocation: - # only accept values we know - validatedLocation[axisName] = axisValue - for dimensionName, dimensionValue in validatedLocation.items(): - dimElement = ET.Element("dimension") - dimElement.attrib["name"] = dimensionName - if type(dimensionValue) == tuple: - dimElement.attrib["xvalue"] = self.intOrFloat(dimensionValue[0]) - dimElement.attrib["yvalue"] = self.intOrFloat(dimensionValue[1]) - else: - dimElement.attrib["xvalue"] = self.intOrFloat(dimensionValue) - locElement.append(dimElement) - return locElement, validatedLocation - - def intOrFloat(self, num): - if int(num) == num: - return "%d" % num - return ("%f" % num).rstrip("0").rstrip(".") - - def _addRule(self, ruleObject): - # if none of the conditions have minimum or maximum values, do not add the rule. - ruleElement = ET.Element("rule") - if ruleObject.name is not None: - ruleElement.attrib["name"] = ruleObject.name - for conditions in ruleObject.conditionSets: - conditionsetElement = ET.Element("conditionset") - for cond in conditions: - if cond.get("minimum") is None and cond.get("maximum") is None: - # neither is defined, don't add this condition - continue - conditionElement = ET.Element("condition") - conditionElement.attrib["name"] = cond.get("name") - if cond.get("minimum") is not None: - conditionElement.attrib["minimum"] = self.intOrFloat( - cond.get("minimum") - ) - if cond.get("maximum") is not None: - conditionElement.attrib["maximum"] = self.intOrFloat( - cond.get("maximum") - ) - conditionsetElement.append(conditionElement) - if len(conditionsetElement): - ruleElement.append(conditionsetElement) - for sub in ruleObject.subs: - subElement = ET.Element("sub") - subElement.attrib["name"] = sub[0] - subElement.attrib["with"] = sub[1] - ruleElement.append(subElement) - if len(ruleElement): - self.root.findall(".rules")[0].append(ruleElement) - - def _addAxis(self, axisObject): - axisElement = ET.Element("axis") - axisElement.attrib["tag"] = axisObject.tag - axisElement.attrib["name"] = axisObject.name - self._addLabelNames(axisElement, axisObject.labelNames) - if axisObject.map: - for inputValue, outputValue in axisObject.map: - mapElement = ET.Element("map") - mapElement.attrib["input"] = self.intOrFloat(inputValue) - mapElement.attrib["output"] = self.intOrFloat(outputValue) - axisElement.append(mapElement) - if axisObject.axisOrdering is not None or axisObject.axisLabels: - labelsElement = ET.Element("labels") - if axisObject.axisOrdering is not None: - labelsElement.attrib["ordering"] = str(axisObject.axisOrdering) - for label in axisObject.axisLabels: - self._addAxisLabel(labelsElement, label) - axisElement.append(labelsElement) - if hasattr(axisObject, "minimum"): - axisElement.attrib["minimum"] = self.intOrFloat(axisObject.minimum) - axisElement.attrib["maximum"] = self.intOrFloat(axisObject.maximum) - elif hasattr(axisObject, "values"): - axisElement.attrib["values"] = " ".join( - self.intOrFloat(v) for v in axisObject.values - ) - axisElement.attrib["default"] = self.intOrFloat(axisObject.default) - if axisObject.hidden: - axisElement.attrib["hidden"] = "1" - self.root.findall(".axes")[0].append(axisElement) - - def _addAxisMapping(self, mappingsElement, mappingObject): - mappingElement = ET.Element("mapping") - if getattr(mappingObject, "description", None) is not None: - mappingElement.attrib["description"] = mappingObject.description - for what in ("inputLocation", "outputLocation"): - whatObject = getattr(mappingObject, what, None) - if whatObject is None: - continue - whatElement = ET.Element(what[:-8]) - mappingElement.append(whatElement) - - for name, value in whatObject.items(): - dimensionElement = ET.Element("dimension") - dimensionElement.attrib["name"] = name - dimensionElement.attrib["xvalue"] = self.intOrFloat(value) - whatElement.append(dimensionElement) - - mappingsElement.append(mappingElement) - - def _addAxisLabel( - self, axisElement: ET.Element, label: AxisLabelDescriptor - ) -> None: - labelElement = ET.Element("label") - labelElement.attrib["uservalue"] = self.intOrFloat(label.userValue) - if label.userMinimum is not None: - labelElement.attrib["userminimum"] = self.intOrFloat(label.userMinimum) - if label.userMaximum is not None: - labelElement.attrib["usermaximum"] = self.intOrFloat(label.userMaximum) - labelElement.attrib["name"] = label.name - if label.elidable: - labelElement.attrib["elidable"] = "true" - if label.olderSibling: - labelElement.attrib["oldersibling"] = "true" - if label.linkedUserValue is not None: - labelElement.attrib["linkeduservalue"] = self.intOrFloat( - label.linkedUserValue - ) - self._addLabelNames(labelElement, label.labelNames) - axisElement.append(labelElement) - - def _addLabelNames(self, parentElement, labelNames): - for languageCode, labelName in sorted(labelNames.items()): - languageElement = ET.Element("labelname") - languageElement.attrib[XML_LANG] = languageCode - languageElement.text = labelName - parentElement.append(languageElement) - - def _addLocationLabel( - self, parentElement: ET.Element, label: LocationLabelDescriptor - ) -> None: - labelElement = ET.Element("label") - labelElement.attrib["name"] = label.name - if label.elidable: - labelElement.attrib["elidable"] = "true" - if label.olderSibling: - labelElement.attrib["oldersibling"] = "true" - self._addLabelNames(labelElement, label.labelNames) - self._addLocationElement(labelElement, userLocation=label.userLocation) - parentElement.append(labelElement) - - def _addLocationElement( - self, - parentElement, - *, - designLocation: AnisotropicLocationDict = None, - userLocation: SimpleLocationDict = None, - ): - locElement = ET.Element("location") - for axis in self.documentObject.axes: - if designLocation is not None and axis.name in designLocation: - dimElement = ET.Element("dimension") - dimElement.attrib["name"] = axis.name - value = designLocation[axis.name] - if isinstance(value, tuple): - dimElement.attrib["xvalue"] = self.intOrFloat(value[0]) - dimElement.attrib["yvalue"] = self.intOrFloat(value[1]) - else: - dimElement.attrib["xvalue"] = self.intOrFloat(value) - locElement.append(dimElement) - elif userLocation is not None and axis.name in userLocation: - dimElement = ET.Element("dimension") - dimElement.attrib["name"] = axis.name - value = userLocation[axis.name] - dimElement.attrib["uservalue"] = self.intOrFloat(value) - locElement.append(dimElement) - if len(locElement) > 0: - parentElement.append(locElement) - - def _addInstance(self, instanceObject): - instanceElement = ET.Element("instance") - if instanceObject.name is not None: - instanceElement.attrib["name"] = instanceObject.name - if instanceObject.locationLabel is not None: - instanceElement.attrib["location"] = instanceObject.locationLabel - if instanceObject.familyName is not None: - instanceElement.attrib["familyname"] = instanceObject.familyName - if instanceObject.styleName is not None: - instanceElement.attrib["stylename"] = instanceObject.styleName - # add localisations - if instanceObject.localisedStyleName: - languageCodes = list(instanceObject.localisedStyleName.keys()) - languageCodes.sort() - for code in languageCodes: - if code == "en": - continue # already stored in the element attribute - localisedStyleNameElement = ET.Element("stylename") - localisedStyleNameElement.attrib[XML_LANG] = code - localisedStyleNameElement.text = instanceObject.getStyleName(code) - instanceElement.append(localisedStyleNameElement) - if instanceObject.localisedFamilyName: - languageCodes = list(instanceObject.localisedFamilyName.keys()) - languageCodes.sort() - for code in languageCodes: - if code == "en": - continue # already stored in the element attribute - localisedFamilyNameElement = ET.Element("familyname") - localisedFamilyNameElement.attrib[XML_LANG] = code - localisedFamilyNameElement.text = instanceObject.getFamilyName(code) - instanceElement.append(localisedFamilyNameElement) - if instanceObject.localisedStyleMapStyleName: - languageCodes = list(instanceObject.localisedStyleMapStyleName.keys()) - languageCodes.sort() - for code in languageCodes: - if code == "en": - continue - localisedStyleMapStyleNameElement = ET.Element("stylemapstylename") - localisedStyleMapStyleNameElement.attrib[XML_LANG] = code - localisedStyleMapStyleNameElement.text = ( - instanceObject.getStyleMapStyleName(code) - ) - instanceElement.append(localisedStyleMapStyleNameElement) - if instanceObject.localisedStyleMapFamilyName: - languageCodes = list(instanceObject.localisedStyleMapFamilyName.keys()) - languageCodes.sort() - for code in languageCodes: - if code == "en": - continue - localisedStyleMapFamilyNameElement = ET.Element("stylemapfamilyname") - localisedStyleMapFamilyNameElement.attrib[XML_LANG] = code - localisedStyleMapFamilyNameElement.text = ( - instanceObject.getStyleMapFamilyName(code) - ) - instanceElement.append(localisedStyleMapFamilyNameElement) - - if self.effectiveFormatTuple >= (5, 0): - if instanceObject.locationLabel is None: - self._addLocationElement( - instanceElement, - designLocation=instanceObject.designLocation, - userLocation=instanceObject.userLocation, - ) - else: - # Pre-version 5.0 code was validating and filling in the location - # dict while writing it out, as preserved below. - if instanceObject.location is not None: - locationElement, instanceObject.location = self._makeLocationElement( - instanceObject.location - ) - instanceElement.append(locationElement) - if instanceObject.filename is not None: - instanceElement.attrib["filename"] = instanceObject.filename - if instanceObject.postScriptFontName is not None: - instanceElement.attrib["postscriptfontname"] = ( - instanceObject.postScriptFontName - ) - if instanceObject.styleMapFamilyName is not None: - instanceElement.attrib["stylemapfamilyname"] = ( - instanceObject.styleMapFamilyName - ) - if instanceObject.styleMapStyleName is not None: - instanceElement.attrib["stylemapstylename"] = ( - instanceObject.styleMapStyleName - ) - if self.effectiveFormatTuple < (5, 0): - # Deprecated members as of version 5.0 - if instanceObject.glyphs: - if instanceElement.findall(".glyphs") == []: - glyphsElement = ET.Element("glyphs") - instanceElement.append(glyphsElement) - glyphsElement = instanceElement.findall(".glyphs")[0] - for glyphName, data in sorted(instanceObject.glyphs.items()): - glyphElement = self._writeGlyphElement( - instanceElement, instanceObject, glyphName, data - ) - glyphsElement.append(glyphElement) - if instanceObject.kerning: - kerningElement = ET.Element("kerning") - instanceElement.append(kerningElement) - if instanceObject.info: - infoElement = ET.Element("info") - instanceElement.append(infoElement) - self._addLib(instanceElement, instanceObject.lib, 4) - self.root.findall(".instances")[0].append(instanceElement) - - def _addSource(self, sourceObject): - sourceElement = ET.Element("source") - if sourceObject.filename is not None: - sourceElement.attrib["filename"] = sourceObject.filename - if sourceObject.name is not None: - if sourceObject.name.find("temp_master") != 0: - # do not save temporary source names - sourceElement.attrib["name"] = sourceObject.name - if sourceObject.familyName is not None: - sourceElement.attrib["familyname"] = sourceObject.familyName - if sourceObject.styleName is not None: - sourceElement.attrib["stylename"] = sourceObject.styleName - if sourceObject.layerName is not None: - sourceElement.attrib["layer"] = sourceObject.layerName - if sourceObject.localisedFamilyName: - languageCodes = list(sourceObject.localisedFamilyName.keys()) - languageCodes.sort() - for code in languageCodes: - if code == "en": - continue # already stored in the element attribute - localisedFamilyNameElement = ET.Element("familyname") - localisedFamilyNameElement.attrib[XML_LANG] = code - localisedFamilyNameElement.text = sourceObject.getFamilyName(code) - sourceElement.append(localisedFamilyNameElement) - if sourceObject.copyLib: - libElement = ET.Element("lib") - libElement.attrib["copy"] = "1" - sourceElement.append(libElement) - if sourceObject.copyGroups: - groupsElement = ET.Element("groups") - groupsElement.attrib["copy"] = "1" - sourceElement.append(groupsElement) - if sourceObject.copyFeatures: - featuresElement = ET.Element("features") - featuresElement.attrib["copy"] = "1" - sourceElement.append(featuresElement) - if sourceObject.copyInfo or sourceObject.muteInfo: - infoElement = ET.Element("info") - if sourceObject.copyInfo: - infoElement.attrib["copy"] = "1" - if sourceObject.muteInfo: - infoElement.attrib["mute"] = "1" - sourceElement.append(infoElement) - if sourceObject.muteKerning: - kerningElement = ET.Element("kerning") - kerningElement.attrib["mute"] = "1" - sourceElement.append(kerningElement) - if sourceObject.mutedGlyphNames: - for name in sourceObject.mutedGlyphNames: - glyphElement = ET.Element("glyph") - glyphElement.attrib["name"] = name - glyphElement.attrib["mute"] = "1" - sourceElement.append(glyphElement) - if self.effectiveFormatTuple >= (5, 0): - self._addLocationElement( - sourceElement, designLocation=sourceObject.location - ) - else: - # Pre-version 5.0 code was validating and filling in the location - # dict while writing it out, as preserved below. - locationElement, sourceObject.location = self._makeLocationElement( - sourceObject.location - ) - sourceElement.append(locationElement) - self.root.findall(".sources")[0].append(sourceElement) - - def _addVariableFont( - self, parentElement: ET.Element, vf: VariableFontDescriptor - ) -> None: - vfElement = ET.Element("variable-font") - vfElement.attrib["name"] = vf.name - if vf.filename is not None: - vfElement.attrib["filename"] = vf.filename - if vf.axisSubsets: - subsetsElement = ET.Element("axis-subsets") - for subset in vf.axisSubsets: - subsetElement = ET.Element("axis-subset") - subsetElement.attrib["name"] = subset.name - # Mypy doesn't support narrowing union types via hasattr() - # https://mypy.readthedocs.io/en/stable/type_narrowing.html - # TODO(Python 3.10): use TypeGuard - if hasattr(subset, "userMinimum"): - subset = cast(RangeAxisSubsetDescriptor, subset) - if subset.userMinimum != -math.inf: - subsetElement.attrib["userminimum"] = self.intOrFloat( - subset.userMinimum - ) - if subset.userMaximum != math.inf: - subsetElement.attrib["usermaximum"] = self.intOrFloat( - subset.userMaximum - ) - if subset.userDefault is not None: - subsetElement.attrib["userdefault"] = self.intOrFloat( - subset.userDefault - ) - elif hasattr(subset, "userValue"): - subset = cast(ValueAxisSubsetDescriptor, subset) - subsetElement.attrib["uservalue"] = self.intOrFloat( - subset.userValue - ) - subsetsElement.append(subsetElement) - vfElement.append(subsetsElement) - self._addLib(vfElement, vf.lib, 4) - parentElement.append(vfElement) - - def _addLib(self, parentElement: ET.Element, data: Any, indent_level: int) -> None: - if not data: - return - libElement = ET.Element("lib") - libElement.append(plistlib.totree(data, indent_level=indent_level)) - parentElement.append(libElement) - - def _writeGlyphElement(self, instanceElement, instanceObject, glyphName, data): - glyphElement = ET.Element("glyph") - if data.get("mute"): - glyphElement.attrib["mute"] = "1" - if data.get("unicodes") is not None: - glyphElement.attrib["unicode"] = " ".join( - [hex(u) for u in data.get("unicodes")] - ) - if data.get("instanceLocation") is not None: - locationElement, data["instanceLocation"] = self._makeLocationElement( - data.get("instanceLocation") - ) - glyphElement.append(locationElement) - if glyphName is not None: - glyphElement.attrib["name"] = glyphName - if data.get("note") is not None: - noteElement = ET.Element("note") - noteElement.text = data.get("note") - glyphElement.append(noteElement) - if data.get("masters") is not None: - mastersElement = ET.Element("masters") - for m in data.get("masters"): - masterElement = ET.Element("master") - if m.get("glyphName") is not None: - masterElement.attrib["glyphname"] = m.get("glyphName") - if m.get("font") is not None: - masterElement.attrib["source"] = m.get("font") - if m.get("location") is not None: - locationElement, m["location"] = self._makeLocationElement( - m.get("location") - ) - masterElement.append(locationElement) - mastersElement.append(masterElement) - glyphElement.append(mastersElement) - return glyphElement - - -class BaseDocReader(LogMixin): - axisDescriptorClass = AxisDescriptor - discreteAxisDescriptorClass = DiscreteAxisDescriptor - axisLabelDescriptorClass = AxisLabelDescriptor - axisMappingDescriptorClass = AxisMappingDescriptor - locationLabelDescriptorClass = LocationLabelDescriptor - ruleDescriptorClass = RuleDescriptor - sourceDescriptorClass = SourceDescriptor - variableFontsDescriptorClass = VariableFontDescriptor - valueAxisSubsetDescriptorClass = ValueAxisSubsetDescriptor - rangeAxisSubsetDescriptorClass = RangeAxisSubsetDescriptor - instanceDescriptorClass = InstanceDescriptor - - def __init__(self, documentPath, documentObject): - self.path = documentPath - self.documentObject = documentObject - tree = ET.parse(self.path) - self.root = tree.getroot() - self.documentObject.formatVersion = self.root.attrib.get("format", "3.0") - self._axes = [] - self.rules = [] - self.sources = [] - self.instances = [] - self.axisDefaults = {} - self._strictAxisNames = True - - @classmethod - def fromstring(cls, string, documentObject): - f = BytesIO(tobytes(string, encoding="utf-8")) - self = cls(f, documentObject) - self.path = None - return self - - def read(self): - self.readAxes() - self.readLabels() - self.readRules() - self.readVariableFonts() - self.readSources() - self.readInstances() - self.readLib() - - def readRules(self): - # we also need to read any conditions that are outside of a condition set. - rules = [] - rulesElement = self.root.find(".rules") - if rulesElement is not None: - processingValue = rulesElement.attrib.get("processing", "first") - if processingValue not in {"first", "last"}: - raise DesignSpaceDocumentError( - " processing attribute value is not valid: %r, " - "expected 'first' or 'last'" % processingValue - ) - self.documentObject.rulesProcessingLast = processingValue == "last" - for ruleElement in self.root.findall(".rules/rule"): - ruleObject = self.ruleDescriptorClass() - ruleName = ruleObject.name = ruleElement.attrib.get("name") - # read any stray conditions outside a condition set - externalConditions = self._readConditionElements( - ruleElement, - ruleName, - ) - if externalConditions: - ruleObject.conditionSets.append(externalConditions) - self.log.info( - "Found stray rule conditions outside a conditionset. " - "Wrapped them in a new conditionset." - ) - # read the conditionsets - for conditionSetElement in ruleElement.findall(".conditionset"): - conditionSet = self._readConditionElements( - conditionSetElement, - ruleName, - ) - if conditionSet is not None: - ruleObject.conditionSets.append(conditionSet) - for subElement in ruleElement.findall(".sub"): - a = subElement.attrib["name"] - b = subElement.attrib["with"] - ruleObject.subs.append((a, b)) - rules.append(ruleObject) - self.documentObject.rules = rules - - def _readConditionElements(self, parentElement, ruleName=None): - cds = [] - for conditionElement in parentElement.findall(".condition"): - cd = {} - cdMin = conditionElement.attrib.get("minimum") - if cdMin is not None: - cd["minimum"] = float(cdMin) - else: - # will allow these to be None, assume axis.minimum - cd["minimum"] = None - cdMax = conditionElement.attrib.get("maximum") - if cdMax is not None: - cd["maximum"] = float(cdMax) - else: - # will allow these to be None, assume axis.maximum - cd["maximum"] = None - cd["name"] = conditionElement.attrib.get("name") - # # test for things - if cd.get("minimum") is None and cd.get("maximum") is None: - raise DesignSpaceDocumentError( - "condition missing required minimum or maximum in rule" - + (" '%s'" % ruleName if ruleName is not None else "") - ) - cds.append(cd) - return cds - - def readAxes(self): - # read the axes elements, including the warp map. - axesElement = self.root.find(".axes") - if axesElement is not None and "elidedfallbackname" in axesElement.attrib: - self.documentObject.elidedFallbackName = axesElement.attrib[ - "elidedfallbackname" - ] - axisElements = self.root.findall(".axes/axis") - if not axisElements: - return - for axisElement in axisElements: - if ( - self.documentObject.formatTuple >= (5, 0) - and "values" in axisElement.attrib - ): - axisObject = self.discreteAxisDescriptorClass() - axisObject.values = [ - float(s) for s in axisElement.attrib["values"].split(" ") - ] - else: - axisObject = self.axisDescriptorClass() - axisObject.minimum = float(axisElement.attrib.get("minimum")) - axisObject.maximum = float(axisElement.attrib.get("maximum")) - axisObject.default = float(axisElement.attrib.get("default")) - axisObject.name = axisElement.attrib.get("name") - if axisElement.attrib.get("hidden", False): - axisObject.hidden = True - axisObject.tag = axisElement.attrib.get("tag") - for mapElement in axisElement.findall("map"): - a = float(mapElement.attrib["input"]) - b = float(mapElement.attrib["output"]) - axisObject.map.append((a, b)) - for labelNameElement in axisElement.findall("labelname"): - # Note: elementtree reads the "xml:lang" attribute name as - # '{http://www.w3.org/XML/1998/namespace}lang' - for key, lang in labelNameElement.items(): - if key == XML_LANG: - axisObject.labelNames[lang] = tostr(labelNameElement.text) - labelElement = axisElement.find(".labels") - if labelElement is not None: - if "ordering" in labelElement.attrib: - axisObject.axisOrdering = int(labelElement.attrib["ordering"]) - for label in labelElement.findall(".label"): - axisObject.axisLabels.append(self.readAxisLabel(label)) - self.documentObject.axes.append(axisObject) - self.axisDefaults[axisObject.name] = axisObject.default - - self.documentObject.axisMappings = [] - for mappingsElement in self.root.findall(".axes/mappings"): - groupDescription = mappingsElement.attrib.get("description") - for mappingElement in mappingsElement.findall("mapping"): - description = mappingElement.attrib.get("description") - inputElement = mappingElement.find("input") - outputElement = mappingElement.find("output") - inputLoc = {} - outputLoc = {} - for dimElement in inputElement.findall(".dimension"): - name = dimElement.attrib["name"] - value = float(dimElement.attrib["xvalue"]) - inputLoc[name] = value - for dimElement in outputElement.findall(".dimension"): - name = dimElement.attrib["name"] - value = float(dimElement.attrib["xvalue"]) - outputLoc[name] = value - axisMappingObject = self.axisMappingDescriptorClass( - inputLocation=inputLoc, - outputLocation=outputLoc, - description=description, - groupDescription=groupDescription, - ) - self.documentObject.axisMappings.append(axisMappingObject) - - def readAxisLabel(self, element: ET.Element): - xml_attrs = { - "userminimum", - "uservalue", - "usermaximum", - "name", - "elidable", - "oldersibling", - "linkeduservalue", - } - unknown_attrs = set(element.attrib) - xml_attrs - if unknown_attrs: - raise DesignSpaceDocumentError( - f"label element contains unknown attributes: {', '.join(unknown_attrs)}" - ) - - name = element.get("name") - if name is None: - raise DesignSpaceDocumentError("label element must have a name attribute.") - valueStr = element.get("uservalue") - if valueStr is None: - raise DesignSpaceDocumentError( - "label element must have a uservalue attribute." - ) - value = float(valueStr) - minimumStr = element.get("userminimum") - minimum = float(minimumStr) if minimumStr is not None else None - maximumStr = element.get("usermaximum") - maximum = float(maximumStr) if maximumStr is not None else None - linkedValueStr = element.get("linkeduservalue") - linkedValue = float(linkedValueStr) if linkedValueStr is not None else None - elidable = True if element.get("elidable") == "true" else False - olderSibling = True if element.get("oldersibling") == "true" else False - labelNames = { - lang: label_name.text or "" - for label_name in element.findall("labelname") - for attr, lang in label_name.items() - if attr == XML_LANG - # Note: elementtree reads the "xml:lang" attribute name as - # '{http://www.w3.org/XML/1998/namespace}lang' - } - return self.axisLabelDescriptorClass( - name=name, - userValue=value, - userMinimum=minimum, - userMaximum=maximum, - elidable=elidable, - olderSibling=olderSibling, - linkedUserValue=linkedValue, - labelNames=labelNames, - ) - - def readLabels(self): - if self.documentObject.formatTuple < (5, 0): - return - - xml_attrs = {"name", "elidable", "oldersibling"} - for labelElement in self.root.findall(".labels/label"): - unknown_attrs = set(labelElement.attrib) - xml_attrs - if unknown_attrs: - raise DesignSpaceDocumentError( - f"Label element contains unknown attributes: {', '.join(unknown_attrs)}" - ) - - name = labelElement.get("name") - if name is None: - raise DesignSpaceDocumentError( - "label element must have a name attribute." - ) - designLocation, userLocation = self.locationFromElement(labelElement) - if designLocation: - raise DesignSpaceDocumentError( - f'