Commit Graph
3487 Commits
Author SHA1 Message Date
Grzegorz MartensandClaude Opus 5.5 210ba20fe6 M11: the architecture doc says Export decodes CCITT and JPEG; the progress logs' wording
docs/architecture.md said every image codec decodes through the
bootstrap renderer; Export ▸ Images decodes CCITT (hayro-ccitt) and JPEG
itself. The progress logs' "(a dithered scan)" sat after "soft mask
counted", as if it described the mask, and so did the
optimize.mono-compression note's "(a dithered scan downsampled to JPEG)".

From reviews with the pdfcraft-review skill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P6yUtvWGPsJpMLyh6pxSXG
2026-10-29 20:48:03 +01:00
Grzegorz MartensandClaude Opus 5.5 8c720e4808 M11: Export reads byte-aligned Group 3 by the codes in its data
"Export ▸ Images reads byte-aligned Group 3 whichever way its fill
falls" read byte-aligned rows unaligned whenever /EndOfLine was set.
That broke byte-aligned Group 4 with /EndOfLine (refused); read Group 3
that says /EndOfLine but has no codes unaligned, and wrongly; and read
codes with the fill before them, but no /EndOfLine, on bytes: every row,
the wrong picture, nothing said. Now:
- Group 4, and rows not on bytes, read as the parameters say;
- Group 3 rows on bytes are read by the data: T.4 sends an end-of-line
  code before the first row when it sends them at all, and no other code
  starts with more than eight zeros. Data that starts with 11 zeros or
  more (fill included) and a 1 has codes, and is read unaligned, where
  the codes mark the rows whether the fill starts each code on a byte or
  ends it on one; other data is read on bytes, whatever /EndOfLine says.
  A reading that comes up short is reported rather than tried the other
  way, where data cut short would read as other rows.

The Group 3 test exports nine pictures, stripes and eight of random
text-like runs (how a misreading comes out depends on the picture),
coded with T.4's terminating codes at /K 0 and 1: no codes, with
/EndOfLine and without; codes each starting on a byte or ending on one,
with /EndOfLine and without. Each exports as its picture, and each a
byte short is reported; at /K 1, codes with none before the first row
(not as T.4 sends them) export as their picture or are reported.
Byte-aligned Group 4 still reads with and without /EndOfLine. The
create.export-all-images parity note says what is still limited: such
codes may export wrong at /K 0, as the create README says. A rule tried
first, reading both ways and refusing an image that gave every row both
ways differently, failed on the stripes without codes (TIFF's Modified
Huffman layout): its unaligned reading gives every row too, wrongly. So
did one that read the other way when the first reading came up short: it
drew data cut short as other rows. cargo-mutants leaves no mutant of the
new lines alive.

From reviews with the pdfcraft-review skill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P6yUtvWGPsJpMLyh6pxSXG
2026-10-29 20:48:02 +01:00
Grzegorz MartensandClaude Opus 5.5 ec4b265b57 M11: "the smaller wins" counts the soft mask in the tests and the docs
- A noisy 640 × 800 soft mask under 1200 × 1500 text: resampled to the
  image's new size it shrinks only a little, by less than the grey
  settings' JPEG adds over the packed image, so packing wins; not
  counting the new mask passed every test before.
- The hex-text half of the bomb test also stores eight-bit
  black-and-white, which must pack, as its comment says.
- The optimize README, the mono-compression parity note and both
  progress logs say the soft mask counts.
- The Copy Image parity note no longer lists CCITT as missing: it
  copies through Export's decoder, which reads CCITT now.

