96 lines
4.4 KiB
Python
96 lines
4.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Verify the FRESH build-dir .pyd (the gateway copy is locked by the running gateway) without
|
|
touching the gateway. Importing pdfengine from the build lib FIRST caches it in sys.modules, so when
|
|
_reflow_repro imports pdfengine it reuses this fresh one. Runs the reflow regression gate + a Raw Text
|
|
(StreamEditor) smoke for the new TJ-positioning path.
|
|
Run: gateway/.venv/Scripts/python.exe tests/edits/_verify_build.py
|
|
"""
|
|
from __future__ import annotations
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
BUILD_LIB = r"C:\Users\furqa\pdfeng-build\win-local-pdfium\lib"
|
|
sys.path.insert(0, BUILD_LIB)
|
|
import pdfengine # noqa: E402 -- FRESH build, cached in sys.modules before the harness imports it
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
import _reflow_repro as H # noqa: E402 -- reuses the cached fresh pdfengine
|
|
|
|
print("pdfengine loaded from:", pdfengine.__file__)
|
|
assert BUILD_LIB.lower() in pdfengine.__file__.lower(), "NOT the fresh build .pyd!"
|
|
|
|
PDF = ROOT / "Mo-Faishal-Qureshi.pdf"
|
|
|
|
|
|
def section(t):
|
|
H.report.write(f"\n########## {t} ##########\n")
|
|
|
|
|
|
# ---- 1. Reflow regression: multi-edit survival + no-op overlay diff (left/justify) ----
|
|
section("REFLOW stress (multi-edit survival)")
|
|
H.stress()
|
|
|
|
section("REFLOW no-op overlay (regression: left + justify paragraphs must stay ~baseline)")
|
|
H.overlay(0, "Java Backend Developer") # flowing left/justify body
|
|
H.overlay(0, "Java Backend Developer", use_lines=True)
|
|
|
|
# ---- 2. Center alignment: reflow a centered heading with align=center; assert no crash + survives ----
|
|
section("CENTER align smoke")
|
|
try:
|
|
doc = pdfengine.PdfDocument.load_from_memory(PDF.read_bytes(), "")
|
|
m = doc.get_page(0).extract_document_model()
|
|
i, p = H.find_para(m, "Software Engineer")
|
|
if p is None:
|
|
H.log(" 'Software Engineer' heading not found (skip center smoke)")
|
|
else:
|
|
op = H.build_reflow_op(p, 0)
|
|
op["data"]["align"] = "center"
|
|
# widen the column to the page content box so centering is visible
|
|
op["data"]["columnLeft"] = 40.0
|
|
op["data"]["columnRight"] = m.width - 40.0
|
|
doc.apply_edits(__import__("json").dumps({"version": "1.0", "operations": [op]}))
|
|
m2 = pdfengine.PdfDocument.load_from_memory(doc.save_full(), "").get_page(0).extract_document_model()
|
|
_, pc = H.find_para(m2, "Software")
|
|
txt = H.para_text(pc) if pc else "<NOT FOUND>"
|
|
H.log(f" center reflow OK; text survived = {('Software' in txt and 'Engineer' in txt)} -> {txt[:60]!r}")
|
|
except Exception as exc:
|
|
H.log(f" CENTER smoke FAILED: {exc}")
|
|
|
|
# ---- 3. Raw Text StreamEditor: extract + same-length replace (P1a TJ preservation) ----
|
|
section("RAW TEXT StreamEditor smoke (P1a)")
|
|
try:
|
|
import tempfile, os
|
|
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".pdf"); tmp.write(PDF.read_bytes()); tmp.close()
|
|
ed = pdfengine.StreamEditor(tmp.name)
|
|
objs = ed.extract_text_objects(0)
|
|
H.log(f" extract_text_objects(0) -> {len(objs)} objects")
|
|
# pick the first object with >=3 ASCII chars; do a SAME-LENGTH edit (exercises TJ redistribution)
|
|
target = None
|
|
for idx, o in enumerate(objs):
|
|
t = o["text"].decode("latin-1") if isinstance(o["text"], bytes) else o["text"]
|
|
if len(t) >= 3 and t.strip() and all(32 <= ord(c) < 127 for c in t):
|
|
target = (idx, t); break
|
|
if target is None:
|
|
H.log(" no suitable ASCII object found (skip)")
|
|
else:
|
|
idx, t = target
|
|
# same-length swap: reverse-safe -> replace each alnum char's case-insensitive 'a'->'e' won't change len
|
|
new = t[:-1] + ("X" if t[-1] != "X" else "Y") # same length, last char changed
|
|
out = tmp.name + ".out.pdf"
|
|
ok = ed.replace_text_object(0, idx, new.encode("latin-1"), out)
|
|
H.log(f" replace obj#{idx} {t!r} -> {new!r} : success={ok}")
|
|
if ok and os.path.exists(out):
|
|
ed2 = pdfengine.StreamEditor(out)
|
|
objs2 = ed2.extract_text_objects(0)
|
|
t2 = objs2[idx]["text"]; t2 = t2.decode("latin-1") if isinstance(t2, bytes) else t2
|
|
H.log(f" re-extract obj#{idx} = {t2!r} (expected {new!r}, match={t2 == new})")
|
|
os.remove(out)
|
|
os.remove(tmp.name)
|
|
except Exception as exc:
|
|
H.log(f" RAW TEXT smoke FAILED: {exc}")
|
|
|
|
OUT = Path(__file__).resolve().parent / "_verify_build.out.txt"
|
|
OUT.write_text(H.report.getvalue(), encoding="utf-8")
|
|
print(f"wrote {OUT}")
|