Commit Graph
4115 Commits
Author SHA1 Message Date
aiongg de5d32c90f Merge #244: Rebind embedded image links when opening relocated packages (#235) 2026-10-09 22:15:59 +00:00
aiongg 553d017065 Merge #229: Prevent mixed gradient color spaces from aborting PDF export 2026-10-09 22:15:59 +00:00
aiongg 29e33dde3d Merge #152: Apply contextual escaping to HTML and EPUB exports 2026-10-09 22:15:59 +00:00
aiongg eedbaf6717 Merge #137: Commands: huge row and column counts can't exhaust memory 2026-10-09 22:15:59 +00:00
aiongg 997d18dcac Merge #149: Import: RTF and Tagged Text keep characters past U+FFFF 2026-10-09 22:15:59 +00:00
aiongg 69fc1c381f Merge #116: Images: set SVG text in the shared fonts when the bundled ones lack it 2026-10-09 22:15:59 +00:00
aiongg 13cc11b041 Merge #115: Compose: don't panic when a hyphenated paragraph starts with a no-break letter 2026-10-09 22:15:59 +00:00
aiongg ea4740cbc8 Control channel: a failed app.open answers with the error and opens no alert
An alert waits for a click that a control client can't give. app.open
reports the error in its response, as engine.execute does; File › Open,
drops and the command line still show the alert.
2026-10-09 22:13:15 +00:00
moppeeer 218dc42e5d Make menu entries selectable across the full row 2026-10-09 23:33:42 +02:00
moppeeer 84be68d9c2 Add A6 sheet preset to the welcome page 2026-10-09 23:28:49 +02:00
DWAA1660andCodex 658d8e1784 M5: rebind embedded image links when opening relocated packages (#235)
Reuse packaged-link candidates for embedded IDML images and native files.
Resolve missing original paths beside the opened document, retaining the
embedded image bytes and any original link that still exists. Bound path
lookup allocations and cover actual package relocation through commands.

Make existing data-merge test path assertions portable to Windows.

Co-authored-by: Codex <noreply@openai.com>
2026-10-09 15:07:20 -06:00
aiongg fd825af133 Copy and paste keys: copied objects paste with Ctrl+V on Linux and Windows
Copying or cutting objects without a native menu bar puts plain text on the
system clipboard (the copied text frames' stories, or U+FFFC), so the paste
key produces a paste event. Pasting into text ignores that placeholder and
pastes the copied objects as before.
2026-10-09 21:03:04 +00:00
aiongg 40eaaea9db Fonts: a family named with a format suffix such as (OTF) resolves to the installed family 2026-10-09 20:49:09 +00:00
aiongg 699d89c0ce IDML: layers import and export in stacking order 2026-10-09 20:44:57 +00:00
aiongg bd1618f131 UI: Quick Apply rows are left-aligned
Rows without a shortcut or style kind were drawn with their label
centred, because add_sized lays a button out centred and only the rows
with a shortcut have an element that fills the remaining width. Each
row is now laid out left to right in a fixed 460×22 area.
2026-10-09 20:30:23 +00:00
Dmytro MatviichukandCodex bb091b924a UK1: add Ukrainian interface translation
Translate existing interface catalogs and wire Ukrainian menu selection, contextual publishing labels and grammatical counters. Validate persisted selection, untouched document data, placeholder/key coverage and bundled Cyrillic glyphs.

Co-Authored-By: Codex <noreply@openai.com>
2026-10-09 22:15:53 +02:00
aiongg 68feb00f29 UI: the Quick Apply shortcut doesn't also run its first entry
Cmd+Return opened Quick Apply, and in the same frame the list saw the
same press as Enter and ran its first entry (New Document). A key press
that fires a command shortcut is now consumed, so nothing else handles
it that frame. This also stops Cmd+Return from inserting a paragraph
break while typing.
2026-10-09 18:55:25 +00:00
Skylar Taylor-Barrick a45375c335 FID2: preserve decoration color resets, geometry and paint order 2026-10-09 17:39:08 +00:00
Vikram Bhaskaran 6aaa4f0b75 Distribute selected table rows evenly and keep the edit undoable.
The rows share one fixed height that preserves their composed total. Merged rows and rows that are not laid out are refused, and the table stays unchanged.
2026-10-10 04:37:44 +11:00
Skylar Taylor-Barrick ee33fe3c35 FID1: prevent mixed gradient color spaces from aborting PDF export 2026-10-09 17:33:18 +00:00
ROSSAandClaude Opus 5.5 6aa92e70b4 PDF/X-4: RGB images carry sRGB, no image interpolation, pdf:Trapped in XMP
- An RGB image with no ICC profile of its own was written as DeviceRGB
  under the CMYK output intent, and the export only warned. In PDF/X-4
  it now carries the sRGB profile (assets/icc/sRGB-v2-magic.icc, ICC
  v2.1, CC0). JPEGs stay passed through. Untagged PNG, GIF and WebP,
  and the decoded TIFF, BMP and PSD fallback, go in as RGB with the
  profile. A re-encoded JPEG keeps the source's RGB profile or gets
  sRGB. Grey images and images with their own profile are unchanged.
- Every image constructor asked for interpolation. PDF/X-4 and PDF/A
  exports now write images without it, which also removes the
  ImageInterpolation error that stopped PDF/A exports with a placed
  image. Plain exports keep it.
- make_pdfx4 writes pdf:Trapped False into the XMP, matching
  /Trapped/False in the document information.
- check_pdfx4 reports an image that asks for interpolation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 12:18:18 -05:00
insanityxoffandClaude Opus 5.5 4e333c652f i18n: add Italian (it) UI translation
Adds an Italian interface catalog (crates/ui-egui/src/i18n/it.rs) covering every
key of the shared table and of the Arabic table (1,430 strings), written from the
English source using ordinary Italian desktop-publishing vocabulary.

Registers "Italiano" in Edit > Interface Language, wires it into tr(), and extends
the i18n and menu tests (lookup, no duplicates, full key coverage, translated menu
titles, language switch frames).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 18:36:55 +02:00
Ville-Pekka Koskinen aeaa9f9818 Swatch menus: make the whole row (chip included) clickable and show chips in the Contextual Task Bar
Add one shared swatch_menu_row (full-width clickable chip + name, current
highlight, hover) and use it in the Properties Appearance/text menus, the
shared swatch_picker (Control bar, Stroke, Character, Paragraph Border/
Shading) and both Contextual Task Bar fill/stroke menus. The Task Bar popups
gain a colour chip per swatch, and every swatch menu now applies on a click
anywhere on the row, including the chip.

Cap the popups at 280 pt so a full-width row keeps the menus InDesign-sized.
Label the chip controls and rows for accessibility.

Test: clicking a swatch chip in the Appearance Fill menu applies it (the Task
Bar uses the same swatch_menu_row helper).
2026-10-09 17:37:23 +03:00
zydvc26s67-webandCopilot App 3018c56e0c Character Styles: wire save and delete into the UI
Mirrors the Paragraph Styles fix (#169): Character Styles had no save
or delete affordance at all — not even an edit dialog existed.

- Add a new characterStyleOptions dialog (General: rename/based-on,
  Basic Character Formats, Character Color) reachable by double-click
  or the row's new "Edit Character Style…" context-menu entry.
- Add a style.character.delete engine command (mirrors
  style.paragraph.delete): rejects built-in styles, reassigns any text
  runs using the deleted style to a replacement (default [None]).
- Add rename and basedOn (with cycle rejection via the existing
  char_based_on_cycles) support to style.character.edit, matching
  style.paragraph.edit.
- Add a deleteCharacterStyle confirmation dialog with the same
  in-use detection and replacement-style picker as the paragraph one,
  wired via a new "Delete Character Style…" context-menu entry.
- Add Styles::resolve_char_style to resolve a character style's
  effective attributes for display, mirroring resolve_para_style.
- Add engine tests (edit persistence/rename, based-on cycle rejection,
  delete with run reassignment, built-in rejection) and UI-level
  dialog tests for both new dialogs.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-10-08 17:01:14 -07:00
zydvc26s67-webandCopilot App fde78978a4 Paragraph Styles: wire save and delete into the UI (#169)
The engine already supported style.paragraph.edit and
style.paragraph.delete, but the UI never exposed a delete action and
edit was only reachable by double-clicking a style row, with no
visible affordance.

- Add "Edit Paragraph Style…" and "Delete Paragraph Style…" entries to
  the paragraph style row context menu (hidden for built-in styles).
- Add a new deleteParagraphStyle confirmation dialog that detects
  whether the style is currently in use and, if so, lets the user pick
  a replacement style (defaulting to [Basic Paragraph]) for affected
  text before deleting.
- Add engine tests covering paragraph style edit (attribute merge and
  rename propagation), based-on cycle rejection, delete with text
  reassignment, and rejection of built-in style deletion.
- Add UI-level tests covering the edit dialog persisting changes and
  the delete dialog removing a style and reassigning text, including
  the default replacement style.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-10-08 14:55:11 -07:00
skylar taylor-barrick 4e4eaad1c8 PERF-MEM: release retired layouts and trim edit-history capacity 2026-10-08 15:35:20 -06:00
Philipp Krüger a11bd021cd feat(pptx): export documents as editable PowerPoint presentations
New designcraft-pptx crate (L3) and file.exportPptx command (File ›
Export PowerPoint…, CLI --export x.pptx). Each page becomes a slide of
native objects: shapes with fills, gradients, strokes and effects,
pictures cropped like their frames, native tables, and text boxes.

Text keeps DesignCraft's composition by default: composed lines end in
line breaks, justified spacing becomes character spacing, and leading
becomes percentage line spacing (PowerPoint rounds exact spacing to
whole points; its 100% line is 1.2 em for every font). Threaded
stories split at their frames and wrapped lines get their own boxes.
lineBreaks: reflow writes one box per frame in native columns instead.

TrueType fonts are embedded as Embedded OpenType when their licences
allow. Objects PowerPoint can't represent (placed PDF/EPS, type on a
path, some effects) are rasterised through the existing
rasterize_where path.

Checked in PowerPoint for Mac and LibreOffice: on the sample magazine,
word positions match DesignCraft's PDF to within 0.8 pt horizontally
and 1.6 pt vertically.
2026-10-08 23:32:53 +02:00
skylar taylor-barrick abf9a40dc0 PERF-CI: use fixed-size chunks in PSD decoding 2026-10-08 15:29:32 -06:00
skylar taylor-barrick b050343a6c links.relink: make replacements atomic and retain EPS sources 2026-10-08 11:13:17 -06:00
Jan Hallvard LarsenandClaude Opus 5.5 c63ca54227 Import: RTF and Tagged Text keep characters past U+FFFF
Placing an RTF or Tagged Text file dropped every character outside the
Basic Multilingual Plane: emoji, mathematical letters and the rarer CJK
ideographs such as 𠮷 (U+20BB7, used in Japanese names). The text
around them came in, so the loss was silent. It also broke our own round
trip: Export Text writes these characters correctly and Place lost them.

Both formats carry such a character as a UTF-16 surrogate pair, two
units in a row: \u-10179?\u-8704? in RTF, <0xD83D><0xDE00> in Tagged
Text. Both readers passed each unit to char::from_u32 on its own. Half a
pair is not a character, so each half was dropped.

A small shared Utf16Units joins the units: a first half waits for the
second, and the pair becomes one character. Half a pair with no partner
is still left out, as before.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 16:33:44 +01:00
Skylar Taylor-Barrick 6a53d95895 Security: escape HTML and EPUB metadata 2026-10-08 15:31:35 +00:00
aiongg 7d064f268e UI: the Contextual Task Bar moves by its grip and stays where it is dropped, with Pin Bar Position and Reset Bar Position in its menu 2026-10-08 11:27:01 -04:00
Jan Hallvard LarsenandClaude Opus 5.5 1ed206642f PDF: a damaged PNG, GIF or WebP is skipped, not fatal
A placed PNG, GIF or WebP that is cut off (an interrupted download) or
has a bad checksum made the whole PDF export fail with
"PDF writer error: Image(Image(ImageRepr {..}), None, "unexpected end of
file")", which names no file. A damaged TIFF or BMP in the same place is
skipped with the warning "image `name` could not be decoded and was
skipped", and the rest of the document is exported.

The exporter decides whether an image can be embedded by asking krilla
to open it. krilla opens PNG, GIF and WebP lazily: it reads the header,
accepts the file, and decodes the pixels only when the PDF is written,
where an error ends the export. TIFF and BMP are decoded by us first, so
they took the skip path.

lossless() now decodes the image first for every format. A file that
does not decode returns None and takes the existing skip-with-warning
path. This costs one extra decode per PNG, GIF or WebP asset: the sample
magazine (two PNGs, 820 KB) exports in 0.19 s instead of 0.17 s in a
debug build.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 16:26:30 +01:00
Jan Hallvard LarsenandClaude Opus 5.5 c84eb56258 UI: closing a document with unsaved changes asks first (#141)
File ▸ Close, its shortcut (Ctrl/Cmd+W) and the tab's × closed a document
with unsaved changes at once. Closing also discards the document's
recovery data, so the edits were gone for good.

The three user paths now go through menus::close_document. A document
with unsaved changes opens a dialog with Save, Don't Save and Cancel:

- Save runs the same Save the menu does, then closes. If the document
  is still unsaved afterwards (the file picker was cancelled), it stays
  open.
- Don't Save closes it.
- Cancel (or Escape) does nothing.

A document with nothing to save closes as before. The engine command
file.close is unchanged and never asks, so scripts, MCP and the control
channel's engine.execute keep working unattended. ui.menu.invoke without
params acts like the menu item, as it does for other dialogs.

Quitting the app is not covered here. Recovery data survives a quit, so
unsaved documents come back on the next start.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 16:20:51 +01:00
Jan Hallvard LarsenandClaude Opus 5.5 20422c4725 UI: the Control panel shows its second row
In every workspace that shows the Control panel (Advanced, Typography,
Printing and Proofing, Book), the second row of fields was cut off at the
panel's lower edge: Y and H in object mode, leading, tracking and the
indents in text mode.

The panel lays its groups out in a row that centres each child
vertically. The two-row grids were placed as if they were one row tall
and then grew downward, so they started 13.5 pt below the panel's top
instead of at it and ended 5.5 pt past the 59 pt panel.

The row is now top-aligned, which is where the font menus, the
separators and the reference-point proxy already sat.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 16:14:24 +01:00
Jan Hallvard LarsenandClaude Opus 5.5 e8ee4cffb4 Docs, About: the Illustrator-class sibling is VectorCraft (#59)
DrawCraft was renamed VectorCraft, and its repository is now
storytold/vectorcraft. Three places still used the old name:

- AGENTS.md and CLAUDE.md told contributors and agents that the sibling
  checkout lives at ../drawcraft. A clone made from the README link lands
  in ../vectorcraft, so that path does not exist.
- The About window's sibling list used the app-page slug "drawcraft".
  getartcraft.com/apps/drawcraft still redirects to /apps/vectorcraft,
  so the link worked, but the README (which the list says it matches)
  uses /apps/vectorcraft.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 16:04:52 +01:00
Jan Hallvard LarsenandClaude Opus 5.5 9ec7858dc9 Web: AltGr and other named keys no longer type their names (#134)
In the browser, pressing AltGr while editing text on the canvas inserted
the word "AltGraph" into the story.

eframe's web backend (0.36.2, web/input.rs text_from_keyboard_event)
turns a keydown into a text event unless the key is on a fixed list of
names. Browsers report many more named keys than that list holds:
AltGraph, Dead, Unidentified, the media keys. Each one arrived as text.
Panel text fields are not affected, because a focused egui text field
takes its text from the browser's input events instead of keydown.

The web shell now drops a text event that is a key name (ASCII letters
and digits, starting with a capital, longer than one character) unless
an egui text field has the keyboard. One key press never types such a
word; a text field's input events can, so they are left alone.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 15:54:52 +01:00
aiongg 9fec15876a UI: captions in a sideways-scrolled bar stay on one line
The last group of a scrolled Control bar row got only the width left
over, so a caption like Columns wrapped one letter per line.
2026-10-08 10:51:46 -04:00
Jan Hallvard LarsenandClaude Opus 5.5 7c739feb6f Commands: huge row and column counts can't exhaust memory
Three commands took a count from their parameters and used it unchecked.
Parameters come from MCP, the control channel and scripts, so the count
is hostile input (never-crash: cap input-sized allocations, checked
arithmetic).

- layout.createGuides made one or two guides per row and column with no
  upper limit: 30 million rows took 1.2 GB, and 2^32 - 1 would exhaust
  memory, which aborts the process past the command guard. A count over
  2^32 wrapped to a small one. Both counts are now capped at 100.
- edit.stepAndRepeat multiplied rows by columns as usize. In debug builds
  the overflow panicked; in release builds 2^32 x 2^32 wraps to 0, passes
  the 1000-copy limit and starts a grid of 2^64 copies. The product is
  now checked, and a grid that large falls back to `count` copies like
  any other grid over 1000.
- file.new stored any column count in the page (4 billion, or a wrapped
  value past 2^32). Page::column_rects builds one rect per column each
  time the columns are laid out. The count is now clamped to 1..=216,
  the range layout.marginsAndColumns already uses.

Each case has a regression test that failed before the fix.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 15:45:15 +01:00
Jan Hallvard LarsenandClaude Opus 5.5 27b5c871bd Images: PSD decode passes clippy on Rust 1.98
Rust 1.98 adds the clippy lint chunks_exact_to_as_chunks. It flags two
loops in the PSD decoder, so `cargo xtask ci` stops at the clippy step on
unmodified main for anyone on current stable.

Both loops now use as_chunks / as_chunks_mut (stable since 1.88, below the
workspace rust-version 1.90). The chunks are arrays, so the try_from that
could never fail goes away too. Decoded pixels are unchanged; the existing
PSD tests cover both paths.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 15:32:20 +01:00
aiongg 63a1f03498 UI: the Properties panel's Character section sets the text's language, from the same list as the Character panel (#130) 2026-10-08 10:14:18 -04:00
aiongg 8ddd20d95d UI: right-clicking a ruler picks that ruler's units, and the vertical ruler measures in the vertical units (#133) 2026-10-08 10:10:41 -04:00
aiongg ca0ab6e551 UI: Character Style Options edits a character style's name, Based On and character formats, opened by double-clicking the style, Edit in its menu, or New Character Style… 2026-10-08 10:03:57 -04:00
aiongg 11585c82fa Styles: editing a character style renames it everywhere it is used, changes its Based On and unsets attributes; [None] can't be edited 2026-10-08 09:46:59 -04:00
aiongg cab09f41eb UI: every font field opens the same searchable font menu, with the search ready for typing and hidden system fonts left out 2026-10-08 09:30:19 -04:00
Shao Wenbin 9f655739fa CJK-REVERT2: withdraw speculative spacing rules and narrow import rejection
Restore the previous preset and character-class coverage. Remove regional punctuation and Tsume policy changes while retaining cluster, tracking, and spacing-allocation repairs. Preserve finite unsupported rows for diagnostics instead of rejecting whole documents.
2026-10-08 20:36:21 +08:00
Shao Wenbin 56febef6ab CJK-REVERT1: revert CID preference and diagnostic expansion
Revert 20ce4f5 because the compatibility extension does not implement CID-based layout and is not required for the verified spacing repairs.
2026-10-08 20:32:53 +08:00
aiongg 79f9d56cef MCP: docs name the UI-only commands precisely; app.links works headless (#62) 2026-10-08 08:30:57 -04:00
aiongg 75aa5356ba MCP: document that text offsets and story lengths are UTF-8 bytes (#68) 2026-10-08 08:30:57 -04:00
Shao Wenbin 20ce4f5ecd CJK-MOJI9: preserve CID preferences and report unsupported glyph classification 2026-10-08 20:24:59 +08:00