Commit Graph
1322 Commits
Author SHA1 Message Date
aiongg e2eae1c62b Merge idml-deleted-text: IDML: tracked deleted text takes no room in the layout (#581)
Conflict: crates/idml/src/import.rs: the branch's `over.change` line moved into next's `range_format()`, which takes the enclosing format for it (its CrossReferenceSource caller passes it too); next's `TextVariableInstance` arm kept beside the branch's `Change` arm, with next's ignore list
Conflict: crates/idml/src/export.rs: kept next's metadata, text-variable and CJK settings helpers and the branch's `change_type`
Conflict: crates/idml/src/tests.rs: kept next's `$ID/` font test and the branch's tracked-change test
2026-10-12 12:36:40 +00:00
aiongg c336d8c990 Merge anchored-settings (update): Groups: a rotated group's bounds follow its children's outlines 2026-10-12 12:35:22 +00:00
aiongg e8d38d13e3 Merge contour-wrap (update): Text wrap: words stay whole beside wrap objects; a segment too narrow for its word stays empty
Conflict: crates/compose/src/lib.rs: kept next's baseline-grid code (grid_end/carry_grid, gyoudori placement) and wrap_slot loop; ported the branch's `need`/`holds`, the `position(holds)` carry selection (which also takes carry_grid) and the `skip_while(!holds)` segment filter, now inside wrap_slot through a `holds` argument (the slot predictor passes one that accepts every segment); #563's wraps_line(line_start) filtering stays
Conflict: crates/compose/src/tests.rs: kept next's Same As Clipping test and the branch's sliver-wrap tests
2026-10-12 12:35:22 +00:00
aiongg c2124b77b8 Merge #488: Fix needless horizontal scrollbar in the app bar at 115% scale (#481)
Conflict: crates/ui-egui/src/chrome.rs: kept next's app bar constants, macOS title-bar layout and search field, and the PR's `tools_slot_width` beside them (the slot-width call itself merged cleanly)
2026-10-12 12:32:18 +00:00
aiongg 91a2155cdd Integration: a selected graphic's Transform x/y are measured from the rulers' zero point
#550 places a selected graphic at x/y from its page's left edge; next measures
transform.set x/y from the rulers' zero point (view.zeroPoint, ruler origin), and the
Transform fields show a graphic's bounds from that point. transform_content now targets
x/y from the same origin as the frame path, so a typed value lands where the fields
say it is.
2026-10-12 12:32:01 +00:00
aiongg 4dbd73a6fd Merge #550: Transform values act on a selected placed graphic, not its frame
Conflict: crates/engine/src/cmd/object.rs: transform.set doc string joins next's "measured from the rulers' zero point" with the PR's `content?`; test modules: kept next's copy_ids_tests/tint_alone_tests and the PR's content_transform_tests
Conflict: crates/ui-egui/src/panels/mod.rs: kept next's swatch_list_tests and the PR's content_selection_tests
Conflict: ROADMAP.md: kept both progress-log lines
2026-10-12 12:29:25 +00:00
aiongg 9753ced510 Integration: one Columns: row per catalog; Step and Repeat labels its grid columns in context
#573 adds a "Columns:" row to the shared table and the ar, it, pt_br and uk catalogs,
which next already has for text frame columns, so the catalogs held the key twice (the
Italian and Ukrainian tests reject duplicates). The earlier rows stay; Step and Repeat
asks for "Columns:" in a "grid" context, which Japanese, Ukrainian and Russian
translate as grid columns (列, Стовпці, Столбцы) rather than text columns.
2026-10-12 12:28:55 +00:00
aiongg baba5b7d4d Integration: the CJK style dialog's composite-font menu takes #466's FontPick
#466 makes font_menu_body return a FontPick (family, and the style when a style row
was picked); next's CJK style dialog (ruby/kenten fonts) still expected a family
string. It sets the family and, when picked, the style, as the PR's own callers do.
2026-10-12 12:28:55 +00:00
aiongg bc413ef7e2 Integration: font menu previews load through the GPU-limit-safe texture loader
#466 moves font_menu_body into font_menu.rs with its own preview cache, which calls
egui's load_texture directly; next's #393 routes every texture through
widgets::load_texture so one past the GPU's maximum side is scaled down instead of
crashing.
2026-10-12 12:27:29 +00:00
aiongg 22d428f747 Merge #466: feat(ui): Lay out the font menu like InDesign's
The PR's first three commits are patch-identical to #536/#537, already on next.

