Select does what selecting does in every editor people already know.
Double-click bare page and a caret opens there; double-click the document's
own words and they open for replacement; what is selected copies with
Ctrl+C, pastes with Ctrl+V and goes with Del. Nothing was taken away — a
drag on empty page still gathers an area, and a drag that starts on words
still selects words to copy.
Type text and Edit text were two buttons for one question the click already
answers. They are one Text tool: land on the document's own words and it
offers to replace them, land anywhere else and it starts new text. The
words light up under the cursor so which is which is visible before
clicking, not after.
The toolbar says what it is for. Select, Text and Sign are labelled and set
apart; the drawing kit and the markup kit are their own groups. Sign gets a
pen icon over a signature rather than a squiggle that could have been
anything.
Signatures take ink — black, blue, red, green — chosen while drawing and
kept with the signature, because people sign in a particular colour and it
belongs to the signature, not to whichever swatch was armed. And they turn:
a grip above the box, free rotation, Shift to snap to 15°, for the signing
line that is not square to the page.
Two faults the tests found, both invisible by eye:
The rotate grip was drawn in the right place and could not be grabbed —
the selection bar floats directly above a mark, which is exactly where the
grip sits, and it swallowed every click. The bar now stands clear of it.
Undo would not undo a first rotation. Restoring a mark with Object.assign
copies the keys the original HAD, so a property the drag introduced
survived the restore; the journal then recorded the rotated state as the
state to go back to. Restoring now forgets keys the original never had,
which fixes every future property with the same shape.
Also: building a document from pictures or joins refuses to start a second
one on top of the first, and says so rather than failing quietly.
Three things the editor made you work around.
Text was typed into a dialog and then placed, so you chose a size and a
weight for words you could not see against the page they were going on.
The click now opens a caret where you clicked, in the font, size and
colour the words will have, with the style bar over it; double-clicking a
stamp reopens it in place. Lining a CSS line box up with a PDF baseline is
measured from the font's own metrics, not guessed.
A signature lived in a single slot. There was nowhere to keep initials as
well as a name, nowhere to change the one you had, and reaching for the
tool again simply stamped the first one — which is the same fault three
times: one slot. It is a library now, with redraw, rename and delete, and
the choice is made when the tool is picked up, so placing stays one click.
An existing single signature is carried into it rather than dropped.
The mark you had just drawn could be resized by its handles and not moved
by its middle, because only the select tool let marks be hit-tested at
all. The SELECTED mark now takes a press whatever tool is armed. A press
anywhere else still draws, and an unfilled shape is still grabbed by its
outline — the same rule select has always followed.
Also: words default to dark ink rather than highlighter yellow, which was
unreadable on white and is now impossible to miss, since you watch
yourself type it.
A PDF that needs a signature, a highlight or a page removed currently sends
the user out to a desktop application or, worse, to a web service that wants
the document uploaded first. Both are poor answers for a browser whose point
is that nothing has to leave the machine. This is a full-tab editor that opens
a PDF, marks it up, fills its forms and saves a new copy, entirely locally.
Two engines, vendored rather than installed, because an add-on ships as a
self-contained folder over the signed update channel and nothing runs a
package manager on the way: pdf.js reads and renders, pdf-lib writes. They
share no state. Everything in between lives in PDF user space — points,
origin bottom-left — which is the one coordinate vocabulary both speak, so a
mark survives zooming, rotating and reordering with no conversion table and
save-time needs to know nothing about how a page happened to be displayed.
The page strip is built from pdf.js's PDFPageView components rather than its
PDFViewer, which renders pages in the file's own order and cannot hide,
reorder or individually rotate one — three of the features here. Text layers
are ours and stay attached for every page, drawn or not, because Theseus's
find bar is Chromium's findInPage over the live DOM and a torn-down text layer
is a page Ctrl+F cannot see. Canvases are virtualised; a letter page at 100%
is 3.4 MB of bitmap.
Redaction is the part worth being careful about. A black box over text hides
nothing — the text stays in the content stream and comes straight out of a
copy-paste — so the editor says so in a modal before the tool can be used,
and on save rebuilds each redacted page as an image, which genuinely removes
it. Pages that were not redacted are untouched. Form widgets and links are
kept, since they were never the leak.
Saving never writes over the original: every save reloads the source bytes and
replays the session onto a fresh copy, so a botched save cannot poison the
next one.
Out of scope for this first version: editing the text that is already in the
document, and writing XFA forms back (pdf-lib cannot, so those are fill-and-
print only, and the editor says so on open).