From reviews with the pdfcraft-review skill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P6yUtvWGPsJpMLyh6pxSXG
2026-10-29 20:48:02 +01:00
Grzegorz MartensandClaude Opus 5.5 e8f3c6bc3e M11: Export ▸ Images reads byte-aligned Group 3 whichever way its fill falls
With /EncodedByteAlign and end-of-line codes, the fill may start each
code on a byte or end it on one (T.4's fill, as TIFF writes it); the
second was misread, and the image refused as short. With end-of-line
codes the decoder now reads unaligned, as the codes mark the rows either
way (probed black-box for /K 0, 1 and 2); codes laid out so under
/EndOfLine false are still misread. The Group 3 test covers both
layouts, /EncodedByteAlign given by reference, and a /BlackIs1 that
inverts the picture given by reference (with_direct_parms resolves both
before the decoder reads them).

From reviews with the pdfcraft-review skill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P6yUtvWGPsJpMLyh6pxSXG
2026-10-29 20:48:02 +01:00
Grzegorz MartensandClaude Opus 5.5 2c7bbbae05 M11: the progress logs say eight-bit black-and-white packs unless the grey settings make it smaller
From reviews with the pdfcraft-review skill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P6yUtvWGPsJpMLyh6pxSXG
2026-10-29 20:48:02 +01:00
Grzegorz MartensandClaude Opus 5.5 d2c3bda6b5 M11: tests for the soft mask in "the smaller wins", the byte totals and Group 3's options
- Eight-bit black-and-white text under a noisy soft mask: the grey
  settings win, as they shrink the mask far more than packing saves.
  Counting the packed image without its mask passed every test before.
- The packing test checks the report's byte totals exactly.
- Export reads Group 3 with end-of-line codes, with rows on a byte
  (EncodedByteAlign), with both, and mixed (/K 1, each row tagged
  one-dimensional) with end-of-line codes and without. Decoding /K 1 as
  one-dimensional, or ignoring EncodedByteAlign, now fails a test.
  /EndOfLine is passed to the decoder, but no input here tells it apart:
  the decoder accepts end-of-line codes either way, as ISO 32000-2
  (Table 11) requires.

From reviews with the pdfcraft-review skill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P6yUtvWGPsJpMLyh6pxSXG
2026-10-29 20:48:02 +01:00
Grzegorz MartensandClaude Opus 5.5 a4058c9242 M11: say redaction removes Create PDF's Group 4 pictures whole for now
Redaction doesn't decode CCITT yet, so a mark over part of a picture
Create PDF stored as Group 4 removes the whole picture (nothing leaks);
before this PR such pictures were eight-bit Flate and had only the
covered pixels cleared. The create README and the image-to-PDF parity
note say so; #4384 adds CCITT to redaction. The workspace's comment on
hayro-ccitt says Export uses it, not only tests.

From reviews with the pdfcraft-review skill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P6yUtvWGPsJpMLyh6pxSXG
2026-10-29 20:48:02 +01:00
Grzegorz MartensandClaude Opus 5.5 096f6431c7 M11: Export ▸ Images reads a lookup table's /DecodeParms given by reference; tests for its claims
- An indexed image's lookup table given as a stream was decoded without
  its /DecodeParms when they came by reference, so a PNG predictor's tag
  bytes ran into the colours (red and blue came out [2,255,0] and black).
  It decodes through with_direct_parms now, as the samples do. This was
  already so on main; #4320 makes the same change.
- Tests for what this PR claims and no test held: Group 3 one-
  dimensional data exports as its picture (mapping /K 0 to Group 4 gave
  all white and passed); CCITT soft and stencil masks export as the same
  masks in Flate (decoding them plainly dropped them and passed); a
  Flate image's predictor given by reference reads as direct; and a
  1-bit TIFF is stored as Group 4 (the TIFF test said "a 1-bit one" but
  wrote eight bits; its comment now says what it writes).

From reviews with the pdfcraft-review skill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P6yUtvWGPsJpMLyh6pxSXG
2026-10-29 20:48:02 +01:00
Grzegorz MartensandClaude Opus 5.5 f33227886d M11: eight-bit black-and-white grey takes the smaller of one bit and the grey settings
An eight-bit grey image of only black and white was packed to one bit
and stored, and the grey settings never saw it. For a dithered scan that
is bigger than what Reduce File Size made of it before, downsampled to
JPEG: 1-bit Flate can't shrink the dots (a 1200 × 1500 dither at 300 ppi
was 157 KB packed, 67 KB as before). Both are made now, and the smaller
as stored wins, counting a soft mask resampled with the image. Clean
text still packs to one bit. The packing also decodes no further than
its samples can need, as the other image steps do now.

