Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Support float:left/right for images and blocks with text wrapping#1319

Merged
odrobnik merged 2 commits into
developfrom
claude/blissful-bohr-9a9390
Jun 12, 2026
Merged

Support float:left/right for images and blocks with text wrapping#1319
odrobnik merged 2 commits into
developfrom
claude/blissful-bohr-9a9390

Conversation

@odrobnik

Copy link
Copy Markdown
Collaborator

What

Implements real CSS float layout. float was parsed into DTHTMLElementFloatStyle but never used — floated images were forced to be their own block as a workaround (the old comment in TextAttachmentHTMLElement said "we don't support float yet"). Now:

  • Floated images become a box at the left/right content edge and the paragraph text wraps around them, returning to full width below the float.
  • Floated blocks (div, span, even table) become a column — explicit CSS width or shrink-to-fit — laid out via the existing table-cell flow, including their backgrounds, padding and nested tables.
  • clear:left/right/both makes paragraphs and floats start below earlier floated content.
  • HTML writer round-trip: floated attachments get a wrapping <span style="float:…">, floated block ranges become <div style="float:…;width:…px">, clear is emitted on paragraph styles.

How

  • Elements emit DTFloatStyleAttribute (and DTFloatWidthAttribute for an explicit width, DTClearFloatsAttribute for clear) over their output range; the attachment workaround that forced floated images to be blocks is removed so they stay inline.
  • CoreTextLayoutFrame._buildLinesWithTypesetter takes marked content out of the flow before the table check (so floated tables become columns), tracks float regions, and narrows the lines that vertically intersect them. Since a line's height is only known after typesetting, each line is re-typeset until the assumed float insets match its real vertical extent.
  • Several floats on one side stack next to each other and move down when out of room; a float that does not fit a finite frame moves whole to the continuation frame (pagination); a standalone floated image absorbs its paragraph break so the flow gets no empty line.
  • The float region (not the line metrics) drives the wrap, so this works on macOS too where attachments have no CT run delegate; the float line's ascent/descent are set from the attachment so its frame matches the image box on both platforms.

Notes for review

  • The native AppKit HTML importer was checked for parity: it carries no float information into attributed strings (a floated img/span is merely promoted to its own paragraph; float/width on a div are dropped, clear ignored). So unlike tables/NSTextTable there was nothing native to mirror, and DT-specific attributes are the only encoding option.
  • Degradation: rendering a DT string in a plain NSTextView now shows a floated image inline in its paragraph (previously: own line). The wrap itself only happens in DT's own layout engine.
  • Known limits (unchanged scope): floats inside table cells are ignored, and a float marker mid-line starts wrapping on the following line instead of splitting the current line box. Width-unknown frames disable floats.
  • Tests: new FloatLayoutTests suite (16 tests: attribute emission, wrap geometry left/right, side-by-side floats, shrink-to-fit, clear, frame height, pagination, writer round-trips) plus a Floats.html demo snippet. Full suite passes: 315 tests on macOS, 307 on the iOS simulator (CI-equivalent invocations).

🤖 Generated with Claude Code

CSS float was parsed into DTHTMLElementFloatStyle but never used; floated
images were forced to be their own block as a workaround. Floats are now
laid out for real:

- Floated content is marked with DTFloatStyleAttribute (plus
  DTFloatWidthAttribute for an explicit CSS width) and taken out of the
  normal flow during line building in CoreTextLayoutFrame.
- A floated image becomes a box at the left/right content edge; a floated
  block (div, span, table) becomes a shrink-to-fit or fixed-width column
  laid out via the table cell flow, including its backgrounds and borders.
- Text lines vertically intersecting a float region are narrowed and
  re-typeset until the assumed insets match the line's real extent;
  several floats on one side stack next to each other and move down when
  there is no room, and a float that does not fit a finite frame moves to
  the continuation frame.
- The clear property (left/right/both) makes paragraphs and floats start
  below earlier floated content, via DTClearFloatsAttribute.
- HTMLWriter round-trips floats: floated attachments get a wrapping span
  with the float style, floated block ranges become a div with float and
  width, and clear is emitted on paragraph styles.
- New Floats.html demo snippet and a FloatLayoutTests suite covering
  attribute emission, wrap geometry, side-by-side floats, shrink-to-fit,
  clear, frame height, pagination and writer round-trips.

Co-Authored-By: Claude Fable 5 <[email protected]>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 552db605af

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/DTCoreText/CoreTextLayoutFrame.swift
Float lines inflated typesetLines.count past the strict-equality
truncation checks, so numberOfLines (and the lines-fitting retry) never
fired after a leading float. Line limits now count only lines of the
normal flow — floated content is out of the flow, like in CSS — and the
checks tolerate jumps (>= instead of ==). The lines-fitting retry also
captures the flow line count so a rebuild cannot recurse with a zero
limit when only float lines exist.

Co-Authored-By: Claude Fable 5 <[email protected]>
@odrobnik odrobnik merged commit 1195831 into develop Jun 12, 2026
2 checks passed
@odrobnik odrobnik deleted the claude/blissful-bohr-9a9390 branch June 12, 2026 07:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant