# Apply\_redactions() — link /URI values survive redaction, plus large output-size inflation

**URL:** <https://forum.mupdf.com/t/apply-redactions-link-uri-values-survive-redaction-plus-large-output-size-inflation/377>\
**Category:** PyMuPDF\
**Created:** [September 17, 2026, 5:54pm UTC](https://forum.mupdf.com/t/apply-redactions-link-uri-values-survive-redaction-plus-large-output-size-inflation/377 "2026-09-17T17:54:10Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![Jaymish\_Patel](https://yyz2.discourse-cdn.com/flex004/user_avatar/forum.mupdf.com/jaymish_patel/32/430_2.png) [@Jaymish\_Patel](https://forum.mupdf.com/u/Jaymish_Patel)\
**Post date:** [September 17, 2026, 5:54pm UTC](https://forum.mupdf.com/t/apply-redactions-link-uri-values-survive-redaction-plus-large-output-size-inflation/377/1 "2026-09-17T17:54:10Z")

</div>

Hi, we’re testing PyMuPDF (1.28.0) for a document redaction tool (still evaluating) and found two separate issues in apply\_redactions(). Posting a short summary, happy to describe more detail if useful.

1. Link annotation /URI values aren’t scrubbed. If a sensitive value appears in page text and in a link’s /URI (common — Word/Acrobat/LaTeX auto-linkify emails and URLs), apply\_redactions() removes it from the visible text but leaves the URI intact and recoverable via page.get\_links(). Redaction reports success; the value is still in the file. Measured across 39 real documents: 26 leaked this way, 444 recoverable values total, 0 leaks on documents with no links.
2. Default image mode inflates output size heavily. apply\_redactions() defaults to images=PDF\_REDACT\_IMAGE\_PIXELS, which re-encodes every image on any touched page — roughly +24MB per page touched in our testing, one document going from 24MB to 4.8GB. Switching to images=PDF\_REDACT\_IMAGE\_REMOVE avoids it with no loss of redaction correctness, but it’s a rough default to hit unknowingly.

---

<div class="post-metadata">

**Author:** ![Jamie\_Lemon](https://yyz2.discourse-cdn.com/flex004/user_avatar/forum.mupdf.com/jamie_lemon/32/5_2.png) [@Jamie\_Lemon](https://forum.mupdf.com/u/Jamie_Lemon)\
**Post date:** [September 17, 2026, 9:07pm UTC](https://forum.mupdf.com/t/apply-redactions-link-uri-values-survive-redaction-plus-large-output-size-inflation/377/2 "2026-09-17T21:07:21Z")

</div>

Looking into this …

---

<div class="post-metadata">

**Author:** ![HaraldLieder](https://yyz2.discourse-cdn.com/flex004/user_avatar/forum.mupdf.com/haraldlieder/32/14_2.png) [@HaraldLieder](https://forum.mupdf.com/u/HaraldLieder)\
**Post date:** [September 18, 2026, 8:55am UTC](https://forum.mupdf.com/t/apply-redactions-link-uri-values-survive-redaction-plus-large-output-size-inflation/377/3 "2026-09-18T08:55:49Z")

</div>

The second point first:  
If you accept the default image processing, then “holes” are punched into image regions that intersect a redaction. The underlying technique converts the respective image into a PNG (!) - which may quite likely be larger than the original format … just think of CCITTFaxDecode or JPX or even normal JPEG.

This is unavoidable!

Choose a different image handling: either not touching images at all or removing images entirely when they intersect a redaction.

For the first problem we need an example PDF exhibiting the problem. Just to be sure, please repeat the text with the most recent PyMuPDF version.

---

<div class="post-metadata">

**Author:** ![HaraldLieder](https://yyz2.discourse-cdn.com/flex004/user_avatar/forum.mupdf.com/haraldlieder/32/14_2.png) [@HaraldLieder](https://forum.mupdf.com/u/HaraldLieder)\
**Post date:** [September 18, 2026, 9:08am UTC](https://forum.mupdf.com/t/apply-redactions-link-uri-values-survive-redaction-plus-large-output-size-inflation/377/4 "2026-09-18T09:08:32Z")

</div>

Out of curiosity: Which save options did you use for the redacted PDFs?

---

<div class="post-metadata">

**Author:** ![Jaymish\_Patel](https://yyz2.discourse-cdn.com/flex004/user_avatar/forum.mupdf.com/jaymish_patel/32/430_2.png) [@Jaymish\_Patel](https://forum.mupdf.com/u/Jaymish_Patel)\
**Post date:** [September 18, 2026, 1:22pm UTC](https://forum.mupdf.com/t/apply-redactions-link-uri-values-survive-redaction-plus-large-output-size-inflation/377/5 "2026-09-18T13:22:26Z")

</div>

Thanks for the quick response on the image handling, that makes sense as a mechanism, and switching to remove-on-intersect is exactly what we’re doing on our side.

Save options: plain doc.tobytes(), no garbage= or clean= options passed for either the marked or redacted output.

Example PDF: rather than sharing one of our real corpus documents, here’s a minimal synthetic snippet that builds the problem PDF in-memory should reproduce standalone:

```auto
import pymupdf

TARGET = "j.doe@example.com"

doc = pymupdf.open()
page = doc.new_page(width=400, height=300)
page.insert_text((50, 100), f"Contact: {TARGET}", fontsize=12)
page.insert_text((50, 200), "Unrelated line of text", fontsize=12)
page.insert_link({
    "kind": pymupdf.LINK_URI,
    "from": pymupdf.Rect(50, 190, 250, 210), # over the unrelated line, not the text
    "uri": f"mailto:{TARGET}",
})
buf = doc.tobytes()
doc.close()

doc = pymupdf.open("pdf", buf)
page = doc[0]
for r in page.search_for(TARGET):
    page.add_redact_annot(r, fill=(0, 0, 0))
page.apply_redactions()
out = doc.tobytes()
doc.close()

d = pymupdf.open("pdf", out)
p = d[0]
print("visible text has target:", TARGET in p.get_text())
print("links remaining:", len(p.get_links()))
print("recoverable from URI:", any(TARGET in (L.get("uri") or "") for L in p.get_links()))

```

On 1.28.0 this prints visible text has target: False, links remaining: 1, recoverable from URI: True, the redaction removes the text but the link survives with the value still in its URI.

We’ll also confirm against the latest release and report back.

---

<div class="post-metadata">

**Author:** ![HaraldLieder](https://yyz2.discourse-cdn.com/flex004/user_avatar/forum.mupdf.com/haraldlieder/32/14_2.png) [@HaraldLieder](https://forum.mupdf.com/u/HaraldLieder)\
**Post date:** [September 18, 2026, 9:07pm UTC](https://forum.mupdf.com/t/apply-redactions-link-uri-values-survive-redaction-plus-large-output-size-inflation/377/6 "2026-09-18T21:07:50Z")

</div>

Well, **_not specifying garbage nor deflate_** contradicts your announced intention of data protection:  
Without garbage collection, old content _ **will** _ be present in the physical PDF file as “ghost” objects, thus invalidating your voiced intentions to protect data. Every PDF expert will be able to extract this ghost information. **_ALWAYS SPECIFY GARBAGE \> 0_** after redacting! In addition, your code will not only be inefficient in terms of data protection, but also in terms of bloating the file size.  
Not requesting compression (`deflate=True` at a minimum) will in addition negate any chance to achieve acceptable file sizes.

Therefore, always use something like `doc.ez_save(...)` if you used redactions.  
Only _ **then** _ we have a worthwhile basis for discussing remaining file size issues.

---

<div class="post-metadata">

**Author:** ![HaraldLieder](https://yyz2.discourse-cdn.com/flex004/user_avatar/forum.mupdf.com/haraldlieder/32/14_2.png) [@HaraldLieder](https://forum.mupdf.com/u/HaraldLieder)\
**Post date:** [September 19, 2026, 9:27am UTC](https://forum.mupdf.com/t/apply-redactions-link-uri-values-survive-redaction-plus-large-output-size-inflation/377/7 "2026-09-19T09:27:00Z")

</div>

~~@Jaymish_Patel - I am still waiting for a reproducing PDF where hyperlinks fail to be removed under redactions.~~

Using your suggested script …!

---

<div class="post-metadata">

**Author:** ![HaraldLieder](https://yyz2.discourse-cdn.com/flex004/user_avatar/forum.mupdf.com/haraldlieder/32/14_2.png) [@HaraldLieder](https://forum.mupdf.com/u/HaraldLieder)\
**Post date:** [September 19, 2026, 10:03am UTC](https://forum.mupdf.com/t/apply-redactions-link-uri-values-survive-redaction-plus-large-output-size-inflation/377/8 "2026-09-19T10:03:19Z")

</div>

@Jaymish_Patel  
The riddle of the non-redacted hyperlink has a simple solution:  
You specified the wrong redaction rectangle! I modified your script somewhat to make the link “from” rectangle visible:

```python
import pymupdf

TARGET = "j.doe@example.com"
link_rect = pymupdf.Rect(50, 190, 250, 210) # this will never be redacted!!!

doc = pymupdf.open()
page = doc.new_page(width=400, height=300)
page.insert_text((50, 100), f"Contact: {TARGET}", fontsize=12)
page.insert_text((50, 200), "Unrelated line of text", fontsize=12)
page.insert_link(
    {
        "kind": pymupdf.LINK_URI,
        "from": link_rect, # over the unrelated line, not the text
        "uri": f"mailto:{TARGET}",
    }
)
page.draw_rect(link_rect, color=(1, 0, 0))
doc.save("test0.pdf")

doc = pymupdf.open("test0.pdf")
page = doc[0]
for r in page.search_for(TARGET): # this is NOT where the link lives!
    page.add_redact_annot(r, fill=(0, 0, 0))
page.apply_redactions()
...

```

Here is how your PDF looks like:

 ![image](https://canada1.discourse-cdn.com/flex004/uploads/mupdf/original/1X/c431dc9fe9484835e80e8a6c237ec344ebb9ba2c.png)

The hyperlink rectangle is _ **not** _ redacted and thus survives.  
You must redact a hyperlink’s “from” rectangle to remove a link.
