Skip to content

The rest of the verbs: marks, protection, and reading a page back - #10

Merged
tannevaled merged 1 commit into
mainfrom
verbs-rest
Aug 28, 2026
Merged

The rest of the verbs: marks, protection, and reading a page back#10
tannevaled merged 1 commit into
mainfrom
verbs-rest

Conversation

@tannevaled

@tannevaled tannevaled commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

The second half of the work #9 began: #9 turned the strip into a panel and put
the page-shaping verbs in it, and this puts the rest of pdfops on the screen.
Rebased onto main now that #9 has landed, so this is one commit.

Two more groups on the strip and two of the four filled out. What is left of
pdfops after the first half is here, less three named below.

group verbs
Marks watermark · page numbers in a shape you choose · a Bates number padded and started where you say · a stamp in one of nine places — over a range of its own, defaulting to every page
File sanitize · flatten the annotations in · drop the annotations / the bookmarks / what the file says about itself · pack it smaller · give it a title and an author
Protect open a file that has a password · user and owner passwords and what a reader may do · take the protection off · what was this protected with?
Read what the page says · what the page carries

Protecting a file without breaking the one promise

The workbench draws a document by writing it out and reading it back. A
document about to be written behind a password therefore could not be drawn at
all: the page would have been replaced by this document cannot be read back.

So it reads its own output back with the password just given. A protected
document is still drawn, and what is drawn is still what would come out of
Save. It is not the same bytes twice — encryption needs randomness, by design —
but it is the same document, protected the same way.

The other half is the password to open a file that already has one. That box
is in the same panel, and a panel opens with no document: type the password,
then press Open. Without it, decrypt was a verb that could only ever be
offered for files it was not needed on.

Reading without pretending

Nothing in Read changes the document, and what it reads it reads out of the
document written and read back — so what it lists is what would come out of
Save. Same rule, kept by the one group that does not change anything.

The list of pictures is headed "What the page itself places", and that
wording is measured rather than hedged:

  • Against poppler's pdfimages over the 157 of 400 real form pages that
    carry any picture, the two agree on 99.
  • Every one of the 58 differences is us listing fewer, never more.
  • Both kinds were run down: pdfimages lists a stencil or soft mask as a row
    of its own beside the picture it belongs to, and it lists pictures drawn by
    an annotation's appearance stream. Removing the annotations from
    cerfa_10011.pdf and asking pdfimages again gives exactly the two pictures
    we list.

Text, against pdftotext on the same 400 first pages:

we found words on          395 pages
pdftotext found words on   395 pages
both, and within 10%       383 of 395  (97.0%)

The five worst are 1999 US Department of Labor forms where we find 10 words
and pdftotext finds 230–530. That is a gap in go-pdfkit/extract, not in
this workbench, and it is worth a look there.

Deliberately left out

  • fields and fill — the Fill in panel already is this, and better.
  • permissions — folded into Protect, which shows what a file allows
    rather than printing it.

Everything else pdfops --help lists is now on the screen.

Measured

Twenty verbs over the same 400 real forms from the eleven issuing
bodies
(400 of the 1 633 real documents; the 635 vendor fixtures excluded).
8 000 outputs, each written by ops, read back by our own reader, parsed by
pdfinfo and drawn by pdftoppm with ink counted on the page. The encrypted
ones were handed to poppler with -upw, the way the workbench hands them to
itself.

7 911 of 7 911 applicable   written, read back, parsed and drawn with ink
   89 not applicable        one-page documents where deleting page one would
                            leave none, which the panel refuses for that reason
poppler drew 7 911 pages, 163 776 327 pixels of ink, 20 702 on average

The browser check now demands a different file

It used to stop at the first press that changed the canvas. That is not a verb:
it is a redraw. It now presses Save after every such press and only stops when
what comes back is a different file.

The change caught its own weakness on the first run — the sweep found the
Read group, whose first control replaces the picture of the page with what
the page says, and called it a verb. With the file test in place:

a panel opened from x=652   (Read)     nothing it does changes the document
a panel opened from x=596   (Protect)  nor does anything there
a panel opened from x=524   (File)     nor there
a panel opened from x=476   (Marks)
a verb pressed in the panel changed the document: pressed at y=183
the tab handed back a 1855 byte PDF after the panel was used
the mark written from the panel is in the saved file

