Skip to content
This repository was archived by the owner on Sep 3, 2026. It is now read-only.

Releases: KenM76/pdfce

pdfce v0.27.0 - redaction removes everything under a mark: images destroyed, vector paths cut

Choose a tag to compare

@KenM76 KenM76 released this 03 Sep 12:15

Workspace version 0.17.0 -> 0.27.0 on GitHub (ten workspace versions;
v0.18.0 through v0.26.0 were deployed to OneDrive without a GitHub
release). The asset is the single-folder portable build: unzip anywhere,
run pdfce-gui.exe or pdfce-cli.exe, no installer, nothing written
outside the folder.

WHAT AN OPERATOR GETS IN v0.26.0 AND v0.27.0

REDACTION NOW REMOVES EVERYTHING UNDER A MARK. Before v0.26.0 a redaction
mark that so much as touched an image's rectangle refused the whole apply
-- "it tells me it can't because there are objects that weren't
redacted" -- and a title-block value inches from a company logo could not
be redacted at all. Now:

  • An image under a mark has the covered pixels DESTROYED, not hidden:
    decoded (raw, JPEG, CCITT, JBIG2, JPEG 2000), the cells overwritten with
    paper, re-encoded losslessly. Its soft mask goes transparent over the
    same cells, because a mask's shape can reveal a signature on its own.
  • An image the mark covers entirely is removed from the page outright.
  • An image also drawn elsewhere (another page, a form, an unmarked spot on
    the same page) gets a copy for the marked placement; the original stays
    for the others, and the report says so.
  • Vector lines, curves and fills crossing a mark are CUT at the boundary
    (v0.27.0): strokes against the mark widened by their stroke width,
    fills clipped to the mark's complement, anything wholly inside deleted.
    Acrobat clips or rasterises here; pdfce excises the geometry.
  • A mark over an image pdfce cannot decode is RETAINED in the output as an
    unapplied mark -- nothing removed under it, no box drawn over it -- and
    every other mark still applies. Refusal is per mark, not per document;
    only when no mark at all can be applied does the run refuse.
  • A shading (sh) whose clip meets a mark is counted and disclosed (not
    cut yet).

Whole-page marks on the two image-bearing drawings that reproduced the
refusal now exit clean with a blank page (780 and 1,089 path objects
dropped); a corner mark cuts 25 objects and 195,960 image cells in 0.3 s.

A PIXEL-LEVEL PROOF NOW GUARDS REDACTION. The test suite rasterises an
applied redaction and asserts nothing is inked inside the mark. It caught a
defect that twenty-two byte-level tests agreed with: destroyed image cells
were black inside a transparent mark. They are paper now.

The redact-apply report gained ten counters (images_cleared,
images_removed, images_cloned_shared, images_overcovered,
marks_retained, vector_paths_cut, vector_paths_dropped,
vector_clips_kept, vector_paths_intersecting, shadings_intersecting)
and three carriers (images, vector_paths, shadings). A non-zero
marks_retained means the output still carries marks.

SINCE THE LAST GITHUB RELEASE (v0.18.0 - v0.25.0, previously OneDrive only)

  • PRINT COLOUR IS COLOUR-MANAGED ON EVERY ROUTE: ICC-based RGB and CMYK,
    Lab, CalRGB and CalGray fills, text, images, indexed images, shadings and
    meshes all convert through the embedded profile or the page's output
    intent. Spot inks have their own plane through images, stencil masks,
    shadings, transparency and knockout groups. The print-conformance sweep
    that stood at 5 failures is at 0.
  • The overprint zero-tint default follows the literal ISO 32000-1 reading
    now that spot inks have a plane (OverprintZeroTintScope, a setting).
  • Vectors can be recoloured; a spot-inked path is refused by name rather
    than destroyed. Geometry inside a form XObject is editable.
  • A page's tab order can be set. A link says where it goes, not only what
    kind of action it carries. A button's action can be read back.
  • Form widgets: a resized widget is redrawn at the new size; an auto-sized
    field fills at the size its box implies; submit/reset/show-hide buttons
    with the safeguards the plan named; renaming a field reports the buttons
    it breaks.
  • Every content-editing verb reads the live session, not the file on disk.

COMPATIBILITY

  • RedactError::ImageRegion no longer exists (v0.26.0); the new variant is
    ImageUndestroyable { page, reason }, raised only when nothing could be
    applied. Code matching other => is unaffected.
  • redact-apply exits non-zero on a disclosed residual as before; on a
    well-formed drawing with no shadings under the mark there is no residual
    now, so the exit code drops from 10 to 0 where it used to trip.
  • Destroyed image cells render as paper (white for grey/RGB, no ink for
    CMYK), not black. A black box appears only when the mark carries an /IC.

