chore(ci): migrate Python tooling to uv and standardize workflow execution (#7386)

# Description of Changes

This PR modernizes the project's Python tooling across GitHub Actions by
migrating CI workflows from pip-based dependency management to `uv` and
aligning Python execution with the engine project's managed environment.

### What was changed

- Replaced `actions/setup-python` and ad-hoc `pip install` steps with
`astral-sh/setup-uv` across CI workflows.
- Configured shared `uv` dependency caching using
`engine/pyproject.toml` and `engine/uv.lock`.
- Updated Python script execution to use `uv run --project engine
--locked` for a consistent runtime environment.
- Replaced package installation steps with `uv sync` for the required
dependency groups (e.g. `tools` and `cucumber`).
- Added Docker image build validation for both production and
development AI engine images.
- Updated workflow cache configuration and Docker build context where
required.
- Removed obsolete Python requirements files that are no longer needed
after the migration.
- Applied minor Python code modernizations, including import cleanup,
modern built-in generic type annotations (`list[...]`, `tuple[...]`,
`float | None`), and small style improvements.
- Removed unnecessary Python formatter/linter extensions from the
development container configuration.

### Why the change was made

- Standardize Python dependency management across the repository.
- Reduce duplicated dependency installation logic in CI.
- Improve workflow performance through shared dependency caching.
- Ensure all Python utilities execute against the same locked dependency
set managed by the engine project.
- Simplify long-term maintenance by eliminating legacy requirements
files and pip-specific workflow steps.


---

## Checklist

### General

- [ ] I have read the [Contribution
Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md)
- [ ] I have read the [Stirling-PDF Developer
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md)
(if applicable)
- [ ] I have read the [How to add new languages to
Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md)
(if applicable)
- [ ] I have performed a self-review of my own code
- [ ] My changes generate no new warnings

### Documentation

- [ ] I have updated relevant docs on [Stirling-PDF's doc
repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/)
(if functionality has heavily changed)
- [ ] I have read the section [Add New Translation
Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags)
(for new translation tags only)

### Translations (if applicable)

- [ ] I ran
[`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md)

### UI Changes (if applicable)

- [ ] Screenshots or videos demonstrating the UI changes are attached
(e.g., as comments or direct attachments in the PR)

### Testing (if applicable)

- [ ] I have run `task check` to verify linters, typechecks, and tests
pass
- [ ] I have tested my changes locally. Refer to the [Testing
Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing)
for more details.

---------

Signed-off-by: Carsten Drewes <c.drewes@stud.uni-hannover.de>
Co-authored-by: albanobattistella <34811668+albanobattistella@users.noreply.github.com>
Co-authored-by: kastenherri <116314318+kastenherri@users.noreply.github.com>
Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: James Brunton <jbrunton96@gmail.com>
This commit is contained in:
Ludy
2026-08-11 08:09:21 +00:00
committed by GitHub
co-authored by albanobattistella kastenherri Anthony Stirling Copilot James Brunton
parent bbaff8d6c4
commit 05eb74022a
64 changed files with 1684 additions and 3258 deletions
+11 -24
View File
@@ -4,12 +4,13 @@ Wrap raw CFF/Type1C data (extracted from PDFs) as OpenType-CFF for web compatibi
Builds proper Unicode cmap from PDF ToUnicode data.
"""
import sys
import re
from pathlib import Path
import sys
from io import BytesIO
from fontTools.ttLib import TTFont, newTable
from pathlib import Path
from fontTools.cffLib import CFFFontSet
from fontTools.ttLib import TTFont, newTable
from fontTools.ttLib.tables._c_m_a_p import cmap_format_4, cmap_format_12
from fontTools.ttLib.tables._n_a_m_e import NameRecord
from fontTools.ttLib.tables.O_S_2f_2 import Panose
@@ -117,15 +118,11 @@ def wrap_cff_as_otf(input_path, output_path, tounicode_path=None):
# Get glyph names
if hasattr(cff_font, "charset") and cff_font.charset is not None:
glyph_order = [".notdef"] + [
name for name in cff_font.charset if name != ".notdef"
]
glyph_order = [".notdef"] + [name for name in cff_font.charset if name != ".notdef"]
else:
# Fallback to CharStrings keys
charstrings = cff_font.CharStrings
glyph_order = [".notdef"] + [
name for name in charstrings.keys() if name != ".notdef"
]
glyph_order = [".notdef"] + [name for name in charstrings.keys() if name != ".notdef"]
otf.setGlyphOrder(glyph_order)
@@ -139,9 +136,7 @@ def wrap_cff_as_otf(input_path, output_path, tounicode_path=None):
# Get defaults from CFF Private dict
private_dict = getattr(cff_font, "Private", None)
default_width = (
getattr(private_dict, "defaultWidthX", 500) if private_dict else 500
)
default_width = getattr(private_dict, "defaultWidthX", 500) if private_dict else 500
# Calculate bounding box, widths, and LSBs
x_min = 0
@@ -280,9 +275,7 @@ def wrap_cff_as_otf(input_path, output_path, tounicode_path=None):
# For CID fonts: glyph names are "cid00123" (5-digit zero-padded)
# For non-CID fonts: glyph names vary but GID == array index
is_cid_font = any(
gn.startswith("cid") for gn in glyph_order[1:6]
) # Check first few non-.notdef glyphs
is_cid_font = any(gn.startswith("cid") for gn in glyph_order[1:6]) # Check first few non-.notdef glyphs
for gid, unicode_val in gid_to_unicode.items():
if unicode_val > 0:
@@ -355,14 +348,10 @@ def wrap_cff_as_otf(input_path, output_path, tounicode_path=None):
cmap4_mac.cmap = {cp: gn for cp, gn in unicode_to_glyph.items() if cp <= 0xFFFF}
cmap_tables.append(cmap4_mac)
cmap.tables = [t for t in cmap_tables if t.cmap] or [
cmap4_win
] # Ensure at least one
cmap.tables = [t for t in cmap_tables if t.cmap] or [cmap4_win] # Ensure at least one
otf["cmap"] = cmap
print(
f"Built cmap with {len(unicode_to_glyph)} Unicode mappings", file=sys.stderr
)
print(f"Built cmap with {len(unicode_to_glyph)} Unicode mappings", file=sys.stderr)
# === Create OS/2 table with correct metrics ===
os2 = newTable("OS/2")
@@ -515,9 +504,7 @@ Examples:
# Add named arguments
parser.add_argument("--input", dest="input_file", help="Input CFF file path")
parser.add_argument("--output", dest="output_file", help="Output OTF file path")
parser.add_argument(
"--to-unicode", dest="tounicode_file", help="ToUnicode mapping file path"
)
parser.add_argument("--to-unicode", dest="tounicode_file", help="ToUnicode mapping file path")
# Add positional arguments for backward compatibility
parser.add_argument("input_pos", nargs="?", help="Input CFF file (positional)")