Commit Graph
1125 Commits
Author SHA1 Message Date
hcchung1 db1102185a docs: record how Acrobat prints a page area that is not relevant for print (#2574)
Observed in Acrobat Pro on the public ISCIII form: the once-only page keeps
its place when printing (7 pages with nothing typed) but is printed blank.
PdfCraft draws its content, and has no relevant attribute or print-off
layer yet; the note says so.
2026-10-17 16:01:39 +08:00
magdale76andClaude Opus 5.5 428cbc083d M10: doc_print takes duplex "printer", leaving two-sided to the printer (#2422)
The headless counterpart of the Print dialog's Printer default: duplex
"printer" sends no two-sided setting, so the printer's own default applies.
The default stays "off", so existing scripts behave as before. Cut and stack
with "printer" sends single-sided sheets, as the dialog does, and still
refuses an explicit edge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 06:42:10 +02:00
magdale76andClaude Opus 5.5 3fbfa4e2af M10: Two-sided starts at Printer default, so the printer's own duplex applies (#2422)
The Print dialog always sent its Two-sided choice, Off unless changed, so a
two-sided default set in the printer's Properties never applied. Acrobat has no
default of its own here and follows the printer's. Two-sided now starts at
Printer default, which sends no duplex setting (craft-print's Job::duplex =
None). Off and the two edges still override it. Cut and stack still sends Off,
since it needs single-sided sheets.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 06:42:10 +02:00
hcchung1 c5708e1cc6 fix(xfa): a page area's occur min gets its pages after the body (#2574)
A page area with <occur min="1" max="1"> that the body never reached was
never laid out, so the public ISCIII form lost the page that holds its
finishing instructions and the TERMINAR MEMORIA button once the repeated
copies over the body pages were removed.

PageArea records occur min (0 without an <occur>). When the body has been
laid out, an area used fewer times than its minimum gets the pages it still
needs, in template order, after the body's pages. The form is 7 pages with
the once-only page last, which is what Acrobat Pro shows.
2026-10-17 11:58:50 +08:00
skylar taylor-barrick 924c4da4ac Merge pull request #2698 from mtlcl410/fix/audit-one-word-2662
M8: the redaction audit finds a cover over one word of a long line (#2662)
2026-10-16 16:05:39 -06:00
enes yıldızandClaude Opus 5.5 06c4a970a2 fix(cli): info, text, render and audit take the owner password of R2–R4 files
`pdfcraft-cli info`, `text`, `render` and `audit` rejected the owner
password of RC4-40, RC4-128 and AES-128 files with "the password is
incorrect", while `run doc_open` accepted it (#2670, item 8). The renderer
authenticates only the user password of revisions 2–4. `Session::open`
works around that by handing it the user password that owner
authentication recovers, but the CLI passed the typed password straight to
`inspect` and `PageRenderer`.

`pdfcraft_engine::render_password_for` now gives any render path outside a
session the password the renderer takes, sharing the recovery with
`Session::open`. The CLI uses it for the renderer and keeps the typed
password for the engine session and the cos document, so the owner
password still lifts copy restrictions in `text`.

The render test decodes the PNG and checks its dimensions and that it
has non-white pixels, for every algorithm with either password.

Refs #2670 (item 8 only).

Checks: cargo fmt --check; cargo clippy -p pdfcraft-cli -p pdfcraft-engine
--all-targets -- -D warnings; cargo test -p pdfcraft-cli; cargo test -p
pdfcraft-engine (and --lib); cargo xtask layers, assets, parity.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 00:15:22 +03:00
mtlcl410andClaude Opus 5.5 4a35a1cb23 M9: rewriting a certified form's XFA data is a form fill (#2759)
The IRCC IMM forms are certified (DocMDP P=2) and then saved once more
with Reader Extensions: a later revision adds the UR3 usage-rights
signature and rewrites the XFA `datasets` packet. The diff classified any
rewritten stream that wasn't page content or the catalog's XMP as "other
changes", so these untouched forms validated as invalid.

A stream the signed revision's /AcroForm /XFA array names `datasets` or
`form` (the form's data and state) is now a form fill when rewritten,
permitted at P=2 and P=3 and refused at P=1 as before. The roles come from
the signed revision, so a later revision that names the template
"datasets" doesn't make rewriting it a form fill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RUNQSPVXx3dDBabRZHAnjz
2026-10-17 01:21:28 +08:00
Harrison BoothandClaude Sonnet 5.5 f976adeb17 M3: the "Save changes?" prompt opens on Save and moves between buttons by keyboard
Save is chosen when the prompt opens, so Enter saves as before. The arrow keys
and Tab / Shift+Tab move the choice between Don't save, Cancel and Save, shown
with a focus ring; Enter or Space press the chosen button, Escape cancels.
Before, Enter always saved, even with another button reached by Tab.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-16 11:20:11 -05:00
Combiler01 9e78afa9c2 merge: reconcile upstream/main with 8-handle image interaction and arrange fixes 2026-10-16 21:44:43 +05:30
Combiler01 035183777a fix:implement 8 point non destructive crop, image resize and Arrange extraction bugs 2026-10-16 21:24:49 +05:30
Guillem Hermida fbd9911d76 fix(forms): renaming a nested field checks the full name it would take
Renaming a field changes only its own /T, so MASTER.a renamed to b
becomes MASTER.b. set_props compared the bare new partial name with the
other fields' fully qualified names: renaming MASTER.a to b succeeded
while MASTER.b existed, leaving two fields with one fully qualified
name, which are one field with a shared value (ISO 32000-2 §12.7.4.2);
and renaming MASTER.a to c was refused because of an unrelated
top-level field c.

The check now compares the new full name, leaving the field itself out
so a kid keeping its own partial name still succeeds, and the error
names that full name. Top-level renames are checked as before.
2026-10-16 17:48:47 +02:00
Guillem Hermida 359a3f2f09 M11: Optimize keeps a JPEG's colour transform (#1201)
Optimize decoded DCT images with the generic JPEG decoder, which converts
three components from YCbCr to RGB whenever the JPEG itself doesn't say
otherwise, and then dropped /DecodeParms. An RGB JPEG stored untransformed
(/ColorTransform 0 and no Adobe marker) therefore came out in other colours
when recompressed, resampled or merged from strips.

The samples are now read with the colour transform ISO 32000-2 §7.4.8
gives them: an Adobe APP14 marker's flag, else /ColorTransform (default 1).
Untransformed samples are taken as stored, and the result is written as a
plain YCbCr JPEG (or Flate), which needs no /DecodeParms. When the marker
and /ColorTransform disagree, a value is out of range, the parameters are
not paired with the filter as §7.4 pairs them, or the decoder reads the
components otherwise (named R, G and B), the image is left as it is.

The tests draw each image with PdfCraft's renderer before and after
Optimize: RGB JPEGs with and without an Adobe marker under each
/ColorTransform, CMYK JPEGs (still left alone) and JPEG strips merged into
one picture.

Part of #1201.
2026-10-16 17:28:19 +02:00
FredApps 8b5ecf8b04 fix #2444: cancel typed font-size edits on Escape and refresh main
Preserve already-merged upstream work and add the focus-loss/Escape regression. Exact candidate tree qualified with workspace tests, 31 WASM checks and a separate successful dependency audit.
2026-10-16 16:54:31 +02:00
aiongg a18aa1414c Integration: #2793 parity notes say malformed Decode keeps the default mapping 2026-10-16 14:24:04 +00:00
aiongg 934a10740d Merge track r1-core (fredapps-r1)
ROADMAP.md, docs/roadmap.md: keep next's status line
2026-10-16 14:17:52 +00:00
aiongg 7a52541347 Merge track r1-forms (fredapps-r1)
ROADMAP.md, docs/roadmap.md: keep both sides' progress-log lines and revision rows, once each
2026-10-16 14:12:24 +00:00
Guillem Hermida 021812b6ee fix(ui): the password prompt takes typing on Linux again (#2818)
The prompt's field requested focus on every frame. In egui 0.36 each
request also interrupts the input method, and egui-winit answers with
set_ime_allowed(false) then (true), which recreates the X11 input
context (or re-enables Wayland text-input) every frame. On Linux typed
keys go through the input method, so nothing typed reached the field.

Request focus only when the field doesn't have it, as the command
search does (#1638). The permissions password prompt shares the box.

Tests type through synthetic IME pre-edits and commits after opening
an encrypted file from the command line, File > Open, the start page
and a drop, and into the permissions prompt; both fail without the fix.
2026-10-16 16:08:17 +02:00
Guillem Hermida 27a2ac47cf fix(ui): the header, footer and watermark previews show the page under the marks (#2837)
The Header and Footer, Watermark and Background dialogs drew their
preview on a blank sheet, so you could not see where the marks would
land on the page's text and pictures. The preview now paints the page's
own thumbnail, rendered off the UI thread like the pages panel's, with a
blank sheet until it arrives. It shows the current page when the page
range selects it, otherwise the first page the range selects, at its
displayed size (after /Rotate), which is the frame the engine lays the
marks out in. A background colour tints the page picture instead of
covering it, as a background behind the content looks.

Fixes #2837.
2026-10-16 16:04:41 +02:00
aiongg d8a4a055b9 Merge PR #2604: fix(xfa): a page area without <occur> has no limit 2026-10-16 14:04:02 +00:00
aiongg bf240b612c Integration: #2688 drop the formula-cell and TSV-export sentences from the xfdf README and form.data-xml-csv-txt notes 2026-10-16 14:04:02 +00:00
aiongg 24b5060c1f Merge PR #2688: M6: distinguish CSV header tabs from TSV delimiters
ROADMAP.md: next's file; the PR's progress line and revision row added once; status-line edit dropped.
docs/roadmap.md: next's file; the PR's revision row and M6 milestone sentence added once; status-line edit dropped.
crates/xfdf/README.md: all three paragraphs kept (#1917, #2686, this PR).
crates/xfdf/src/tests.rs: #2686's test and this PR's two tests all kept.
parity/acrobat-features.toml: form.data-xml-csv-txt keeps every evidence entry and note.
2026-10-16 14:03:59 +00:00
aiongg e56dede417 Merge PR #1919: M4: respect indirect page rotations when reading and editing
ROADMAP.md: next's file plus this PR's 2026-10-13 progress line and revision row; the #1919 refresh line and the status line repeating an M13 item dropped
docs/roadmap.md: next's file plus this PR's revision row; its refresh status line and the refresh note in the M4 row dropped
2026-10-16 14:03:45 +00:00
aiongg 834495382d Merge PR #2686: M6: preserve complete plain XML form values
ROADMAP.md: next's file; the PR's progress line and revision row added once; status-line edit dropped.
docs/roadmap.md: next's file; the PR's revision row and M6 milestone sentence added once; status-line edit dropped.
crates/xfdf/README.md: both paragraphs kept (#1917 export whitespace, this PR's plain XML import text).
parity/acrobat-features.toml: form.data-xml-csv-txt keeps both evidence entries and both notes.
2026-10-16 14:03:31 +00:00
aiongg 70abceba0d Merge PR #2267: fix(filters): preserve output-limit errors in raw Flate fallback
ROADMAP.md: next's revision history plus this PR's row; the #2267 refresh progress line dropped
docs/roadmap.md: next's revision history plus this PR's row; refresh wording dropped from the status line and the M1 row
2026-10-16 14:03:27 +00:00
aiongg 3fd58fc558 Merge PR #1917: M6: preserve whitespace in XML and XFDF attributes
ROADMAP.md: next's file; the PR's progress line and revision row added once; refresh line and status-line edits dropped.
docs/roadmap.md: next's file; the PR's revision row and M6 milestone sentence added once; refresh note and status-line edit dropped.
2026-10-16 14:02:53 +00:00
aiongg 7d69b6ebc7 Merge PR #2580: Encryption: /Perms limits what an edited /P allows in AES-256 files 2026-10-16 14:02:36 +00:00
aiongg 1a773e8098 Merge PR #2717: M1: read an xref table numbered from 1 that begins with object 0 (#2676)
crates/cos/src/document.rs: union with #2525: header_relative revisions, the renumbering log line, and log passed to read_section on the first try and the header-relative retry
2026-10-16 14:02:26 +00:00
aiongg 9477e2a3fd Merge PR #2793: M10: preserve component Decode in lossless image export 2026-10-16 14:02:08 +00:00
aiongg fae7f4451c Merge PR #2532: fix(redact): removed links and comments leave the structure tree too (#2447) 2026-10-16 14:02:08 +00:00
aiongg c2a634ec4c Merge PR #2816: M10: running text is not a table row in Word, HTML and PowerPoint export (#2806) 2026-10-16 14:02:08 +00:00
aiongg a17794f170 Merge PR #2698: M8: the redaction audit finds a cover over one word of a long line (#2662) 2026-10-16 14:02:08 +00:00
Guillem Hermida cdb1010f40 fix(edit): a paragraph's width handle rewraps it from its whole handle, not just the inner half (#2276)
In Edit text & images, a paragraph's width handle is drawn centred on its
box's edge, and the edge grabs from up to 6 px outside the box. Object
selection sees a press first, though, and handed it to the paragraph tool only
on the box itself. A press on the outer half of the handle started an area
selection instead: its rectangle followed the pointer like a resized frame,
and on release the paragraph kept its old width and the text never moved.

Object selection now also leaves a press on a closed paragraph's edge grip to
the paragraph tool, through edit_text_ui::grabs_edge. That uses the same edge
test, box and upright-page rule as the tool itself, so the two can't drift
apart. The rewrap is the existing EditTextBlock with a width, so undo, redo
and save are unchanged.

Fixes #2276
2026-10-16 15:59:12 +02:00
mtlcl410andClaude Opus 5.5 942be18504 parity: form.fill-sign-date has 14 presets now
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RUNQSPVXx3dDBabRZHAnjz
2026-10-16 19:20:48 +08:00
boneylandandClaude Opus 5.5 bf2eb470ed M6: detected form fields get no border or fill
Prepare a form ▸ Detect form fields places each field on a line or box the
page already draws, but every new widget got a grey /MK /BC and an opaque
white /MK /BG, so the fill hid the printed form underneath. Detected fields
now clear both in the same undoable step; fields drawn by hand keep the
defaults.

Refs #294, #2831.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 18:59:09 +08:00
mtlcl410andClaude Opus 5.5 f9db1e31fd M10: running text is not a table row in Word, HTML and PowerPoint export (#2806)
Table detection took a sentence whose link was set in another colour
and font, split into blocks that run on into each other, for two table
columns, and took a paragraph's lines, whose box overlaps the line
before, for a cell beside it. The table it built put the paragraph's
first line in one cell, a fragment of the link in another, and dropped
the paragraph's later lines. A block that starts where another on the
same line ends now adds no column of its own, and a row whose cells
overlap is not a table row; a superscript beside a number stays in its
table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RUNQSPVXx3dDBabRZHAnjz
2026-10-16 16:47:49 +08:00
alehelandClaude Opus 5.5 a9b0db12c6 fix(edit): show backgrounds on pages that paint their own white backdrop
Add Background draws behind the page content, so on a page that first
fills itself with opaque white (browsers' Save as PDF, print drivers) the
background was hidden completely and the tool looked broken.

- pdfcraft-edit: new `backdrop` module. A strictly detected backdrop (one
  axis-aligned `re`, opaque white in a device colour space, painted before
  anything else and covering the crop box) is replaced by turning that one
  fill into `n` in a per-page copy of its stream; the original stream is
  left untouched for other pages. Remove background restores it, unless
  the copy was edited since. Page-sized pictures (scans) are reported only.
- Background gains `replace_backdrop` (default on); the default colour is
  a visible light yellow instead of near-white.
- Dialog: offers to replace the backdrop, warns about scanned pages,
  checks pages a few per frame, preview draws text over the colour.
- Automation: `doc_background` takes `replace_backdrop`; new read-only
  `doc_backdrops` lists pages with a backdrop.

Acrobat's behaviour on such pages is unobserved; recorded as our choice
in parity/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 10:46:02 +02:00
Enrico SitaandClaude Opus 5.5 26051871d0 M11: remove PDF/A or PDF/X identification (optimize.remove-standards-id)
A file edited after it was made PDF/A or PDF/X can keep claiming a
standard it no longer meets. organize::remove_standard_id removes the
claim: the pdfaid: XMP properties for PDF/A, or GTS_PDFXVersion and
GTS_PDFXConformance in the document information and as pdfxid: XMP
properties for PDF/X. Output intents and other claims (PDF/UA) stay.

The XMP is edited with the packet editor Document Properties already
uses (sync_info and the new remove_properties share its read and write),
so the rest of the packet stays byte for byte and unsafe packets are left
alone. It is one undoable edit, in Standards ▸ PDF/A as a button worded
like its undo label, and the standards_remove_id tool (pdfa or pdfx).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 09:45:31 +02:00
Ahmad Mujahid 9409265c35 M10: apply component Decode during lossless image export 2026-10-16 11:06:55 +04:00
PerchAnton f79ab9ae2d M12: update viewer-warning PR against main (#2589) 2026-10-16 07:48:00 +03:00
PerchAnton d78df1f163 M12: update growable-field PR against main (#2569) 2026-10-16 07:44:37 +03:00
PerchAnton 4d0f2e4dde M12: update print-button PR against main (#2572) 2026-10-16 07:41:57 +03:00
PerchAnton 39e2481d7d M12: update rich-text limit PR against main (#2570) 2026-10-16 07:37:07 +03:00
hcchung1andClaude Sonnet 5.5 a51380e869 docs: record the XFA page area occur fix in parity and the ROADMAP log (#2574)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-16 10:46:39 +08:00
FredApps f4ff7fe433 Refresh Pages sidebar shortcut after main updates (#2052) 2026-10-16 04:07:56 +02:00
Osvaldo DuranandClaude Opus 5.5 e5c2ea2bff Merge upstream main 5fdd820b into pr/attachments
Conflicts: ATTRIBUTION.toml, crates/ui-egui/src/i18n/es.tsv, crates/ui-egui/src/i18n/vi.tsv. Catalogs keep upstream's rows, then this branch's rows upstream lacks; ATTRIBUTION.toml is upstream's with every sha256 recomputed; progress lines keep both sides, upstream's first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 21:31:50 -04:00
Chiranjeevi Uandskylar taylor-barrick 5fdd820bdb fix(ui): validated signature display, snapshot tab reuse (#1895)
* fix(ui): validated signature display, snapshot tab reuse

Signature widgets with layered appearances baked the unverified state
into the page, so a valid signature kept reading 'Signature Not Verified'
while the banner and panel said valid. Validated verdicts are now drawn
into the icon and caption layers in the display copy only: the signed
bytes are untouched, saving keeps the original appearance, and files
without those layers or without a valid verdict render exactly as before.

Other valid signatures still get the verdict mark over their widget,
sharing one style with the Signatures panel, and hide it again once the
appearance itself shows the verdict.

View signed version and View revision opened a new tab on every click,
and every tab showed the document title, so repeats piled up identical
tabs. Repeats now reuse the snapshot tab, snapshot tabs read like their
source plus the suffix in the tab strip, window title and Combine picker,
and carry a notice bar naming the revision with a Close that asks about
saving first.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* Address review comments on validated appearances and trust refresh

Count every field when checking shared appearance streams, so a
caption shared with an unvalidated field is never rewritten for the
valid one alone.

Pick the icon slot by drawing instead of length, and only when a
single layer qualifies. Bare rectangles never count as the icon, and
ambiguous or missing slots fall back to the overlay badge.

Revalidate and invalidate together through one helper, so trusting a
certificate always drops rendered pages. Trust alone still marks
nothing dirty.

Tests: mixed-verdict sharing, longer decorative borders, competing
and doubled layers, capture-before preservation, trust invalidation
without zooming, and signed Session round trips with rendering,
saving and passwords.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* Prove the icon slot geometrically and cover real layered signatures

A lone line drawing is still not the icon: frames and borders span the
box while the status mark lives in a corner of it. The icon now has to
sit strictly inside the appearance frame and cover at most half of it;
anything else falls back to the overlay badge. Widgets without a
measurable frame veto the rewrite the same way.

Tests: parsed extents and inhabitant checks as units, both reported
border repros plus a frameless widget, and real layered signatures
built with cms::sign_detached through Session open, trust, rendering,
save and reopen, plain and password-protected.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* Identify the icon by shape instead of geometry

A lone line drawing is still not the icon, but size and position were
the wrong signals: the real status mark is large, and geometry blocked
it. Frames, borders and panels are rectangles by nature while the mark
is not, so a drawing made only of axis-aligned rectangles is decoration
however it is stroked. Singularity, shared-stream and template checks
are unchanged; anything ambiguous still falls back to the overlay badge.

Tests: rectangularity as a unit, nested line-drawn frames, and a
triangular fixture mark in place of the rectangular one.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* Budget the appearance walk and keep the marks with the bytes

The walk that finds a signature's icon and caption capped depth,
layers and fields, but not the work: a dictionary whose entries all
point at the next one is visited once per entry per level, so a small
hostile file never finishes opening. The walk now counts its visits
and gives up, and a field whose tree cannot be read to the end stops
the whole pass, because a layer we never read may be the one two
fields share and a rewritten shared layer puts one field's verdict on
another field's page. The sharing count is exact again with that: two
widgets of one field pointing at the same appearance claim it once,
or the field vetoes its own rewrite.

A mark used to outlive the bytes it described. A failed display write
left the page showing the file's own caption while the verdict still
claimed its state was drawn; a metadata edit dropped the mark while
the page kept drawing the state, so the badge landed on top of it. The
write fallback now drops the marks and logs the loss, and metadata
carries them over.

Tests: a self-referential appearance tree, which never finished before
the budget, two widgets sharing one appearance, a metadata edit that
keeps its mark, a save that goes through the writer instead of
short-circuiting on the opened file, and the snapshot bar's Close
button pressed for real.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* Cite the new regressions in parity, restore main's installer note

The appearance entry now cites the bounded walk and the save through
the writer, and says what a tree that cannot be read to the end does.
The installer entry carried main's older text, which still claims
signed installers are planned; main's newer note is taken verbatim.

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>

* M9: preserve rounded decoration beside raster status glyphs

* M9: place appearance progress beside signature history

* M9: retain appearances with ambiguous curved layers

* M9: keep signature verdicts in viewer overlays

Defer generic appearance rewriting: caption text and a unique drawing do not establish an icon role. Preserve original artwork for saves, image and text exports, and both print modes while retaining snapshot reuse and trust refresh. Cover chamfered borders and mixed signer/caption streams through signed Session round trips.

---------

Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
Co-authored-by: skylar taylor-barrick <hi@skylar.cc>
2026-10-15 19:16:48 -06:00
Krzysztof Cieślik ad9e5d2ba9 fix(edit): keep a one-row centred title centred when it grows (#2581)
- Detect a one-row centred title through a new `is_centred` helper in
  `text_regions.rs`, shared by both rewrite paths, instead of the multi-row
  ragged-centres rule that can never fire on a single row.
- A lone row counts as centred when its centre sits within one font size of
  the page centre and its left edge is not the page's dominant left edge, so a
  full-width left-aligned line at the margin is not mistaken for a title.
- In the single-style path, a centred block with no explicit width or
  alignment no longer has its width stretched to the page margin; the line is
  centred in the box it already sits in, so a grown row keeps its centre and
  every wrapped row shares it.
- Gate the new rule behind main's `upright` flag: `is_centred` measures
  page-space rects, which are not meaningful across a turned baseline; frame
  coordinates (#1443) are the tool for that, not this change.
- Add `a_one_row_title_keeps_its_centre_when_it_grows_and_when_it_wraps`,
  built from the issue's fixture; red before the fix (centre 398.025 vs
  expected 306), green after.
2026-10-16 02:49:20 +02:00
mtlcl410andClaude Opus 5.5 271cb6161f M1: read an xref table numbered from 1 that begins with object 0 (#2676)
Some producers head a classic xref table "1 n" and then write object 0's
free entry first, so every row is one number off and object 1 reads as
free. The renderer coped, but cos did not: printing a scanned invoice
built from cos dropped object 1 (page 1's image) and printed the page
blank. Such a subsection is now read from 0 when its second row locates
an object that is numbered 1, in both xref readers, and the repair is
logged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RUNQSPVXx3dDBabRZHAnjz
2026-10-16 08:35:35 +08:00
FredApps 44d795d9f8 Refresh Pages sidebar select-all against main (#2052) 2026-10-16 02:16:58 +02:00
Krzysztof Cieślik a3cbb85536 M7: let a list marker open an item and a hanging indent continue it (#2564) 2026-10-16 01:30:11 +02:00