VERIFIED

cargo test --workspace 5,090 / 0; cargo fmt --check and
cargo clippy --workspace --all-targets --all-features -D warnings clean;
cargo check for wasm32-unknown-unknown; cd fuzz && cargo check --bins;
cargo tree -p pdfce-core -p pdfce-render free of any GUI or network
dependency; the full script gate sweep including both filing gates; fuzz
target 15 (redact_apply) 14,796 runs / 0 crashes; fresh-folder smoke test
of both binaries from the packaged folder; CI green at the release tip.

pdfce v0.17.0 - copy and paste almost anything, and a bug that could silently erase every dimension in a drawing

Choose a tag to compare

@KenM76 KenM76 released this 30 Aug 14:00

Workspace version 0.16.0 -> 0.17.0. A minor bump: substantial new public
API across the clipboard, ce dimensions and form widgets, plus one new
persisted setting and four new EditError variants. 59 commits, 27
Passes (166.0 through 179.2), 92 files, +31,271/-793.

WHAT AN OPERATOR GETS

COPY AND PASTE NOW COVERS ALMOST EVERYTHING. Before this release the
clipboard carried page content and little else. It now carries form
fields, sticky notes, stamps, text boxes, links, file attachments and
nearly every other annotation type, whole pages, and whole bookmark
subtrees -- within one document or between two. Cut exists alongside
copy for annotations, mixed selections and form fields, and refuses
before deleting anything the clipboard could not carry back.

Pasting bookmarks BETWEEN two documents is something Acrobat cannot do at
all.

DIMENSION TEXT YOU CAN OVERRIDE AND TAKE BACK. A ce dimension can print
whatever you type instead of its measurement -- and the measurement stays
underneath. Clearing the override restores the real value exactly, with
no re-measurement, even after saving and reopening days later. Put
<DIM> in the text and it is replaced by the live measurement, so
2X <DIM> TYP keeps tracking the geometry when the drawing is rescaled.

FORM FIELDS CAN BE ROTATED. /MK /R plus a redrawn appearance --
counterclockwise, quarter turns, and the box stays where you put it while
the content turns inside it. Writing the rotation flag alone would have
been a no-op in any modern PDF reader, so pdfce redraws the appearance
for the field types it authored and says plainly when it cannot.

BOLD FALLBACK IS NOW YOUR CHOICE, AND THE DEFAULT ASKS NOTHING.
style_policy = auto | warn | refuse. auto decides and tells you
afterwards; warn says so loudly when a weight was faked; refuse is
what pdfce always did -- stop and name the real face. Naming a font
yourself, or forcing the fake one, is unaffected.

THREE DIMENSION-GROUP COMMANDS BECAME SCRIPTABLE. Rename, delete (with an
explicit policy for the members) and move-between-groups had working
engine code that no command line could reach.

FIXES THAT CHANGE WHAT A FILE SAYS

★ DELETING THE DEFAULT DIMENSION GROUP COULD SILENTLY ERASE EVERY
DIMENSION IN A DRAWING. The saved file looked perfect and the dimensions
still drew on screen; the next open showed none, and the next save made
it permanent. Refused by name now, before anything is written.

★ CALIBRATING WITH 55 5/8" PRODUCED DIMENSIONS LABELLED 55.62 mm --
the inch magnitude wearing a millimetre label, 25.4x out, on a drawing.
All five accepted notations were affected. The command's own help text
had promised the opposite behaviour in writing.

★ THREE MORE COMMANDS REPORTED SUCCESS AND DID NOTHING -- or worse,
authored a dimension into a group nobody chose, at a scale nobody chose.
Found by driving every identifier-taking verb against the shipped binary
rather than reading the code; everything outside the ce-dimension group
family was already correct.

A PASTED ce DIMENSION SHOWED A DIFFERENT NUMBER from the one it was
copied from, because the group's scale did not travel with it.

A DOTTED FIELD NAME SILENTLY DESTROYED the terminal field it nested
under, value and all.

pdfce WAS TELLING YOU IT COULD NOT DO SOMETHING IT CAN. The message shown
when it faked a bold weight asserted that no real bold face existed and
that pdfce "cannot make this change with a genuine typeface" -- while the
same report named the real face it had just skipped. That class of error
is the rarer one: it UNDERSTATES the software, and sends an operator to a
harder remedy for no reason.

COLOUR

