Commit Graph
564 Commits
Author SHA1 Message Date
.CooCooandDylan M. Taylor 5fb75aaefc Properties: Text Frame section shows inset spacing and vertical justification (#2661)
* Properties: Text Frame section shows inset spacing and vertical justification

Properties › Text Frame now shows the frame's inset spacing under the
columns: Top and Bottom, a link that makes all four the same, then Left
and Right. A non-rectangular frame shows one Inset, the distance its text
keeps from the outline. Four buttons set Vertical Justification to Top,
Center, Bottom or Justify. Type on a path shows neither.

The section also shows with the Type tool in a frame. There it changes
the frame that shows the insertion point, or every frame that holds part
of the selected text, as Object › Text Frame Options does.

* Properties: one Inset for a non-rectangular frame keeps the other sides

The single Inset sets only the top inset, the one kept from the
outline. A rectangular frame that holds part of the same selected text
keeps its own sides.

* Properties: the one Inset field and the composer share the frame outline test

FrameOutline::of_item builds an item's outline with its corner options
applied. The composer uses it to keep the top inset from the outline,
and the Properties panel uses it to decide whether a frame shows one
Inset, so the two give the same answer for every frame.

* Text Frame Options: frames taken while typing stay with their document

OK is refused when another document became active after the dialog opened, so the saved frame ids do not change frames in that document.

* Properties: the inset link starts linked again after Revert

The link's state is kept per document state instead of per document, so
a frame with equal insets in the reverted document starts linked.

* Text Frame Options: frames taken while typing are refused after Revert

Revert keeps the document's id but replaces its state, so a new frame
can reuse a saved frame id. The dialog now ties the frames to the
document state, and OK is refused after Revert as it is in another
document.

* Properties: the one Inset of a non-rectangular frame sets all four sides

The composer bounds a shaped frame's text by its rectangle less all four
insets as well as by the outline, so a single Inset that changed only the
top left hidden sides in place: lowering a uniform 96 pt inset to 0 kept
text overset. When every frame the field changes is shaped, it now sets
all four sides. With a rectangular frame among them, only the top inset
changes and each frame keeps its other sides.

* Fix text frame dialog merge resolution

* Keep text frame preview targets fixed

* Fix target field key inference

* Tests: Text Frame Options show four sides for text through an oval and a rectangle

With the Type tool and text selected through an oval and a threaded
rectangle, with the oval as the caret's stored frame, the dialog shows
the four inset sides, a changed Top leaves the rectangle's other sides
as they were, and the link stays off while the rectangle's sides are
uneven.

* Properties: the inset link starts off when another frame has uneven sides

With selected text that runs through several frames, the link of Text
Frame > Inset Spacing started linked when the frame the section shows had
four equal insets, and a changed Top then set all four sides of every
frame. The link now starts linked only when every frame the field changes
has four equal insets. Its state is kept for that set of frames, so a link
turned on for one frame does not carry over to selected text that also
runs through a frame with uneven sides.

---------

Co-authored-by: Dylan M. Taylor <dylan@dylanmtaylor.com>
2026-11-12 17:27:08 -05:00
.CooCoo 6923c220ad Text wrap: Bottom and Center move a line down between a contour's arms (#2719)
A line set in the open middle of a U-shaped contour now goes down to the
contour's base. The clearance check tests the occupied parts of the
contour in the line's band, as line setting does, instead of its outer
extent across both arms.
2026-11-12 17:24:40 -05:00
.CooCoo c803ea80b1 Compose: vertical justification in a shaped frame tests lines to their baselines (#2708)
Center, Bottom and Justify tested every moved line except the last against
the frame's outline down to its descent, so an outline that narrows under an
earlier line held the text higher by that line's descent. Each moved line is
now tested from its top down to its baseline, and its descenders may cross
the outline, as the last line's already did. Above footnotes and in vertical
frames the descenders still stay inside.
2026-11-12 17:24:34 -05:00
.CooCoo 9595f3d62e Anchored text frames: test nested hosts in groups, footnotes and table cells (#2722)
Adds three tests for a text frame anchored inside another anchored text
frame: the outer frame as a member of an anchored group with a rotation
and a scale, the inner frame in a footnote of the outer frame, and the
inner frame in a table cell of the outer frame. Each checks the inner
frame's spread, transform and page.
2026-11-12 17:23:50 -05:00
dotCooCoo fb77a73d93 Compose: Center on the grid takes the fitting step nearest the middle in a shaped frame
When the grid step nearest the centered place puts the lines outside a frame that narrows and widens again, Center now tries the steps on both sides of it, nearest first, instead of only the steps above it.
2026-11-12 13:05:34 -08:00
dotCooCoo 946e3ea4c1 Compose: grid-aligned vertical justification takes only grid steps that fit a shaped frame
In a frame that narrows and widens again, Bottom, Center and Justify now
try lower grid steps until the lines fit the outline, and leave the lines
in place when none does, instead of taking the next lower step unchecked.
A grid whose step is too small to count across the column is now treated
as no grid when a line is placed, so the line keeps a finite baseline.

Tests cover a waist-shaped frame on a 40 pt grid, a tiny-step grid that
starts away from the line, and Justify with a wrapped paragraph under
All Lines and First Line Only.
2026-11-12 13:05:34 -08:00
Dylan M. Taylor fc5221060c Fix vertical justification test arguments 2026-11-12 15:59:29 -05:00
Dylan M. Taylor f88e6711e0 Merge latest main into baseline grid justification PR 2026-11-12 15:54:56 -05:00
Dylan M. Taylor 1f2900fb50 Merge main into baseline grid justification PR 2026-11-12 15:29:33 -05:00
dotCooCoo e05d943d23 Merge main into fix/bottom-aligned-text-wrap 2026-11-12 12:24:22 -08:00
dotCooCoo cc7f289187 Merge main into fix/bottom-aligned-text-wrap 2026-11-12 10:50:05 -08:00
Codex 2a2d73ead9 Merge main preserving incremental composition and alpha-wrap caching 2026-11-12 19:41:39 +01:00
FredApps 17b74601fb Merge pull request #2663 from dotCooCoo/feat/alpha-channel-text-wrap
Text Wrap: Alpha Channel contours follow a transparent image's visible pixels
2026-11-12 19:38:50 +01:00
dotCooCoo a8656662e9 Compose: vertical justification looks up each wrap's anchored object once
The object anchored at each story byte is indexed once per column, and each wrap's object is found once, not once for every line.
2026-11-12 09:29:14 -08:00
dotCooCoo 48ebdd9ffc Merge main into fix/bottom-aligned-text-wrap
Center and Bottom take the least room any line has above a wrap under it,
measured from the baseline as main now measures the bottom of the text.
Justify checks each row's room together with main's outline and area checks
in one search, so shaped-frame containment and wrap clearance hold together.
The Bottom tests now expect the last baseline on the text area's bottom.
2026-11-12 09:24:37 -08:00
FredApps 2348380198 Merge main and preserve PR #2663 behavior 2026-11-12 18:14:28 +01:00
FredApps 268f432385 Merge main and preserve PR #2699 behavior 2026-11-12 18:14:28 +01:00
Codex e81b2ea99e Merge main and preserve table query options and ID guidance 2026-11-12 18:13:30 +01:00
FredApps f3f5cd3e23 Merge pull request #2589 from dotCooCoo/fix/nested-anchored-frame-placement
Anchored text frames: place a frame anchored inside another anchored frame
2026-11-12 18:02:52 +01:00
FredApps 2522b78ef3 Merge pull request #2656 from dotCooCoo/fix/preflight-off-interface-thread
Preflight: check the document on a background worker, not on every edit
2026-11-12 17:56:38 +01:00
dotCooCoo 5e60c4d8fe Compose: vertical justification stays finite with an extreme grid increment
A grid increment so small that the room holds more steps than an f64
can count made the grid step count infinite, and Bottom, Center and
Justify then moved the grid-aligned lines to an infinite baseline. Such
an increment now leaves the lines placed as they are without a grid.
A Center step so large that doubling it overflows no longer turns a
zero step count into a NaN shift; the lines stay where they are.
2026-11-12 08:54:29 -08:00
Dylan M. Taylor e2b5dbaa8a Merge pull request #2648 from dotCooCoo/fix/issue-1999
Compose: vertical Center, Bottom and Justify measure to the last baseline
2026-11-12 11:29:27 -05:00
dotCooCoo e22a6362cc Compose: list every case that composes a story whole in gap 20
docs/gaps.md now lists the same cases as docs/architecture.md and
reuse::plain. A doc comment on rejoin says that a run style an edit uses
for the first time stops the reuse until the old composition reaches the
same style at the same index.
2026-11-12 06:53:12 -08:00
dotCooCoo ba81cb05e9 Compose: an edit composes only the paragraphs it can change
Every edit of a story composed it from its first paragraph, so a typed
character in a long threaded story cost as much as laying out the whole
story: 456 ms in a story threaded over 300 pages.

A composition that lays every paragraph once keeps its state before each
paragraph. After an edit, composing starts one paragraph above the first
changed one, in the kept state and with the lines above copied. It stops
at the first unchanged paragraph it reaches in the state kept for that
paragraph, and copies the lines from there on with their story offsets
moved by the length of the edit.

- The cache does this when only the story differs from its entry.
- Composed whole as before: stories with footnotes, endnotes, tables,
  anchored objects, named lists, tracked deletions, balanced columns,
  vertical justification other than Top, column rules, type on a path,
  spanning or split paragraphs, paragraph borders or shading, and a
  layout that goes back over a paragraph (keep options).
- An edit that moves lines into other frames is composed to the end of
  the story.

In a 240-paragraph story a typed character composes 3 paragraphs
(240 before).

Docs: docs/architecture.md, docs/gaps.md (gap 20), ROADMAP.

Refs #2298
2026-11-12 06:21:26 -08:00
dotCooCoo 95fdb96680 Text wrap: a clipped contour visits only the edges in each line band
A clipped contour now looks only at the edges of the outline and its clips
that reach a line band, and charges its work budget for those alone. An
outline with thousands of points elsewhere no longer exhausts the budget
and falls back to the projections' common part. The Text wrap row of
docs/typography-parity.md notes that an image over 40 megapixels, or one
that can't be decoded, follows the frame's path.
2026-11-12 05:19:22 -08:00
autocraft-bot[bot] 54294ef791 Merge main into feat/alpha-channel-text-wrap 2026-11-12 11:38:19 +00:00
Dylan M. Taylor 027bbae9cb Engine: index style, tag, swatch and run lookups; bound polygon sides, Change All and spelling candidates
* fix(engine): index lookups that were quadratic in a document's styles, tags, swatches and runs

* address review: first swatch per name in book sync; bound spelling input before collecting

* address review: keep the last source style per name in book sync; bound GREP replacement bytes while generating them

* address review: write GREP replacements into a byte-bounded buffer; gate keep_last test off wasm

* merge main into fix/sec-engine-linear-lookups

* address review: make the suggestion length-cap test deterministic
2026-11-12 06:28:15 -05:00
dotCooCoo 61428f05d9 Compose: Justify keeps later lines out of an object anchored in an earlier line
An object anchored in a line that Justify moves goes down with that line.
The lines after it now stop where the object ends up, so the last line no
longer runs into it. The Bottom tests also check that a wrap that does not
stop the text leaves the last line on the text area's bottom.
2026-11-11 22:15:31 -08:00
dotCooCoo eda934fff0 Compose: vertical justification keeps moved lines inside a shaped frame
Center, Bottom and Justify moved the lines of a shaped frame without
testing them against the outline, so in a frame that narrows towards the
bottom a line set in a wide band ended up crossing the outline. Each moved
line's slot is now tested against the outline over its new band, as when
the line was fitted, and the text goes only as far down as every slot
fits. In a frame that narrows and widens again, the search scans the
rooms from the most down (every point, at most 512 steps) before halving,
so the text can still reach a lower part past a narrow waist.
2026-11-11 21:54:27 -08:00
dotCooCoo 7af2bd3b0b Merge feat/alpha-channel-text-wrap from origin into feat/alpha-channel-text-wrap 2026-11-11 21:53:41 -08:00
dotCooCoo bdabdc8155 Preflight: show a pending first check as checking, and rebuild document indexes on grid changes
- The status bar and the Preflight panel show a gray light and "Checking" until the active
  document's first check ends, instead of a green "No errors".
- The cross-reference and running-header indexes are keyed on the baseline grid and the font
  scope as well, so a change to either builds them again, in a cache and in a cache over it.
- The test helper that waits for the preflight worker fails when the worker ends without a result.
2026-11-11 21:51:37 -08:00
dotCooCoo ae1de74270 Text wrap: an Alpha Channel contour counts only inside the graphic's clipping path
Opaque pixels outside the placed graphic's clipping path (inverted or not) are not drawn, and they no longer push text aside. An outline that only touches a clip's edge blocks nothing.
2026-11-11 21:45:24 -08:00
dotCooCoo 30c72a032e Compose: vertical justification keeps text out of a wrap under it
Center, Bottom and Justify moved the lines down without looking at text
wraps, so a number aligned to the bottom of its frame was drawn over a bar
whose wrap should have kept it above the bar.

The lines now move down only as far as the wraps under them allow. A
baseline comes down to the top of a wrap under its slot, and a table
fragment stops with its bottom there. Contour wraps are checked along the
band each line sweeps through, beside the line as well as under it. Only
wraps that reach into a line's column count, as when the lines are set.
Center and Bottom move the lines together by the least room; Justify keeps
the first row in place and limits each later row by the wrap under it. An
anchored object that moves with its line is skipped under Center and
Bottom.
2026-11-11 21:44:46 -08:00
dotCooCoo 544ebaf571 Text wrap: a relinked image's new outline recomposes the text around it
- each traced Alpha Channel outline has a serial number, and the composition signatures include the outlines in use, so a relink or link update that changes only the image data no longer leaves text laid out around the old image
- a wrap with the default alpha threshold is saved without it, as before thresholds were stored
- docs/typography-parity.md says Alpha Channel contours follow the image's visible pixels
2026-11-11 21:33:53 -08:00
dotCooCoo b6d6d7e091 Text wrap: relinked images and graphic wraps on anchored frames update the layout
- The composition cache compares each Alpha Channel outline by a serial
  number, so a relink or link update lays the text out again around the
  new image.
- A contour clipped by its frame blocks only the part of the outline the
  frame shows, even where the two share x positions in one line band.
  A fragmented frame, or shapes too detailed for a line's budget, keep
  the intersection of the two projections.
- An anchored frame whose wrap is set only on its placed graphic wraps
  text.
- The default alpha threshold is not saved.
- docs/typography-parity.md describes the Alpha Channel contour.
2026-11-11 21:26:11 -08:00
dotCooCoo 97ed25093f Text wrap: a vertical open path keeps its offset, and previewed images are traced
A contour's zero-width spans (a vertical open path) are kept until the
wrap offset widens them, so text no longer crosses such a path. A record
previewed into an image placeholder gets the image's Alpha Channel
outline, as an edit would.
2026-11-11 20:23:59 -08:00
dotCooCoo 9fcab8e66b Text wrap: Alpha Channel outlines fit composition, and an empty one wraps nothing
An outline with more path elements than composition follows is traced on
a coarser grid, so a fragmented image keeps an Alpha Channel contour
instead of falling back to the frame. An image with no pixel more opaque
than the threshold leaves the text alone.
2026-11-11 20:13:21 -08:00
dotCooCoo 1fbb7e86b0 Merge #1084 (Alpha Channel text wrap) into main 2026-11-11 19:50:30 -08:00
dotCooCoo ee69d5ac39 Preflight: the worker keeps its compositions out of the interface thread's cache
The worker checks the document state it was given, which is often one
edit behind the interface. It composed that older state into the shared
composition cache, replacing the interface thread's composition of the
current state, so the next frame composed the edited story again (about
14 ms per edit for a 10-page story).

Cache::over makes a cache that uses another cache's compositions of the
same state and keeps whatever it composes to itself. The worker checks
with one over the interface thread's cache, keeps it for the next check
of the same document, and drops it after a minute without jobs.

With no active document, the job waiting for a busy worker is dropped,
so it no longer keeps a closed document in memory until another
document opens.
2026-11-11 19:42:03 -08:00
dotCooCoo 5c6d45908d Compose: vertical justification keeps grid-aligned lines on the baseline grid
With Align to Baseline Grid, Center and Bottom moved the lines by any
amount, so they left the grid. They now move by whole grid steps: Bottom
puts the last baseline on the lowest grid line above the bottom inset,
and Center on the grid line nearest the centered position. With Justify
the first line stays in place and the last baseline goes to the lowest
grid line above the bottom inset; the lines between are spread evenly.
2026-11-11 19:25:22 -08:00
dotCooCoo 9041fd3153 Compose: Bottom, Center and Justify ignore corner radius and descent room
Bottom and Justify now put the last baseline on the bottom inset in a
frame with a corner radius as in a rectangular one, so a radius no longer
moves the text when the bottom inset is above 0. Center puts the last
baseline halfway between where Top sets it and the bottom inset even when
the frame is too short for the descenders under it, so Cap Height text in
a tight frame is centered. The last line's descenders hang below its
baseline, into the bottom inset and past the frame's edge when the inset
is smaller than the descent. An earlier line whose descenders reach below
the last baseline (tight leading) is not moved past the text area. Above
footnotes and in vertical frames the descenders still stay inside the
text area.
2026-11-11 18:02:16 -08:00
dotCooCoo 86867e8e42 Compose: vertical Center, Bottom and Justify measure to the last baseline
Vertical justification measured the free space down to the bottom of the
last line's descenders, so Center set text too high (most visibly with a
Cap Height first baseline) and Bottom and Justify left the descender depth
empty. The last baseline is now the bottom of the text: Bottom and Justify
put it on the bottom inset, and Center leaves as much room below it as
above the first line. The descenders may reach into the bottom inset but
not past the frame's edge; above footnotes, in vertical frames and in
shaped frames they stay inside the text area. Tables keep their bottom.

A shaped frame no longer tests the part of a line above its text area
against the outline, so a small corner radius leaves a Cap Height first
line where a rectangular frame puts it.

Fixes #1999
2026-11-11 17:31:56 -08:00
dotCooCoo b18506e3b5 Anchored text frames: place a frame anchored inside another anchored frame
placed::locate only looked for an anchored frame in host frames that sit
on a spread. When the host frame was itself anchored, the inner frame got
no placement and composed where it is stored, on no page. The host frame
is now placed through its own host first, up to MAX_DEPTH hosts out.
2026-11-10 19:31:41 -08:00
skylar taylor-barrick c2211e0f41 Merge pull request #1161 from y-az/feat/anchored-text-frames
Anchored text frames: own stories and ids through copy, paste, duplicate, release and delete
2026-11-10 20:48:08 +01:00
aiongg 0078f832a7 Merge pull request #2179 from storytold/fix/sec-grep-compile-and-scan-budget
GREP: refuse subroutine calls and bound the empty-match scan
2026-11-09 21:23:30 -05:00
Youssef Azriouil 7a65f847c2 Anchored text frames: own stories and ids through copy, paste, duplicate, release and delete
Copying, pasting, deleting or releasing an anchored text frame left a
document that Document::check rejects, so once saved it could not be
opened again.

- Copy and paste: copied text takes the stories of its anchored text
  frames along. Pasting text (also into another document) gives the
  anchored copies fresh ids and stories of their own
  (Document::adopt_anchored), and the text clipboard keeps a copy of the
  source document holding only those stories and the graphics the text
  and they place, which come along under the destination's asset ids
  (Document::adopt_anchored_from). A story shown by frames in several
  copied stories is copied once per paste and threads all their copies, as
  the original does, so shared stories don't multiply. Copied frames
  (clip_doc) keep the stories of their anchored frames too.
- Document::fix_anchored_text_frames, run after edits and for new
  documents: copies with taken ids get fresh ones and stories of their own,
  deleted anchored frames leave their threads and their stories go with
  them, and a frame whose story is missing gets an empty one.
- Release threads the released frame, under its new id, to its story.
- The generated endnote story is set before the repair, and again when the
  repair changed anything: text with an anchored frame pasted into it is
  replaced, and the repair copies and removes stories with their endnotes.

Duplicating and pasting frames already give anchored items fresh ids and
story copies on main (#1522); this covers text copied with them, deletion
and release.
2026-11-04 21:45:49 +01:00
aiongg dffef02773 Merge pull request #1755 from Hamza0101/fix/korean-sans-fallback
Fonts: a missing Korean sans face falls back to a sans one (#1692)
2026-11-04 07:07:31 -05:00
MarekSpodBiedry 7215a559e5 Hyphenation: enable the Danish hypher patterns (#2282) (#2293) 2026-11-04 07:01:31 -05:00
Hamza 39ea3513a4 Korean fallback: spell 바탕 right, and choose the label's fallback style once 2026-11-04 12:55:57 +05:00
aiongg 6c2ab93b03 Merge pull request #2231 2026-11-04 04:14:12 +00:00