Skip to content
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

text metrices for single space returns 0, it returns non-zero for multiple spaces #91

Open
htmlui opened this issue Aug 24, 2020 · 5 comments

Comments

@htmlui
Copy link

htmlui commented Aug 24, 2020

In your Getting Started docs you have used following method to know the span of text. but span of single space is returned 0
it returns non-zero if there are multiple spaces.

Following testcase would reproduce the issue.
conf: Linux (blend2d statically linked)

#include <blend2d.h>
#include < string >
#include < iostream >

int main(int argc, char* argv[]) {
BLFontFace face;
BLResult err = face.createFromFile("NotoSans-Regular.ttf");
BLFont font;
font.createFromFace(face, 50.0f);
BLTextMetrics tm;
BLGlyphBuffer gb;
gb.setUtf8Text(" ");
font.shape(gb);
font.getTextMetrics(gb, tm);
std::cout << tm.boundingBox.x1 - tm.boundingBox.x0 << std::endl;
return 0;
}

@kobalicek
Copy link
Member

Interesting, thanks! I think the current behavior means that if you have multiple spaces it counts space advance, but if you have a single space it doesn't for some reason. I will look into this.

@kobalicek
Copy link
Member

I have checked this and I'm not sure what would be the best way of solving the issues.

If you get a metrics of a text that has a single space the bounding box would be zero, because the right side bearing is equal to the width of the space - thus it would be decreased from the bbox.x1, which would make it zero. If you measure two spaces like this, the bounding box would include the first space although as it would also subtract right side bearing from x1, but it would only subtract the last one (it doesn't check for empty glyphs).

I'm wondering how other libraries deal with this, maybe the metrics should be updated a bit to not include bounding box, but to include information that is more relevant to the content.

@fdwr
Copy link

fdwr commented Apr 11, 2022

I'm wondering how other libraries deal with this

In the Windows DirectWrite API (which I worked on for 10 years), there are two separate concepts:

(a) the layout cell bounding box (see DWRITE_TEXT_METRICS)
(b) the glyph ink bounding box (see DWRITE_GLYPH_METRICS and DWRITE_OVERHANG_METRICS).

Alas the concepts are not conveyed in the API as simple as they could be :/, as there is a bunch of TrueType terminology in there (like "side bearings" and "overhangs") and annoying typographic legacy (lead type limitations really don't apply to digital type), but at the most basic concept, they're just rects. e.g.

struct BLTextMetrics2
{
    BLBox layoutBox; // conveys the horizontal advance, vertical advance, ascent, and descent
    BLBox inkBox // conveys the bounding box of the graphical via TrueType side bearings
};

So for the layout box, a space has a bounding box equal to the hmtx horizontal advance width × (ascent+descent) (at least for horizontal layout, whereas vertical text layout IIRC has a height of the vertical advance from VMTX), and so 3 spaces would be 3x wider than 1 space.

For the glyph ink bounding box, spaces have no graphic content and thus an empty box (the sidebearings collapse to zero), meaning 3 spaces would have a 0×0 box just as 1 space would - the union of two empty boxes should remain an empty box, independent of glyph translation. So the layout box is nonempty but the ink box is empty.

For diacritic marks, the situation is reversed - they have an empty layout box (no advance width, at least for most diacritics of the Unicode category Mn mark-nonspacing) but they have a non-empty ink bounding box.

  • red box - glyph ink bounds
  • cyan box - layout cell bounds
  • green arrows - horizontal and vertical origin and advances

image

-cheers, a random spelunker

@kobalicek
Copy link
Member

kobalicek commented Apr 11, 2022

That's a very nice explanation, thanks!

When designing the API I guess I was so worried about the performance of ink box in CFF fonts, which has to be calculated (unlike TTF where it's provided by the font itself). Now, when I see your explanation it totally makes sense to make some changes in Blend2D.

It would be nice @fdwr to have you around to provide feedback when new features are developed / to comment the issues in the existing API :)

BTW in DWRITE_TEXT_METRICS there is also widthIncludingTrailingWhitespace member, is this dealing with a similar problem like this issue?

@fdwr
Copy link

fdwr commented Apr 11, 2022

It would be nice to have you around to provide feedback when new features are developed

Sure, tag my name, and I'll see it GitHub notifications. Note I'm 99% unfamiliar with the Blend2D API (just came across it today, mentioned from LunaSvg as an alternate possible backend), but I'm happy to look over the font/typography aspects, as I have some experience :b (color fonts via SVG and embedded bitmaps, variable font selection, bidi algorithm, line breaking. vertical text layout...).

BTW in DWRITE_TEXT_METRICS there is also widthIncludingTrailingWhitespace member, is this dealing with a similar problem like this issue?

Not quite. Some scenarios want to know the layout width of all characters including trailing whitespace, but most scenarios do not want to consider the trailing whitespace of wrapped lines when computing the width of the UI control (like say a tooltip or label). So the DWRITE_TEXT_METRICS::width defaults to omitting whitespace and is really widthExcludingTrailingWhitespace.

unlike TTF where it's provided by the font itself

I suppose you return the ideal metrics from the glyf table rather the pixel precise values after scaling, given (from what I read elsewhere) that Blend2D does not apply hinting. DirectWrite (being newer than GDI) moved away from grid-fitted integral advance widths (though it still supports them as an option for layout compat with GDI) and uses ideal advance widths. It does render glyphs though with grid-fitted contours so the stems snap nicely vertically rather than being blurry. Just beware that a Turing complete TrueType rasterizer is a lot of work and long tail bug farm 😅.

When designing the API I guess I was so worried about the performance of ink box in CFF fonts, which has to be calculated

Yeah, I recall there being more computational cost with CFF in some regards, but we just cache the values in the IDWriteFontFace instance (with an in-process cache, plus a delayed font cache service that asynchronously caches future requests in case other processes make the same request or even a new instance of the current process - something you don't so easily have the luxury of :b).

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

No branches or pull requests

3 participants