Skip to content

fix(android): center inline images on the cap height like iOS - #1089

Merged
hryhoriiK97 merged 2 commits into
software-mansion:mainfrom
pingdotgg:fix/android-inline-image-alignment
Oct 27, 2026
Merged

hryhoriiK97 merged 2 commits into
software-mansion:mainfrom
pingdotgg:fix/android-inline-image-alignment

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

What/Why?

On Android an inline image is drawn with its bottom near the baseline (y - h + 0.1h), so an icon taller than the capitals stands above the line of text. iOS centers inline attachments on the font's cap height (ENRMImageAttachment, (capHeight - height) / 2), so the same Markdown lines up on iOS and sits high on Android.

Both ImageSpan copies (the React Native package and enriched-markdown-android) now center the image on the cap height of the paint it is drawn with, matching iOS. Line metrics are unchanged, since getSize and chooseHeight don't look at inline images.

Measured in T3 Code on an Android 17 emulator and an iOS 27 simulator. The offset is the gap between the image's center and the text's cap center, in cap heights:

Android before (+0.20) Android after (+0.02) iOS (-0.11)

Testing

  • New InlineImagePlacementTest (native graphics, like SpoilerPaintingTest) draws an inline image between text and checks that its painted center is the cap-height center. It fails on main (9.5 against 11.0) and passes here.
  • enriched-markdown-android: :ui:testDebugUnitTest (526 tests) and :ui:ktlintCheck pass. React Native package: testDebugUnitTest and ktlintCheck pass.
  • Verified on an Android 17 emulator in T3 Code (screenshots above).

PR Checklist

  • Code compiles and runs on iOS, no iOS change
  • Code compiles and runs on Android
  • Updated documentation/README if applicable, not applicable
  • Ran example app to verify changes, verified in a downstream app
  • E2E tests are passing, Maestro screenshots with inline images may need updating
  • Required regression tests have been added

Model: Claude Opus 5.5. Harness: Claude Code in T3 Code.

An inline image's bottom sat on the baseline, so an icon taller than the
capitals stood above the line of text. iOS centers inline attachments on the
font's cap height; Android now draws the image centered on the cap height of
the paint it is drawn with. Both ImageSpan copies change the same way.
juliusmarminge added a commit to pingdotgg/t3code that referenced this pull request Oct 24, 2026
Android drew an inline image with its bottom on the baseline, so icons sat
above the line while iOS centers them on the cap height. The Enriched
Markdown archive now carries software-mansion/enriched-markdown#1089, which
centers them the same way on Android.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Centered on the cap height, an inline image taller than the text ran past
both edges of its line and the view clipped it. iOS grows the line for such
an attachment; getSize now widens the line metrics the same way.

Re-records the Android inline image screenshots for the new placement.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@hryhoriiK97

hryhoriiK97 commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

Pushed a fixup on top: 7bf92da (fix(android): grow the line to fit a tall inline image)

Centering on the cap height fixes normal-size icons, but Android never let an inline image change the line height (chooseHeight skips inline images and getSize didn't touch the line metrics). So an icon taller than the text now ran past both edges of its line, and the view clipped it at the top and bottom. iOS grows the line for such an attachment. getSize now widens the line metrics to fit the centered image, in both ImageSpan copies. It only ever widens them, so default-size icons keep their line height. draw and getSize share one inlineImageTop helper.

Checked in the RN example on the Pixel 9 / API 36 emulator, and on the iOS simulator for reference. The first three rows use the default 20dp icon (body text, wrapped text, heading); the last row uses inlineImage.size: 40.

Android main Android, this PR before the fixup Android, with the fixup iOS
  • InlineImagePlacementTest gets a second case, aTallInlineImageGrowsItsLine: an icon three times the font size must be fully painted inside its line. It fails without the getSize change. All 527 :ui tests and ktlint (both packages) pass.
  • Re-recorded inline_image_display.png for the RN example (.maestro/enrichedMarkdownText) and the native example (.maestro/androidExample).
  • The other image-tagged Android flows pass, except paragraph_image_combo and list_image_combo. Those two fail identically with main's ImageSpan, so they're unrelated drift.

@hryhoriiK97
hryhoriiK97 merged commit 6de1863 into software-mansion:main Oct 27, 2026
20 checks passed
juliusmarminge added a commit to pingdotgg/enriched-markdown that referenced this pull request Nov 6, 2026
Upstream merged our software-mansion#1086, software-mansion#1087, software-mansion#1088 and software-mansion#1089; the fork's copies of
software-mansion#1088 and software-mansion#1089 were reverted first because upstream edited them before
merging. Conflicts were the md4c flag lists, where upstream added
strikethrough (software-mansion#1090) next to our preserveWindowsPaths, and the parser
TurboModule's flag struct now has ten fields.
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.

2 participants