The subtractive compositing work continued: an ink probe that reports
what is in the colorant buffer at the moment it stops existing, a census
of which DeviceCMYK operands real files actually paint, and a measured
correction to a shipped default's justification -- it had claimed
agreement with a reference engine on grey neutrality that measurement
does not support. The conversion itself is unchanged and remains the
sibling iccce project's domain.

COMPATIBILITY

  • style_policy defaults to auto, so --bold-synthetic on a page that
    has a real bold face now SUCCEEDS where it previously refused. Set
    style_policy = refuse to keep the old behaviour.
  • Four ce-dimension verbs now refuse an unknown group id where they
    previously returned success. A cached group id will produce an error
    that did not exist before; that error is correct and the previous
    silence was not.
  • A document carrying a ce-dimension text override is written at sidecar
    schema version 4 and an older pdfce will refuse to edit its dimensions.
    A document without one is still written at version 3 and is unaffected.

VERIFIED

cargo test --workspace --all-features green; cargo fmt --check and
cargo clippy --workspace --all-targets --all-features -D warnings
clean; cargo check for wasm32-unknown-unknown; cd fuzz && cargo check --bins; cargo tree -p pdfce-core -p pdfce-render free of any
GUI or network dependency; the full 29-command gate sweep via
tools/run-gates.sh, including both filing gates.

pdfce v0.16.0 - a colour your file asked for, and a diagnostic that was lying about it

Choose a tag to compare

@KenM76 KenM76 released this 29 Aug 12:35

v0.16.0: a colour your file asked for, and a diagnostic that was lying about it

Workspace version 0.15.0 -> 0.16.0. A minor bump: rendered colour changes on
print-destined files, and a new SourceKind variant in pdfce-render.

WHAT AN OPERATOR GETS

If you open a print-ready PDF -- one that declares an output intent, which is
most files that came from a prepress workflow -- pdfce was rendering some of
its colours wrong.

Not subtly. On a measured test page, a green the file specified exactly was
coming out with 40% of its yellow ink missing and a black component invented
from nothing that the file never asked for. It rendered as a duller, bluer
green than any viewer would show you.

The cause: pdfce threw away the exact ink values the file stated and re-derived
them from its own on-screen approximation of that colour. A one-way trip. The
file said "75% cyan, no magenta, 100% yellow, no black" and pdfce painted
something else.

That is fixed. The colour pdfce paints is now the colour the file specified,
and it is closer to Acrobat on every channel.

★ AND THE DIAGNOSTIC THAT SHOULD HAVE CAUGHT IT

pdfce carries a counter whose entire job is to say "this page was composited
from approximations rather than from the ink the file stated". While the above
was happening across 40,000 pixels, that counter read ZERO -- byte-identical to
a page rendered perfectly.

So pdfce approximated and reported that it had not. That is the project's
"never sneaky" rule broken rather than an accuracy gap, and it is why the bug
took six hypotheses and a wrong test to find instead of one render. The counter
now reports honestly, and is verified in both directions -- because a counter
stuck at zero looks exactly like a correct one.

WHAT WAS DELIBERATELY NOT DONE

The one-line version of this fix would have relabelled the colour space as
plain CMYK. That makes the colour correct AND silently changes how overprinting
inks composite on every such file, because that label is the single place the
standard attaches a special overprint rule (§8.6.7, Table 149 Row 1). It trades
a colour bug for an overprint bug, and nothing in the conformance suite would
have caught it.

A separate classification was added instead, which answers the two questions
independently: yes the ink values are readable, no this is not a DeviceCMYK
source. A test exists solely to fail if a future change takes the shortcut.

The change is narrow on purpose: four-component ICC spaces over DeviceCMYK,
outside image samples, only. Every other ICC-tagged space renders exactly as it
did in v0.15.0.

VERIFICATION

conformance suite ..... 51 patches, 6 FAIL / 29 pass / 16 unresolved,
0 render errors -- IDENTICAL to v0.15.0's baseline.
No verdict moved and none should have: the affected
panel already passed its own criterion, which cannot
see colour accuracy.
bash tools/run-gates.sh ......... 28 commands
cargo fmt --all --check / cargo clippy -D warnings clean
portable package built and smoke-tested from a fresh folder

No dependency changes since v0.14.0, so THIRD_PARTY_LICENSES.md is unchanged.

Co-Authored-By: Claude Opus 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01AcgfF2qoRL3i9NtC9JXRit

pdfce v0.15.0 - four things that could not be edited, and now can

Choose a tag to compare

@KenM76 KenM76 released this 29 Aug 11:01

v0.15.0: four things that could not be edited, and now can

