censor/Censor
1
37
Fork
You've already forked Censor
3

feat: let new redactions not redact old redactions #139

Open
opened 2026年03月13日 12:16:06 +01:00 by mahlzahn · 0 comments

Description

Repeated redacting of same areas removes old visual redaction rectangles, regression of !115. Labelled as feature, because it is only a visual annoyance without safety impication and a workaround exists: not draw rectangles on top of old redactions, or to redraw multiple redaction rectangles.

How to reproduce?

  1. Redact a document with some rectangles
  2. Save it
  3. Continue redacting, drawing new redaction rectangles touching old redaction rectangles
  4. Save it
  5. Observe that the old redaction rectangles (which are just drawings now), are removed

Implementation

1. Detection of old redaction rectangles

Maybe detect old redaction rectangles and redraw them after applying the redaction. But, that is indeed a little difficult, because rectangles may express sensitive information, e.g.,
hello
is only drawn with black rectangles and still conveys a message.

Redaction rectangle information with PyMuPDF
 {'items': [('re', Rect(242.28219604492188, 110.953125, 322.3974304199219, 137.4697265625), 1)],
 'type': 'fs',
 'even_odd': False,
 'fill_opacity': 1.0,
 'fill': (0.0, 0.0, 0.0),
 'rect': Rect(242.28219604492188, 110.953125, 322.3974304199219, 137.4697265625),
 'seqno': 7,
 'layer': '',
 'level': 1,
 'stroke_opacity': 1.0,
 'color': (0.0, 0.0, 0.0),
 'width': 1.0,
 'lineCap': (0, 0, 0),
 'lineJoin': 0.0,
 'closePath': False,
 'dashes': '[] 0'}
Thus, this option would likely come with decreased security.

2. Keep record of redactions during a session

During a session (redact ..., save, redact ..., save, redact ..., save, ...), the issue could be solved in two ways. Both involve keeping track of all redactions.

  1. apply all redactions only always on the first version

    • this is may be fine, but increases the load and is more error-prune.
  2. apply all redactions always on the current version

    • this sounds like best option. It will redact the old redactions and draw others on top of them
  3. apply only the redactions of the current step, but redraw redaction rectangles of previous steps if they have been removed.

    Pseudo code
    old_drawings = {page.number: page.get_drawings() for page in document}
    old_redactions = {page.number: page.old_redactions for page in document}
    document.apply_redactions(...)
    def matches(redaction, drawing):
     return redaction.rect == drawing.rect # approximately, check also color, etc.
    def draw(drawing):
     pymupdf.shape ...
    for page in document:
     new_drawings = page.get_drawings()
     for redaction in old_redactions:
     for drawing in new_drawings:
     if matches(redaction, drawing):
     new_drawings.remove(drawing)
     break
     else:
     for drawing in old_drawings[page.number]:
     if matches(redaction, drawing):
     draw(drawing)
     old_drawings[page.number].remove(drawing)
     break
    

I tend to prefer the safer and less error-prune approach of 2. A difficulty may be in the level of allowed customization, as seen in #11, but with 2. it shouldn’t be an issue (to be tested ...)

3. Keep record of redactions in redacted documents

Somehow, safe the information of the redactions inside the document, not only via drawings. Then, these rectangles could be redrawn also like in 2. It would require to safe the annotation parameters (size, color, etc.) inside the PDF file.

## Description Repeated redacting of same areas removes old visual redaction rectangles, regression of !115. Labelled as feature, because it is only a visual annoyance without safety impication and a workaround exists: not draw rectangles on top of old redactions, or to redraw multiple redaction rectangles. ## How to reproduce? 1. Redact a document with some rectangles 2. Save it 3. Continue redacting, drawing new redaction rectangles touching old redaction rectangles 4. Save it 5. Observe that the old redaction rectangles (which are just drawings now), are removed ## Implementation ### 1. Detection of old redaction rectangles Maybe detect old redaction rectangles and redraw them after applying the redaction. But, that is indeed a little difficult, because rectangles may express sensitive information, e.g., ![hello](/attachments/0a25d676-0c63-40da-b0dc-b4a1605e2a1c) is only drawn with black rectangles and still conveys a message. <details><summary>Redaction rectangle information with PyMuPDF</summary> ```py {'items': [('re', Rect(242.28219604492188, 110.953125, 322.3974304199219, 137.4697265625), 1)], 'type': 'fs', 'even_odd': False, 'fill_opacity': 1.0, 'fill': (0.0, 0.0, 0.0), 'rect': Rect(242.28219604492188, 110.953125, 322.3974304199219, 137.4697265625), 'seqno': 7, 'layer': '', 'level': 1, 'stroke_opacity': 1.0, 'color': (0.0, 0.0, 0.0), 'width': 1.0, 'lineCap': (0, 0, 0), 'lineJoin': 0.0, 'closePath': False, 'dashes': '[] 0'} ``` </details> Thus, this option would likely come with decreased security. ### 2. Keep record of redactions during a session During a session (redact ..., save, redact ..., save, redact ..., save, ...), the issue could be solved in two ways. Both involve keeping track of all redactions. 1. apply all redactions only always on the first version - this is may be fine, but increases the load and is more error-prune. 2. apply all redactions always on the current version - this sounds like best option. It will redact the old redactions and draw others on top of them 3. apply only the redactions of the current step, but redraw redaction rectangles of previous steps if they have been removed. <details><summary>Pseudo code</summary> ```py old_drawings = {page.number: page.get_drawings() for page in document} old_redactions = {page.number: page.old_redactions for page in document} document.apply_redactions(...) def matches(redaction, drawing): return redaction.rect == drawing.rect # approximately, check also color, etc. def draw(drawing): pymupdf.shape ... for page in document: new_drawings = page.get_drawings() for redaction in old_redactions: for drawing in new_drawings: if matches(redaction, drawing): new_drawings.remove(drawing) break else: for drawing in old_drawings[page.number]: if matches(redaction, drawing): draw(drawing) old_drawings[page.number].remove(drawing) break ``` </details> I tend to prefer the safer and less error-prune approach of 2. A difficulty may be in the level of allowed customization, as seen in #11, but with 2. it shouldn’t be an issue (to be tested ...) ### 3. Keep record of redactions in redacted documents Somehow, safe the information of the redactions inside the document, not only via drawings. Then, these rectangles could be redrawn also like in 2. It would require to safe the annotation parameters (size, color, etc.) inside the PDF file.
4.1 KiB
Sign in to join this conversation.
No Branch/Tag specified
main
v0.7.3
v0.7.2
v0.7.1
v0.7.0
v0.6.0
v0.5.0
v0.4.0
v0.3.0
v0.2.0
v0.1.0
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
censor/Censor#139
Reference in a new issue
censor/Censor
No description provided.
Delete branch "%!s()"

Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?