Also

A picture handed over from the Read panel is no longer labelled a PDF on
its way out of the tab. A browser told a JPEG is a PDF opens it in a viewer
that cannot read it.

Checks

100% of statements — and one of the tests written for it found a real crash:
compress measured the file before packing it, and measuring a document that
is not there dereferenced nil. go vet and gofmt clean on nine targets,
-race clean, and every panel was rendered and looked at, which is how the
Marks group was found running off the bottom of the window before its two pairs
of numbers were put side by side.

@tannevaled
tannevaled changed the base branch from verbs-panel to main August 27, 2026 19:50
Two more groups on the strip and two of the four filled out. What was left of
pdfops after the first half is here, less the three that have no place in a
workbench and are named below.

  Marks     a watermark, page numbers in a shape somebody chose, a Bates
            number padded and started where they say, a stamp in one of nine
            places — over a range of its own, defaulting to every page,
            because a page number is nearly always wanted on all of them
  File      sanitize, flatten the annotations in, drop the annotations, the
            bookmarks or what the file says about itself, pack it smaller,
            give it a title and an author
  Protect   open a file that has a password, put a user and an owner password
            on this one and say what a reader may do with it, take the
            protection off, and ask what the file it came from was protected
            with
  Read      what the page says, and what it carries

PROTECTING A FILE WITHOUT BREAKING THE ONE PROMISE

The workbench draws a document by writing it out and reading it back, so a
document about to be written behind a password could not be drawn at all: the
page would have been replaced by "this document cannot be read back". It reads
its own output back with the password just given instead, so a protected
document is still drawn. It is not the same bytes twice — encryption needs
randomness, by design — but it is the same document, protected the same way.

The other half is the password to open a file that already has one. That box
is in the same panel, and the panel opens with no document: type the password,
then press Open. Without it, decrypt was a verb that could only ever be
offered for files it was not needed on.

READING WITHOUT PRETENDING

Nothing in Read changes the document, and what it reads it reads out of the
document written and read back — so what it lists is what would come out of
Save, which is the same rule the rest of the workbench keeps.

The list of pictures says "what the page itself places", and that wording is
measured rather than hedged. Against poppler's pdfimages over the 157 of 400
real form pages that carry any picture at all, the two agree on 99. Every one
of the 58 differences is us listing FEWER, never more, and both kinds were run
down: pdfimages lists a stencil or soft mask as a row of its own beside the
picture it belongs to, and it lists pictures drawn by an annotation's
appearance stream. Removing the annotations from cerfa_10011.pdf and asking
pdfimages again gives exactly the two pictures we list.

Text, against pdftotext on the same 400 first pages: both find words on 395,
and the counts agree within 10% on 383 of those (97.0%). The five worst are
1999 US Department of Labor forms where we find ten words and pdftotext finds
between 230 and 530 — a gap in the extractor rather than in this workbench,
and worth a look there.

LEFT OUT, ON PURPOSE

  fields    the Fill in panel already is this, and better
  fill      the same
  permissions  folded into Protect, which shows what a file allows rather than
               printing it

MEASURED

Twenty verbs over the same 400 real forms from the eleven issuing bodies —
8 000 outputs, each written by ops, read back by our own reader, parsed by
pdfinfo and drawn by pdftoppm with ink counted on the page. The encrypted ones
were given to poppler with -upw, the way the workbench gives them to itself.

  7 911 of 7 911 applicable outputs passed all four
     89 not applicable: one-page documents where deleting page one would leave
        a document with none, which the panel refuses for that reason

The browser check now demands that the file the tab hands back be a DIFFERENT
file before it will call a press a verb. That change caught its own weakness on
the first run: the sweep found the Read group, whose first control replaces the
picture of the page with what the page says, and called it a verb because the
canvas changed. It now sweeps past Read, Protect and File — none of which
changes what is drawn — and stops on the watermark in Marks, whose mark is then
found in the file that came out.

Also: a picture handed over from the Read panel is no longer labelled a PDF on
its way out of the tab. A browser told a JPEG is a PDF opens it in a viewer
that cannot read it.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit 83d6444 into main Aug 28, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant