@@ -0,0 +1,43 @@
|
||||
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
|
||||
Reference in New Issue
Block a user