44 lines
2.0 KiB
Python
44 lines
2.0 KiB
Python
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import io
|
|
import pathlib
|
|
import re
|
|
|
|
import PIL.Image
|
|
import PIL.ImageChops
|
|
|
|
import generator.engraving.types
|
|
|
|
ENGINE_NAME = 'Vamp Python'
|
|
PAGE_WIDTH = 976
|
|
PAGE_HEIGHT = 484
|
|
|
|
def check_content(data: bytes, name: str) -> None:
|
|
with PIL.Image.open(io.BytesIO(data)) as image:
|
|
if image.format != 'PNG' or image.size != (PAGE_WIDTH, PAGE_HEIGHT):
|
|
raise generator.engraving.types.EngravingError(f'{name}: expected a {PAGE_WIDTH}x{PAGE_HEIGHT} PNG page')
|
|
image.load()
|
|
white = PIL.Image.new('RGBA', image.size, 'white')
|
|
white.alpha_composite(image.convert('RGBA'))
|
|
ink = PIL.ImageChops.difference(white.convert('RGB'), PIL.Image.new('RGB', image.size, 'white')).convert('L').point(lambda x: 255 if x > 32 else 0)
|
|
bounds = ink.getbbox()
|
|
if not bounds:
|
|
raise generator.engraving.types.EngravingError(f'{name}: engraver produced a blank page')
|
|
if bounds[0] < 4 or bounds[1] < 4 or bounds[2] > PAGE_WIDTH - 4 or bounds[3] > PAGE_HEIGHT - 4:
|
|
raise generator.engraving.types.EngravingError(f'{name}: notation reaches the page edge; refusing potentially clipped output')
|
|
|
|
def check_page(path: pathlib.Path, validated_pages: set[str] | None = None, *, expected_sha256: str | None = None) -> str:
|
|
# Trust content, never a filename or a caller-supplied checksum alone.
|
|
if path.is_symlink() or not path.is_file():
|
|
raise generator.engraving.types.EngravingError(f'{path.name}: expected a regular PNG file')
|
|
data = path.read_bytes()
|
|
digest = hashlib.sha256(data).hexdigest()
|
|
if expected_sha256 is not None and (not isinstance(expected_sha256, str) or not re.fullmatch('[0-9a-f]{64}', expected_sha256) or digest != expected_sha256):
|
|
raise generator.engraving.types.EngravingError(f'{path.name}: score page checksum mismatch')
|
|
if validated_pages is None or digest not in validated_pages:
|
|
check_content(data, path.name)
|
|
if validated_pages is not None:
|
|
validated_pages.add(digest)
|
|
return digest
|