121 lines
3.7 KiB
Python
121 lines
3.7 KiB
Python
"""Regression coverage for canonical-coordinate PDF export."""
|
|
|
|
import json
|
|
|
|
import pymupdf
|
|
|
|
from app.services.export.renderer import export_annotations
|
|
|
|
|
|
def _source_pdf(path) -> None:
|
|
document = pymupdf.open()
|
|
page = document.new_page(width=720, height=936)
|
|
page.set_cropbox(pymupdf.Rect(36, 72, 648, 864))
|
|
page.set_rotation(90)
|
|
document.save(path)
|
|
document.close()
|
|
|
|
|
|
def test_export_preserves_rotation_and_flattens_supported_annotations(tmp_path) -> None:
|
|
source = tmp_path / "source.pdf"
|
|
_source_pdf(source)
|
|
annotations = [
|
|
{
|
|
"id": "text",
|
|
"page": 0,
|
|
"type": "text",
|
|
"rect": {"x": 40, "y": 50, "width": 180, "height": 40},
|
|
"props": {
|
|
"text": "Exported text",
|
|
"fontFamily": "Liberation Sans",
|
|
"fontSize": 14,
|
|
"color": "#112233",
|
|
"styles": {"0": {"0": {"fill": "#ff0000", "fontWeight": "bold"}}},
|
|
},
|
|
},
|
|
{
|
|
"id": "line",
|
|
"page": 0,
|
|
"type": "draw",
|
|
"rect": {"x": 40, "y": 110, "width": 80, "height": 40},
|
|
"props": {
|
|
"paths": [[0, 0], [80, 40]],
|
|
"strokeColor": "#ff0000",
|
|
"strokeWidth": 2,
|
|
"opacity": 1,
|
|
},
|
|
},
|
|
{
|
|
"id": "highlight",
|
|
"page": 0,
|
|
"type": "highlight",
|
|
"rect": {"x": 40, "y": 170, "width": 100, "height": 20},
|
|
"props": {"color": "#ffff00", "opacity": 0.4},
|
|
},
|
|
{
|
|
"id": "signature",
|
|
"page": 0,
|
|
"type": "signature",
|
|
"rect": {"x": 40, "y": 220, "width": 160, "height": 50},
|
|
"props": {
|
|
"mode": "type",
|
|
"text": "Ava",
|
|
"fontFamily": "Great Vibes",
|
|
"color": "#000000",
|
|
},
|
|
},
|
|
{
|
|
"id": "shape",
|
|
"page": 0,
|
|
"type": "shape",
|
|
"rect": {"x": 180, "y": 170, "width": 60, "height": 30},
|
|
"props": {"kind": "ellipse", "strokeColor": "#0000ff", "strokeWidth": 2},
|
|
},
|
|
{
|
|
"id": "future",
|
|
"page": 0,
|
|
"type": "future-stamp",
|
|
"rect": {"x": 0, "y": 0, "width": 10, "height": 10},
|
|
"props": {},
|
|
},
|
|
]
|
|
|
|
exported = export_annotations(source, annotations, tmp_path / "assets")
|
|
output = tmp_path / "exported.pdf"
|
|
output.write_bytes(exported)
|
|
|
|
with pymupdf.open(output) as document:
|
|
page = document[0]
|
|
assert page.rotation == 90
|
|
assert page.rect.width == 792
|
|
assert page.rect.height == 612
|
|
assert "Exported text" in page.get_text("text")
|
|
assert "Ava" in page.get_text("text")
|
|
|
|
# Normalize only for inspection: the exported PDF still retains the
|
|
# original page rotation above.
|
|
page.set_rotation(0)
|
|
words = page.get_text("words")
|
|
text_word = next(word for word in words if word[4] == "Exported")
|
|
assert 35 <= text_word[0] <= 45
|
|
assert 45 <= text_word[1] <= 60
|
|
assert len(page.get_drawings()) >= 3
|
|
|
|
|
|
def test_export_does_not_mutate_annotation_input(tmp_path) -> None:
|
|
source = tmp_path / "source.pdf"
|
|
_source_pdf(source)
|
|
annotations = [
|
|
{
|
|
"id": "unknown",
|
|
"page": 0,
|
|
"type": "not-yet-supported",
|
|
"rect": {"x": 1, "y": 2, "width": 3, "height": 4},
|
|
"props": {"future": True},
|
|
}
|
|
]
|
|
before = json.loads(json.dumps(annotations))
|
|
|
|
export_annotations(source, annotations, tmp_path / "assets")
|
|
|
|
assert annotations == before
|