A chart or SmartArt diagram in a DOCX was dropped on import: it took no
room, so the text after it moved up (five of the six chart documents in
docxide-pdf's fixtures came out a page short), and nothing was drawn.
They are now an InlineObject::Graphic: it takes its room, floats and
wraps like a picture, and draws a list of neutral items (shapes, paths,
one-line texts, pictures) that layout scales to the object's box.
- Charts (read/chart.rs) are drawn from the values the chart part
caches: clustered, stacked and 100% bar and column charts, line, area,
pie, doughnut and scatter charts, with title, legend, gridlines, axis
labels and Excel number and date formats.
- SmartArt (read/diagram.rs) is drawn from the drawing Word stores
beside the diagram: preset shapes, theme colours, text and pictures.
- Theme colour handling is shared (read/drawing_color.rs).
- Each chart or diagram part is read once, only through a relationship
of its type, under a per-file budget of items.
- Tagged PDF tags them as figures with their alternative text, and the
accessibility check reports charts without one.
Not yet: charts and diagrams aren't written back when saving a DOCX
(they weren't before either), so the editor doesn't let you move or
resize them. Radar, stock, 3-D and combination charts draw their first
supported chart type only; SmartArt pictures aren't clipped to their
shape.
docxide-pdf bench (365 documents, vs main + #135): page counts right
320 -> 326 (all five chart documents and one SmartArt document), Jaccard
passes 208 -> 215, SSIM passes 76 -> 79; eight documents better, none
worse.
Co-authored-by: Oliver Zieschang <4963183+naxvog@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Windows metafiles (WMF, EMF), common for logos, crests and diagrams in
older documents, couldn't be decoded and showed as grey boxes, on screen
and in PDF.
The new crate wordcraft-metafile (layer 1) reads them from the public
[MS-WMF] and [MS-EMF] specifications into paths and bitmaps, and places
them in a picture's box; render and pdf paint the result as vectors.
It covers the drawing records Office pictures use (lines, polygons,
curves, rectangles, ellipses, arcs, path brackets), pens and brushes,
the mapping modes and world transforms, and bitmaps in DIB form with the
usual raster operations. Input is treated as hostile: every read is
bounded, and a byte budget caps what one picture may allocate.
Not yet: text records (ExtTextOut) and EMF+ records are skipped, so a
metafile whose content is mostly text shows only its lines and fills.
docxide-pdf bench (365 documents, vs main + #135): the twelve documents
with WMF/EMF pictures show them; the pictures are a small part of their
pages, so the scores move little (Jaccard passes 208 -> 209, five
documents up to 3 SSIM points better, none worse).
Co-authored-by: Oliver Zieschang <4963183+naxvog@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Show section breaks with Show/Hide ¶ (#42)
With formatting marks on, the last paragraph of a section now ends in a
dotted double rule with the break's name centred in it (Section Break
(Next Page), (Continuous), (Even Page), (Odd Page)) instead of a plain ¶.
The type shown is the start type of the section that follows. The mark
is screen-only: PDF export skips it, and print/PNG never turn marks on.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Show/Hide ¶: put the paragraph mark past mixed-direction text (#274)
The paragraph mark (and the section-break mark) was anchored at the caret
position after the paragraph's logically last character. When that text
runs against the paragraph's direction (Hebrew ending a left-to-right
paragraph, Latin ending a right-to-left one) that position is inside the
line, so the mark was drawn on top of a letter. The mark now sits at the
line's visual end for the paragraph's direction: right of the rightmost
glyph in a left-to-right paragraph, left of the leftmost in a right-to-left
one. Pure left-to-right and pure right-to-left lines are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Tests: shapes and pictures can be clicked, resized, dragged and nudged
Fixed by #46 but only text boxes were covered. These walk the editor's
path for shapes (rectangle, ellipse, star, arrow) and pictures: hit test,
select, resize with arrange.bounds, drag to float with Square wrap,
nudge, undo. Covers #142 and #82.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Font list offers every weight a family installs, named as Word names them (#219)
Fonts were listed and matched only by their typographic family name
(name ID 16, e.g. "Roboto"), so a face other than Regular, Bold or Italic
(Light, Medium, SemiBold, Black, Condensed...) never appeared in the font
list, and a document asking for "Roboto Light" fell back to a substitute.
Each face now also carries its legacy family name, the one Word lists and
stores in documents: name ID 1 for static fonts ("Source Sans 3
Semibold"), or the family plus the style words other than Regular, Bold
and Italic for a variable font's named instances ("Inter Light"). The
font list includes these names, has_family and face() resolve them (bold
and italic still choose or synthesise within the faces the name covers),
and asking for a legacy name loads the whole installed family. The
system font scan now also reads fvar, so a variable font's instances are
listed before it is loaded.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Absolutely positioned VML text boxes float instead of sitting inline (#149)
A legacy VML shape (w:pict > v:shape / v:rect) with position:absolute in its
style was read as an inline object, so a tall text box in a header made the
header as tall as the box and pushed the body down the page.
The reader now maps the VML style to the same float model DrawingML anchors
use: left/margin-left and top/margin-top offsets, mso-position-*-relative
reference areas, mso-position-horizontal/vertical alignment, mso-wrap-distance-*
and w10:wrap (square, tight, through, topAndBottom; none or absent is in front
of the text, or behind it at a negative z-index). Shapes without
position:absolute stay inline. Saving writes the boxes as DrawingML anchors
with the same placement.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Reject All removes paragraph breaks inserted with Track Changes on (#229)
Enter under Track Changes already recorded the new paragraph mark as an
insertion, but accepting or rejecting only cleared that flag, so Reject All
left the paragraphs split. Rejecting an inserted mark (or accepting a deleted
one) now joins the paragraph with the next one again; Accept All, Reject All,
Accept/Reject at the caret or over a selection, Next/Previous Change and the
Reviewing Pane all see paragraph marks.
Backspace at a paragraph start and Delete at a paragraph end now record a
tracked deletion of the mark (or remove a mark the same author inserted)
instead of only moving the caret; tracked deletions across paragraphs mark
the paragraph marks too.
DOCX reads and writes paragraph-mark revisions (w:ins / w:del in the
mark's w:rPr), and a tracked mark is drawn in its author's colour when
formatting marks are shown.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Table Layout: Text Direction turns cell text (#226)
The Text Direction button only flipped a flag that layout and rendering
ignored (and docx always wrote it as btLr), so pressing it did nothing
visible.
Cells now carry a text direction (horizontal, top-to-bottom tbRl,
bottom-to-top btLr). The button cycles through them like Word's, for every
selected cell. Layout runs turned text along the cell's height: an exact
row height sets the line length, otherwise the row grows to fit the
unwrapped text. Turned lines are drawn in a turned frame on screen and in
PDF, and clicks, caret, and selection follow the turn. docx reads and
writes w:textDirection with the matching value.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Chinese interface: embed the Chinese UI font in releases, fall back to an installed one (#241)
The release workflow pinned storytold/craft-fonts at abb8331, the commit that added the
Japanese fonts. Noto Sans CJK SC came one commit later (8dcdacd), so release builds embedded
only the Japanese faces and the Simplified Chinese interface fell back to BIZ UDPGothic, which
lacks about 157 simplified-only hanzi (页, 选, 删 …) and drew them as boxes. Document text was
unaffected because it uses the system font fallback.
- Bump the craft-fonts pin in the release and FreeBSD workflows to 8dcdacd.
- With CRAFT_FONTS_REQUIRED, a native build now fails when the craft-fonts manifest has no
Jpan or Hans font, so an outdated pin can't ship silently again.
- When no embedded face covers the interface language, the UI font families get an installed
CJK font (desktop only): a short per-platform list of files in the system font folders
(Windows Deng/msyh/simhei/simsun, macOS PingFang/STHeiti, Linux Noto Sans CJK/WenQuanYi),
header-checked, size-capped, and the face chosen by coverage (a SC face in a pan-CJK
collection), so egui never sees a file it can't parse.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Add a System interface theme that follows the OS appearance (#115)
The interface theme is now a three-way choice: Use system setting,
Light or Dark, in File > Options > General and in a new Interface Theme
menu in View > Dark Mode. System follows the light/dark appearance egui
reports (winit on macOS, Windows and Linux; prefers-color-scheme on the
web) at launch and live, and falls back to light when the OS reports
none. egui's own theme is now pinned to ours, so an OS change no longer
swaps in egui's unstyled style.
Saved preferences migrate: the old "dark" switch becomes Dark or Light
and is never written again. Light stays the default for new installs.
New ui.theme command (light/dark/system); ui.dark still sets Light/Dark.
The dark page (View > Switch Modes) is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* #248: read the installed CJK UI font only when CJK text is shown
A build without craft-fonts read a large system CJK font on the first frame even
for an English interface. Now it is loaded only for a CJK interface language, or
once File > Options shows the language list (names in their own scripts).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Enter under Track Changes already recorded the new paragraph mark as an
insertion, but accepting or rejecting only cleared that flag, so Reject All
left the paragraphs split. Rejecting an inserted mark (or accepting a deleted
one) now joins the paragraph with the next one again; Accept All, Reject All,
Accept/Reject at the caret or over a selection, Next/Previous Change and the
Reviewing Pane all see paragraph marks.
Backspace at a paragraph start and Delete at a paragraph end now record a
tracked deletion of the mark (or remove a mark the same author inserted)
instead of only moving the caret; tracked deletions across paragraphs mark
the paragraph marks too.
DOCX reads and writes paragraph-mark revisions (w:ins / w:del in the
mark's w:rPr), and a tracked mark is drawn in its author's colour when
formatting marks are shown.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Render: draw on-screen text like macOS, on whole pixels
Pages looked lighter and thinner than in Word and other Mac apps:
- macOS thickens glyphs a little when it draws text on screen (font
smoothing). RenderOptions::text_darkening strokes each outline by the
amount macOS uses (from Pathfinder's measurements: 1.21% of the pixel
size, 1.25x that vertically, capped at 0.3 px, none past 72 ppem). The
editor turns it on for on-screen pages on macOS; PDF/PNG exports and
other platforms are unchanged.
- Page textures were drawn at fractional positions after scrolling, so
every glyph was resampled and blurred. They now land on whole device
pixels at their own size.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Merge follow-ups: page textures use #165's texel alignment; clippy as_chunks in a test
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: ssamstudio <267867645+ssamstudio@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
View › Switch Modes (and File › Options › Dark page) shows the page as dark
grey (80% black, #333333) with every document colour's lightness inverted
and its hue kept, so black text turns white and dark-blue headings light
blue. Pictures are untouched and the caret turns white. Screen only: saving,
printing and PDFs are unchanged. The setting is remembered between runs.
Refs #185.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- body on a titlePg first page now starts below the first-page header
- decode tiff pictures
- fade images through an opacity layer; vello_cpu panics on image sampler alpha
Clean-room foundations for WordCraft: Arc-shared document model with
paragraph/run editing, styles, lists, sections and tables; Word-style
font substitution; first-fit line breaking with tabs, lists, alignment,
pagination, tables, headers/footers and hit testing; vello_cpu page
rasteriser.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>