Workspace version 0.14.0 -> 0.15.0. A minor bump: substantial new public
surface on pdfce-core (bookmark moves, expand/collapse, on-demand font
resources, annotation rotation) and new pdfce-cli subcommands.

43 Pass commits since v0.14.0.

WHAT AN OPERATOR GETS

The headline is a standing instruction closing. The operator named four things
about a PDF that pdfce could not fully edit and asked for all four. All four
now work:

ROTATION anything carrying a /Rect turns about a point you choose
(Pass 155.0), and ce dimensions turn too (Pass 159.0) --
a rotation is an isometry, so the measured value is unchanged
by construction.

ce DIMENSIONS rotate; scaling was DECLINED by name rather than half-built,
because it has no honest reading -- set_group_scale is the
operation actually wanted.

BOOKMARKS rename and delete (Pass 156.0/157.0), then REORDER and
RE-PARENT (Pass 161.0). A bookmark dragged anywhere in the
tree takes its children with it; its destination never moves.
Expand/collapse ships alongside, as a separate verb, because
whether a move should reveal a collapsed destination has two
defensible answers and both now exist.

FONTS text can be restyled to a face the document DOES NOT CONTAIN
(Pass 162.0), for the fourteen faces every PDF reader is
required to have. pdfce authors the font resource on demand,
with widths, embedding nothing. A face outside those fourteen
still refuses by name -- that needs a real font program.

ALSO IN THIS RELEASE

  • a comment can be written onto a shape that already exists (Pass 154.0),
    and markup can be authored at an opacity in one undo entry (Pass 81.1);
  • annotations can be MOVED and RESIZED, not only created and deleted
    (Pass 149.0/151.0), with a stroke-width toggle the operator ruled must
    exist;
  • OCR is an edit to the open document rather than a different file
    somewhere (Pass 135.0/135.1);
  • the objects inside a form XObject are reachable, so clicking one finds it
    (Pass 136.0/136.1/136.2/138.0);
  • a spot colour that painted NOTHING on a print-bound page now paints
    (Pass 130.3), an overprinting image respects overprint (Pass 130.2), and
    grey stops knocking spot ink out (Pass 143.0);
  • a gradient and a solid of the same ink are the same colour (Pass 137.x);
  • the CMYK default became Calibrated by operator ruling (Pass 153.0);
  • form-field properties are editable after placement, including size
    (Pass 134.0).

TWO DEFECTS FIXED THAT NOBODY REPORTED

Both were found by running the shipped binary rather than by a failing test:

  • the font work was writing a resource the file did not contain: the CLI's
    save path was one of THREE, and only two were wired. The command reported
    success and saved a file whose text named a font that was not there.
  • two refusal messages told the operator to use a verb that has never
    existed (rotate_widget, set_dimension_label), read at the exact moment
    they were blocked. Both messages now say the capability is not built yet,
    and a gate (check-cited-verbs-exist.py) makes a phantom impossible.

VERIFICATION

bash tools/run-gates.sh ............ 28 commands, all clean
python tools/check-passes-filed.py --strict-tip ... clean
python tools/check-commits-filed.py ............... clean
cargo fmt --all --check / cargo clippy -D warnings clean
cargo tree -p pdfce-core / -p pdfce-render ........ no GUI, no network deps
portable package built and smoke-tested from a fresh folder

No dependency changes since v0.14.0, so THIRD_PARTY_LICENSES.md is unchanged
and was not regenerated.

Co-Authored-By: Claude Opus 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01AcgfF2qoRL3i9NtC9JXRit

pdfce v0.14.0 - the ceiling on print-colour blending is yours to set

Choose a tag to compare

@KenM76 KenM76 released this 26 Aug 20:25

The ceiling on print-colour blending is yours to set

A page that specifies print colours is blended in ink rather than on
screen. That costs 20 bytes per pixel, so it grows with the square of the
zoom — and above a memory ceiling pdfce fell back to blending on screen
instead. The result: the same page could come out with slightly different
colours at different zoom levels
, with nothing able to say where the
boundary was. The ceiling was a fixed 256 MB that nothing outside the
renderer could read and nobody could move.

Now it is yours.

  • In the settings window (Colour group) and in settings.txt, as
    max_cmyk_buffer_bytes. No upper limit — raise it to keep exact
    colours further in. It costs that memory, and about 50% more time on the
    pages it applies to.
  • A value your machine cannot supply is not a crash. pdfce attempts the
    allocation, falls back, and tells you.
  • pdfce-cli render-page --max-cmyk-buffer-bytes <SIZE> — 1gib,
    512mb, 268435456, default, or 0. The refusal note now names the
    ceiling in force, the pixels it permits and the pixels the raster wanted.

