Files
pdf/tests/edits/forensic_compare.py
T
2026-07-30 16:48:46 +05:30

113 lines
4.1 KiB
Python

"""Compare forensic snapshots and print diff tables."""
import json
import sys
from pathlib import Path
def load(p):
return json.loads(Path(p).read_text(encoding="utf-8-sig"))
def diff_table(a, b, keys):
rows = []
for k in keys:
va, vb = a.get(k), b.get(k)
changed = va != vb
rows.append((k, va, vb, "CHANGED" if changed else "same"))
return rows
def print_table(title, rows):
print(f"\n=== {title} ===")
print(f"{'Property':<22} {'Before':<40} {'After':<40} {'Status'}")
for k, va, vb, st in rows:
sa = json.dumps(va) if not isinstance(va, str) else va
sb = json.dumps(vb) if not isinstance(vb, str) else vb
if len(sa) > 38: sa = sa[:35] + "..."
if len(sb) > 38: sb = sb[:35] + "..."
print(f"{k:<22} {sa:<40} {sb:<40} {st}")
def compare_advances(orig, new):
oa, na = orig or [], new or []
diffs = []
for i in range(max(len(oa), len(na))):
ov = oa[i] if i < len(oa) else None
nv = na[i] if i < len(na) else None
if ov != nv:
diffs.append((i, ov, nv))
return diffs
def main():
extract_path = Path(sys.argv[1] if len(sys.argv) > 1 else "forensic_extract_out.json")
wasm_path = Path(sys.argv[2] if len(sys.argv) > 2 else "forensic_wasm_out.json")
ex = load(extract_path)
wasm = load(wasm_path)
keys = [
"text", "fontId", "fontName", "fontSize", "color",
"glyphCount", "advanceCount", "paragraphWidth", "baseline", "lineHeight",
"origLinesProvided",
]
orig = ex["original"]
entry_payload = ex["editEntry"]
one_payload = ex["editOne"]
# WASM layout snapshots
from forensic_extract import snapshot_from_wasm_layout, para_signature # type: ignore
seed_fn = orig.get("fontName", "")
entry_wasm = snapshot_from_wasm_layout("ENTRY_WASM", wasm["editEntryWasm"]["layout"], seed_fn)
one_wasm = snapshot_from_wasm_layout("ONE_WASM", wasm["editOneWasm"]["layout"], seed_fn)
print_table("ORIGINAL vs EDIT_MODE_ENTRY (payload)", diff_table(orig, entry_payload, keys))
print_table("ORIGINAL vs EDIT_MODE_ENTRY (WASM layout)", diff_table(orig, entry_wasm, keys))
adv_diff_entry = compare_advances(orig.get("advances"), entry_wasm.get("advances"))
if adv_diff_entry:
print(f"\nAdvance diffs ORIGINAL vs ENTRY_WASM: {len(adv_diff_entry)} positions")
for i, ov, nv in adv_diff_entry[:20]:
print(f" [{i}] {ov} -> {nv}")
else:
print("\nAdvance arrays IDENTICAL: ORIGINAL vs ENTRY_WASM")
print_table("EDIT_ENTRY vs AFTER_ONE_CHAR (payload)", diff_table(entry_payload, one_payload, keys))
print_table("ENTRY_WASM vs ONE_WASM", diff_table(entry_wasm, one_wasm, keys))
adv_diff_one = compare_advances(entry_wasm.get("advances"), one_wasm.get("advances"))
if adv_diff_one:
print(f"\nAdvance diffs ENTRY_WASM vs ONE_WASM: {len(adv_diff_one)} positions")
for i, ov, nv in adv_diff_one[:20]:
print(f" [{i}] {ov} -> {nv}")
# Line widths
print("\n=== LINE WIDTHS ===")
print(f"Original lineWidths: {orig.get('lineWidths')}")
print(f"Entry WASM lineWidths: {entry_wasm.get('lineWidths')}")
print(f"One WASM lineWidths: {one_wasm.get('lineWidths')}")
# Signatures chain
sigs = {
"original": para_signature(orig),
"entry_payload": para_signature(entry_payload),
"entry_wasm": para_signature(entry_wasm),
"one_payload": para_signature(one_payload),
"one_wasm": para_signature(one_wasm),
}
print("\n=== SIGNATURE CHAIN ===")
prev = None
for name, sig in sigs.items():
changed = prev and sig != prev
print(f" {name}: {sig}" + (" <-- FIRST CHANGE from " + prev_name if changed else ""))
if changed and prev:
pass
prev_name = name
prev = sig
# First mutation
chain = list(sigs.items())
base = chain[0][1]
for i in range(1, len(chain)):
if chain[i][1] != base:
print(f"\nFIRST SIGNATURE CHANGE: {chain[i][0]} (vs original)")
break
if __name__ == "__main__":
main()