Bug 261463

Summary: Line height rounding compounds in element height
Product: WebKit Reporter: Matias Szylkowski <mszylkowski>
Component: TextAssignee: Nobody <webkit-unassigned>
Status: NEW ---    
Severity: Normal CC: ahmad.saleem792, bya, karlcow, mmaxfield, simon.fraser, webkit-bug-importer, zalan
Priority: P2 Keywords: BrowserCompat, InRadar
Version: Safari 16   
Hardware: Mac (Intel)   
OS: macOS 13   
See Also: https://bugs.webkit.org/show_bug.cgi?id=225695
https://bugs.webkit.org/show_bug.cgi?id=261212
Attachments:
Description Flags
rendering in Safari, firefox, chrome
none
testcase none

Description Matias Szylkowski 2023-09-12 09:00:46 PDT
On multiline text elements with a non-integer line height, the line-height flooring compounds the errors onto the element height. For instance, if `line-height: 9.9px` for a text with 10 lines, the height of the element will be `90px` in Safari (tested in 16 and 17-TP) whereas other browsers (tested in Chrome and Firefox) compute the element height as `99px`. This is reflected in the rendering of each line of text, that in Safari renders each line on the `floor(lineHeight) * lineNumber` pixel (note how the flooring happens before multiplying), whereas on other browsers the lines render on the `round(lineHeight * lineNumber)` pixel (where it computes the correct Y value without rounding/flooring the lineHeight).

This has a lot of repercussions in Safari only:
- `line-height` cannot animate smoothly in Safari.
- Websites cannot rely on `line-height` to control the size of their text elements in Safari.
  - Eg: If I want a text element to span 100px vertically with 8 lines of text, I can't use `line-height: 12.5px` in Safari. The element would be `12px * 8 = 96px` height.

The ways of fixing this (for web developers) are very cumbersome and less performant:
- Setting the font-size and line-height of the text element to a large multiple of the desired size (so Safari has a higher precision), and using transforms to scale it down.
- Manually calculate the Y positions of each line of text, and set the line-height per line in a separate container to achieve that (eg: to span 25px in two lines, use `<div style="line-height="12px">Hello</div><div style="line-height="13px">World</div>`).
- Setting the line-height to an integer and use `transform: scaleY()` to compensate.
- Use images to draw text accurately.

---

Below is a minimal demo. 

https://codepen.io/mszylkowski/pen/xxmgdXb

Setup: Height of box should theoretically be 24.9*10=249

Safari (16 and 17-TP): Computed height is { clientHeight: 240, boundingClientRect.height: 240 }
Firefox: Computed height is { clientHeight: 249, boundingClientRect.height: 249 }
Chrome: Computed height is { clientHeight: 249, boundingClientRect.height: 248.984375 }

Note: this might be a good candidate for improving the interop of Webkit: https://webkit.org/blog/13706/interop-2023/

Found a few similar/related bugs:
- https://bugs.webkit.org/show_bug.cgi?id=225695 (maybe this is a regression?)
- https://bugs.webkit.org/show_bug.cgi?id=216601 (the computed value was fixed)
Comment 1 Karl Dubost 2023-09-14 01:44:33 PDT
Created attachment 467678 [details]
rendering in Safari, firefox, chrome
Comment 2 Karl Dubost 2023-09-14 01:45:17 PDT
Created attachment 467679 [details]
testcase
Comment 3 Karl Dubost 2023-09-14 01:46:53 PDT
Safari:  90px
Firefox: 99px
Chrome:  98.90625px
Comment 4 Radar WebKit Bug Importer 2023-09-19 09:01:14 PDT
<rdar://problem/115729143>
Comment 5 Ahmad Saleem 2023-10-31 14:32:26 PDT
I think Alan was looking into going 'fractional' route for IFC in bug 261212 and I added all possible 'fractional' related issues as 'See Also' in it.