For anything built against pdfce-core / pdfce-render

Four new public items let a caller decide before rastering whether the
raster will have exact colours — will_composite_in_cmyk(w, h, max_bytes)
is the one to use. pdfce-core::settings gains max_cmyk_buffer_bytes,
parse_byte_size, format_byte_size and ByteSizeError. Documented in
docs/core-api/03-capabilities.md §7.3a, including the part the API alone
does not answer: the ceiling bounds one buffer, and a page with nested
transparency can hold up to about four page-sized ones at once.

Pass 132.0, decision 089.

Also

A correction to the numbers shipped with that Pass: every "A4" percentage in
its operator-facing prose had been computed on a page that is not A4. The
mechanism was right; the sheet was wrong. The default ceiling reaches
518% zoom on real A4, not 534%.


Portable, single folder, no installer. Unzip anywhere and run
pdfce-gui.exe or pdfce-cli.exe. Your settings live in userdata/ beside
them — keep that folder when you update.

pdfce v0.13.0 - two conformance patches that were failing for two different reasons

Choose a tag to compare

@KenM76 KenM76 released this 26 Aug 14:03

Two conformance patches that were failing for two different reasons

Both carry the same instruction — "No X must be visible when rendered
correctly"
— and both showed an X. The causes had nothing to do with each
other, which is why they took two fixes.

1. The same palette, applied twice

A JPEG 2000 image can carry a colour lookup table inside the image file, and
the PDF around it can declare one too. This document had both, holding
identical bytes
.

pdfce applied both. The first turned the samples into a colour; the second
then read that colour as if it were a position in the table, looked for
entry 114 in a table with one entry, found nothing, and painted black.

Apply either table alone and the answer is correct. Applying both is the only
way to be wrong — which is exactly why a conformance suite ships the pair.

2. Ink converted to screen colour and back, for no reason

A page destined for print composites in ink. Handed an image whose samples
were already ink, pdfce converted it to screen colour and straight back
again on the way in.

★ And no better conversion would have fixed it. Ink→screen is
many-to-one — a rich black and a plain black look identical on a monitor — so
there is no way back. The fix is not to make the trip.

CMYK images now carry their ink all the way to the compositor. 17 of 51
conformance patches keep their ink where they previously converted it
; six
go from thousands of converted pixels to zero.

That includes images whose colour is stored as a one-entry palette, which is
how flat colours normally ship in print files — and which is why the first
attempt at this fix changed nothing at all.

★★ What this is not

Neither patch passes because pdfce's colour matches Acrobat. It doesn't.

square (drawn as a path) the image
pdfce, before (238, 29, 35) (225, 63, 50) X visible
pdfce, now (238, 29, 35) (238, 29, 35) X gone
Acrobat (227, 0, 11) (227, 0, 11)

pdfce now agrees with itself, which is precisely what these patches test.
It still does not agree with Acrobat, because ICC colour profiles are not
applied — that is a separate, known gap and it is unchanged here.

A self-consistency test passing is not a colour-accuracy claim, and this
release does not make one.

Also corrected: a gap that turned out not to exist

While preparing this release, per-image overprint was going to be built next.
Reading the specification first showed most of it must not be: the table
governing overprint scopes its per-component rule to colours "not in a
sampled image"
, so for an image the conforming behaviour is to paint it
normally — which pdfce already did.

Our own documentation said the opposite in four places, one of them a heading
telling readers the fix was impossible. All four are corrected. What remains
genuinely outstanding is narrower and is now named precisely.

One change that is not additive

A CMYK image on a print-destined page now composites to different pixels
than in v0.12.0 — correctly, but differently. If you compare renders
byte-for-byte against the previous release, you will see it. The
cmyk_bridged_pixels diagnostic has also changed meaning and reads much lower
on print files; a companion cmyk_native_image_pixels is new.


Windows x64, single folder, no installer, no registry writes. Extract and run
pdfce-gui.exe or pdfce-cli.exe. MIT licensed; third-party attributions and
the OCR model weights' own provenance ship in the folder.

pdfce v0.12.0 - OCR that works, and two silences broken

Choose a tag to compare

@KenM76 KenM76 released this 26 Aug 06:52

OCR that works, and two silences broken

OCR had never worked. Now it does.

pdfce has shipped an OCR engine since 12 August. It produced garbage on
every page
— not degraded output, noise — and nothing had ever measured it
end to end.

The cause was one wrong model file. pdfce bundles two neural networks: one
that finds where the words are, one that reads them. The detection model was
a build that does not work with the library version pdfce uses. Isolated by
swapping one file at a time, which is why the answer is known rather than
merely fixed — the recognition model was never at fault.

