Commit Graph
412 Commits
Author SHA1 Message Date
aionggandBrandon Thomas 70974afc66 Lists: numbering formats and expressions from IDML; an empty paragraph gets no bullet or number (#298)
* Lists: numbering formats from IDML (A, B, C; i, ii, iii; 01, 02 …)

A paragraph's numbering format, number expression, bullet character and
text after the bullet import from IDML and export back. Numbered labels
come from the expression ("^#.^t", "Tabel ^#^t") with its
metacharacters expanded, in place of a fixed "n." label. Adds 001 and
0001 number styles, keeps letter and roman labels short for huge
numbers, and counts saturate instead of overflowing.

* Lists: an empty paragraph gets no bullet or number

A list paragraph with no characters before its return draws no label,
and numbering carries on past it: in a story's own list, in named lists
(also across stories) and in cross-reference paragraph numbers.

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-10 04:08:56 -04:00
aiongg 2b8dc2bdeb IDML: text variables keep their text
A TextVariableInstance becomes the document's variable character instead
of being dropped. The file's TextVariable definitions replace the default
set: custom text, dates, file name, last page number, chapter number and
running headers are computed; other types (live captions) keep their
settings and show the result recorded in the file. Creation and
modification dates come from the package's XMP metadata.

Export writes the definitions, each instance with its current result,
and the XMP dates.
2026-10-10 07:55:33 +00:00
b581031d91 IDML: resolve table styles and correct cell height fitting (#288)
* ci: simplify testkit dependency match for Clippy

* IDML: resolve table styles and shared cell edges

* Compose: size table cells by baseline and physical bounds

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 03:50:09 -04:00
d0d4812ada Set a text selection as warichu inside the parent line (#280)
* Set warichu as smaller lines inside the parent line.

Type › Warichu stacks the selected run after the line is broken, closes up the rest of the line, and round-trips the IDML warichu properties. The line break itself still sees the full-size run.

* Keep clicks, the caret, and Up/Down on each warichu row.

The glyphs were already stacked inside the parent line, but hit testing, the caret, and vertical movement still used that parent line. Break minimums, row widths, spacing, and tab leaders now follow the small lines, and underlines sit on the small baseline.

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 03:47:14 -04:00
8146737e97 Text: a column, frame or page break ends its paragraph (#285)
A break character is stored as the last character of the paragraph it
ends, followed by the usual '\n' separator. The story model, paragraph
ranges, styles, editing and every exporter keep splitting paragraphs on
'\n' only, so the change stays in the places that create, lay out or
write break characters:

- IDML import writes the break and ends the paragraph, so the range that
  follows keeps its own paragraph style. NextOddPage and NextEvenPage map
  to new odd and even page break characters; IDML export writes a
  paragraph that ends in a break as that one Br with its
  ParagraphBreakType, so export and re-import give the same story.
- Word and RTF import, Type > Insert Break Character (with odd and even
  page breaks added) and typing end the paragraph after a break. Typing
  between a break and its return goes to the next paragraph; deleting
  either deletes both.
- Layout: a break that ends a paragraph leaves no empty line; the next
  paragraph starts in the next column, frame or page, as a paragraph start
  option would. Page breaks and Next Page start options move to the next
  frame on a later page; odd and even ones to the next page with that
  page number parity (no such frame: overset). A break inside a paragraph
  from an older document still moves the rest of that paragraph on.

Storing the break as a paragraph separator of its own would have changed
every place that counts or splits paragraphs on '\n'.

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 03:36:47 -04:00
aiongg 8ae759a848 Merge #298: Lists: numbering formats and expressions from IDML; an empty paragraph gets no bullet or number
Conflict: crates/compose/src/lib.rs: next's list_label match with its list_label glyph flag; arms take #298's empty-paragraph rule, number_label/bullet_label and saturating counter
Conflict: crates/compose/src/tests.rs: next's file with #298's appended tests
2026-10-10 07:15:49 +00:00
aiongg 107f658f0e Lists: an empty paragraph gets no bullet or number
A list paragraph with no characters before its return draws no label,
and numbering carries on past it: in a story's own list, in named lists
(also across stories) and in cross-reference paragraph numbers.
2026-10-10 07:01:05 +00:00
aiongg 2a2d20e0c2 Lists: numbering formats from IDML (A, B, C; i, ii, iii; 01, 02 …)
A paragraph's numbering format, number expression, bullet character and
text after the bullet import from IDML and export back. Numbered labels
come from the expression ("^#.^t", "Tabel ^#^t") with its
metacharacters expanded, in place of a fixed "n." label. Adds 001 and
0001 number styles, keeps letter and roman labels short for huge
numbers, and counts saturate instead of overflowing.
2026-10-10 06:57:28 +00:00
60ad82497b Text frames: column rules on screen, in PDF and in IDML; Column Rules in Text Frame Options (#274)
* Text frames: column rules draw between columns on screen and in PDF

A frame with Column Rule on gets a bar centred in each gutter, inside the
insets, broken around paragraphs that span columns; right-to-left and
vertical frames included. object.textFrameOptions takes columnRuleWeight
and columnRuleColor (unknown swatches are an error) and an inset array
whose null sides stay; document.inspect reports the rule.

* Text Frame Options: Column Rules fields; OK changes only edited fields

The dialog shows the selected frame's options, with an inset field per
side, and sends only what the user changed, so unequal insets survive.
The rule weight is read in points.

* Column rules: tint, horizontal offset and top/bottom insets; IDML import and export

Text Frame Options gains Tint %, Top Inset, Bottom Inset and Horizontal
Offset for the rule, and object.textFrameOptions and document.inspect
take and report them. IDML reads the ColumnRule attributes of
TextFramePreference on frames and object styles and writes them when a
rule is set.

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 02:49:00 -04:00
aiongg 57801d86a5 Merge #295: Text: Split Columns sets a paragraph in sub-columns within its column
Conflict: crates/compose/src/lib.rs: #295's split_cfg block and sub-column-aware next_column closure, then next's `go` closure unchanged (StartParagraph::NextColumn and column breaks reach the next sub-column; other start options go through Cursor::start, whose next_frame/next_page start a new split part)
Conflict: crates/compose/src/tests.rs: both sides' tests (closing brace restored after next's last test)
Conflict: crates/idml/src/tests.rs: both sides' tests (closing brace restored after next's last test)
2026-10-10 06:41:22 +00:00
01500ceb04 Paragraph Rules: a dialog for the selected paragraphs and a section in Paragraph Style Options (#263)
* Paragraph rules: a partial ruleAbove/ruleBelow edit keeps the other rule fields; Rule Above honours Text width

* Paragraph Rules: dialog for the selected paragraphs and a Paragraph Style Options section

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 02:28:09 -04:00
aiongg f39897db0b Text: Split Columns sets a paragraph in sub-columns within its column
Consecutive paragraphs with the same Split Columns settings form a split
block: their text fills the sub-columns of the column one after the
other from the block's top, inset by the outside gutter and separated by
the inside gutter, then moves on to the next column. When text follows
the block, its last part is balanced across the sub-columns and the text
continues below the deepest one at the full column width; a block that
ends the story fills its sub-columns in turn. Keep options don't apply
between a block's lines.

IDML: SplitColumnInsideGutter and SplitColumnOutsideGutter import and
export (paragraph attributes split_inside_gutter, split_outside_gutter).
2026-10-10 06:22:38 +00:00
06b4e4e860 Add A6 sheet preset to the welcome page (#248)
Co-authored-by: moppeeer <339764117+moppeeer@users.noreply.github.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 02:18:47 -04:00
617e487dff Styles: Character Style Options dialog (#144)
* Styles: editing a character style renames it everywhere it is used, changes its Based On and unsets attributes; [None] can't be edited

* 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…

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 02:14:30 -04:00
aiongg fe16e00e37 PDF/A keeps placed images; right-to-left tables and columns (#128)
* PDF: PDF/A exports keep placed PNG, JPEG, GIF and WebP images by writing them without interpolation (#71)

* Tables: new tables take their paragraph's direction, and the Table panel sets a table's direction (#100)

* Text: stories made in a right-to-left-bound document fill their columns from the right (#100)
2026-10-10 01:13:48 -04:00
aiongg e1ed4ff118 Merge #288: IDML: resolve table styles and correct cell height fitting
Conflict: crates/idml/src/import.rs: #288's table style import (based_on, region flags, borders, row/column fills and strokes) and #288's table() style resolution; next's alt_fills (#83) is unused and removed (#83's defaults hold under #288's cascade)
Conflict: crates/idml/src/tests.rs: next's file with #288's appended tests
Conflict: crates/doc/src/styles.rs: tests only; kept both
2026-10-10 05:12:12 +00:00
skylartaylor 56a8150709 IDML: resolve table styles and shared cell edges 2026-10-09 21:57:31 -06:00
aiongg 2a80cfe54c Merge #285: Text: a column, frame or page break ends its paragraph; odd and even page breaks
Conflict: crates/compose/src/lib.rs: start options, the break after the previous paragraph and a break inside a paragraph go through a local `go` that sends NextColumn to next's span-aware next_column (#276) and the rest to #285's Cursor::start; the in-paragraph break takes #283's `brk` (the line's last glyph); #283's ends_para uses is_break_char; Cursor keeps next's resume() and adds #285's start/next_page (#285's old next_baseline header dropped, next's is kept); the table branch is #285's
Conflict: crates/idml/src/tests.rs: tests only; kept both
Conflict: crates/textimport/src/docx.rs: tests only; kept both
2026-10-10 03:51:42 +00:00
aiongg 69f8d0fcdd Text: a column, frame or page break ends its paragraph
A break character is stored as the last character of the paragraph it
ends, followed by the usual '\n' separator. The story model, paragraph
ranges, styles, editing and every exporter keep splitting paragraphs on
'\n' only, so the change stays in the places that create, lay out or
write break characters:

- IDML import writes the break and ends the paragraph, so the range that
  follows keeps its own paragraph style. NextOddPage and NextEvenPage map
  to new odd and even page break characters; IDML export writes a
  paragraph that ends in a break as that one Br with its
  ParagraphBreakType, so export and re-import give the same story.
- Word and RTF import, Type > Insert Break Character (with odd and even
  page breaks added) and typing end the paragraph after a break. Typing
  between a break and its return goes to the next paragraph; deleting
  either deletes both.
- Layout: a break that ends a paragraph leaves no empty line; the next
  paragraph starts in the next column, frame or page, as a paragraph start
  option would. Page breaks and Next Page start options move to the next
  frame on a later page; odd and even ones to the next page with that
  page number parity (no such frame: overset). A break inside a paragraph
  from an older document still moves the rest of that paragraph on.

Storing the break as a paragraph separator of its own would have changed
every place that counts or splits paragraphs on '\n'.
2026-10-10 03:41:12 +00:00
aiongg 66d6479c09 Merge #274: Text frames: column rules on screen, in PDF and in IDML; Column Rules in Text Frame Options
Conflict: crates/idml/src/import.rs: next's text_frame_options(&[&El]) object-style chain with #274's column rule fields; #274's frame_options takes the prefs list and looks up ColumnRuleStrokeColor through Prefs; the frame site resolves the rule colour's swatch after building options from the chain (the chain borrows the importer)
Conflict: crates/idml/src/tests.rs: tests only; kept both
Conflict: crates/compose/src/tests.rs: tests only; kept both
Conflict: crates/pdf/src/tests.rs: tests only; kept both
2026-10-10 01:05:14 +00:00
aiongg 7d0bdbc916 Column rules: tint, horizontal offset and top/bottom insets; IDML import and export
Text Frame Options gains Tint %, Top Inset, Bottom Inset and Horizontal
Offset for the rule, and object.textFrameOptions and document.inspect
take and report them. IDML reads the ColumnRule attributes of
TextFramePreference on frames and object styles and writes them when a
rule is set.
2026-10-10 00:33:27 +00:00
aiongg e1cc16325a Merge #263: Paragraph Rules: a dialog for the selected paragraphs and a section in Paragraph Style Options
Conflict: ROADMAP.md: kept next's IDML1 line and #263's Paragraph Rules line
Conflict: crates/compose/src/lib.rs: Rule Above uses #263's first line and Column/Text width with #83's rule_color() for colour and tint
Conflict: crates/engine/src/cmd/text.rs: type.para params doc lists next's drop cap fields and #263's ruleAbove/ruleBelow; format_paras is #263's
Conflict: crates/ui-egui/src/dialogs.rs: kept both confirm arms (characterStyleOptions, paragraphRules) and both sets of tests
2026-10-09 23:59:08 +00:00
aiongg fa13bf24f4 Paragraph rules: a partial ruleAbove/ruleBelow edit keeps the other rule fields; Rule Above honours Text width 2026-10-09 23:42:11 +00:00
aiongg ee24cb949b Merge #129: Fix IDML Mojikumi spacing, kinsoku composition, and nested text preflight
#129's first commit (4f8d230) holds #124 and #126, which are already merged. Each conflicted file is a three-way merge of #129's own commits (4f8d230..pr/129) onto this branch, which takes #129's side against #126 and keeps the chain's (#81/#84) code.
Conflict: ROADMAP.md: kept both entries
Conflict: crates/compose/src/shape.rs: #129's Mojikumi metrics added; #81's pieces, the #126 composite-note piece and the #124 inline-object metrics unchanged
Conflict: crates/compose/src/tests.rs: tests only; #129's tests added, no duplicates of #124/#126's
Conflict: crates/engine/src/cmd/fonts.rs: #129's pub(super) for_each_story; #126's tests kept once
Conflict: crates/engine/src/cmd/preflight.rs: #129's nested-story paragraph rules; the used_fonts loop kept once
Conflict: crates/engine/src/tests.rs: tests only; #129's test added
Conflict: crates/fonts/src/fontdb.rs: #126 content (same in #129); no change
Conflict: crates/idml/src/import.rs: #129's Mojikumi import added
Conflict: crates/idml/src/tests.rs: tests only; #129's tests added
Conflict: crates/ui-egui/src/dialogs.rs: #126 content (same in #129); no change
Conflict: crates/ui-egui/src/lib.rs: #126 content (same in #129); no change
2026-10-09 22:52:22 +00:00
aiongg 988e45b0bb Merge #126: Fix composite font resolution, missing-glyph diagnostics, and document-wide font replacement
Conflict: crates/compose/src/shape.rs: #81 collects paint-only pieces and shapes them later, #126 shapes composite-font note labels inline. A footnote reference in a composite font becomes a Piece with `composite_note`, shaped alone in the piece pass with #126's per-entry label code (from the run's env, as in #126); other references stay ordinary pieces. #126's label fallback in shape_segment passes #81's StyleRuns.
Conflict: crates/fonts/src/fontdb.rs: #126's canonical_family lookup sits at the top of exact_style_traits, so #81's styles/traits_of/linked_style and #247's format-suffix fallback all use it; face() resolves canonical, then canonical without the format suffix, then #126's substitute_family, through #247's find_or_load; has_family is canonical_family as named or without the suffix
Conflict: crates/ui-egui/src/lib.rs: kept #84's system-clipboard copy match and added #126's Find Font on open and _fonts reset after it
Conflict: crates/ui-egui/src/dialogs.rs: OK label match has #146's Save and #126's Replace All; #80's alert-only OK kept, #126's Done label for findFont; find_font keeps #143's font_combo with #126's FALLBACK_FAMILY default
Conflict: crates/compose/src/tests.rs, crates/idml/src/tests.rs: tests only; kept both
Conflict: ROADMAP.md: kept both entries
2026-10-09 22:49:44 +00:00
aiongg 01799b4927 Merge #124: IDML: preserve anchored text frames and footnote artwork
Conflict: crates/compose/src/shape.rs: inline object on the baseline: #124's rule (its box alone sets the extents, not max with the font's) applied to every metric #83 tracks (ascent, typo_ascent, cap, xh); the above-line arm keeps #83's four fields
Conflict: crates/compose/src/tests.rs: tests only; kept both
Conflict: crates/engine/src/tests.rs: tests only; kept both
Conflict: crates/idml/src/tests.rs: tests only; kept both
2026-10-09 22:46:08 +00:00
aiongg 277c442814 Merge #96: Compose: an empty paragraph takes its mark's character format 2026-10-09 22:43:00 +00:00
aiongg 71074a987b Merge #84: Text editing, snapping and text threading (#23)
Conflict: crates/ui-egui/src/control.rs: tests only; kept #118's automation_contract_tests module and #84's tests module
Conflict: crates/ui-egui/src/lib.rs: kept #119's ImportedFile Inbox type and #84's ClipboardFn
Conflict: crates/ui-egui/src/menus.rs: tests only; kept #239's two Quick Apply tests and #84's copy/paste test
2026-10-09 22:41:29 +00:00
aiongg 4b24103b24 Merge #83: Layout: first baselines, Jump Object wrap, drop caps, IDML defaults 2026-10-09 22:40:47 +00:00
aiongg f4f0896f85 Merge #144: Character Style Options dialog; style.character.edit renames everywhere, validates Based On, null unsets; New Character Style opens the dialog
Conflict: crates/ui-egui/src/dialogs.rs: took #144's structure (shared basic_character_formats and character_color sections); paragraph_style_options reads a clone of the document as #143 does, so the font menu can borrow the app; basic_character_formats takes &mut DesignApp and uses #143's font_combo (font_menu_rows is gone) with a × button that clears a style's family
2026-10-09 22:35:29 +00:00
aiongg c18912db69 Merge #128: PDF/A keeps placed images; right-to-left tables and columns
Conflict: crates/pdf/src/export.rs: took #223's side (RasterMode with #147's decode check) and dropped #128's Exporter interpolate field; #128's RTL story/table work and its PDF/A test are kept
2026-10-09 22:16:55 +00:00
moppeeer 84be68d9c2 Add A6 sheet preset to the welcome page 2026-10-09 23:28:49 +02: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
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
Shao Wenbin 20ce4f5ecd CJK-MOJI9: preserve CID preferences and report unsupported glyph classification 2026-10-08 20:24:59 +08:00
Shao Wenbin 1e32767cef CJK-MOJI8: preserve narrow punctuation and manual body compression 2026-10-08 20:18:30 +08:00
Shao Wenbin bc43965a45 CJK-MOJI7: validate spacing data and inspect nested typography rules 2026-10-08 20:14:16 +08:00
Shao Wenbin d9f24b6ad5 CJK-MOJI6: respect glyph clusters and regional punctuation metrics 2026-10-08 20:09:29 +08:00
Shao Wenbin 14b04cbaf8 CJK-MOJI4: implement public preset boundary policies and punctuation classes 2026-10-08 20:01:57 +08:00
Shao Wenbin e1755c3cbe CJK-MOJI1: resolve Mojikumi tables and preserve IDML references 2026-10-08 19:50:50 +08:00
Shao Wenbin 4f8d230d70 CJK-BASE: integrate font resolution and special-text preflight branches 2026-10-08 19:50:38 +08:00
Shao Wenbin 2dbbf98900 FONT1: preserve composite font references and unify usage checks
Resolve imported composite fonts by resource identity and preserve literal
display names. Export AppliedFont as an object reference instead of registering
a composite definition as an ordinary font family.

Share composite lookup and typed font usage between composition, font inventory,
and preflight. Include nested cells and notes, and report only component fonts
selected by the document's characters.

Add synthetic coverage for reference round trips, footnote character mapping
and metrics, nested font inventory, component replacement, and undo.
2026-10-08 18:21:10 +08:00
Shao Wenbin 7c10b3ad41 IDML1: preserve anchored text frames and footnote artwork
Resolve forward story references before importing embedded frames and groups.
Compose embedded stories, retain anonymous-story objects for screen and PDF
rendering, and invalidate layout caches when embedded frame geometry changes.

Measure inline objects by geometry, fill open contours, reject cyclic frame
references, and distinguish unplaced stories from located overset text.

Add synthetic round-trip, composition, preflight, pixel-rendering, and PDF
regressions. Update the roadmap with the supported import and rendering paths.
2026-10-08 18:08:44 +08:00
aiongg 0f52f3ba89 Text: stories made in a right-to-left-bound document fill their columns from the right (#100) 2026-10-08 01:57:30 -04:00
Brandon ThomasandClaude Opus 5.5 38ec9ce7e0 Merge #106 (includes #83, #84, #81) into fix/empty-paragraph-mark
An empty paragraph's mark format now also supplies #83's first-baseline
heights through line_metrics.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 23:30:23 -04:00
Brandon ThomasandClaude Opus 5.5 01eef9ed21 Merge origin/main into fix/empty-paragraph-mark
The empty paragraph's mark metrics feed line_metrics alongside main's CJK
line reference.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 23:28:22 -04:00
aiongg 1bdca77eea Merge the fork's feat/text-editing-and-frames 2026-10-07 23:12:38 -04:00
aiongg 673039f5dc Merge the fork's feat/layout-parity 2026-10-07 23:12:31 -04:00
aiongg b40e221d58 Text: a frame removed from a thread keeps the story's direction
Tests that a frame taken out of a vertical or right-to-left thread gets
a new story with the thread's vertical setting and column direction.
2026-10-07 23:02:03 -04:00