Skip to content

[css-text] [css-inline] Define how to deal with inline backgrounds whose boundary falls into a ligature #5251

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
emilio opened this issue Jun 22, 2020 · 26 comments
Labels
Closed as Retracted When the person who raised the issue thinks that there's no issue after all. css-text-3 Current Work Testing Unnecessary Memory aid - issue doesn't require tests Tracked in DoC

Comments

@emilio
Copy link
Collaborator

emilio commented Jun 22, 2020

For this test-case:

<!doctype html>
<style>
  div {
    font-size: 180px;
    font-family: "Nimbus Roman", Times;
  }
  span { background: rgba(255, 0, 0, .5); }
  span + span { background: rgba(0, 255, 0, .5); }
</style>
<div><span>f</span><span>iona</span></div>

This is what I see on different browsers:

shaping-across-elements-2

Both Gecko's and Blink's behavior make some amount of sense to me. It seems Blink puts the whole ligature inside the first inline box. Gecko instead does something where the text is painted atomically on top of the inline backgrounds, crossing the inline boundary.

WebKit's behavior of not shaping the ligature may also be reasonable for this case (not in general of course).

It's not clear to me what the right answer is... But maybe the spec ought to have a model of how this is supposed to work?

@emilio
Copy link
Collaborator Author

emilio commented Jun 22, 2020

cc @jfkthame @kojiishi @litherum

@emilio
Copy link
Collaborator Author

emilio commented Jun 22, 2020

One interesting consequence of Chromium's approach is that you can't select the ligature individually... For editing it ends up working, IIRC, because they fallback to the legacy engine (which does the same thing WebKit does...)

@dbaron
Copy link
Member

dbaron commented Jun 23, 2020

It also may be interesting to see what happens if you add color styles, although I think the result will be similar.

I believe Gecko's approach (which @rocallahan implemented initially) involves estimating the boundary within the ligature based on the advances of the pre-ligature glyphs, although I'm not sure if this is still the case. It would be interesting to know if there are font metrics that could help with finding the boundary more accurately.

I think the working group has discussed this issue at least once in the past.

@rocallahan
Copy link

The approach I implemented simply divides the ligature into equal-sized horizontal pieces. Works OK for Latin text but fails spectacularly for vertical ligatures, for example. At the time I don't think fonts had any information we could use to make a better decision, but I think that might have changed.

The Webkit approach seems bad to me because I've always assumed that wrapping inline content in an unstyled span, or adding decorative styles like "color", should not change layout.

@jfkthame
Copy link
Contributor

jfkthame commented Jun 23, 2020

In theory, fonts can include information to help with dividing up the advance width of a ligature, so that for example an "st" ligature might have a boundary position at 2/3 of the advance rather than half-way. In practice, however, hardly any fonts actually have that data; and it wouldn't help with vertical ligatures such as may be found in some Asian scripts, etc.

For the example here, I'd argue that gecko's behavior is clearly better than webkit or blink (and is what the spec calls for), even though there are certainly complex cases where the results are less than ideal.

@kojiishi
Copy link
Contributor

One interesting consequence of Chromium's approach is that you can't select the ligature individually...

Blink supports caret-and-selection-within-a-ligature by, currently, similar method as Gecko, but as @jfkthame pointed out, OpenType has a table for caret position within a glyph. Ideally we'd like to support that, but it hasn't happened yet. IIRC @litherum had a WIP for WebKit for this.

For painting, we don't apply this logic because while there are cases where this works good, like this example, as @jfkthame pointed out, there are cases where this looks strange. For example:
https://output.jsbin.com/tatovac

I think it's better to let authors clarify what they want. If authors want color/background correctly, like when they want to show diff in colors, they should apply font-variant-ligature: none.

In whatwg/html#5611, we discussed whether to apply unicode-bidi: isolate to <ins> and <del> or not, it looks like we prefer to let authors decide, because the desired behavior vary by authors' intentions. I think this is similar to that?

@jfkthame
Copy link
Contributor

One interesting consequence of Chromium's approach is that you can't select the ligature individually...

Blink supports caret-and-selection-within-a-ligature by, currently, similar method as Gecko,

Partly, but not entirely. Using a font that supports an "ffi" ligature, for example:

data:text/html,<div style="font:100px linux libertine o" >office

If I drag-select slowly across this text, I can see that the individual characters making up the "ffi" ligature can be selected one by one; so I can, for instance select just the middle "f".

But now try this version:

data:text/html,<div style="font:100px linux libertine o" >of<span>f</span>ice

