Skip to content

[css-inline] text-layout: glyph-bounding-box #2228

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

Open
tobireif opened this issue Jan 26, 2018 · 69 comments
Open

[css-inline] text-layout: glyph-bounding-box #2228

tobireif opened this issue Jan 26, 2018 · 69 comments
Labels
css-inline-3 Current Work

Comments

@tobireif
Copy link

tobireif commented Jan 26, 2018

I need consistent positioning of large and medium-sized text across operating systems.

The large text on the page
https://tobireif.com/non_site_stuff/test_case_for_font_position_report/
gets positioned differently eg in Chrome on Mac vs in Chrome on Windows:

The space above the large text is 102px in Chrome on Mac
vs 125px in Chrome on Windows.

The space below the large text is 75px in Chrome on Mac
vs 52px in Chrome on Windows.

Another test-page:
https://tobireif.com/non_site_stuff/test_case_for_font_position_report_yet_another_font/

From the above test-page:

Chrome on MacOS:

35439338-8cb9f178-029a-11e8-86aa-cf19bf836e6c

Chrome on Windows:

35439340-90798dc8-029a-11e8-88a4-5518f09775f3

The large-text positioning / the space above and below the large text should be the same across operating platforms, for this font and for all fonts where the effective position currently is different across operating systems.

In order to not break existing content there needs to be a new CSS property,
eg
position-of-glyphs: consistent-across-platforms;
or eg
text-layout: glyph-bounding-box;
(The latter would set the same text positioning that browsers use for SVG.)

There are many other pages/sites/layouts where medium/large text and its position is important.

I need a way to ensure the same vertical text position across platforms.

The alternatives are not great:

a) Applying spacing:

eg

if (navigator.platform.startsWith("Win")) {
  var h1 = document.querySelector("h1");
  h1.style.marginTop = "-0.25em";
  h1.style.marginBottom = "0.15em";
}

This is a hack.

b) Fixing each problematic font:

This is not always feasible: There might be no time to do this, many web devs use fonts hosted by external services, and thirdly the font license might not allow any modifications.

c) Using SVG text instead of HTML text:

Replacing each respective text with SVG has major drawbacks: Some might fear that it might impact eg search engine visibility. Imagine a newspaper considering to use SVG for its headlines while search engines handle h1/h2/etc well - they probably wouldn't take that risk. And it might also impact accessibility, see https://www.google.com/search?q=svg+text+screen+readers . It's a potential issue as far as I can see (eg in a given specific screen reader), while HTML text can be expected to just work. Also, SVG is meant for graphics (which might contain text or not) - a pure text page (eg an article) coded in HTML shouldn't have to use SVG just to get consistent cross-OS text-layout (for its large text instances).

Thus it would be great if consistent cross-OS text-layout would be available in HTML as well (not just in SVG as is currently the case).

I hope that CSS will support opting into the glyph-bounding-box-based layout (which browsers already have implemented for SVG) as option for (typically large) HTML text.

eg h1 {text-layout: glyph-bounding-box}
or h1 {glyph-positioning: glyph-bounding-box}
(The exact names for the property and value could be different.)

That would solve the issue completely.

@tobireif
Copy link
Author

Should the browser position the large text in the above example screenshots consistently across OSs?

Or could CSS offer a property?

@fantasai fantasai changed the title Consistent vertical positioning of large and medium-sized text across OSs [css-inline] Consistent vertical positioning of large and medium-sized text across OSs Jan 27, 2018
@fantasai fantasai added the css-inline-3 Current Work label Jan 27, 2018
@fantasai
Copy link
Collaborator

@litherum might have some insight on this...

@fantasai fantasai reopened this Jan 27, 2018
@tobireif
Copy link
Author

Just to clarify: I don't have a preference.

I need consistent positioning (the same visual position) of large and medium-sized text in all browsers across all OSs.

If that should and will be ensured by all browsers then that'd be fine with me.

If instead (or eg for a specific category of fonts) CSS can and will offer some property that allows me to ensure this (and if all browsers then support it) then that'd be fine with me as well.

@tobireif
Copy link
Author

I hope that the two screenshots show that the issue is quite severe in some cases.

There are workarounds, but I hope that there will be a solution.

@tobireif
Copy link
Author

Perhaps CSS could offer eg "text-metrics: platform-independent" or eg "text-position: platform-independent".

No matter the name, I need a solution 😀

@kojiishi
Copy link
Contributor

Rather than picking one metrics, I prefer to allow authors to set the length from the top of box to the baseline. If we consider this is for web fonts, it should solve, and it also solves some other cases I've heard of.

WDYT? @FremyCompany @fantasai @litherum @dbaron

@tobireif
Copy link
Author

allow authors to set the length from the top of box to the baseline

If that would solve the issue I describe then yes that would be a good option.

A detail: Let's say I work on a website/webapp on Mac OS. There's a large word on the page, and the spacing and layout is set just right for the visual text position. Now I want to ensure that the text stays in that same y-position no matter on which platform. I'd set the new property - but to which value? I'd have to measure the length from the top of box to the baseline on Mac OS before I could set it for all OSs.

That measuring would be tedious eg if there are several web fonts.

Instead I need a non-numerical value that ensures that the position of the text (of the glyphs) are always the same on all OSs. The workflow would then be simpler: Right away after setting up the webfont I'd set eg "position-of-glyphs: same-on-all-platforms". Then I'd set my spacing and layout based on that reliable text position. Then when I test the page/site/webapp on other OSs there'd be no issue regarding the position of the text / of the glyphs - the glyph position would be same in all browsers, on all OSs, and on all devices == there would be no mismatch with the spacing of my layout.

What do you think? @kojiishi @FremyCompany @fantasai @litherum @dbaron

@dauwhe
Copy link
Contributor

dauwhe commented Feb 16, 2018

In case it's helpful, here are the font metrics (via FontForge and the OS/2 table) for two of your examples:

font name em size typo ascent typo descent win ascent win descent cap height
Frankfurter 1000 976 -24 1143 35 667
Hetilica-Bold 2048 1536 -512 1953 326 1415

Does this happen for all fonts? Do you get better results with line-height: normal or with less mixing of units?

@tobireif
Copy link
Author

Thanks for caring!

Does this happen for all fonts?

To the extreme extent shown in the screenshots it happens to just a subset of the web-fonts I checked. But sometimes I do use fonts where it happens to that extreme extent, and for these cases it would be really great to have some (nice and quick to use) solution. It should be as easy as adding one line of CSS, without having to get the font metrics, for example 😀

Do you get better results with line-height: normal or with less mixing of units?

Here's a version with line-height:normal and only em units (both regarding the h1):

https://tobireif.com/non_site_stuff/test_case_for_font_position_report_yet_another_font_line-height_normal_just_em_units/

Screenshot of that page in Chrome on MacOS:

z90c075c7a1c62e3f10b

Screenshot of that page in Chrome on Windows:

zce0ac738eca40d4ab87

@tobireif
Copy link
Author

Here's code from a website where I used a hack to compensate the difference in glyph-positioning:

(Warning: it's from an older website of mine, uses jQuery 😮)

(function() {

/*
The font rendering/spacing is different across OSs and browsers, so
here goes a hack:
*/

var svg = $('#svg');
var javascript = $('#javascript');
var css = $('#css');
var pixi = $('#pixi');
var angular = $('#angular');

var ua = navigator.userAgent.toLowerCase();
// Firefox:
if($.browser.mozilla) {
  angular.css({
    'margin-top': '10px',
    'left': '-170px'
  });
}
if(/windows/.test(ua)){
  // Firefox:
  if($.browser.mozilla) {
    svg.css({'margin-top': '-24px'});
    javascript.css({'margin-top': '-3px'});
    css.css({'margin-top': '-12px'});
    pixi.css({'margin-top': '36px'});
  }
  // Chrome:
  if(/chrome/.test(ua)) {
    svg.css({'margin-top': '-22px'});
    javascript.css({'margin-top': '-1px'});
    css.css({'margin-top': '-12px'});
    pixi.css({'margin-top': '38px'});
  }
  // IE, Edge:
  if(/msie|trident|edge/.test(ua)) {
    svg.css({'margin-top': '-14px'});
    angular.css({
      'margin-top': '10px',
      'left': '-180px'
    });
    javascript.css({'margin-top': '12px'});
    css.css({'margin-top': '1px'});
    pixi.css({'margin-top': '28px'});
  }
}

}());

I think the horribleness of the above hack demonstrates that a solution is required 😀

@tobireif
Copy link
Author

In the above hack there's
(shortened)

if($.browser.mozilla) {
  angular.css({
    'left': '-170px'
  });
}

and

if(/windows/.test(ua)){
  if(/msie|trident|edge/.test(ua)) {
    angular.css({
      'left': '-180px'
    });
  }
}

So it seems that for that word & that font I had to apply x-axis compensation.

I need a way to ensure that the glyphs in medium/large text get placed in the same position, regarding the x-axis and regarding the y-axis, in all browsers on all OSs on all devices. The property and value could be named eg "position-of-glyphs: same-on-all-platforms".

(Sub-pixel differences are negligible. And: I never noticed an issue regarding the glyph positions in small text - just in medium/large text. Perhaps the property could be ignored for small text.)

@tobireif
Copy link
Author

tobireif commented Mar 2, 2018

Is it inside the scope of the CSS WG to offer some solution for the (sometimes drastic) difference in large-text positioning across OSs?

@kojiishi
Copy link
Contributor

kojiishi commented Mar 5, 2018

One question; what do you expect if font cascading or fallback occurs? Should the baseline position stay, or should it be adjusted according to the used font?

@tobireif
Copy link
Author

tobireif commented Mar 5, 2018

I need a way to ensure that the glyphs in medium/large text get placed in the same position, regarding the x-axis and regarding the y-axis, in all browsers on all OSs on all devices.

If the following tests pass, in all bowsers on all OSs on all devices, then the issue would resolved well enough as a first step.

https://tobireif.com/non_site_stuff/test_case_for_font_position_report/
https://tobireif.com/non_site_stuff/test_case_for_font_position_report_yet_another_font_line-height_normal_just_em_units/
https://tobireif.com/non_site_stuff/test_case_for_font_position_report_yet_another_font/

And (right away or later) consistent glyph-positioning should also be ensured for the cases you list.

(I don't know what you mean by "font cascading", but I guess that both "font cascading" and "fallback" both mean that a different font is used eg until the respective web-font is loaded and applied.)

The fix / the property should work for all fonts on all pages for any type of font, for fonts supplied via @ font-face, and for fonts loaded from the local system (eg "non-web-fonts") (including "-apple-system" and BlinkMacSystemFont).

The default (eg in the absence of the new property) should be consistent glyph-positioning for all fonts used on a page, in all stages of the loading phase, in all browsers on all platforms (OSs and devices).

Should the baseline position stay, or should it be adjusted according to the used font?

The glyphs should always get positioned in the same position (x-axis and y-axis, not just baseline) across platforms - per used font.

@kojiishi
Copy link
Contributor

per used font.

Thank you, that was what I wanted to ask, sorry for not being clear. So in your use case, the position should change if the used-font is changed.

@tobireif
Copy link
Author

No problem! Yep, I think that glyph positioning should always be correct - per font, and across all browsers/OSs/devices.

@tobireif
Copy link
Author

@fantasai wrote on January 27:

@litherum might have some insight on this...

Nobody has posted objections since ~seven weeks, and I have shown that the issue can be quite severe for large-text based layout and needs a solution, thus perhaps it would be OK to add this to the agenda?

@dauwhe
Copy link
Contributor

dauwhe commented Mar 15, 2018

It's hard to discuss this without understanding what's being done differently in different browsers. Are line layout rules from CSS 2.1 actually being violated? Is this only a problem with certain unusual font metrics? I don't have access to Windows, so it's hard for me to test.

@jonjohnjohnson
Copy link

@tobireif Aside from specs, I've solved similar text-rendering inconsistencies by replacing text with svg text. -> http://jsbin.com/xequz/edit?js,output Here, control the sizes by altering the height/width of the li tags the text is within, figured it couldn't hurt to share.

@kojiishi
Copy link
Contributor

While I agree to solve this problem, and I'm fine to put on F2F agenda, I think this will take some time to get to conclusion. fantasai and I had some early rough discussion last week, but we hope to solve similar related requests as well, and we need to design carefully about overflow, fallback, etc. The new property is likely to be added to CSS inline level 4, but we still have work for CSS inline level 3.

The "per used font" is a bit challenging. I was hoping to solve one feature to solve two requests, but this makes it impossible. Re-thinking what the best feature would be.

@tobireif
Copy link
Author

tobireif commented Mar 16, 2018

@dauwhe wrote:

I don't have access to Windows, so it's hard for me to test.

Trying to reproduce the issue is a great first step! I use https://crossbrowsertesting.com/ - it's a great service for testing websites on many different device/OS/browser combinations. (I can not imagine doing web development without such a service - or a huge local device lab.)

It's hard to discuss this without understanding what's being done differently in different browsers. Are line layout rules from CSS 2.1 actually being violated? Is this only a problem with certain unusual font metrics?

I'm a web developer. The issue has been bugging me for years - I coded elaborate workarounds, and I hope that there will be a real solution for the future.

I submitted the issue description including exact pixel measurements of the cross-OS difference of one case, provided three test pages, and eight screenshots. I should (and have to) leave the rest - including further investigations and including the solution - to the CSS WG and to the browser vendors.

Is this only a problem with certain unusual font metrics?

I need consistent glyph positioning across OSs, browsers, and devices - no matter whether the font and it's metrics can be considered unusual or not 😀

@tobireif
Copy link
Author

@jonjohnjohnson

Thanks for your input! That's a workaround, I had coded a different workaround, and there might be more options for workarounds. Now I need a solution 😀

@tobireif
Copy link
Author

@kojiishi wrote:

While I agree to solve this problem, and I'm fine to put on F2F agenda, I think this will take some time to get to conclusion. fantasai and I had some early rough discussion last week,

Sounds promising!

but we hope to solve similar related requests as well, and we need to design carefully about overflow, fallback, etc. The new property is likely to be added to CSS inline level 4

It would be a dream come true. Seriously.

For me it is one or the main issues in web development (because many layouts contain large text) and the issue has been existing for many years, so a solution would be most welcome.

but we still have work for CSS inline level 3.

The "per used font" is a bit challenging. I was hoping to solve one feature to solve two requests, but this makes it impossible. Re-thinking what the best feature would be.

I'm confident that you and @fantasai etc can find a great solution.

@tobireif
Copy link
Author

Adding a hack again in a current project 🙁

if (navigator.platform.startsWith("Win")) {
  var h1 = document.querySelector("h1");
  h1.style.marginTop = "-0.25em";
  h1.style.marginBottom = "0.15em";
}

@litherum
Copy link
Contributor

You are right that consistent font metrics across the Web are valuable. However, it is also true that it is valuable for a platform’s web browser to follow the platform conventions for font metrics. This is particularly important for Web Views inside native apps. It is very common for apps to use Web Views to draw content as-if it was native (and it should look like native content). Similarly, it’s impossible for the implementation of a Web View to know if a Web View being used by a particular app should have web-like font metrics or native-like font metrics.

In addition, on a more pragmatic note, changing the metrics of every font used in a browser would change the layout of every website, which is super scary.

@litherum
Copy link
Contributor

litherum commented Apr 10, 2018

I need a way to ensure that the glyphs in medium/large text get placed in the same position, regarding the x-axis and regarding the y-axis, in all browsers on all OSs on all devices.

This is undesirable. Browsers shouldn’t be required to have exactly compatible shaping engines and rendering engines. Consider a browser that is bundled with the OS - such a browser shouldn’t have to include its own independent shaping engine and font rasterizer that are only used for that browser and not for the rest of the OS (for reasons of binary size, performance, and correctness). There is industry competition in text engines, which is only possible if browsers have the freedom to innovate.

@tobireif tobireif changed the title [css-inline] Consistent vertical positioning of large and medium-sized text across operating systems [css-inline] text-layout: glyph-bounding-box Jun 26, 2018
@tobireif
Copy link
Author

tobireif commented Jun 26, 2018

do you have additional examples? With how many fonts are you facing this problem?

With way too many. It's an issue in almost every project where large text is part of the design. (I always do testing across operating systems, devices, and browsers.)

A test-page using the font Hetilica:
https://tobireif.com/non_site_stuff/test_case_for_font_position_report_yet_another_font/

A test-page using the popular font Frankfurter:
https://tobireif.com/non_site_stuff/test_case_for_font_position_report/

Other fonts where the issue occurs include Ruritania, QuigleyWiggly, Scratch, and BigFish.
See the code at #2228 (comment) . Each word "#svg" etc uses a different one of the above fonts. There were text-layout issues across operating systems and across browsers.

And there are several StackOverflow pages which most likely are the tip of the iceberg.

I would strictly prefer not have per-@font-face overrides for metrics. Implementing and maintaining such overrides introduces a big maintenance burden and unnecessary implementation cost, I doubt this is the right solution to this problem.

I agree! They'd mean way too much work for the web developer, and for implementers.

All I want is being able to opt into the same text-layout that browsers use for SVG text, for example

text-layout: glyph-bounding-box;

This line would set the same text positioning that browsers use for SVG.

One line of CSS to type for the web developer, and for implementers it probably wouldn't be a lot of work because that text-layout has already been implemented for SVG text.

And it wouldn't break any existing websites because it'd be opt in (via the new property).

If there are technical issues with the fonts, it is possible to contact the vendor and get them fixed.

Typical project deadlines don't leave time for requesting, awaiting, and hoping for a fix - for each used font. And some type foundries might never reply. And the license might not allow me to fix it.

A browser-side override is not a reasonable solution to work around fonts with inconsistent metrics.

The sad truth is that it's a widespread issue, and fixing each font is not feasible, thus a solution is required. Some designs feature many fonts. In one design (one single page) I had to compensate the text-layout for five(!) fonts (see the example in #2228 (comment) ). I'd much prefer it if every font would work well cross-platform, but alas that is not the case.

All I want is being able to opt into the same text-layout that browsers use for SVG text, eg

glyph-positioning: as-for-svg;

or eg

text-layout: glyph-bounding-box;

Having such a property would mean that we could opt into text layout that's consistent across platforms without having to replace the HTML text with SVG text, without having to wait for font creators to fix each and every used font, and without having to compensate for the differences by applying OS-specific spacing. Instead we could opt into an already-implemented text-layout option where the glyphs stay in the same position across platforms.

@jonjohnjohnson
Copy link

Wonder if #3240 could/would be used to also solve this per os issue?

@kojiishi
Copy link
Contributor

Wonder if #3240 could/would be used to also solve this per os issue?

Yes, that is the intention.

@tobireif
Copy link
Author

Any solution would be a big step forward for text in layout on the web! Keep us updated 😀

@tobireif
Copy link
Author

Or perhaps glyph-spacing: bounding-box

@dejayc
Copy link

dejayc commented Jun 17, 2019

All I want is being able to opt into the same text-layout that browsers use for SVG text

@tobireif you do realize that SVG text rendering is not the same thing as browser-based text rendering, right? They each have different features, advantages, and drawbacks.

Your desire for browsers to "just be able to use SVG logic for non-SVG content" is a bit like asking for a car that could just use the same propellor as your boat.

@dejayc
Copy link

dejayc commented Jun 18, 2019

BTW it's almost surreal that the following article was published the very day I came across this issue.

https://product.voxmedia.com/2019/6/17/18524029/the-ballad-of-drop-caps-and-design-systems

@tobireif
Copy link
Author

I need consistent positioning of large and medium-sized text across operating systems.

@tobireif
Copy link
Author

tobireif commented Jun 23, 2019

Regarding the article: It compares the rendering across browsers, not across operating systems. Their fix line-height: 1 does not fix the issue across OSs (eg https://tobireif.com/non_site_stuff/test_case_for_font_position_report_yet_another_font/ in Chrome on Mac vs Windows).

The issue is shown in the top post on this page here -> see the image below "Chrome on MacOS" vs "Chrome on Windows".

@tobireif
Copy link
Author

tobireif commented Jun 23, 2019

@tobireif
Copy link
Author

tobireif commented Sep 5, 2019

I hope that this issue will get resolved soon.

@tobireif
Copy link
Author

It would be great if this could get added to the agenda.

@litherum
Copy link
Contributor

I opened #4792 with the proposal I made in #2228 (comment)

@faceless2
Copy link

faceless2 commented May 20, 2020

I finally got around to doing some proper testing. Here's what I believe is the algorithm for extracting ascent, descent and line-gap from OpenType fonts across several browsers:

int ems = post.ems; // 1000, 2048 etc
boolean usetypo = (os2.fsSelector & 128) != 0; // USE_TYPO_METRICS
int ascent; // distance from text-top to baseline; positive
int descent; // distance from text-bottom to baseline; negative
int linegap; // add to ascent and -descent to get default line-height

if (safari_on_mac || chrome_on_mac || safari_on_ipad || chrome_on_ipad) {
    ascent = hhea.ascent;
    descent = hhea.descent;
    linegap = hhea.linegap;
} else if (firefox_on_mac || chrome_on_linux || chrome_on_android || edge_on_android || samsung_on_android || prince || bfo) {
    ascent = usetypo ? os2.sTypoAscender : hhea.ascent;
    descent = usetypo ? os2.sTypoDescender: hhea.descent;
    linegap = usetypo ? os2.sTypoLineGap : hhea.linegap;
} else if (firefox_on_linux || firefox_on_android) {
    ascent = usetypo ? os2.sTypoAscender : hhea.ascent;
    descent = usetypo ? os2.sTypoDescender: hhea.descent;
    linegap = os2.sTypoLineGap;
} else if (chrome_on_windows || firefox_on_windows || edge_on_windows || ie11) {
    ascent = usetypo ? os2.sTypoAscender : os2.usWinAscent;
    descent = usetypo ? os2.sTypoAscender : -os2.usWinDescent;
    linegap = usetypo ? os2.sTypoLineGap : hhea.linegap; // note ie11 puts all linegap after line.
}
if (firefox) {
    if (linegap == 0 && ascent - descent <= ems) {
        if (usetypo || !firefox_on_android) { // apparently, according to crossbrowsertesting.org
            linegap = ems * 1.2 - ascent + descent; // line-height always 1.2em
        }
    } else if (ascent - descent < ems) {
        linegap += ems - ascent + descent;
    }
}

I'd hoped to show exhaustive working in one place, but the testing got a bit out of hand and there are just too many combinations! Plus some of this I'd already established so didn't need to retest. I didn't test anything really weird (e.g. -ve ascents) and I haven't fully confirmed the firefox "fix up the line-gap" algorithm is the same on all platforms. So there's undoubtedly more to this, but I think it's pretty close.

(Edit: updated to add prince, our renderer, and some other results according to http://crossbrowsertesting.org)

@faceless2
Copy link

faceless2 commented May 20, 2020

I've made a hopefully coherent demonstration of this at https://bfo.com/misc/metrics/ - it doesn't demonstrate the firefox "fix-up the line gap" algorithm, but does show the special handling when linegap==0

@fantasai
Copy link
Collaborator

Fwiw, Gecko's normal computation is in GetNormalLineHeight

@faceless2
Copy link

Aha! Thank you @fantasai. So the "fix up the line gap" constant is NORMAL_LINE_HEIGHT_FACTOR

@tobireif
Copy link
Author

tobireif commented Jan 8, 2021

Finally there's a great solution for the issue where browsers display large text at different vertical positions across operating systems: font metrics overriding.

Check out my post:
https://tobireif.com/posts/ensuring_the_correct_vertical_position_of_large_text/

I'm leaving this ticket open, because having eg text-layout: glyph-bounding-box would mean we'd have a solution that doesn't require the web developer to figure out property values / percentages.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
css-inline-3 Current Work
Projects
None yet
Development

No branches or pull requests

10 participants