Conflict: crates/engine/src/cmd/text.rs: next's side (its `type.selectionAttrs` already carries the PR's selected text, plus next's later `mixed` and typing-format work)
Conflict: crates/fonts/src/fontdb.rs, crates/fonts/src/lib.rs: next's side (the PR's font-kind code is already there through #536; next adds aliases, PostScript names, mac_fonts)
Conflict: crates/ui-egui/src/lib.rs: kept both field sets (`recent_new_documents`, `font_sample`/`font_preview_size`)
Conflict: crates/ui-egui/src/panels/mod.rs: took the PR's move of `font_menu_body` into font_menu.rs; next's #447 test edit and `swatch_list_tests` stay
Conflict: crates/ui-egui/src/i18n.rs, crates/ui-egui/src/i18n/it.rs, ROADMAP.md: kept both sides' rows
2026-10-12 12:27:18 +00:00
aiongg 1f69461ff5 Merge #575: Effects panel: the level row, Isolate Blending and Knockout Group
Conflict: crates/ui-egui/src/i18n.rs: kept next's Step and Repeat rows and the PR's Effects rows
Conflict: crates/ui-egui/src/i18n/ar.rs, it.rs, ja.rs, pt_br.rs, uk.rs: kept both sides' rows
Conflict: ROADMAP.md: kept both progress-log lines
2026-10-12 12:25:51 +00:00
aiongg 19bf6dd868 Merge #573: Step and Repeat: InDesign's dialog with Create as a grid and Preview
Conflict: crates/ui-egui/src/dialogs.rs: kept next's Cell Options functions and the PR's Step and Repeat code; both sides added `cancel()`, joined into one that ends a dialog preview (PR) and puts back the Preferences language (next); tests keep next's `document_setup_app` and the PR's Step and Repeat test (the PR's copy of `dialog_context` already exists further down)
Conflict: ROADMAP.md: kept both progress-log lines
2026-10-12 12:25:32 +00:00
aiongg 8cd0e74a33 Merge #571: Styles: Load Styles from another document or IDML file
Conflict: crates/ui-egui/src/lib.rs: import_file takes the PR's `styles` early return first, then next's content-sniffing `layout` line
Conflict: ROADMAP.md: kept both progress-log lines
2026-10-12 12:24:34 +00:00
aiongg dcabd61597 Merge #570: Blend modes don't blend with the paper
Conflict: crates/render/src/lib.rs: push_layer after next's back document-grid drawing and pop_layer before the front grid, so items never blend with the grid
Conflict: ROADMAP.md: kept both progress-log lines
2026-10-12 12:24:20 +00:00
aiongg 4fb32bdb1b Merge #567: Swatches: Load Swatches refuses .ase files over 100,000 colors and merges in linear time
Conflict: ROADMAP.md: kept both progress-log lines (the PR's 2026-10-12 line above next's 2026-10-11 lines)
2026-10-12 12:24:00 +00:00
aiongg fbf0fabd1a Merge #565: fix(compose): enable Norwegian hyphenation patterns
Conflict: docs/typography-parity.md: kept next's Justification row (#531 note), took the PR's Hyphenation row (Portuguese and Norwegian)
2026-10-12 12:23:42 +00:00
aiongg 40e7b1fed7 Merge upstream main 33842244 2026-10-12 12:23:26 +00:00
aiongg 4c296c05bf Groups: a rotated group's bounds follow its children's outlines 2026-10-12 12:04:24 +00:00
aiongg f1a1f318ba IDML: tracked deleted text takes no room in the layout (#581) 2026-10-12 12:00:42 +00:00
aiongg 7ddd99ee4c Text wrap: words stay whole beside wrap objects; a segment too narrow for its word stays empty 2026-10-12 11:58:08 +00:00
Brandon ThomasandClaude Opus 5.5 3384224419 Release prep: 0.7.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 05:44:24 -04:00
moppeeerandClaude Opus 5.5 fbb238af88 feat(ui): Effects panel shows the level, Isolate Blending and Knockout Group
The Effects panel had the blend mode, opacity and Drop Shadow only. Like InDesign's it now names the level the settings apply to with its blend mode and opacity (Group: Color Dodge 100%, Object: Normal 100%), and has Isolate Blending and Knockout Group, enabled for a selected group, through object.transparencyGroup.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:03:34 +02:00
moppeeerandClaude Opus 5.5 cff0e8b2f8 feat(ui): Step and Repeat dialog with Create as a grid and Preview
Edit > Step and Repeat opened the generic command form (Count 1..1000, Dx, Dy, Rows, Columns, Ids). It now has InDesign's dialog: Repeat (Count, or Rows and Columns when Create as a grid is on), Offset (Vertical, Horizontal, in the document's ruler units or any unit typed) and Preview, which shows the copies on the page while the dialog is open. Cancel, Esc and ui.dialog.cancel take the preview away; OK makes the copies once, as one undo step. A value that isn't a number keeps the dialog open and says so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 10:58:17 +02:00
noah1400 d298555e43 Styles: Load Styles from another document or IDML file (#405)
Styles could not be taken from one document into another; a client's
styles had to be rebuilt in every new file. The Paragraph Styles and
Character Styles panels now have Load Paragraph Styles, Load Character
Styles and Load All Text Styles: pick a .designcraft or .idml file,
tick the styles to load and choose what happens to one the document
defines differently (redefine, keep, or load under a new name). The
styles, swatches and lists a loaded style names come along.
`style.load` does it in one undo step.
2026-10-12 10:41:37 +02:00
moppeeerandClaude Opus 5.5 6cc6f92dad fix(render): Blend modes don't blend with the paper
The renderer painted the paper, then blended page items with it: black
text in Color Dodge over blank paper dodged the white to white and
vanished, and other modes showed the same way. In InDesign (and in PDF,
where the page group starts transparent) the paper is not a backdrop:
over blank paper an object in any blend mode shows as it is, and only
the objects below it are blended with. On spreads that use a blend mode
the items are now drawn in their own group, laid over the paper once;
other spreads render as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 10:33:52 +02:00
moppeeerandClaude Opus 5.5 6d4d013156 fix(ui): Bounded font previews; a family missing from the read is read once
- Previews were kept in the context's data for each sample text, size
  and colour, and never let go: every new text selection loaded textures
  for every visible row. They now live in a cache of at most 256 that
  drops the least recently used.
- A family the background read didn't list (installed since) was read
  from disk on the UI thread every frame and never kept; it is now read
  once and stored with the others.
- Return and Escape in a dialog's font menu are covered by #447's test
  on main; this branch's duplicate is dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 10:03:50 +02:00
moppeeerandClaude Opus 5.5 b2d8d3af7b fix(ui): The font menu's filters don't keep every font loaded
The class, Recently Added and Show Similar Fonts filters read every
family's kind through face(), which loads the family and keeps its bytes
for the session (with no size cap). They use family_kind now, which
reads each file and lets it go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 09:56:51 +02:00
moppeeerandClaude Opus 5.5 5116b6ade6 fix(ui): The font menu keeps its keys, filters off the UI thread, uses theme colours
- Return, Escape and the arrows are consumed by the menu (Escape closes
  it), so a dialog under the menu never confirms or cancels (#447).
- The class and Recently Added filters and Show Similar Fonts read every
  family's kind on a background thread, once per document font scope;
  the menu says Loading fonts... meanwhile. The web reads its few bundled
  fonts inline.
- The colour font badge uses theme tokens instead of fixed colours.
- Every font field, the hand-drawn ones included, reports as a combo box
  with the current family as its value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 09:56:51 +02:00
moppeeerandClaude Opus 5.5 40ba9915c9 test: The recently used font is the topmost row, whatever fonts are installed
With few fonts installed the font's own row in the main list is on screen
too, so the label matched twice (FreeBSD CI).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 09:55:02 +02:00
moppeeerandClaude Opus 5.5 8b0bc54bdb feat(ui): Lay out the font menu like InDesign's
The font menu (Control bar, Character panel, Properties, task bar, style
dialogs, Find Font, Glyphs) now has InDesign's layout:

- a search field with a clear button, and a Filters row: classification
  (All Classes, serif, slab serif, sans serif, script, monospaced,
  decorative, symbol), Show Favorites Only, Show Recently Added (fonts
  added in the last 30 days);
- the sample text (Selected Text, Sample, Aa Bb Cc, a pangram, digits)
  in three sizes, remembered with the UI state;
- Recently Used fonts at the top, then every family;
- a family of several styles shows how many and opens to list them, each
  previewed in its own face; picking a style applies it as it is;
- each family's format after its sample, and Show Similar Fonts (≈) to
  list the families of the same class nearest by PANOSE.

Find More and the activated-fonts filter are Adobe Fonts services and
are left out. The font combo is a field and a plain popup now: egui's
combo box kept its menu at the size it last had, so a family opening or
a filter clearing didn't show. The ≈ button stays while the pointer is
on it (the row under it no longer counts as hovered there). New strings
are translated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 09:55:02 +02:00
dotCooCoo 45f2a2090b Contributors: add dotCooCoo (Robert Lee) 2026-10-12 00:53:01 -07:00
moppeeerandClaude Opus 5.5 84a919d57a feat(engine): Report the selected text; remember recently used fonts
type.selectionAttrs reports the start of the selected text on one line
(`selectedText`, up to 60 characters), for previews such as the font
menu's Selected Text sample. The preferences keep `recentFonts`, the
fonts last picked in the font menus.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 09:43:22 +02:00
moppeeerandClaude Opus 5.5 cff8a9aa07 feat(fonts): A family's kind without keeping it loaded
ScopedFonts::family_kind reads a family's format, class, PANOSE and file
date from its Regular face. A loaded face is asked; an installed family
that isn't loaded is read from its file, which is let go afterwards,
instead of loading every family the font menu's filters look at for the
rest of the session. Files over MAX_DOCUMENT_FONT_BYTES are skipped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 09:43:21 +02:00
moppeeerandClaude Opus 5.5 eb80809034 feat(fonts): Font format, classification and PANOSE for the font menus
Each face reports what InDesign's font menu shows and filters by, read
once from its tables: its format (TrueType, OpenType/CFF, variable,
colour), its class (serif, slab serif, sans serif, script, monospaced,
decorative, symbol) from PANOSE, else the OS/2 IBM family class, else
the family name, else the shape of its "l" (serifs widen the foot), its
PANOSE numbers (with a distance for similar fonts) and when its file
was added.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 09:43:21 +02:00
moppeeerandClaude Opus 5.5 be4f60ff85 fix: A selected graphic's Transform values are the graphic's
With a placed graphic selected, transform.set acted on the graphic but
the Control bar and Properties still read the frame's values: after
setting the image of a -45° frame to 0°, a spinner step sent -44, and a
200 pt frame holding a 400 pt image showed W 200, so 201 halved the
image. transform.info now reports the graphic's values at the top level
in that case, and sel_info gives the graphic's bounds.

Also: `id` names the target explicitly, like `ids`, so it isn't taken
as a content selection; and a content selection of locked frames
records no empty undo step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 09:42:06 +02:00
moppeeerandClaude Opus 5.5 2f21b005f3 fix(engine): Transform values act on a selected placed graphic, not its frame
With a frame's content selected (Direct Selection, or selection.set
{content: true}), transform.set still rotated, scaled and moved the
frame. It now transforms the placed graphic inside the frame and leaves
the frame alone, as InDesign's Transform panel does: rotation, shear and
scale are absolute and measured as transform.info reports the content;
x, y, width and height are the graphic's bounds on the page. A
`content` parameter picks either explicitly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 09:36:32 +02:00
Akash Bhat c310cf72ed fix(compose): enable Norwegian hyphenation patterns
Enable hypher/norwegian in designcraft-compose so no/nb/nn map to the bundled hyph-no.tex patterns. Add unit + composition regression tests, NOTICE attribution, and update parity docs.

Fixes #535.
2026-10-12 12:09:40 +05:30
dotCooCoo 591cb579b3 Swatches: Load Swatches refuses .ase files over 100,000 colors and merges in linear time
ase::read accepted any number of colors in a swatch exchange file, and
swatch.load looked up each incoming name with a linear search through
the document's swatches. The time to load a file grew with the square of
its number of colors: in a test build, 50,000 colors used 2.5 s of CPU
and 100,000 used 9.6 s. No limit applied to larger files.

ase::read now returns an error for a file with more than 100,000 colors
(ase::MAX_COLORS). load_swatches looks up each name's first swatch in a
HashMap, and each color added from the file is indexed too. A file of
100,000 colors now loads in under 0.1 s. The merge follows the same
rules as before: names already in the document are kept unless replace
is set, a name repeated in the file matches its first copy, and a
locked swatch is left unchanged.
2026-10-11 23:18:46 -07:00
MarekSpodBiedry 2c2e3d73c7 Drop the arithmetic-only app bar test; the test window test covers it (#481) 2026-10-12 08:06:16 +02:00
MarekSpodBiedry f8f8eb619f Test the app bar scroll bar at 115% scale in the test window (#481) 2026-10-12 08:05:01 +02:00
aiongg e5c5f043ee Integration: format 2026-10-12 05:56:41 +00:00
aiongg 5f6ee139da Integration: anchored objects wrap through next's wrap_obstacles (#557 × next's wrap sides and contours)
#557 built an anchored object's exclusion as its offset bounding box; next's Exclusion also has a side and a contour, built by wrap_obstacles for page items. wrap::obstacles keeps the object and its parent → spread transform, and wrap::exclusions runs wrap_obstacles with it, so anchored objects get sides (spine sides by page), contours (Same As Clipping from the clip), custom boundaries, Jump Object and the wrap of a graphic inside, each tagged with the anchor byte (after). Without this the merge doesn't compile. Test: anchored_object_wraps_with_its_contour_and_side.
2026-10-12 05:56:35 +00:00
aiongg 9a95cee172 Merge anchored-wrap: Text wrap: anchored objects wrap text (#557)
Conflict: ROADMAP.md: both progress lines
Conflict: docs/target-app-parity.md: next's header, anchored objects wrapping text added to its change list
Conflict: docs/typography-parity.md: next's header (dated 2026-10-12) with #557's change added; Text wrap row: next's text and caveat, plus "on page items and anchored objects"
Conflict: crates/compose/src/lib.rs: next's Exclusion (side, contour) with #557's after field (Exclusion::wraps_line; next's constructors and the tests' helper set None); next's contour_path and wrap_obstacles in frame_specs; next's line loop (carry, grid, wrap_slot) with the line's start byte passed to wrap_slot; wrap_slot, free_slots and estimate_slots take line_start and skip exclusions that don't wrap the line (Jump Object test and in-band edges included); #557's re-break trigger stays in the line loop
Conflict: crates/compose/src/cache.rs: next's ComposeOptions (section_marker) with #557's neighbour compositions
Conflict: crates/compose/src/tests.rs: both test blocks
Conflict: crates/render/src/damage.rs: #557's test before next's patched_diff helper
2026-10-12 05:54:58 +00:00
aiongg 6e9517b053 Text wrap: anchored objects wrap text (#557)
An anchored object with text wrap pushes aside the text of other stories
whose frames it overlaps on its spread, and the lines of its own story
after the line holding its anchor (custom and above-line objects). Its own
story is composed again with the objects where the previous pass placed
them until they settle, at most four more times. The composition cache
keys a story wrapped by other stories' anchored objects on their
signatures, and canvas damage repaints the stories they wrap.
2026-10-12 05:51:56 +00:00
aiongg 62520485e8 Integration: format 2026-10-12 05:48:15 +00:00
aiongg b4e2e553e5 Merge #408: Align panel: add objdistribute-* icons, reuse upstream objalign-*
Conflict: crates/ui-egui/src/icons.rs: test module keeps both (next's arrow fill test, #408's distribute icon test)
2026-10-12 05:48:01 +00:00
aiongg 5284bca791 Integration: the Color Picker from the Fill/Stroke box follows Formatting Affects Text (#552 × next's color_target)
#552 opens the picker without the "text" field and reads the selected text's colour itself. On next the proxy's swatches already come from color_target, which follows colors_affect_text (selected text, or text frames with Formatting Affects Text on), so the special case goes and the dialog gets "text": colors_affect_text(app) as the Swatches and Color panels' pickers do. Without it, OK coloured the frame instead of its text. Test: the_color_picker_from_the_fill_box_follows_formatting_affects_text; #552's test shares its double-click helper.
2026-10-12 05:46:45 +00:00
aiongg 048baca521 Merge #552: Double-clicking the Fill or Stroke box opens the Color Picker
Conflict: ROADMAP.md: both progress lines
2026-10-12 05:41:55 +00:00
aiongg 64b834f2e0 Merge anchored-settings: IDML: anchored objects take unset anchor settings from their object style (#555)
Conflict: crates/idml/src/import.rs: next's with_style kept after attr_or_style (#555 has no counterpart there); the anchored arm merged as next's header (TextFrame, no has_text guard) with #555's body
2026-10-12 05:41:43 +00:00
aiongg 3e7cca3a0d Integration: text wrap Same As Clipping follows the graphic's clipping path (#450 × #384)
#384's contour wrap followed the frame's path for Same As Clipping, which was all a graphic could clip to; #450 gives graphics clipping paths. contour_path takes the clip region (Graphic::clip_region) when the graphic has one. Test: a triangle clip widening downwards, Same As Clipping, left side: lines beside the narrow top run further in than those at the base.
2026-10-12 05:40:04 +00:00