Here, when I try drag-selecting across the text, the "ffi" ligature gets highlighted as an indivisible unit.

For painting, we don't apply this logic because while there are cases where this works good, like this example, as @jfkthame pointed out, there are cases where this looks strange. For example:
https://output.jsbin.com/tatovac

Agreed, that looks strange, but I don't see how Blink's result (where the entire ligature is painted with the color/background of its first component) is better. That means the intended styling in something like

data:text/html,<div style="font:100px linux libertine o" >o<span style="background:cyan">f</span><span style="background:magenta">f</span><span style="background:yellow">i</span>ce

is essentially lost.

If an author doesn't want the "strange" result Firefox gives with your jsbin example, they can disable the ligature, or they can apply the background to the whole word instead of a single character; but Blink's approach here makes it extremely awkward to go the other way.

@kojiishi
Copy link
Contributor

But now try this version:

Thank you for finding it, it's likely a bug, we'll investigate.

...but I don't see how Blink's result ... is better

Agreed it's not better. I think Blink behavior always makes it clear so that authors can notice easier, and encourages authors to clarify the intention. If "fi" and "ffi" works at some levels, I guess most Latin authors will not notice it may not work in some other cases.

I tried Fira Code but it seems it does some magic to paint partial glyphs, haven't figured out what it does.
https://output.jsbin.com/romalif

@jfkthame
Copy link
Contributor

Fira Code has an unconventional implementation whereby it inserts extra "spacer" glyphs to maintain monospaced rendering in the presence of ligatures:

$ hb-shape --font-file FiraCode-Regular.ttf "<="
[less.spacer=0+1200|less_equal.liga=1+1200]

I haven't been through all the font lookups to confirm the exact sequence of operations, but what has happened here is not a ligation, but two substitutions: "<" replaced by the less.spacer glyph, and "=" replaced by less_equal.liga (which has its ink shifted left so that it is centered over the glyph origin rather than within the advance width, so it appears in the middle of the overall 2-char-wide space).

@kojiishi
Copy link
Contributor

Just to clarify, sorry if I mislead you, I didn't mean to say Blink's is better than others. I think I'm similar to @emilio, each option has reasons to choose, and I'm not sure which one is the best at this moment. We have only less than one year experiences of cross-element shaping yet.

Looking at reported cases so far and cases we came up with, I started leaning towards to "break shaping at color/background/text-decorations boundary" -- so WebKit in this case -- is the most "not incorrect" option. WDYT?

p.s., thank you @jfkthame for the analysis on Fira Code, this is very interesting technique. That explains how color is applied to Fira Code.

@emilio
Copy link
Collaborator Author

emilio commented Jun 24, 2020

Breaking shaping at background etc. differences is a bit unfortunate, quoting @rocallahan above:

The Webkit approach seems bad to me because I've always assumed that wrapping inline content in an unstyled span, or adding decorative styles like "color", should not change layout.

I think I agree and I would like to avoid layout invalidation due to background / color changes and such.

@jfkthame
Copy link
Contributor

Breaking shaping at background etc. differences is a bit unfortunate, quoting @rocallahan above:

The Webkit approach seems bad to me because I've always assumed that wrapping inline content in an unstyled span, or adding decorative styles like "color", should not change layout.

I think I agree and I would like to avoid layout invalidation due to background / color changes and such.

Yes, agreed.

FWIW, Webkit (Safari) seems to break shaping at inline boundaries even when there's no style difference at all. See https://codepen.io/jfkthame/pen/gOPRpQG, which shows a plausible usage of color on inline elements where interrupting shaping is definitely not desired. It looks fine in both Firefox and Chrome, but is broken in Safari; and it remains broken (i.e. the words are not shaped correctly) even if the span { color: red; } rule is completely removed.

@fantasai
Copy link
Collaborator

Relevant spec sections, fwiw:

I think this is really a case where the spec should leave it undefined. There's no clearly correct answer, and what's possible depends on the font technology.

@frivoal
Copy link
Collaborator

frivoal commented Jul 2, 2020

I agree with @fantasai. Specifically, the existing part of spec text that is most relevant to this is:

The rendering characteristics of a typographic character unit divided by an element boundary is undefined. Ideally each component should be rendered according to the formatting requirements of its respective element’s properties while maintaining correct shaping and positioning of the typographic character unit as a whole. However, depending on the nature of the formatting differences between its parts and the capabilities of the font technology in use, this is not always possible. Therefore such a typographic character unit may be rendered as belonging to either side of the boundary, or as some approximation of belonging to both.

@frivoal
Copy link
Collaborator

