Archive
TR

Text removed, alternative text retained

In this test, removing the text and rewriting the PDF with MuPDF did not remove its alternative text.

Bottom line

In twelve official MuPDF 1.28.5 runs, the text disappeared from ordinary extraction while alternative values remained in three input types. Changing one 1.28.2 DLL call's target field cleared the structural value but left the inline value unchanged.

Limits

Four controlled laboratory PDFs were used; no person's document was examined. The source line already exists in 2019. There is no claim of first discovery or a vendor-confirmed vulnerability.

Documents

The documents follow the file's reading order. Each step gives its note first, with the document it rests on beside it.

Document 1, Text is absent from extraction, data remains in the file

The twelve saved outputs were reopened. Ordinary extraction no longer finds the specimen, while the three alternative-text input types retain it in their objects.

  1. Absent from ordinary extraction
  2. Alternative value remains

What it does not show. These are four controlled laboratory PDFs, not a measurement of every producer's documents.

Preserved raw measurement recordsOfficial mutool 1.28.5, 12 records

Step by step

  1. Document 2, Visible and alternative text agree

    All four inputs draw AAAA. Where alternative text is present, it also says AAAA; the test does not depend on contradictory strings.

    Sourcemutool rendering of the test inputcontrols/structure.pdf, page 1

    Actual MuPDF render of CSINT's explicitly labelled test PDF. Not a screenshot; not resized.

  2. Document 3, Redaction removes the visible text

    A Redact annotation was applied, the file was fully rewritten, and this image was rendered from the saved output. The procedure did more than draw an overlay.

    What it does not show. A clean-looking image does not show that every object in the file is clean.

    Sourcemutool rendering of the saved outputresults/20261020T102048Z/structure-1.pdf, page 1

    Actual MuPDF render of CSINT's test output. Not a screenshot; not resized.

  3. Document 4, AAAA in the reopened PDF

    The values were read from saved files with pypdf, independently of MuPDF, rather than from an old in-memory document.

    1. Value in the structure tree
    2. Value in the content stream

    SourcePreserved raw measurement recordsstructure-1.pdf and inline-1.pdf, independent object reading

  4. Document 5, The two builds provide different evidence

    File measurements use official 1.28.5; native call records use the 1.28.2 DLL. They are not presented as one build.

    What it does not show. The official 1.28.5 executable was not traced at instruction level.

    SourcePreserved raw measurement recordsupstream-manifest.json and native structure-observe-1.json

  5. Document 6, The ActualText branch uses the Alt key

    The branch writing cleaned alternative text passes 0x1c, which selects Alt. ActualText is 0x16 in this DLL.

    1. 0x1c loaded into R8D
    2. The value-writing call

    What it does not show. This is static disassembly. The next record supplies runtime evidence.

    SourceGhidra 12.1.4, actual application windowMuPDF 1.28.2 DLL, RVAs 0x11ec8e to 0x11ecab

    CSINT's actual Ghidra window capture. Pointer drawing was disabled; only the code area was cropped. Text was not redrawn.

  6. Document 7, The runtime call receives the same key

    One call's key at the same return address was changed only in the experiment's child process. Two inputs, two arms and three repeats produced twelve records.

    1. 0x1c in observation
    2. 0x16 in the control

    What it does not show. This is not a fixed release; the DLL on disk was unchanged.

    SourcePreserved raw measurement recordsresults/20261020T102417Z-native, update_setter messages

  7. Document 8, The structural value clears, the pixels match

    The intervention changed the saved structural value. The rendered images remain identical despite that difference.

    1. AAAA and empty value
    2. Matching RGB hash

    What it does not show. The inline value was not cleared by this key change. It is not a complete patch.

    SourcePreserved raw measurement recordsstructure-observe-1.pdf, structure-key-control-1.pdf and RGB comparison

  8. Document 9, The same assignment was added in 2019

    Measuring the 2026 release does not date the behavior's introduction to 2026. The older source assignment is recorded separately.

    1. The 2019 change
    2. The added assignment

    What it does not show. Historical releases were not executed and first-discovery status is unproven.

    SourcePreserved raw measurement recordsprior-art/commit-2019.json, commit abcb3e6

