Commit Graph
16 Commits
Author SHA1 Message Date
mmazzieriandClaude Opus 5.5 75fc50fb77 DOCX: picture brightness, contrast and transparency (VML washout, a:lum, a:alphaModFix) (#1961)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 21:47:44 +01:00
FredApps 8218dea5bd Fix #5559 follow-ups: generated list prefix, hidden-text objects, turned clipped lines, kept-markup checks once per export
- update_generated keeps what precedes a wrapped list's field (r4468529243)
- from_doc keeps objects in hidden text when hidden text is written (r4469018820)
- clipped cell lines in a turned frame are drawn once, clipped, turned (r4469018900)
- kept::while_writing works out reply map and page fingerprint once per export (r4468529227)
- odt_charts fixture declares its namespaces (r4468529259)
- guard test for character styles based on a missing parent (r4469018843, not a bug)
2026-11-04 17:43:46 +01:00
FredApps d2f504e29a Review fixes: package-wide PBKDF2 budget, CLI keeps the ODF style, turned linked boxes, path close, centred PDF fill, doc status (gap 19) 2026-11-04 12:50:36 +01:00
alehelandClaude Opus 5.5 56c85834f1 Merge origin/main into the ODT branch (gap 19)
Conflicts kept both sides. Where the two branches met:
- Para.mark: both added it. It stays CharProps (ODT needs it); HTML's
  `<p>&nbsp;</p>` converts its Fmt, and to_doc reads it in one place.
- Arrowheads: the PR's line ends (top left to bottom right) with main's
  arrowhead styles.
- inline_rect: the PR's depth below the baseline with main's rule room.
- Spacing and Cell literals take main's new fields.
- The zip entry guard (#3660) also covers the encrypted ODT paths, which
  parsed a package before it; regression test in tests_more/encryption.rs.
- chunks_exact to as_chunks in odt/frames.rs and odt/path.rs (clippy).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:51:35 +01:00
41d46625e0 DOCX: VML horizontal rules take their line (#3956)
* DOCX: VML horizontal rules take their line

A web page's <hr> pasted into Word is a VML rule: `v:rect` with
`o:hr="t"`, often `width:0`. WordCraft took the width as written and
drew a 1 pt dot where Word draws a line across the page.

Measured on Word with a synthetic document: a rule with no width and
no `o:hrpct` spans the line inside the paragraph's indents; `o:hrpct`
is a share of it in tenths of a percent (500 = half); `o:hrpct="0"`
keeps the shape's own width; `o:hralign` places a shorter one (left,
centre, right); `fillcolor` and `stroked` apply as to any VML shape.
WordCraft now matches each case within 0.1 pt.

- wordcraft-doc: ShapeExtra::hr (HorizontalRule: share, alignment,
  Word's standard look).
- wordcraft-docx: `o:hr`, `o:hrpct`, `o:hralign`, `o:hrstd` read; a rule
  is written back as the same VML rule (DrawingML has none), and Word
  opens the re-saved file with the same lines.
- wordcraft-layout: a rule takes its whole line and is drawn at its
  share; measured for a table column it asks for no width, so it
  follows its column. (Word draws a rule in a cell at the page's text
  width, centred on the cell and past its edges; it stays in the cell
  here.)

docs/file-format-parity.md is updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Horizontal rules: only an inline, shown rule is one

Review: the rule arm of the writer ignored the shape's placement, so a
rule imported with position:absolute or visibility:hidden came back
inline and visible. Only a rule in the text and shown is read as a rule
now (a pasted <hr> is one); a positioned or hidden one stays a plain
shape, which keeps both when saved. The writer checks the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Horizontal rules: a see-through fill is kept; column measuring asks for the right width

Review: `write_hr` wrote only the fill's colour, so a rule with `v:fill
opacity` came back opaque after a save; the opacity now goes back as `v:fill`.

Review: measured for a table column, every rule asked for no width, so one of
its own width (`o:hrpct="0"`) could end up clipped by a narrow column; it now
asks for its width. AutoFit to contents measures on a line of its own width
that the rule took for a real line, so a rule sharing its line made the
column as wide as it could go; any measuring width now counts.

`Placed::Shape` boxes its extras: with the rule, `Placed` went past clippy's
large_enum_variant limit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Horizontal rules: placed in what the line has left; rotation and flips kept on save

Review: a whole-line rule ignores a first-line indent or a line narrowed by a
float. Measured in Word, it does too: the width stays the share of the
paragraph's width and the rule runs past the margin, or under a float on its
right. What did differ was the placement: Word centres and right-aligns a rule
in what the line has left, with a first-line indent moving the end of that room
along, and a right-aligned rule beside a float was drawn off the page here. The
display now places it that way (`ParaLayout::rule_room`).

Review: a save dropped a rule's rotation and flips; they now go back in its
style, as Word writes them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: VML horizontal-rule revision row at the top of the history

---------

Co-authored-by: Daniele Mori <Dinasty@hotmail.it>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-11-02 10:55:20 -05:00
Fin WarmanandBrandon Thomas 4f2d0da04c Shapes: align soft-edge paint and caret opacity (#1600)
* Shapes: a solid fill keeps its transparency

A shape's fill colour can carry a:alpha, its opacity. WordCraft dropped
it, so a translucent white wash laid over a picture was drawn opaque and
hid the picture. The transparency is now read into the shape, drawn on
screen and in the PDF, and written back.

* Fix #1600 freeform and soft-edge fill opacity

* #1600: wrap canonical caret sampler signature

* #1600: format canonical integration

* #1600: assert canonical DOCX alpha round-trip

* #1600: document canonical shape opacity API

* Adapt alpha fixture to rectangle render bounds

* docs: one file-format-parity row for soft-edge fill opacity, status line

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-11-02 10:53:00 -05:00
Claude 078945e10c Emphasis marks: modelled, drawn, set, and kept in DOCX and ODT
Word's Font › Emphasis mark (a mark over or under each character, used
to stress East Asian text) wasn't modelled. DOCX `w:em` and ODT
`style:text-emphasize` were dropped.
- Model: `CharProps::emphasis` (dot, comma, circle, dot below, or none
  to turn off a style's).
- Layout: a mark is drawn over (or under) each character, and the line
  is made taller to fit it.
- `format.emphasisMark {"mark": …}` sets it, and Format state reports
  it. The catalog lists it under Home › Font.
- DOCX: `w:em` is read, and written in its place in `w:rPr` (after
  `w:cs`, before `w:lang`).
- ODT: `style:text-emphasize` maps to it: an accent is the comma, a
  disc the dot, a dot below the under-dot.

Also fixes two problems from the previous commit (ODT variables):
- Field updates blanked a SET field's stored result. The layout
  already draws nothing for SET, so its result stays as it was, as
  `ask_and_set_fields_draw_nothing` pins.
- A clippy lint (manual char comparison) failed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBVQLyDnrG8Tn1yDQcUabC
2026-11-02 09:46:23 +00:00
8a191fbf11 Shapes: arrowheads keep their kind and size and are drawn at Word's sizes (#5192)
Word draws an arrowhead 2, 3 or 5 times the line's width long and wide
(small, medium, large; `a:headEnd`/`a:tailEnd` `len`/`w`), a line under
2 points as a 2-point one. Every arrowhead was drawn one size and saved
as a medium triangle: the kind, width and length are now read (DrawingML
and VML), kept and written back, and the size is drawn as Word does.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-11-01 14:46:16 -05:00
6ab1023ed6 Layout: tab leader dots spaced and placed as in Word (#5144)
* Layout: tab leader dots spaced and placed as in Word

Measured in Word for Windows with six fonts at four sizes: Word spaces leader
dots by the dot's advance at the font size in whole 600-dpi pixels (the size
itself in whole pixels), so a TH SarabunPSK dot at 16 pt (2.59 pt) is drawn
every 2.64 pt and a Tahoma one at 22 pt every 6.6 pt. The dots sit on the
page's grid of that pitch, from the first place after the text before the
tab to the last one that ends before the text after it. WordCraft drew them
at the unrounded advance, half a dot after the text and 1.2 dots short of
the number, so a table of contents line had a dot or two more than Word's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Layout: each leader dot's place comes from its grid index

The leader's dots were placed by adding the pitch to the last place, so on
a long leader the rounding of each addition added up. From 72 to 528 pt at
2.64 pt it came to more than the 0.001 pt allowed at the end, and the last
dot, which ends exactly on 528 pt, was left out. `leader_xs` now works out
each place as its grid index times the pitch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Layout: name the leader slack and glyph limit

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Layout: leader dots use the font size rounded to whole pixels

Word rounds the font size to the nearest 600-dpi pixel before it rounds the
dot's advance (Arial 14 pt: 116.7 px, so 117, dots every 3.96 pt); taking the
floor drew them 3.84 pt apart, as measured at 11, 14, 17, 20 and 26 pt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* leader_xs: saturate the glyph range end

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-11-01 14:19:41 -05:00
Claude b87c80cdc7 Layout: text in a turned text box turns with it
A turned text box (free rotation) drew its frame turned and its text
upright. Its text now turns with it about the box's centre, as Word
draws it, on screen and in PNG and PDF export: upside down when the
box is flipped across, never mirrored by a side-to-side flip.

PDF export draws item by item, to tag each paragraph, so it turns each
of the box's lines the same way, and tags the turned lines as their
paragraph's text rather than as decoration.

Clicks and the caret inside a turned box still go by its upright text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBVQLyDnrG8Tn1yDQcUabC
2026-11-01 17:42:07 +00:00
Claude 45b852ad4a Layout: in-line drawings move with their run's raise or lowering
An in-line picture, shape or chart always stood on the baseline,
whatever its run's position. Word moves an in-line picture with its
run's position (`w:position`, Font › Advanced › Position):
- lowered, it reaches below the baseline (at most until its top is on
  the baseline) and the line's descent grows to hold it;
- raised, it stands clear of the baseline at its own height.

A drawing's cluster now reaches `obj_h` above the baseline and `obj_d`
below it (negative when raised). It is painted, hit-tested and wrapped
from both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBVQLyDnrG8Tn1yDQcUabC
2026-11-01 17:24:15 +00:00
Claude f217aafa53 Layout: a sloped line's arrowheads sit at its ends
The renderers draw a line from its frame's top left to its bottom right
(DrawingML's `line`; flips turn the frame), but the arrowhead code and
`end_direction` assumed a line from the bottom left to the top right, so
a sloped line's arrowheads were drawn at the other diagonal's corners,
apart from the line. Horizontal and vertical lines hid it. The
arrowheads now start from the line's own ends, pointing along it.

Regression test: `tests::a_lines_arrowheads_sit_at_its_ends`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBVQLyDnrG8Tn1yDQcUabC
2026-11-01 07:15:27 +00:00
2f78615291 #4720: Chart and SmartArt text the font lacks is drawn in a fallback face, so Chinese labels no longer show boxes (#4726)
Chart and diagram text was shaped in one face, the document's body font,
so Chinese legends and axis labels showed boxes while body text was fine.
Characters the face lacks now go to a fallback face, one face per run
when one covers it all, as in body text; a bold fallback face without a
bold style is emboldened.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-30 00:12:55 -04:00
Dylan M. Taylor 937c7e532d Refactor layout for strict Clippy checks 2026-10-28 01:06:48 +00:00
NetterSilverandClaude Opus 5.5 ffa127c615 Skip per-character table lookups for plain text in layout and PDF export
Shortcuts that give the same answers as the lookups they skip:

- is_rtl: nothing before U+0590 is right to left, so Latin, Greek and
  Cyrillic text skips the bidi class search.
- strong_rtl and is_mark: ASCII letters are the only strong ASCII
  characters, and no ASCII character is a mark.
- cluster_starts: in ASCII text every character starts a grapheme except LF
  after CR, and no ASCII character joins a cluster.
- display lines: a glyph run's vectors and text are sized up front, and the
  symbol-font check is made once per run instead of once per cluster.
- PDF glyph runs: the baseline (the y most glyphs share) is only worked
  out for italic runs, the only ones that use it, and a run on one baseline
  returns it without counting.

Tests check each shortcut against the full lookup (every code point for the
bidi classes, every ASCII pair for the clusters).

PDF export of the export_bench document, interleaved with main: median
293 ms -> 241 ms (-18 % with the previous commit). The PDF bytes are
identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 13:39:09 +01:00
aiongg 5a84dd2f4f refactor(layout): split display.rs into display/ 2026-10-26 21:16:49 +00:00