"""Read-only native SVG conservation check. Python 3 stdlib; never overwrite a receipt. Usage: check-wrapped-svg.py BASELINE.svg CANDIDATE.svg E|F NEW-OUTPUT.json Both native export directories must retain their original eight-card.json input. """ from pathlib import Path import argparse import hashlib import json import math import re import sys import xml.etree.ElementTree as ET TARGET = "63009b5f-539e-5914-b630-ec6b7fefb4d5" NAME = "Technician service conversation" FIXTURE_SHA = "9db1015369f450d5810c96a01fddb8a009d57f6aafbda5182703f15ded23d9c8" NS = "{http://www.w3.org/2000/svg}" # Independent expected logical metrics printed by the pinned Rust/Iced tests at # ebb6a9980b11c4d4df2f7e413c6b135a337f6f6d. Never infer these from candidate SVG. # Lines: canonical display text, left x, top y, shaped baseline y, glyph advance. METRICS = { "E": {"rect": [1102.4126, 254.5, 119.174805, 40.0], "lines": [ ["Technician service", 1110.4126, 258.5, 271.8828, 103.174805], ["conversation", 1125.4089, 274.5, 287.8828, 73.18213], ]}, "F": {"rect": [1180.0, 226.2, 89.18213, 56.0], "lines": [ ["Technician", 1194.2905, 230.2, 243.58281, 60.601074], ["service", 1205.0847, 246.2, 259.58282, 39.012695], ["conversation", 1188.0, 262.2, 275.58282, 73.18213], ]}, } def require(condition, message): if not condition: raise ValueError(message) def digest(data): return {"bytes": len(data), "sha256": hashlib.sha256(data).hexdigest()} def close(actual, expected, label): value = float(actual) require(math.isfinite(value) and abs(value - expected) <= 0.0002, f"{label}: expected {expected}, got {actual}") def element_attrs(element, tag, fixed, numeric): require(element.tag == NS + tag, f"Expected {tag}, got {element.tag}") require(set(element.attrib) == set(fixed) | set(numeric), f"Unexpected attributes on {tag}") for key, value in fixed.items(): require(element.get(key) == value, f"{tag}.{key} changed") for key, value in numeric.items(): close(element.get(key), value, tag + "." + key) def strip_immediate_label(raw, candidate): edge = re.compile(rb']*\bdata-edge="' + TARGET.encode() + rb'"[^>]*>.*?', re.DOTALL) matches = list(edge.finditer(raw)) require(len(matches) == 1, "Target edge must occur exactly once") start = matches[0].end() pattern = (rb'.*?' if candidate else rb']*/>]*>[^<]*') label = re.compile(pattern, re.DOTALL).match(raw, start) require(label is not None, "Target label must immediately follow its edge") return raw[:start] + raw[label.end():], raw[start:label.end()] def check(args, report): baseline, candidate = Path(args.baseline), Path(args.candidate) before, after = baseline.read_bytes(), candidate.read_bytes() report["inputs"] = {"baseline_svg": {"path": str(baseline), **digest(before)}, "candidate_svg": {"path": str(candidate), **digest(after)}} fixture_bytes = [] for key, svg in [("baseline_project", baseline), ("candidate_project", candidate)]: path = svg.parent / "eight-card.json" data = path.read_bytes() report["inputs"][key] = {"path": str(path), **digest(data)} require(digest(data)["sha256"] == FIXTURE_SHA, key + " differs from pinned full Project") fixture_bytes.append(data) require(fixture_bytes[0] == fixture_bytes[1], "Full input Projects differ") roots = [ET.fromstring(data) for data in [before, after]] for root in roots: require(root.tag == NS + "svg", "Expected SVG root") require(roots[0].attrib == roots[1].attrib, "Root attributes/viewBox changed") require(roots[0].get("viewBox") == "24 -12 1566 874", "Pinned full scene viewBox changed") edges = [[e for e in root.iter() if e.get("data-edge") is not None] for root in roots] for found in edges: require(len(found) == 12 and len({e.get("data-edge") for e in found}) == 12, "Expected all 12 distinct edge UUIDs") require([ET.tostring(e) for e in edges[0]] == [ET.tostring(e) for e in edges[1]], "Edge order, UUID, path, orientation/marker, drawing attributes or title changed") for root in roots: children = list(root) target_index = next(i for i, e in enumerate(children) if e.get("data-edge") == TARGET) require(children[target_index].find(NS + "title").text == NAME, "Canonical edge title changed") bchildren = list(roots[0]) bi = next(i for i, e in enumerate(bchildren) if e.get("data-edge") == TARGET) element_attrs(bchildren[bi + 1], "rect", {"fill": "white"}, {"x": 1180., "y": 262.5, "width": 233., "height": 24.}) element_attrs(bchildren[bi + 2], "text", {"text-anchor": "middle", "dominant-baseline": "central", "font-family": "sans-serif", "fill": "#637487"}, {"x": 1296.5, "y": 274.5, "font-size": 13.}) require(bchildren[bi + 2].text == NAME, "Baseline target is not canonical one-line label") groups = [e for e in roots[1].iter() if e.get("data-label-experiment") is not None] require(len(groups) == 1, "Exactly one experimental label group is allowed") group = groups[0] element_attrs(group, "g", {"data-label-experiment": "wrapped"}, {}) cchildren = list(roots[1]) ci = next(i for i, e in enumerate(cchildren) if e.get("data-edge") == TARGET) require(cchildren[ci + 1] is group, "Wrapped group belongs immediately to target edge") expected = METRICS[args.profile] parts = list(group) require(len(parts) == 2 + len(expected["lines"]), "Unexpected wrapped group contents") element_attrs(parts[0], "title", {}, {}) require(parts[0].text == NAME and not list(parts[0]), "Canonical group title changed") element_attrs(parts[1], "rect", {"fill": "white"}, dict(zip(["x", "y", "width", "height"], expected["rect"]))) for actual, (name, x, top, baseline_y, width) in zip(parts[2:], expected["lines"]): element_attrs(actual, "text", {"font-family": "Segoe UI", "fill": "#637487"}, {"x": x, "y": baseline_y, "font-size": 13.}) require(actual.text == name and not list(actual), "Ordered complete display line changed") require(" ".join(e.text for e in parts[2:]) == NAME, "Wrapping altered the complete canonical text") close(expected["rect"][2], max(line[4] for line in expected["lines"]) + 16., "Expected advance plus padding") close(expected["rect"][3], 8. + 16. * len(expected["lines"]), "Expected line height plus padding") bremaining, bremoved = strip_immediate_label(before, False) cremaining, cremoved = strip_immediate_label(after, True) require(bremaining == cremaining, "Raw SVG bytes outside ONLY target's immediate label presentation changed") report.update({ "status": "PASS", "full_project_bytes_equal": True, "edge_uuids": [e.get("data-edge") for e in edges[0]], "all_12_edge_records_exact": True, "root_and_viewBox_exact": True, "all_remaining_svg_bytes_exact": True, "remaining_svg": digest(bremaining), "removed_baseline_presentation": digest(bremoved), "removed_candidate_presentation": digest(cremoved), "expected_logical_metrics": expected, "numeric_absolute_tolerance": 0.0002, "conservation_scope": "All edge/path/orientation/drawing, card, port and unrelated label bytes preserved; only target immediate label presentation removed for comparison.", "limits": "Logical SVG conservation and declared typography coordinates only. No browser/font availability, glyph/raster parity, screenshot reading, native input-event history, save/reopen or usability claim.", }) def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("baseline") parser.add_argument("candidate") parser.add_argument("profile", choices=["E", "F"]) parser.add_argument("output") args = parser.parse_args() report = {"status": "FAIL", "profile": args.profile, "script": digest(Path(__file__).read_bytes()), "metrics_source": "ebb6a9980b11c4d4df2f7e413c6b135a337f6f6d Rust/Iced test stdout; wrapped-label-regression-first.log"} # Exclusive creation precedes reads; a failed prior run is never overwritten. with Path(args.output).open("x", encoding="utf-8") as output: try: check(args, report) except Exception as error: report["failure"] = {"type": type(error).__name__, "message": str(error)} json.dump(report, output, indent=2) output.write("\n") print(json.dumps(report, indent=2)) return 0 if report["status"] == "PASS" else 1 if __name__ == "__main__": sys.exit(main())