AiAnonymize_v3 / pdf /rendering /svg_method.py
Alessandro Tomassini
deploy(hf): overlay README/Dockerfile da huggingface/, senza docs/binari/model
c42a5a1
Raw
History Blame Contribute Delete
3.21 kB
"""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("</svg>", "\n".join(elements) + "\n</svg>")
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'<rect {rect} fill="none" stroke="{color}" stroke-width="2" '
f'rx="2" ry="2"><title>{label}</title></rect>'
)
elif params.style == "label":
fs = max(7.0, min(h * 0.7, 14.0))
out.append(
f'<rect {rect} fill="{color}" opacity="{params.opacity}" rx="2" ry="2">'
f'<title>{label}</title></rect>'
f'<text x="{x0 + w / 2:.1f}" y="{y0 + h / 2 + fs / 3:.1f}" '
f'font-size="{fs:.1f}" fill="white" text-anchor="middle" '
f'font-family="sans-serif" font-weight="bold" '
f'pointer-events="none">{label}</text>'
)
else: # fill
out.append(
f'<rect {rect} fill="{color}" opacity="{params.opacity}" '
f'rx="2" ry="2"><title>{label}</title></rect>'
)
return out