kas-gui / kas-text

Rich text processing
Apache License 2.0
58 stars 2 forks source link

HarfBuzz integration #7

Closed dhardy closed 3 years ago

dhardy commented 3 years ago

@RazrFalcon @alexheretic I wonder if you could help me with this?

This code partly works (using glyph-brush to do the drawing). It does do shaping and lay out a line of text which seems okay (though I haven't implemented BIDI yet).

Questions:

dhardy commented 3 years ago

Also @manuel-rhdt as the author of harfbuzz_rs might be willing to take a quick look?

RazrFalcon commented 3 years ago

I store a Face and create a Font on each use

hb_font_create is esentually free. hb_face_create is the expensive one.

since I only know the size at time of usage

You can change font size at runtime.

HarfBuzz apparently doesn't report the text index of each glyph?

Yes, it's cluster.

dhardy commented 3 years ago

You can change font size at runtime.

Not without mutable storage, which I don't have. But good to know that hb_font_create is fast.

Yes, it's cluster.

So simple? I initially tried cluster and codepoint the other way around :facepalm:

Thanks!

harfbuzz

RazrFalcon commented 3 years ago

codepoint becomes glyph_id after shaping. It's an optimization that hb uses. A bit confusing, I agree.

dhardy commented 3 years ago

Regarding the 1 / 128 factor: do you happen to know if that is correct?

RazrFalcon commented 3 years ago

Not sure what 128 should mean, but it should be font_size/units_per_em (harfbuzz_rs::Face::upem). Assuming you're not setting the font size manually.

Different fonts have different units_per_em. In resvg, I'm not setting the font size at all, e.g. using the "font units" during shaping. And scaling everything to required "font size" later manually.

dhardy commented 3 years ago

I'm not clear on the difference between font size and PPEM. Is it to do with hints for small font sizes?

But the factor I was talking about concerns the x_advance output (and co.). It's necessary to put the glyphs next to each other.

RazrFalcon commented 3 years ago

Is it to do with hints for small font sizes?

HarfBuzz doesn't do hinting.

x_advance depends on a font size. If you are not setting the font size, hb will use font units (units_per_em). Otherwise it will scale the advance and offset values respectively.

dhardy commented 3 years ago

Aha, so I should set font_size not ppem, then I don't need the scale factor. I guess that makes more sense? Glyphs seem a bit spaced out. I wish there was clear documentation of units (both HarfBuzz and glyph-brush).

layout

RazrFalcon commented 3 years ago

I think hb_font_set_scale is what you're looking for.

dhardy commented 3 years ago

No, what I'm looking for is units, partly to match between libs and partly to be able to expose standard units for font sizes.

Putting this together, an 'M' in 10-point font on a 96-DPI screen should be 13.333 pixels wide. Seems to check out.

ab-glyph uses just PxScale, which is the "scale in pixels". If I set this to 42 and use my handy screen-ruler, an 'M' is significantly smaller than 42 pixels wide (with both serif and sans-serif fonts, more-so with the latter). Reading the code, PxScale is just a multiplier on the font measures exposed by ttf-parser, which also don't have units specified. Since the latter's your code @RazrFalcon I'm guessing you know the answer?

HarfBuzz exposes three methods:

I don't understand why the first two are different, given the usual definition of point-size for a font. Maybe I need to look at some more code...

RazrFalcon commented 3 years ago

ttf-parser doesn't support font size at all. Everything is in font units, aka units per em.

HarfBuzz exposes three methods

Just look at the sources. First two doesn't do anything you are expecting.

RazrFalcon commented 3 years ago

@dhardy this is how hb-shape's --font-size option works:

https://github.com/harfbuzz/harfbuzz/blob/069c5de9f8d95ac8fb1b909e3276af9887311222/util/options.cc#L711..L721

dhardy commented 3 years ago

Right, I missed a concept: fonts have an embedded unit. Also, it seems that when HB says "pixel" it doesn't always mean "pixel" thanks to the Apple Retina malarkey.

Also, the docs specify that "ppem" is "pixels per em". There seems to be a special case when this is zero. And "many fonts enable and disable hints based on the size it is used at, so setting this is important for font objects".

This lets me guess that x_scale has units "X per font-unit" where X is the same units as x_advance (thus can be 1 pixel or can be more for sub-pixel precision).

dhardy commented 3 years ago

Okay, after sorting out the font units for ab-glyph, things line up properly. But I still don't quite get the HB methods:

RazrFalcon commented 3 years ago

ppem is used during raster glyphs processing and hinting (not the rendering one). It doesn't affect x_advance dramatically. Only set_scale does.

The formula is x_advance * (font_size / units_per_em)

dhardy commented 3 years ago

Indeed, setting ppem doesn't have much visible effect. I'll just leave it unset then not knowing what to set it to?

The formula is x_advance * (font_size / units_per_em)

Only if I set set_scale(units_per_em, units_per_em). I guess this makes sense to use units_per_em for the sub-pixel precision, but it is overkill. A smaller scale such as 4 would be mostly the same visually while limiting the number of sub-pixel offsets for glyphs (potentially improving glyph caching).

RazrFalcon commented 3 years ago

Only if I set set_scale(units_per_em, units_per_em).

Yes, which is default.

dhardy commented 3 years ago

Alright, thanks for the help @RazrFalcon!

Next up will probably be BIDI processing, then fixes for left/right navigation, then font formatting. Still a lot to do but at least it seems I don't need to worry about font fallbacks (well, works for me)!