What it does now, measured on a synthetic scan (200 dpi, blurred, skewed
0.35°, sensor noise, grey paper):

words read correctly 47 of 47
invisible layer vs. the ink 0.90 pt median — about 1/80 inch

That is the first real accuracy figure this project has had. Every earlier
number was a word count.

Try it:

pdfce-cli ocr scan.pdf --page 1 -o searchable.pdf
pdfce-cli find-text searchable.pdf --needle "a word on the page"

There is nothing to look at afterwards — the text is invisible by design,
sitting under the image so the page prints exactly as before. That is what
Acrobat does too. Check it by searching, not by looking.

How the scan was made, because it could not be downloaded

Licensing forbids checking a real-world PDF into this repository, which is why
OCR quality went unmeasured for two weeks. So the scan is manufactured:
pdfce renders a page it authored, degrades the raster the way a sheet-fed
scanner does, and wraps the result as an image-only PDF.

The useful part is that the answer key comes free. The vector original says
where every word really is, from real font metrics; the OCR'd copy says where
OCR put it. Two rectangles, two completely different routes, which must agree —
and that catches the failure a word count cannot: a text layer that reads every
word perfectly and lands in the wrong place.

tools/ocr-accuracy.py scores content and position separately, for that reason.

Two silences broken

A search that found nothing and a redaction that marked nothing look
identical whether the term is absent or the document's text was never readable
in the first place. Some PDFs render text perfectly that no software can
search — which is exactly why the failure is invisible.

Both now say so, naming each font and the reason.

★ The redaction one is the serious half. You ask for every occurrence of a
name to be removed, the run reports success, and the file still contains it.
It now warns even when it marked something, because the partial case is the
dangerous one: "2 marks created" reads as success and says nothing about a
third occurrence the search could never reach.

Also

  • Image quality on ordinary pages matches Acrobat again — a downsampling
    default changed on a side-by-side comparison against Acrobat Reader.
  • Rendering presets for the PDF subset standards (list-standards,
    render-page --standard pdf-x4). Each value states whether it is sourced to
    a clause of the standard or is pdfce's own judgement where the standard is
    silent. For PDF/X-4, one of six is a claim about the standard at all.
  • Rotated pages no longer put OCR text in the wrong place.
  • fetch-ocr-models downloads the weights, verified against a pinned
    checksum, for builds that do not carry them. It is opt-in at build time
    (--features download) and is not in this release's binaries — which
    therefore contain no network code at all. You do not need it: the weights
    are in the folder.
  • The OCR weights now actually ship inside the portable folder. They did
    not before, so OCR failed in every release build while working in every
    development one.

A note on the downloadable build

This release carries no network code. pdfce does not need a network to do
anything, and the default build does not contain an HTTP or TLS stack — a
claim you can check against the THIRD_PARTY_LICENSES.md in the folder. The
OCR model downloader exists but has to be asked for at build time.

One change that is not additive

MinifyFilter's default moved from point-sampling to smoothing. If you compare
renders byte-for-byte against a previous version, minified images will differ.
That is the intent — it is what "matches Acrobat again" means — but it is a
real change rather than an addition.

Known, and named rather than left to be found

  • Redaction covers what the search found, not what a reader can see.
    On a scanned page only the OCR layer is searchable, and OCR is a guess. A
    misread word cannot be marked, and no warning will tell you.
  • Type 3 text still cannot be edited. Neither can Acrobat edit it.

Windows x64, single folder, no installer, no registry writes. Extract and run
pdfce-gui.exe or pdfce-cli.exe. MIT licensed; third-party attributions ship
in the folder, and the OCR model weights carry their own PROVENANCE.md
recording their CC-BY-SA-4.0 terms and creator.

pdfce v0.11.0 - a search that no longer lies by omission

Choose a tag to compare

@KenM76 KenM76 released this 25 Aug 23:13

A search that no longer lies by omission

Yesterday's release made Type 3 fonts draw — the glyphs that are little
content streams rather than a font program. This one makes that text
searchable and copyable, and, where it cannot be, makes pdfce say so.

The problem, in one sentence

A search that returns 0 results for a word plainly visible on the page
looks exactly like a search for a word that genuinely is not there.

Why that happens

A Type 3 glyph is named by an arbitrary key inside the document — /g13,
/square — which means nothing outside that one file. So unless the file
carries a /ToUnicode character map, there is no way at all to know what
letter a shape represents. The text still draws perfectly. It simply cannot
be searched, copied or extracted.

