Review follow-up: an offset equal to the end of the info header is only
right when no bitfield masks or colour table come after it; otherwise the
pixels were read from the masks or the table. It is now repaired like an
offset inside the headers. Offsets strictly inside the masks or table are
still taken as given (they may come from an overstated biClrUsed).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
Review follow-up: the repair reads the 40-byte header's compression and
colour count, which a 16-byte OS/2 header does not have. New cases pin
a 12-byte core header (3-byte palette entries) and a biClrUsed of u32::MAX.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
LiveCycle Designer wrote BMPs whose file header gives bfOffBits = 0. image
(0.25) seeks to that offset as given, so the pixels were read from the file
header on and the picture was drawn shifted and wrapped around (#4937).
`pdfcraft_xfa::image::bmp_with_pixel_offset` recomputes the offset from the
info header (its size, BI_BITFIELDS masks in a 40-byte header, the palette)
when the declared one lies inside the headers and the computed one fits in
the file; anything else is passed through unchanged and not copied. XFA
pictures (size and decode) and `create::from_images` both go through it, so
pdfcraft-create gains a dependency on pdfcraft-xfa (L4 -> L3).
Fixes#4937
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
kurbo 0.13.1's stroker can give a cubic that is nearly a line (all control points within a
rounding error of its chord) a NaN join, and vello_common's `flatten::fill` drops any path
containing NaN, so the whole outline vanished. An Illustrator outline ending in
`73.763 -0.536 l 73.764 -0.536 73.764 -0.536 73.764 -0.536 c` did (#4907). kurbo tracks the
bug class in linebender/kurbo#617 and #519; #610 is unreleased.
Vendored vello_common patch (2): when the expanded stroke is not finite, `flatten::stroke`
strokes the path again with every quadratic or cubic whose control points lie within the
tolerance of its chord replaced by a line, one element out per element in. A finite outline
is unchanged, and a path with non-finite input is still dropped. `stroke` and
`StripGenerator::generate_stroked_path` take a `Clone` path; vello_cpu's callers pass
`&BezPath` and a copied slice iterator.
Tests: `flatten::tests::a_tiny_degenerate_cubic_does_not_drop_the_stroke` and the render
crate's `stroke_degenerate_cubic` pixel test fail with the fallback disabled;
`a_finite_outline_is_stroked_as_before` and `a_path_with_nan_is_still_dropped` are guards.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
An auto-size single-line field's size follows its height, up to about 12,500 pt for a
14,400 pt field. The editor measured the line's width by laying it out at that size,
before `field_screen_font` clamps to `MAX_SCREEN_FONT`, and egui rasterizes every glyph it
lays out: one such measurement took 1.2 s in a debug build, on every frame. The width is
now measured at no more than `MAX_SCREEN_FONT` and scaled, which is exact up to the bound
and linear above it.
`tools::forms_ui::tests::a_line_is_never_laid_out_above_the_screen_font_bound` fails with
the bound removed (laid out at 12,522 pt).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
Inside a text object q/Q save and restore Tm and Tlm too (ISO 32000-2
§9.4.1, as corrected by the PDF Association's erratum,
pdf-association/pdf-issues #368). The renderer's saved state already
holds both. The text-editing interpreter and the heading detector kept
them across Q: text after Q was listed (and, once edited, moved) where
the q ... Q block left the pen, and a word continued after Q was split
("Chap ter One"). Both saved stacks now hold the two matrices.
Tests: pdfcraft-edit and pdfcraft-a11y
tests::q_inside_a_text_object_restores_the_text_matrices. Reverting
the edit fix puts the first case at y = 600, not 700; restoring only Tm
puts the T* case at 586, not 686; reverting the a11y fix reads
"Chap ter One". Each fails its test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
Keep the print module split and reapply the /Contents space join in
imposition::decoded_contents, matching the renderer and the organize pruner.
Co-authored-by: jonathanwxh-cell <jonathanwxh-cell@users.noreply.github.com>
main deleted crates/engine/src/reexports.rs and moved its re-exports
into crates/engine/src/lib.rs; the comb_auto_text_size re-export this
branch added now sits in lib.rs. forms_ui.rs moved to
crates/ui-egui/src/tools/; the branch's hunks carried over unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqmHYx8FiWM5PcsoiErgp9
Two content conflicts, both from main splitting the files this branch edits.
crates/cos/src/document.rs: main moved Document into document/*.rs.
Keep that split. A missing /ID is still pdfcraft_crypt::file_id of the
encryption seed (document/open.rs); generated_id is gone. The regression
lives in document/tests.rs.
crates/engine/src/edit.rs: main moved EditCtx and run_edit into
edit/run.rs. Keep that split. Protect still draws its seed and default
owner password from getrandom on EditCtx and fails closed
(EditError::Protection) when the CSPRNG errors or returns all zeros.
Co-authored-by: jonathanwxh-cell <jonathanwxh-cell@users.noreply.github.com>
In Edit text & images a click inside a paragraph opens it for typing, and selecting the whole
box meant aiming for its thin frame: a press a few pixels inside put the caret in the text
instead (#2203). Esc in the inline editor now leaves the text and selects the paragraph's box,
as Acrobat does, so Esc then Delete removes it, the arrow keys nudge it and its frame drags it.
Esc still discards an unapplied draft.
The editor asks `ObjectSelection` to select the paragraph, and the next `prepare`, which has the
document's inventory, selects it after its own Escape check, so the Esc that closed the editor
cannot clear the new selection.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The only content conflict was crates/engine/src/lib.rs: main moved
EditCtx and run_edit into edit.rs, while this branch taught Protect
to draw keys and salts from getrandom and fail closed. Keep main's
module split and apply that CSPRNG behaviour on EditCtx in edit.rs.
The regression test imports EditCtx and run_edit from the edit module.
Co-authored-by: jonathanwxh-cell <jonathanwxh-cell@users.noreply.github.com>