Spaces:
Running
Running
Alessandro Tomassini
deploy(hf): overlay README/Dockerfile da huggingface/, senza docs/binari/model
c42a5a1 | """Astrazioni del sottosistema di rendering PDF (SOLID). | |
| Ogni metodo di visualizzazione/redazione e' una *strategia* isolata che | |
| implementa `RenderMethod`. Sono registrate in un `RenderRegistry` e abilitate | |
| o escluse da configurazione: aggiungere/togliere un metodo non tocca il codice | |
| esistente (Open/Closed) ne' il livello web (Dependency Inversion). | |
| - Single Responsibility: ogni metodo vive in un proprio file e sa solo | |
| produrre il proprio output. | |
| - Liskov: tutti i metodi rispettano la stessa firma `render(params) -> output`. | |
| - Interface Segregation: l'interfaccia espone solo `info` e `render`. | |
| """ | |
| from __future__ import annotations | |
| from abc import ABC, abstractmethod | |
| from dataclasses import dataclass, field | |
| from enum import Enum | |
| from core.contracts import PageGeometry | |
| class RenderKind(str, Enum): | |
| """Natura dell'output di un metodo.""" | |
| VIEW = "view" # immagine renderizzata inline (SVG/PNG) | |
| CLIENT = "client" # rendering lato client (solo dati: raw + overlay) | |
| DOWNLOAD = "download" # file scaricabile (PDF) | |
| class MethodInfo: | |
| """Metadati di un metodo, usati anche dalla UI per costruire i controlli.""" | |
| key: str | |
| label: str | |
| kind: RenderKind | |
| media_type: str | |
| styles: tuple[str, ...] | |
| default_style: str | |
| description: str | |
| destructive: bool = False # True solo per la hard redaction | |
| class RenderParams: | |
| """Parametri di una richiesta di rendering. Ogni metodo usa cio' che serve.""" | |
| pdf_bytes: bytes | |
| boxes: list[dict] = field(default_factory=list) | |
| pages: tuple[PageGeometry, ...] = field(default_factory=tuple) | |
| page: int = 0 | |
| style: str = "fill" | |
| scale: float = 1.5 | |
| dpi: int = 144 | |
| opacity: float = 0.85 | |
| class RenderOutput: | |
| """Risultato neutro di un rendering.""" | |
| data: bytes | |
| media_type: str | |
| headers: dict[str, str] = field(default_factory=dict) | |
| filename: str | None = None | |
| def boxes_on_page(params: RenderParams) -> list[dict]: | |
| """Box della richiesta che appartengono alla pagina corrente (`params.page`). | |
| Filtro condiviso dai metodi che renderizzano una singola pagina (SVG/PNG), | |
| dove prima era ripetuto inline (`if b.get("page") != params.page`). | |
| """ | |
| return [b for b in params.boxes if b.get("page") == params.page] | |
| class RenderMethod(ABC): | |
| """Interfaccia comune a tutti i metodi di rendering PDF.""" | |
| #: Metadati statici del metodo (override nelle sottoclassi o via __init__). | |
| info: MethodInfo | |
| def supports_style(self, style: str) -> bool: | |
| return style in self.info.styles | |
| def render(self, params: RenderParams) -> RenderOutput: | |
| """Produce l'output del metodo a partire dai parametri.""" | |
| class RenderRegistry: | |
| """Collezione ordinata di metodi abilitati (Open/Closed).""" | |
| def __init__(self) -> None: | |
| self._methods: dict[str, RenderMethod] = {} | |
| def register(self, method: RenderMethod) -> RenderRegistry: | |
| if method.info.key in self._methods: | |
| raise ValueError(f"Metodo gia' registrato: {method.info.key}") | |
| self._methods[method.info.key] = method | |
| return self | |
| def has(self, key: str) -> bool: | |
| return key in self._methods | |
| def get(self, key: str) -> RenderMethod | None: | |
| return self._methods.get(key) | |
| def infos(self) -> list[MethodInfo]: | |
| return [m.info for m in self._methods.values()] | |
| def __len__(self) -> int: | |
| return len(self._methods) | |