Adobe Acrobat has the identical limit. The difference is that Acrobat
gives up silently. pdfce now tells you:

find-text report.pdf --needle "invoice"
...
matches=0 unreadable_codes=214 type3_no_tounicode=2 identity_no_tounicode=0

pdfce-cli: find-text: 2 Type 3 font(s) in this document carry NO /ToUnicode
  CMap (ISO 32000-1 §9.6.5) — their glyphs are content streams named by
  arbitrary /CharProcs keys, so text set in them RENDERS correctly and cannot
  be searched or copied. Acrobat is gated on the same entry
pdfce-cli: find-text: 214 of 3,180 character code(s) could not be mapped to
  Unicode ... A zero match count for this document is therefore not evidence
  the needle is absent

Each offending font is named individually, so you know which text on the
page is out of reach rather than just that some of it is.

Try it

tounicode_gate.pdf is attached — a three-line synthetic page built for this
release. The first line carries a character map and is findable; the other two
do not and are not. All three draw identically, which is the whole point.

pdfce-cli find-text tounicode_gate.pdf --needle "HI!"
pdfce-cli extract-text tounicode_gate.pdf
pdfce-cli render-page tounicode_gate.pdf --page 1 --output gate.png

Also in this release

  • extract-text reports type3_no_tounicode= on its summary line and in its
    JSON output, and names each affected font on stderr.
  • A second, wider defect was found and fixed on the way: pdfce's per-font
    diagnostics were keyed on the font's name, and the PDF standard gives a
    Type 3 font no name at all. The effect was worse than under-counting —
    the first unnamed font on a page silenced every unnamed font behind it,
    including warnings that had nothing to do with Type 3. Such fonts are now
    reported individually and identified by the name the page itself uses.
  • New pdfce-core verb EditSession::search_text, returning the hits and the
    diagnostics together. find_text and find_text_with are unchanged.

Known, and named rather than left to be discovered

  • Redaction by search does not yet disclose this. A "redact every match"
    run over text pdfce cannot read marks nothing and says nothing. Next up.
  • Type 3 text cannot be edited. Acrobat cannot edit it in place either.

Windows x64, single folder, no installer, no registry writes. Extract and run
pdfce-gui.exe or pdfce-cli.exe. MIT licensed; third-party attributions
ship in the folder.

pdfce v0.10.0 - text that was a blank page

Choose a tag to compare

@KenM76 KenM76 released this 25 Aug 21:11

Text that was a blank page

Some PDFs define their letters as little drawing programs rather than as a
font file — one content stream per glyph. The standard calls them Type 3
fonts, and they turn up in old TeX and dvips output, in prepress and CAD
exports, and in anything converted from a bitmap font.

pdfce recognised them, counted them, and drew nothing. If a page used one, the
text simply was not there.

They render now — both flavours the standard allows: glyphs drawn with path
operators, and glyphs drawn as a 1-bit stencil (ISO 32000-1 §9.6.5, Tables 112
and 113). Both routes a font can reach a page by — the Tj family and a
shading-pattern fill — go through the same code.

Measured, not asserted: all 49 files in the test corpus that carry a Type 3
font now render, with no glyph unaccounted for. One of them draws a heading
that was an entirely blank page before this release.

The rule that decides every glyph's colour

A Type 3 glyph declares, in its own first operator, whether it specifies its
shape and its colour or only its shape. In the second case the standard
says any colour the glyph sets for itself "shall be ignored" and the glyph
takes the colour of the surrounding text.

No public source established whether Acrobat actually obeys that — so a probe
was built and run in Acrobat before any of this was written. It obeys: the two
declarations produce opposite colours from identical instructions. pdfce
follows the clause knowing the clause and Acrobat agree.

Getting this backwards would paint every glyph of a colour-setting font the
wrong colour, on every page, in a way that looks deliberate.

Bitmap glyphs are stencils, and stay stencils

A bitmap Type 3 glyph is a region of the page to be painted, not a picture to
be resampled — so pdfce never smooths one, at any zoom. Acrobat does not
either; it is what drove the old practice of converting TeX Computer-Modern
fonts to Type 1. Verified across a 64× zoom range, on both sides of the point
where the image switches between being shrunk and being enlarged.

Three new things pdfce-cli render-page will tell you

  • type3_glyphs — how many glyphs were drawn by running a program rather than
    by looking up a shape.
  • type3_glyphs_missing — a character was shown whose glyph does not exist in
    the file. The spacing still happened, so this counts letters that are
    absent, never text that has moved.
  • type3_colors_ignored — how many colour instructions pdfce deliberately
    dropped, under the rule above. Not a shortfall: it is what the standard
    requires. It is reported because anyone debugging a glyph that came out the
    "wrong" colour deserves to be told the instruction they can see in the file
    was dropped on purpose rather than missed.

