Commit Graph
6 Commits
Author SHA1 Message Date
Brandon ThomasandClaude Opus 5.5 cc47975960 Use clamp for the wrap pass bounds
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 21:43:16 -04:00
Brandon ThomasandClaude Opus 5.5 a9256308e3 Keep at least four wrap passes for a story with few anchored objects
A single wrapping object moved by keep options or vertical justification had four passes to
settle before; one-per-object would have given it two.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 21:39:35 -04:00
garrytanandcapy-ai[bot] 4ac4219fd2 compose: settle a story's own anchored wraps by object count, not 4 passes
A story whose custom or above-line anchored objects wrap its own later
lines is composed first without those wraps, then again with each
object where the previous pass placed it. That loop stopped after a
fixed MAX_PASSES = 4 extra compositions. Each object's wrap pushes down
the anchors of the objects after it, so in a chain of such objects the
k-th object only lands where it belongs once the k - 1 wraps before it
are in place. From roughly the fifth chained object on, the loop ran
out before the story settled: the final composition placed the object
in one spot but wrapped the text around where an earlier pass had put
it, and lines printed straight over the object.

Bound the loop by the story's own wrapping objects instead: n + 1
passes for n such objects (inline objects never wrap their own story
and are left out). Composition 0 has no own wraps, so object 1 is
already where it belongs; by induction pass k places object k + 1
correctly, pass n places the last one, and pass n + 1 wraps the text
around it, after which the positions cannot move. The existing settled
check still stops a story early as soon as its objects stop moving,
so stories that settle sooner cost nothing extra. MAX_PASSES stays as
an absolute safety net, raised to 64, for layouts that keep moving
their objects (keep options, vertical justification) and for stories
with very many anchored objects.

Test: chained_anchored_objects_all_wrap_their_own_storys_lines anchors
eight 100 x 60 pt bounding-box objects six lines apart in one story,
each hanging from its anchor line, and checks that every object sits
on its anchor line and every line it hangs over moves clear of it.
Without this change it fails at the seventh object (a line 790 pt down
sits at x 0 inside the object's band at 718 pt); with it, it passes.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-10-28 10:23:57 +00:00
aiongg 4be45a713a Anchored text frames compose against the grid and page where they are placed (#1710)
* Anchored text frames compose against the grid and page where they are placed

An anchored text frame is not a spread item, so frame_specs composed it at
its stored transform: no page, no page side, no page rect, and a baseline
grid phase taken from that transform. A note set to Only Align First Line
to Grid snapped to the wrong line, and page-number markers and
spine-relative alignment inside it saw no page.

The composer now looks up the host story's composition first, finds where
it placed the frame (in its frames' anchored objects, footnotes or table
cells) and composes the frame's story against that placement
(ComposeOptions::placed). The cache does the same and adds the placement
to the entry's signature, so a note recomposes when its host moves it.
Host lookups stop after eight levels, which bounds stories anchored in
each other.

* Anchored text frames: the cache keys on the placed spread's pages
2026-10-24 08:30:03 -04: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 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