"""Metodo B — rendering SVG server-side con rettangoli di redazione.""" from __future__ import annotations from xml.sax.saxutils import escape as xml_escape from pdf.rendering.base import ( MethodInfo, RenderKind, RenderMethod, RenderOutput, RenderParams, boxes_on_page, ) from pdf.rendering.styles import severity_rgb255 class SvgRenderMethod(RenderMethod): info = MethodInfo( key="svg", label="B · SVG", kind=RenderKind.VIEW, media_type="image/svg+xml", styles=("fill", "outline", "label"), default_style="fill", description=( "SVG vettoriale generato dal server con i riquadri incorporati. " "Geometria precisa, nessuna dipendenza client, testo selezionabile." ), ) def render(self, params: RenderParams) -> RenderOutput: import fitz doc = fitz.open(stream=params.pdf_bytes, filetype="pdf") page = doc.load_page(params.page) mat = fitz.Matrix(params.scale, params.scale) svg_text = page.get_svg_image(matrix=mat) page_w = page.rect.width * params.scale page_h = page.rect.height * params.scale doc.close() elements = self._build_elements(params) svg = svg_text.replace("", "\n".join(elements) + "\n") return RenderOutput( data=svg.encode("utf-8"), media_type=self.info.media_type, headers={ "X-Page-Width": f"{page_w:.1f}", "X-Page-Height": f"{page_h:.1f}", "Cache-Control": "private, max-age=300", }, ) def _build_elements(self, params: RenderParams) -> list[str]: out: list[str] = [] s = params.scale for b in boxes_on_page(params): r, g, bl = severity_rgb255(b.get("severity", "media")) color = f"rgb({r},{g},{bl})" x0, y0 = b["x0"] * s, b["y0"] * s w, h = (b["x1"] - b["x0"]) * s, (b["y1"] - b["y0"]) * s label = xml_escape(str(b.get("label", ""))) rect = f'x="{x0:.1f}" y="{y0:.1f}" width="{w:.1f}" height="{h:.1f}"' if params.style == "outline": out.append( f'{label}' ) elif params.style == "label": fs = max(7.0, min(h * 0.7, 14.0)) out.append( f'' f'{label}' f'{label}' ) else: # fill out.append( f'{label}' ) return out