What is not in this release

Searching and copying text set in a Type 3 font. A Type 3 glyph name means
nothing on its own, so extraction depends on the file carrying a /ToUnicode
map — a separate capability from drawing, and one Acrobat is equally dependent
on. Named here rather than left to be discovered.

Getting it

pdfce-v0.10.0-windows-x64-portable.zip — unzip anywhere and run
pdfce-gui.exe. No installer, no registry writes, nothing outside the folder;
your settings land in userdata/ beside the binaries. pdfce-cli.exe is a
first-class scriptable CLI, not a debug tool — start with --help.

MIT licensed. Third-party attribution ships in the folder and is generated,
never hand-written.

pdfce v0.9.0 - gradient meshes draw

Choose a tag to compare

@KenM76 KenM76 released this 25 Aug 19:13

Gradient meshes draw

pdfce could already paint the two simple gradient kinds — the straight-line and
circular ones. The other four, where the gradient's shape is stored as a packed
grid of triangles or curved patches rather than as a formula, it recognised,
counted, and refused. On a page that used one, you got nothing where the
artwork belonged.

All four now render: ShadingType 4 (free-form Gouraud triangle meshes),
5 (lattice-form), 6 (Coons patches) and 7 (tensor-product patches),
ISO 32000-1 §8.7.4.5.5–.8. Both ways a shading reaches a page — the sh
operator and a PatternType 2 fill — go through the same decoder.

This is what an Illustrator gradient mesh is made of, and it is what the
prepress conformance suite pdfce measures itself against tests in its
shading patch.

How well

That patch pairs every live shading with a bitmap of what a correct render
produces, and prints its own criterion on its face: "The shadings should look
like the reference image."
So it can be scored with no external reference at
all. Correlation of each shading against its own reference image:

cell before after Acrobat, same file
type 7 mesh −0.044 0.949 0.981
type 3 (already worked) 0.9985 0.9985 0.9979
type 2 (already worked) 0.9997 0.9997 0.9998
type 7 mesh −0.044 0.997 0.997

The second mesh cell now matches its reference more closely than Acrobat's own
render of the same file does.

The small remaining gap on the first cell is not the mesh. pdfce's mesh
differs from Acrobat's mesh by the same amount pdfce's plain photograph on
that page differs from Acrobat's plain photograph — 26.5 versus 29.6. The
reference image contains no mesh at all, so what is left is the CMYK-to-screen
colour table, which belongs to a different part of the pipeline and is already
being worked on elsewhere.

The parts that are pdfce's choice rather than the standard's

The standard leaves two things open here, and they are handled differently on
purpose.

How finely a curved patch is approximated is unspecified, and the
smoothness tolerance the standard does define bounds colour error rather
than geometric deviation. pdfce picks the density from the patch's size on
screen
, so it refines as you zoom in. That is deliberately not a setting: the
right value is a function of zoom, and a knob nobody can set correctly is worse
than no knob.

Where one patch record ends and the next begins, when the packed fields do
not land on a byte boundary, genuinely has two defensible readings — the
standard states the rule for a vertex and a patch has no vertices, and
ISO 32000-2 repeats the sentence unchanged, so it will not be settled by a
later edition. That one is a setting: mesh_patch_padding, defaulting to
the reading under which the standard's own cross-reference has any content.
Every mesh measured so far reads identically either way.

Also in this release

  • Three new machine-readable counters on pdfce-cli render-page —
    mesh_records, mesh_truncated, mesh_unusable — and a named reason for
    every mesh pdfce declines. A shading that cannot be drawn says why.
  • A truncated mesh stream paints the records that were complete and reports
    the discard, rather than refusing the whole shading.
  • A fuzzing target for the mesh stream decoder: 1,107,957 runs, no crash.
  • The tooling that keeps this project honest got two repairs of its own — the
    fuzz harness had not linked on Windows since the OCR engine landed, and a
    repository hygiene check could both leak the term it was meant to suppress
    and miss files written moments earlier.

Getting it

pdfce-v0.9.0-windows-x64-portable.zip — unzip anywhere and run
pdfce-gui.exe. No installer, no registry writes, nothing outside the folder;
your settings land in userdata/ beside the binaries. pdfce-cli.exe is a
first-class scriptable CLI, not a debug tool — start with --help.

MIT licensed. Third-party attribution ships in the folder and is generated,
never hand-written.