From reviews with the pdfcraft-review skill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P6yUtvWGPsJpMLyh6pxSXG
2026-10-29 20:48:02 +01:00
Grzegorz MartensandClaude Opus 5.5 546f78c042 M11: the optimizer reads eight-bit grey through /DecodeParms given by reference
Packing eight-bit black-and-white grey to one bit decoded with
Stream::decoded, which ignores /DecodeParms given by reference. With PNG
predictor 10 each row then kept its tag byte, a 0, so the shifted rows
still passed as black and white and were packed and stored skewed. It
now decodes through Document::with_direct_parms (#4304), and the test
of images with their parameters by reference covers this case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BfMGHQNgdq5yf5tKZHGR3z
2026-10-29 20:48:02 +01:00
Grzegorz MartensandClaude Opus 5.5 8af04af01b M11: Export ▸ Images reads /DecodeParms given by reference
Stream::decoded reads only direct parameter dictionaries, so an image
behind [/FlateDecode /CCITTFaxDecode] whose Flate entry is a reference
lost its predictor, and the CCITT decoder got the predictor's bytes; a
plain Flate image with its predictor given by reference was misread the
same way. Export now decodes through
pdfcraft_cos::Document::with_direct_parms (#4304), as the optimizer
does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BfMGHQNgdq5yf5tKZHGR3z
2026-10-29 20:48:02 +01:00
Grzegorz MartensandClaude Opus 5.5 7ca7e01f92 M11: Export ▸ Images decodes CCITT fax images behind other filters
A fax image with /Filter [/ASCII85Decode /CCITTFaxDecode], say, was
skipped: only a lone CCITT filter went to the CCITT decoder. Now the
filters before it are undone first, and its parameters are the
/DecodeParms entry at its own place in the array (ISO 32000-2 §7.4.1),
not the first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BfMGHQNgdq5yf5tKZHGR3z
2026-10-29 20:48:02 +01:00
Grzegorz MartensandClaude Opus 5.5 66b5ad2ab7 M11: Export ▸ Images reads CCITT numbers given by reference, and decodes up to the image's height
A CCITT image whose /K, /Columns or /Rows is an indirect integer was read
with the defaults (K 0, 1728 columns), and one whose /Rows is below its
/Height was cut short and skipped. The numbers are now resolved, and the
image is decoded up to its /Height rows whatever /Rows says, still bounded
by the size the export checked (review of #4335).

The doc comment of the optimizer's bilevel step now says what Retain
leaves to the grey settings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BfMGHQNgdq5yf5tKZHGR3z
2026-10-29 20:48:02 +01:00
Grzegorz MartensandClaude Opus 5.5 3204051c3b M11: Export ▸ Images reads CCITT parameters given in an array
A CCITT image whose /DecodeParms is a one-element array (common beside
/Filter [/CCITTFaxDecode]) was decoded with the default parameters: K 0
and 1728 columns. Wider scans were skipped, and a fax 1728 wide could be
misread. The parameters are now read from a dictionary or the one in an
array (ISO 32000-2 §7.4.2). The module doc no longer lists CCITT as
undecodable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BfMGHQNgdq5yf5tKZHGR3z
2026-10-29 20:48:02 +01:00
Grzegorz MartensandClaude Opus 5.5 4712abaabb M11: Export ▸ Images decodes CCITT fax images
Group 4 images (now written by Create PDF and the optimizer) would
otherwise stop exporting: they were reported as "can't be exported
yet". A CCITT fax image (its only filter) is decoded through
hayro-ccitt with its /DecodeParms (K, Columns, Rows, EndOfBlock,
EndOfLine, EncodedByteAlign, BlackIs1), masks included; /Columns has to
be the image's width, so a hostile value can't size the buffers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BfMGHQNgdq5yf5tKZHGR3z
2026-10-29 20:48:02 +01:00
Grzegorz MartensandClaude Opus 5.5 3d6e11ecdb M11: the optimizer stores eight-bit black-and-white grey images at one bit
An eight-bit grey image whose samples are all 0 or 255 is packed to one
bit per pixel and then stored as Monochrome Images asks (Group 4, or
JBIG2 with #4320), when that is smaller as stored; its decode array
stays. Indexed images, a colour-key /Mask and Retain are left alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BfMGHQNgdq5yf5tKZHGR3z
2026-10-29 20:48:02 +01:00
Grzegorz MartensandClaude Opus 5.5 9883da3bc3 M11: Create PDF stores black-and-white images at one bit, as Group 4 when smaller
A scan saved as a 1-bit PNG decodes to eight-bit grey and was stored
that way, larger than the PNG it came from; a bilevel TIFF was stored at
one bit but with Flate. Now any image of only pure black and white
(1-bit, eight-bit grey, a two-colour palette, RGB without a profile) is
stored at one bit per pixel, as CCITT Group 4 or Flate, whichever is
smaller as stored. A grey profile stays; RGB with a profile keeps its
colours.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BfMGHQNgdq5yf5tKZHGR3z
2026-10-29 20:48:02 +01:00
Grzegorz MartensandClaude Opus 5.5 1b7164cab1 M11: the spec parity table says the Optimizer encodes CCITT Group 4
docs/pdf-spec-parity.md's §7.4 row still said there was no CCITT
encoding; it names the Optimizer's Group 4 encoder now, with a status
line and a revision row.

From reviews with the pdfcraft-review skill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P6yUtvWGPsJpMLyh6pxSXG
2026-10-29 20:47:39 +01:00
Grzegorz MartensandClaude Opus 5.5 656c6ef0f4 M11: name every reader that doesn't take Group 4 yet; count the new partial in the parity tables
- Not only Export ▸ Images refuses a CCITT image until #4335: so does
  everything that exports an image through it. Save Image As refuses
  it, Copy Image fails, and Word, PowerPoint and HTML export leave the
  picture out, with the page not reported. The optimize README and the
  mono-compression parity note name them.
- optimize.mono-compression is partial now, so docs/target-app-parity.md
  counts it on the table's own measurement: K Optimize and standards 15
  shipped and 11 partial of 58 (35.3%), breadth 57.0% (423 + 75 of 808),
  mainstream 63.3% (292 + 45 of 497), with a status line and a revision
  row. ROADMAP.md's breadth rows and its K row say the same.

From reviews with the pdfcraft-review skill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P6yUtvWGPsJpMLyh6pxSXG
2026-10-29 20:47:39 +01:00
Grzegorz MartensandClaude Opus 5.5 b76ffe64f1 M11: the decode allowance takes ASCIIHex text broken into lines
The optimizer's images decode no further than twice their samples plus
a byte a row, meant for ASCIIHex text under Flate; but hex text broken
into lines, as it usually is, is a little more than twice, so such an
image was left alone as if it were a decompression bomb (a 300 dpi
letter scan stayed Flate). It is three bytes for each now, a separator
for every two digits, and a test stores both images as hex text in
lines of 64 digits.

From reviews with the pdfcraft-review skill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P6yUtvWGPsJpMLyh6pxSXG
2026-10-29 20:47:39 +01:00
Grzegorz MartensandClaude Opus 5.5 4543bae3bf M11: say redaction and export don't read Group 4 yet; Reduce File Size's descriptions
- Redaction doesn't decode CCITT, so a mark over part of an image the
  optimizer stored as Group 4 removes the whole image (nothing leaks),
  and Export ▸ Images refuses it. The optimize README and the
  mono-compression parity note say so. Later PRs of this stack add both
  (#4335 export, #4384 redaction).
- doc_reduce's description, Engine::reduced_bytes and docs/cli-batch.md
  now list the black-and-white step; docs/target-app-parity.md's
  optimize row gives the measured counts and no longer lists CCITT as
  missing; the M11 milestone row lists it as done.

From reviews with the pdfcraft-review skill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P6yUtvWGPsJpMLyh6pxSXG
2026-10-29 20:47:39 +01:00
Grzegorz MartensandClaude Opus 5.5 3143c4a044 M11: tests for which 1-bit images become Group 4, and the byte totals
A test now covers what qualifies and what doesn't: an ICC profile of
one component and a DeviceN of one colourant convert, three components
and two colourants (more than a bit a pixel) don't; an image mask
converts with /BitsPerComponent 1 given, not with 8; an image of exactly
80 million pixels converts, one row more doesn't. The byte totals are
checked exactly. Each of those rules could be broken before with no
test failing (cargo mutants). one_component's comment now says /G
counts with DeviceGray, as the code does.

From reviews with the pdfcraft-review skill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P6yUtvWGPsJpMLyh6pxSXG
2026-10-29 20:47:39 +01:00
Grzegorz MartensandClaude Opus 5.5 88c089bd29 M11: the optimizer decodes an image no further than its samples can need
A 1-bit image was decoded for Group 4 up to MAX_DECODED (1 GiB), so a
few kilobytes of Flate could cost a gigabyte, and the same held for the
samples of a colour or grey image read to resample it (pixels). Both now
stop at twice the samples plus a byte a row and 4 KiB, as redaction
does; data past that can only be a decompression bomb, and the image is
left alone.

From reviews with the pdfcraft-review skill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P6yUtvWGPsJpMLyh6pxSXG
2026-10-29 20:47:39 +01:00
Grzegorz MartensandClaude Opus 5.5 e6fddf155b M11: /Crypt doesn't count against with_direct_parms' chain limit
The decoders leave /Crypt out wherever it stands (Stream::filters), so
a chain of /Crypt and eight other filters decodes, but with_direct_parms
counted nine and left its parameters as they were: a predictor given by
reference was then skipped. Only the other filters count now, and a
/Crypt entry is null in the copy, as no decoder reads it. Tests cover
that chain and one exactly as long as a decoder takes.

From reviews with the pdfcraft-review skill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P6yUtvWGPsJpMLyh6pxSXG
2026-10-29 20:47:39 +01:00
Grzegorz MartensandClaude Opus 5.5 58945d1f5b M11: the optimizer reads numbers inside /DecodeParms given by reference
Document::with_direct_parms resolved an indirect /DecodeParms and its
array entries but not the numbers inside (/Predictor 9 0 R), which
Dict::int doesn't follow. A 1-bit image with such a predictor was
decoded without it, its rows shifted by their tag bytes, and Group 4 of
that stored in its place; resampling read images the same way. It now
keeps, of each parameters dictionary, the numbers and flags the decoders
read, resolved, and /JBIG2Globals as a reference. Only those small
values are copied (an entry that is no dictionary is null), so a file
naming one big object from every key and filter can't have it copied a
hundred times.

The change is #4384's, moved down so that every PR stacked on this one
reads such parameters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P6yUtvWGPsJpMLyh6pxSXG
2026-10-29 20:47:39 +01:00
Grzegorz MartensandClaude Opus 5.5 70d6815c6d M11: Persian for the PDF Optimizer's black-and-white image rows
Persian came to main after these rows were written, so fa.tsv had neither "Monochrome Images"
nor "CCITT Group 4". They go beside "Grayscale Images", worded like the Arabic and Kurdish rows,
with the Persian digit the catalogue uses elsewhere ("AES ۱۲۸-بیتی").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P6yUtvWGPsJpMLyh6pxSXG
2026-10-29 20:47:39 +01:00
Grzegorz MartensandClaude Opus 5.5 7add9b38e3 M11: the optimizer reads images through /DecodeParms given by reference
Stream::decoded reads only direct parameter dictionaries, so an image
whose /DecodeParms is a reference (whole, or as array entries) was
decoded without its predictor. For a 1-bit image with a PNG predictor
the rows came out shifted by their tag bytes, Group 4 of that was still
smaller, and the picture was replaced by a skewed one; resampling and
merging image strips read the same way.

pdfcraft_cos::Document::with_direct_parms gives the stream with the
parameters its filters use made direct: only the entries at the
filters' places, and none for a chain longer than any decoder takes,
so at most MAX_CHAIN objects are copied. The optimizer's pixels() and
the Group 4 step decode through it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BfMGHQNgdq5yf5tKZHGR3z
2026-10-29 20:47:39 +01:00
Grzegorz MartensandClaude Opus 5.5 faa582e7f7 M11: doc_optimize refuses an unknown key in mono
A misspelled key ({"mono": {"compresson": "retain"}}) left the preset's
setting in place unnoticed; it is now InvalidArgs (from CodeRabbit's
review).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BfMGHQNgdq5yf5tKZHGR3z
2026-10-29 20:47:39 +01:00
Grzegorz MartensandClaude Opus 5.5 0a5f8bce2d M11: black-and-white images: only DeviceGray counts as a one-component name; refuse a malformed mono
A bare /Indexed, /I or /CalGray is no colour space on its own, so it can
only name a resource, which might be RGB: such an image is now left
alone instead of being read as one bit per pixel. doc_optimize refuses a
mono that isn't an object or a compression that isn't a known name,
instead of reading it as the default (from CodeRabbit's review).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BfMGHQNgdq5yf5tKZHGR3z
2026-10-29 20:47:39 +01:00
Grzegorz MartensandClaude Opus 5.5 f175beb25a M11: PDF Optimizer stores black-and-white images as CCITT Group 4 when smaller
pdfcraft_filters::encode_g4 encodes packed 1-bit rows as Group 4 (ITU-T
T.6, /K -1) through the fax crate (MIT, already in the tree via tiff),
with rows capped at 32,767 samples so its 16-bit positions can't
overflow; round trips are checked against hayro-ccitt.

The optimizer (Settings::mono, CCITT G4 by default, so Reduce File Size
too) re-encodes a 1-bit image of one colour component, or an image
mask, stored with no filter or general-purpose ones, when that is
smaller as stored. The samples decode to the same bits, so colour
space, decode array and masks stay. PDF Optimizer gets a Monochrome
Images row and doc_optimize a mono option. optimize.mono-compression
goes from planned to partial (JBIG2 and monochrome downsampling not
yet).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BfMGHQNgdq5yf5tKZHGR3z
2026-10-29 20:47:39 +01:00
Dylan M. Taylor c735fb7da5 Make PdfCraft's app color red (#5245)
* Make PdfCraft's app colour red

The icon tile, the Windows PDF file icon's badge and lines, the DMG background and the README
badges move from green #12a58a to crimson #b70228 (darker shade #8a011e, fold #f3cfd6).
Every PNG, .ico and .icns is re-rendered from the edited master SVGs with packaging/icons.sh
and resvg 0.47; the .icns is written with Pillow because iconutil is macOS-only, with
iconutil's size set. The DMG background is re-rendered with generate.py; its .DS_Store is
unchanged. ATTRIBUTION.toml hashes updated; cargo xtask assets passes.

* Fix OCR model attribution hashes
2026-10-29 12:13:07 -04:00
aiongg 3109bb2359 Merge pull request #5236 from FredApps/integration/kenm76-2026-10-29
Integrate 13 KenM76 fixes for forms, text and rendering
2026-10-28 22:32:00 -04:00
FredApps d99538145c Clarify integrated vendor regression coverage 2026-10-29 02:43:29 +01:00
FredApps 97c062a14b Complete additive integration fields and test boundaries 2026-10-29 02:40:41 +01:00
FredApps f280f3a282 Integrate KenM76 PR #5228
# Conflicts:
#	vendor/README.md
#	vendor/hayro-interpret/src/device.rs
2026-10-29 02:39:15 +01:00
FredApps 7fc2033421 Integrate KenM76 PR #5199
# Conflicts:
#	crates/render/tests/it/main.rs
#	vendor/README.md
2026-10-29 02:38:56 +01:00
FredApps 1b0e3b6110 Integrate KenM76 PR #5178
# Conflicts:
#	vendor/README.md
2026-10-29 02:38:44 +01:00
FredApps e34cb1d1cc Integrate KenM76 PR #4991
# Conflicts:
#	crates/automation/tests/it/automation.rs
#	crates/forms/src/author/edit.rs
#	crates/forms/src/tests.rs
#	crates/forms/src/values.rs
#	parity/acrobat-features.toml
2026-10-29 02:38:25 +01:00
FredApps 07edd05aea Integrate KenM76 PR #4987
# Conflicts:
#	crates/forms/src/author/edit.rs
#	crates/forms/src/tests.rs
2026-10-29 02:38:03 +01:00
FredApps 9e30913d6e Integrate KenM76 PR #4977 2026-10-29 02:36:20 +01:00
FredApps d1fcc12e07 Integrate KenM76 PR #4943 2026-10-29 02:36:20 +01:00
FredApps ebd7578cc3 Integrate KenM76 PR #4913
# Conflicts:
#	crates/render/tests/it/main.rs
2026-10-29 02:36:20 +01:00
FredApps d9ddd8a8b0 Integrate KenM76 PR #4906 2026-10-29 02:35:58 +01:00
FredApps b0555cb918 Integrate KenM76 PR #4898
# Conflicts:
#	vendor/README.md
2026-10-29 02:35:58 +01:00
FredApps a66686cb0d Integrate KenM76 PR #4862 2026-10-29 02:34:57 +01:00
FredApps f716bd1462 Integrate KenM76 PR #4841
# Conflicts:
#	crates/automation/tests/it/main.rs
2026-10-29 02:34:57 +01:00
FredApps 2e6f7e5d3f Integrate KenM76 PR #4831 2026-10-29 02:34:41 +01:00
KenM76andClaude Opus 5.5 367f7d5432 fix(render): a pattern before an isolated CMYK group no longer moves it
From the review of #5228. The finished CMYK group was drawn through
whatever paint transform a preceding shading pattern left on the
context, so its cyan half showed magenta. Hayro patch (27) now resets
the paint transform before drawing the group's pixels.

Two regression tests, both reported in that review:
- a pattern painted before the group (fails without the reset);
- the Color blend mode over an empty CMYK group, swept over all 256 K
  levels (failed on nine levels before #5199's vello_cpu fix was merged
  in; passes with it).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
2026-10-28 20:40:16 -04:00
KenM76 e518e27778 Merge #5199 (fix/color-blend-gray-black) so the CMYK K ink pass blends gray with Color correctly 2026-10-28 20:37:34 -04:00
KenM76andClaude Opus 5.5 3aeafeb09e Merge origin/main into fix/cleared-default-stays-cleared
Resolves test-file conflicts where main and this branch both appended tests:
main's file plus this branch's appended tests, unchanged.
fmt, clippy -p pdfcraft-forms -p pdfcraft-automation, and both crates' tests pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
2026-10-28 19:18:37 -04:00