frivoal commented Jul 2, 2020

Small clarification at d1714e7

@fantasai
Copy link
Collaborator

fantasai commented Jul 2, 2020

I think there is not much else we can do here. @emilio , should I close the issue or do you want something additional?

@rocallahan
Copy link

rocallahan commented Jul 2, 2020

Authors are forewarned that dividing grapheme clusters or ligatures by element boundaries may give inconsistent or undesired results.

This is unhelpful advice. Authors can't know where ligatures may occur so it is not actionable (except maybe by not putting inline element boundaries inside "words").

Making shaping work across unstyled inline element boundaries does not depend on font technology. Why not at least mandate that? And why not recommend that browsers should make a best-effort attempt to support style changes?

@fantasai
Copy link
Collaborator

fantasai commented Jul 2, 2020

Making shaping work across unstyled inline element boundaries does not depend on font technology. Why not at least mandate that? And why not recommend that browsers should make a best-effort attempt to support style changes?

@rocallahan That is already required: https://www.w3.org/TR/css-text-3/#boundary-shaping

@rocallahan
Copy link

Ah. That text does look pretty good. "no effective change in formatting" perhaps needs clarification, though. Does changing the color of a span count as "formatting"? I would say style changes that don't affect layout should not count as "formatting" here.

@emilio
Copy link
Collaborator Author

emilio commented Jul 2, 2020

I think there is not much else we can do here. @emilio , should I close the issue or do you want something additional?

No, I'm ok closing.

@fantasai
Copy link
Collaborator

fantasai commented Jul 5, 2020

@rocallahan I think that's too strict. Breaking optional ligatures to correctly handle a color change might be reasonable, for example. That's why the spec isn't so tight, because of cases like this. But if there's no change at all, the element boundary should cause no change in rendering.

@fantasai fantasai added Closed as Retracted When the person who raised the issue thinks that there's no issue after all. and removed Commenter Response Pending labels Jul 5, 2020
@jfkthame
Copy link
Contributor

jfkthame commented Jul 6, 2020

Breaking optional ligatures to correctly handle a color change might be reasonable

The browser cannot, in general, know which shaping behaviors are "optional" and which are required for orthographically correct rendering of the content.

The spec need not mandate exactly how color changes get rendered if they occur in the midst of shaped sequences, given that sometimes there is no clear "right" answer, but I think it's a reasonable expectation that changing color should never affect layout.

@fantasai
Copy link
Collaborator

fantasai commented Jul 6, 2020

The browser cannot, in general, know which shaping behaviors are "optional" and which are required for orthographically correct rendering of the content.

This is what rlig vs the various other ligature features are supposed to distinguish.

The spec need not mandate exactly how color changes get rendered if they occur in the midst of shaped sequences, given that sometimes there is no clear "right" answer, but I think it's a reasonable expectation that changing color should never affect layout.

The author is asking for conflicting things. I don't know that there's a clearly correct answer to solving that conflict, so as currently drafted the specs are leaving that up to the UA to figure out. Probably you're right that changing color should not affect layout in a dynamic environment, but if some print implementation thinks that's a higher-fidelity interpretation of the author's intent than keeping the ligature all one color or divided by percentage... I don't think I want to make them non-conformant.

@fantasai fantasai closed this as completed Jul 6, 2020
@faceless2
Copy link

This is what rlig vs the various other ligature features are supposed to distinguish.

That should be true, but the boundary is fuzzy even in the OpenType spec: Khmer, for instance, requires the "clig" (contextual ligatures) feature for layout, despite it being theoretically discretionary.

@jfkthame
Copy link
Contributor

jfkthame commented Jul 7, 2020

This is what rlig vs the various other ligature features are supposed to distinguish.

That should be true, but the boundary is fuzzy even in the OpenType spec: Khmer, for instance, requires the "clig" (contextual ligatures) feature for layout, despite it being theoretically discretionary.

There's that; and then there's also the fact that ligation (mapping two or more encoded text units to a single rendered glyph) can happen in other OpenType features, depending on details of font construction -- for example, various Indic shaping features may be implemented as ligatures. Ligature lookups are not limited to occurring in features called "ligatures".

@fantasai
Copy link
Collaborator

fantasai commented Jul 8, 2020

@jfkthame Sure, and, none of that invalidates what the spec already says. So I don't think there's an issue here to solve.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Closed as Retracted When the person who raised the issue thinks that there's no issue after all. css-text-3 Current Work Testing Unnecessary Memory aid - issue doesn't require tests Tracked in DoC
Projects
None yet
Development

No branches or pull requests

8 participants