Record details

Archive number
CSINT-NEWS-2026-013. Open in archive
Published
Event examined
Date of the documents
All documents are dated 20 October 2026.
Image handling and rights
Console view of CSINT's preserved measurement files, not the initial execution. Pointer drawing was disabled at capture; the frame was cropped without changing its pixels. Applies to 6 of 9 documents; the ones that differ carry their own note.
Highlights
The yellow highlights were added later. The original image is unchanged.
Full account and source transcriptMethod, timeline and full text. The findings are in the documents above.

The claim reviewed

Can the alternative text of removed content survive in a saved PDF even when the image and ordinary text extraction look clean?

Limits of the test

  • The file measurements with official 1.28.5 are separate from the native traces in the 1.28.2 DLL.
  • Changing one call's key is not a complete fix. It does not clear inline alternative text.
  • Four small laboratory PDFs, one computer and a full redaction rectangle were measured. No conclusion covers every PDF producer or document type.
  • The bounded prior-report search does not prove first discovery. No report was sent to the vendor and there is no vendor confirmation.

Text on the page and data in the file

A PDF's ActualText field can supply alternative text for content. In this experiment, the visible and alternative strings both say AAAA. The result does not depend on deliberately contradictory strings.

The first input has no alternative text. The second stores it inline, the third in the structure tree and the fourth in both places. Each document states that it is a synthetic fixture, not a historical document.

Actual redaction and the reopened file

Official mutool 1.28.5 processed each input in three separate runs. A Redact annotation covered the entire specimen and applyRedactions(true, 2, 1, 0) applied it. The output was saved as a new, non-incremental file with garbage=4 and compression.

The saved files were reopened. MuPDF and the separate pypdf library no longer found AAAA through ordinary text extraction. Reading the content operators and structure dictionaries with pypdf revealed the alternative values. This checked the persisted objects, not just the appearance of a black box.

All four input renders have identical pixels within the renderer. All four redacted renders also match. That equality does not prove that no data remains elsewhere in the file.

Which field receives the cleaned value?

Native inspection used the MuPDF 1.28.2 DLL supplied with PyMuPDF. Source, callback layout and Ghidra disassembly were read together. The branch for edited ActualText passes the Alt key to the setter.

In this DLL, Alt is 0x1c and ActualText is 0x16. RVA 0x11ec9e loads 0x1c into R8D; the call at 0x11ecab returns to 0x11ecb0. Frida records show that call receiving an empty cleaned string with the Alt key.

The control arm changed only the key of that call to 0x16. The structural ActualText became empty while the rendered image stayed the same. The DLL on disk was unchanged; the intervention affected only a call argument in our own child process.

Inline properties take a different path

The structural value and the value written directly into the content stream do not follow the same cleanup path. The inline case did not enter the tracked structural setter, and the key intervention did not change its result.

The one-key intervention is therefore not presented as a complete patch. Named properties, nested forms, partial redactions and other object types are outside this comparison.

The source line goes back to 2019

The change committed on 19 June 2019 states an intention to redact corresponding structure-tree text. The added ActualText branch already uses the Alt key. A 27 November 2020 change adds protection for null or deleted tags without changing that key.

The same assignment was present in source revision b531369 dated 19 October 2026. That revision was inspected, not executed. Historical released binaries were not tested, so affected-release boundaries cannot be inferred from these dates.

The bounded public search did not locate an earlier report of the same native writeback observation. That does not establish first discovery. Bugzilla 703179 was inaccessible; private or differently worded reports cannot be ruled out.

How the records were prepared

Measurement and verification scripts were run with Codex on this computer. The console images show preserved raw records being read afterwards, not the initial execution. The Ghidra image comes from the actual application window; the PDF images are actual mutool renders.

The pointer was excluded during capture. Frames were cropped on integer pixel boundaries and encoded losslessly; text was not redrawn and pixels were not retouched. Yellow highlights are a separate layer. Original captures, private raw records and file hashes were retained.

The linked data file is a public projection of the raw measurements. Absolute local paths, process IDs and module load addresses were omitted; measured values, relative call addresses and original-record hashes remain.

Sources