Commit Graph
1501 Commits
Author SHA1 Message Date
aiongg 71db506182 Merge pr/643: UI: the Effects dialog, as in InDesign (#643)
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/cmd/object.rs
#	crates/ui-egui/src/dialogs.rs
#	crates/ui-egui/src/i18n.rs
#	crates/ui-egui/src/i18n/ar.rs
#	crates/ui-egui/src/i18n/it.rs
#	crates/ui-egui/src/i18n/ja.rs
#	crates/ui-egui/src/i18n/pt_br.rs
#	crates/ui-egui/src/i18n/uk.rs
#	crates/ui-egui/src/menus.rs
#	crates/ui-egui/src/panels/properties.rs
2026-10-13 00:12:33 +00:00
aiongg 6797589402 Merge pr/647: Text: edit type on a path along the curve (#647)
# Conflicts:
#	crates/engine/src/cmd/text.rs
2026-10-13 00:10:31 +00:00
aiongg 2732f8c2e6 Integration (#636): with Direct Selection an anchor under the pointer wins over a type-on-a-path bracket, so the end anchors (where the default brackets sit) can be dragged again 2026-10-13 00:09:13 +00:00
aiongg 99e6922981 Merge pr/636: Tools: drag type-on-a-path start and end brackets (#636)
# Conflicts:
#	ROADMAP.md
#	crates/idml/src/tests.rs
#	crates/tools/src/lib.rs
#	crates/tools/src/select.rs
#	crates/tools/src/tests.rs
#	crates/ui-egui/src/canvas.rs
2026-10-13 00:08:12 +00:00
aiongg 1cf1477183 Integration (#630): the Pen continuing from a path's first anchor adds anchors before it (path.appendAnchor at: start) instead of reversing the path, so arrowheads stay on their ends and a press then Esc leaves no undo step 2026-10-13 00:06:15 +00:00
aiongg 575839cc4f Merge pr/630: Tools: the Pen continues an open path from either end point (#630)
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/cmd/object.rs
#	crates/tools/src/lib.rs
#	crates/tools/src/pen.rs
#	crates/tools/src/tests.rs
#	crates/ui-egui/src/canvas.rs
#	crates/ui-egui/src/i18n.rs
#	crates/ui-egui/src/i18n/ar.rs
#	crates/ui-egui/src/i18n/it.rs
#	crates/ui-egui/src/i18n/ja.rs
#	crates/ui-egui/src/i18n/pt_br.rs
#	crates/ui-egui/src/i18n/uk.rs
2026-10-13 00:02:51 +00:00
aiongg 8ad77980ac Merge pr/648: Eyedropper: a sampled colour goes to the stroke when the Stroke box is in front (#648)
# Conflicts:
#	ROADMAP.md
#	crates/tools/src/xform.rs
#	crates/ui-egui/src/toolbar.rs
2026-10-13 00:01:26 +00:00
aiongg d7d71380ad Merge pr/610: Links: placed text files can stay linked (Word, RTF, text) (#610)
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/cmd/file.rs
#	crates/engine/src/cmd/linked.rs
#	crates/engine/src/cmd/links.rs
#	crates/engine/src/cmd/place_text.rs
#	crates/engine/src/cmd/prefs.rs
#	crates/engine/src/lib.rs
#	crates/ui-egui/src/dialogs.rs
2026-10-13 00:00:51 +00:00
aiongg 7e4c75fce9 Merge pr/662: Commands: reject unknown spread values instead of falling back to page 1 (#662)
# Conflicts:
#	crates/engine/src/cmd/file.rs
#	crates/engine/src/cmd/guides.rs
#	crates/engine/src/cmd/object.rs
2026-10-12 23:59:28 +00:00
aiongg 605b31c6d6 Merge pr/663: CLI: accept a UTF-8 BOM in scripts and document the syntax in script --help (#663) 2026-10-12 23:56:41 +00:00
aiongg 5b903c036e Merge pr/637: Index: keep level 3 and 4 topics in a run-in index (#637)
# Conflicts:
#	contributors/people.toml
2026-10-12 23:56:37 +00:00
aiongg 14eff698dd Merge pr/639: Fonts: fall back to a font made for Malayalam (#639) 2026-10-12 23:56:26 +00:00
aiongg e7ad004f4d Merge main: mirror gradient midpoints when reversing; preserve leading empty Tagged Text paragraphs 2026-10-12 23:56:21 +00:00
Jason Brashear 91263c912f fix(textimport): preserve leading empty Tagged Text paragraphs
Preserve paragraph breaks after a ParaStyle tag starts the story so leading empty paragraphs and their styles survive Tagged Text import.

Validated individually and after #638 with all six cargo xtask ci gates (1,278 tests passed, four existing tests ignored, 15 WASM compilation checks), six independent paragraph/header/Unicode probes, and an actual export-to-placement command smoke test.

Refs #641
2026-10-12 17:21:52 -06:00
Jason Brashear cc5b4a1f0f fix(color): mirror gradient midpoints when reversing
Move and mirror segment midpoints when reversing gradient stops so reversed gradients preserve color and opacity interpolation.

Validated locally on macOS with all six cargo xtask ci gates (1,277 tests passed, four existing tests ignored, 15 WASM compilation checks), plus independent regressions, undo/redo, native and IDML round trips, and PNG/PDF rendering.

Refs #638
2026-10-12 17:20:54 -06:00
Marco Arnulfo 9e15d5a17e fix(cli): accept a UTF-8 BOM in scripts and document the syntax in script --help (fixes #654) 2026-10-13 00:41:46 +02:00
Marco Arnulfo 3d59dd5d97 fix(cmd): reject unknown spread values instead of falling back to page 1 (part of #651) 2026-10-13 00:38:54 +02:00
aiongg 9487b80220 Integration: format 2026-10-12 22:24:41 +00:00
aiongg b234500e9b Integration (#646): wrap_slot's no-progress check reads next <= b (clippy neg_cmp_op_on_partial_ord) 2026-10-12 22:24:31 +00:00
aiongg 5b01f90baf Merge shaped-text-frames: Text frames: lines follow an oval or polygon frame's shape (#646)
Conflict: crates/compose/src/lib.rs: modules and exports both (outline, optical, parabox, hit_note_with); placement = next's carry/grid/wrap_slot code, entered for outlines too (and the first-word `need`); free_slots starts from the outline's intervals with cut set (1.5 em filter, segments in reading order, reversed for RTL); the branch's free_slot dropped
Conflict: crates/compose/src/lib.rs: next's bands are top..baseline (descenders hang into wraps); a shaped frame's outline holds the descenders too: wrap_slot and free_slots take the line's descent, used only for the outline's intervals (the branch's oval tests check descender corners)
Conflict: crates/compose/src/lib.rs: estimate_slots takes the band (the paragraph's ascent from line_metrics, max 0.8 em, and descent, max 0.25 em, for a shaped frame; next's 0.8 em and no descent otherwise), returns early only without wraps and outline; est_first uses that ascent; wrap_slot's 1 pt step for outlines (and contours) returns None when it makes no progress or is not finite
Conflict: crates/compose/src/tests.rs: tests both
Conflict: ROADMAP.md: logs both
2026-10-12 22:23:22 +00:00
aiongg 1ff2e978b2 Integration (colour-entry x #552): the Tools panel's Fill/Stroke double-click opens the Color Picker with picker_fields (CMYK stays CMYK); the Formatting Affects Text picker test fills the fields the picker writes for a typed colour 2026-10-12 22:17:14 +00:00
aiongg 283f3f5bbc Merge colour-entry: Color Picker and Color panel keep typed values; colours read on a fixed scale (#323)
Conflict: crates/engine/src/cmd/style.rs: object.color params doc and enabled = next's (text?, has_text or selection) + the model form; apply_color = next's text targets and caret typing format + the branch's rounding-based swatch reuse
Conflict: crates/ui-egui/src/dialogs.rs: next's deleteCharacterStyle / swatchOptions arms; colorPicker = the branch's (no-op without edits, model-form colour) with next's `text`
Conflict: crates/ui-egui/src/panels/swatches.rs: double-click opens the picker with picker_fields + next's `text`; spectrum = the branch's model form + next's `text`
Conflict: docs/target-app-parity.md: the branch's Color panel line + next's Threading and Place text lines
Conflict: ROADMAP.md: logs both
2026-10-12 22:17:14 +00:00
aiongg cb141d6a2b Merge image-colour-profiles: Images: CMYK and profiled photos show in their colours
Conflict: ROADMAP.md: logs both
2026-10-12 22:10:50 +00:00
aiongg 116b3b4ec9 Text frames: lines follow an oval or polygon frame's shape (#646) 2026-10-12 22:07:43 +00:00
aiongg 6483a43ba8 Integration (docx-lists x #360 list levels): Word levels set list_level (ilvl + 1) and lvlRestart; parent numbers in a label are ^k level references, written as text only where the model's level numbers differ from Word's (legal labels, unnumbered levels); Start At from a simulated per-level ListCounter; tests nested_levels_and_restarts adjusted, parent_numbers_the_model_lacks_are_text 2026-10-12 22:07:29 +00:00
aiongg 8ce45eaef6 Merge docx-lists: Word import: bullets and numbering from numbering.xml (#626)
Conflict: crates/textimport/src/docx.rs: tests both
Conflict: ROADMAP.md: logs both
2026-10-12 22:04:39 +00:00
aiongg e391002be6 Merge text-stroke-behind: Text: stroke behind fill (#619)
Conflict: crates/render/src/text.rs: the branch's paint_glyphs with next's fill and stroke overprint (overprint_fill / overprint_stroke, an overprinting() Multiply layer around each paint); next's cell_stroke(kind, weight, length); path text strokes don't overprint, as before
Conflict: crates/pdf/src/text.rs: the branch's text_run / paint_outlines / glyph_outlines split with next's skew tan, vertical mark offsets and split_clusters glyph order (#421)
Conflict: crates/engine/src/cmd/paths.rs: next's shatai transform + the branch's stroke_behind_fill key
Conflict: crates/idml/src/tests.rs: tests both
Conflict: docs/typography-parity.md, ROADMAP.md: headers combined, logs both
2026-10-12 22:04:10 +00:00
aiongg 8811669ee0 Merge alt-arrow-kerning: Type: Alt+Left/Right kerns at the caret and tracks a selection (#621)
Conflict: crates/engine/src/cmd/prefs.rs: next's document.preferences params doc + kerningIncrement
Conflict: crates/engine/src/tests.rs: tests both
Conflict: docs/target-app-parity.md: next's UI and Pen lines + the branch's Tools line
Conflict: ROADMAP.md: logs both
2026-10-12 21:58:22 +00:00
aiongg 86848092ed Merge autoflow-columns: Autoflow: one frame per column, as the page's columns are set (#625)
Conflict: crates/engine/src/cmd/place_text.rs: autoflow(d, sid, max_pages, cmd, primary): next's parent-spread refusal and cmd-named errors + the branch's per-column body; callers file.place, text.thread (primary false) and Smart Text Reflow (primary true)
Conflict: crates/engine/src/lib.rs: Smart Text Reflow passes next's "Smart Text Reflow" and primary
Conflict: crates/engine/src/cmd/file.rs: next's file.place params doc with the branch's autoflow wording
Conflict: docs/target-app-parity.md: next's Threading line + the branch's Place text line
Conflict: ROADMAP.md: logs both
2026-10-12 21:54:55 +00:00
aiongg 7f494e4f3f Color Picker: hex and number fields keep typed text; CMYK colours stay CMYK; the page is not dimmed
The hex field keeps its text while focused, commits each time it parses
(#rgb, rrggbb, any case) and shows the colour again when focus leaves. R/G/B
and editable C/M/Y/K fields hold their values as typed. Picking on the field
or by hex keeps a CMYK colour CMYK through the CMYK working space; typed R/G/B
values make it RGB. OK without edits leaves the fill or stroke alone. Dialogs
draw no backdrop, so the page keeps its colours behind them (#323).
2026-10-12 21:53:06 +00:00
aiongg d050223b78 Colour entry: panel number fields keep typed text; colours are read on a fixed scale
The Color panel's C/M/Y/K and R/G/B fields keep the typed number until Enter
or leaving the field, and send {model, ...} colours in 0..1, which object.color
reads without guessing the scale (M 1 is 1 %, R 1 is not pure red). Colours
that round to the same swatch name reuse that swatch instead of adding hidden
twins. The hex parser rejects signs and other non-hex characters (#323).
2026-10-12 21:53:06 +00:00
aiongg 3381b97e31 Merge #629: Complete the Arabic (العربية) catalog: every missing key translated
Conflict: crates/ui-egui/src/i18n/ar.rs: both sides; the PR's 11 keys next already has dropped (Black, Bottom:, Columns:, Cyan, Horizontal:, Left:, Magenta, Right:, Top:, Vertical:, Yellow)
2026-10-12 21:51:40 +00:00
aiongg 2cc95c64a3 Integration (#618 review): differing SVG fill-opacity and stroke-opacity take the fill's for the object with a warning (items have one opacity); test differing_fill_and_stroke_opacity_takes_the_fills_and_warns 2026-10-12 21:49:21 +00:00
moppeeerandClaude Opus 5.5 142e5d91e0 feat(eyedropper): A sampled colour goes to the stroke when the Stroke box is in front
As in InDesign, the Eyedropper applies a colour sampled from a placed image
to the fill or the stroke, whichever is in front in the toolbox: with the
Stroke box in front a selected line takes it on its stroke (it has no
fill to show it). The toolbox's state reaches tools through
ViewInfo/ToolContext::stroke_in_front.

Depends on #572.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 23:48:11 +02:00
aiongg 09e8118b21 Merge #618: Paste vector art from Illustrator, VectorCraft or Figma as editable paths
Conflict: crates/engine/src/cmd/mod.rs: next's file/layout exports + is_svg_markup
Conflict: crates/ui-egui/src/lib.rs: next's logic(); the PR's RequestPaste hook and request_system_paste field dropped (next reads the system clipboard through Services::clipboard_text); run() routes edit.paste with SVG markup and no insertion point to edit.pasteSvg at next's view centre (with_system_clipboard's spread/at)
Conflict: crates/ui-egui/src/menus.rs: next's activate/menu_enabled; pastes_from_system dropped
Conflict: crates/ui-egui/src/canvas.rs: PR's view_centre dropped (next's canvas::view_centre); PR's Event::Paste SVG arm dropped (the paste event reaches run() through edit.paste); tests both, the PR's two UI tests rewritten against run() (paste event in the middle of the view; native-menu Edit > Paste reading SVG from the system clipboard, markup stays text at an insertion point)
Conflict: docs/control-protocol.md: ui.ime and ui.paste rows
Conflict: ROADMAP.md: logs both
2026-10-12 21:47:29 +00:00
aiongg f4e769369c Merge main: fix(macos): make Dock Quit cancelable; test(recovery): make failed republishing fixtures deterministic
Conflict: Cargo.toml: next's winit patch (rust-windowing/winit#4694, Windows mixed-DPI drags) kept; main's skylartaylor/winit patch held back (Cargo takes one patch per crate; both are single commits on v0.30.13 for different platforms, no published commit has both)
Conflict: Cargo.lock: next's winit source; main's ui-egui tempfile dev-dependency taken
Conflict: apps/designcraft/src/main.rs: next's startup; main's macOS with_close_requested_on_terminate block left out (needs main's winit patch); apps/designcraft/Cargo.toml keeps next's (no macOS winit dependency)
Conflict: ROADMAP.md: headers combined, logs both
Conflict: docs/target-app-parity.md: headers combined
2026-10-12 21:40:47 +00:00
moppeeerandClaude Opus 5.5 f96895f400 feat(text): Edit type on a path along the curve
The Type tool's clicks, drags and double-clicks on type on a path went to
the character at the same spot of the straightened line, and the caret
and highlight were drawn straight. A click now finds the character where
its nearest point along the path is (compose::PathTextMap, from the line
to the path and back, as path_glyphs places the glyphs); the caret stands
across the path there and the highlight bends with the text. A click on
the letters, above the path, hits the frame.

Depends on #636 (PathWarp::nearest).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 23:35:58 +02:00
aiongg b20ddf87ec Images: CMYK and profiled photos show in their colours
Placed rasters are converted to display sRGB once, at decode time, in one
module (designcraft-images colour):
- embedded ICC profiles in JPEG, PNG, TIFF, WebP and PSD (RGB and grey) are
  applied; sRGB-like profiles leave the pixels exactly as decoded
- CMYK JPEGs (incl. YCCK, Adobe-inverted values), CMYK TIFFs and CMYK PSD
  composites go through their embedded CMYK profile, else the working CMYK
  space, with the colour settings' intent and black-point compensation
- oversized (> 16 MiB), unreadable or mismatched profiles are ignored with a
  log line

The render image cache is rebuilt when the colour settings change. PDF/A
exports of CMYK TIFFs get the same managed RGB as the screen.
2026-10-12 21:30:41 +00:00
Jeffrey Thayne d3c1d3d029 Merge remote-tracking branch 'origin/main' into linked-docx
# Conflicts:
#	ROADMAP.md
2026-10-12 15:21:00 -06:00
Jeffrey ThayneandClaude Opus 5.5 bb7e81425f Links: never read a linked text file that isn't a regular file
Checking a linked text file's status (every links.list, which the
Links panel polls) compared sizes and then read the whole file, so a
link to a FIFO or a device such as /dev/zero (size 0) never returned.
Only regular files are read now, and no more than the Data Merge
source limit (256 MiB): anything else shows as missing; a file over
the limit shows as modified when its size changed and as tooLarge
("Too large to check") when it didn't. Update and Relink read through
the same guard, and placing a file too large to check doesn't link it.
Unix test: a FIFO, /dev/zero, a folder and an over-limit file each
return promptly without being read (each call runs on a thread with a
timeout, so a regression fails instead of hanging). Thanks @aiongg.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 15:15:01 -06:00
moppeeerandClaude Opus 5.5 4769cadb77 feat(ui): The Effects dialog, as in InDesign
Object > Effects (Transparency..., Drop Shadow... and the rest, and their
shortcuts) and the Effects panel's fx button open the Effects dialog on
the chosen effect, turned on: the effect list with checkboxes on the left
and a summary line, the chosen page's settings on the right in the
document's units (Blending, Position with X/Y offsets, Options...),
Preview on the page while it is open, Cancel putting the page back, and
OK as one undo step.

- engine: object.effects {effects (merged, kept in range), opacity, blend}
- doc: drop and inner shadows default to InDesign's 120 degree light
- ui: dialogs::cancel (a previewing dialog reverts)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 23:14:15 +02:00
aiongg 270dd46b50 Text: stroke behind fill (#619)
A character attribute strokeBehindFill paints a text stroke under the fill
at double width, so its full weight lies outside the letters and they keep
their shape. Screen and PNG rendering, PDF export (stroke as outlines
first, then the filled text, so the text is not duplicated; clipped to the
outside of the glyphs when there is no fill), Create Outlines (outside-
aligned stroke), the Properties panel and Character Style Options.

IDML StrokeAlignment on CharacterStyleRange, CharacterStyle and
ParagraphStyle imports and exports as the attribute (OutsideAlignment =
on, CenterAlignment = off).
2026-10-12 21:06:42 +00:00
Marco Arnulfo de31bd5647 fix(fonts): fall back to a font made for Malayalam (part of #628)
Malayalam took the first installed file mapping it: on macOS Arial Unicode MS, which has no Malayalam shaping, so conjuncts like ക്ഷ fell apart. Fonts made for Malayalam are tried first, loaded on demand through the CJK chain lookup.
2026-10-12 23:03:22 +02:00
aiongg 7515dfeaf5 Word import: bullets and numbering from numbering.xml (#626)
Paragraphs with w:numPr, directly or through their paragraph style, become
bullet or numbered paragraphs: Word's number formats, label text, start
numbers and overrides, level restarts, the text after the label and the
level's indents. Parent levels in a label are written as text. Unreadable or
hostile list definitions leave plain paragraphs.
2026-10-12 20:59:17 +00:00
skylar taylor-barrick 64f0ca8fa6 fix(macos): make Dock Quit cancelable
Route AppKit termination through the existing unsaved-document close transaction using the immutable winit patch. Restore pending prompts from hidden or minimized windows and preserve answers across repeated requests.

Validation: all six local gates, native cancellation/retry/save smoke, and hosted platform checks.

Fixes #544.

PR #623.
2026-10-12 14:52:12 -06:00
webdevtodayjason 98f1340f61 fix(index): keep level 3 and 4 topics in a run-in index 2026-10-12 15:50:26 -05:00
webdevtodayjason f605ca3157 chore(contributors): add Jason Brashear 2026-10-12 15:50:26 -05:00
aiongg 86a0ce2cf7 Type: Alt+Left/Right kerns at the caret and tracks a selection
With the Type tool, Option/Alt+Left/Right with a caret changes the manual kerning of the pair at the caret by the Kerning/Tracking increment (a new document preference, 20/1000 em by default, in Preferences > Units & Increments); with a selection it changes the tracking of the selected text. Cmd (Ctrl on Windows and Linux) multiplies the step by five. A pair without manual kerning starts from 0. The new type.kernStep command does the work, one undo step per press.

Moving by words is Cmd+Left/Right on macOS and Ctrl+Left/Right on Windows and Linux; Option/Alt+Shift+Left/Right still extends the selection by words. Fixes #621.
2026-10-12 20:48:14 +00:00
aiongg cb38862846 Autoflow: one frame per column, as the page's columns are set
Autoflow on Place threads one frame per page column (right to left in a right-to-left story) and adds pages until the story fits; a frame the user drew or selected stays as it is, and later columns of its page are filled when it sits in one. Empty frames autoflow made at the end of the thread are removed. Pages Smart Text Reflow adds get a primary frame with the page's columns, and it removes trailing pages that hold only empty frames of the story. Fixes #625.
2026-10-12 20:39:04 +00:00
moppeeerandClaude Opus 5.5 cded7d997e feat(tools): Drag type-on-a-path start and end brackets
Selected type on a path shows its start and end brackets across the path,
as in InDesign, and the Selection and Direct Selection tools drag them
along it (Move Bracket, one undo step): the text starts and ends where
they are, and text past the end bracket is overset.

- doc: PathType.end (None: the path's end)
- geom: PathWarp::nearest, the arc length of the nearest point
- engine: type.pathOptions {end}; brackets stay on the path and can't cross
- compose: the line runs from the start to the end bracket
- idml: EndBracket read and written
- tools: Cursor::PathBracket

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 22:32:56 +02:00