Support Harfbuzz in Metrics View#5522
Conversation
| context->get_enc_map = SFGetMap; | ||
| context->get_char_width = MVCharWidth; | ||
| context->get_kern_offset = MVGetKernOffset; | ||
| context->script_is_rtl = ScriptIsRightToLeft; |
There was a problem hiding this comment.
HarfBuzz has hb_script_get_horizontal_direction() which might be more up to date (hb_buffer_guess_segment_properties() will call it if the direction is not set or set to HB_DIRECTION_INVALID). But I still think a separate UI control to set direction is needed since a few scripts can be written horizontally in both directions.
There was a problem hiding this comment.
ScriptIsRightToLeft() is passed for use in the legacy shaper, HarfBuzz won't use it. Regarding the UI, I have some enhancements in mind, but for now I prefer to keep UI changes at the necessary minimum. GDraw poses quite a few challenges in its workings.
| auto ttf_map_it = ttf_map_.find(glyph_info.codepoint); | ||
| SplineChar* glyph_out = (ttf_map_it != ttf_map_.end()) | ||
| ? ttf_map_it->second | ||
| : get_notdef_glyph(); |
There was a problem hiding this comment.
There is hb_buffer_set_not_found_glyph() that allow setting any arbitrary glyph index to be used instead of 0, you can, say, set it to a special value that is guaranteed not to be a valid glyph index like INT32_MAX and intercept it here. In case it makes things any simpler/faster.
There was a problem hiding this comment.
Yes, I tried it, but Ubuntu 22 has HarfBuzz 2.7.4, and hb_buffer_set_not_found_glyph() was introduced in 3.1.0. Besides, I need to check the glyph index anyway, it would be a crash otherwise.
|
The high-level HarfBuzz shaper structure is as follows:
The building of HarfBuzz shaper is heavy, and eventually we shall make an effort to improve its performance. Currently the TTF is written to a temporary file, and as far as I can see, it's not even guaranteed to reside in RAM. In Windows it's always on the disk (see For now I would prefer to keep the scope of this PR as is, but I can see several possibilities to improve the performance in future:
|
|
I'll take another look at this review-wise, but I suggest that @iorsh should start thinking about documentation. |
| int16_t cmax, clen; | ||
| SplineChar **chars; /* Character input stream */ | ||
| struct opentype_str *glyphs;/* after going through the various gsub/gpos transformations */ | ||
| ShapeMetrics *metrics; /* One for each glyph above */ |
There was a problem hiding this comment.
Comment on difference between ShapeMetrics (metrics_core) and metricchar (metrics_ui)
| mv->glyphs = shaper_apply_features(mv->shaper, mv->chars, feats, | ||
| script, lang, mv->pixelsize, mv->vertical); | ||
| if (mv->glyphs == NULL) { | ||
| mv->glyphs = calloc(1, sizeof(struct opentype_str)); |
There was a problem hiding this comment.
By convention, output of ApplyTickedFeatures() is terminated by a zeroized structure and is never NULL (see source). As we move to shaper interface, this convention should hold, but we put this allocation here out of caution.
| } | ||
|
|
||
| // TODO: Move metrics calculations completely into shapers, and make MetricsView::metric const. | ||
| mv->metrics = (ShapeMetrics*)shaper_metrics(mv->shaper); |
There was a problem hiding this comment.
Return this from shaper_apply_features()
| return shaper_defs; | ||
| } | ||
|
|
||
| const char* get_default_shaper() { |
There was a problem hiding this comment.
Add user preference for default shaper.
Retain current shaper per session, which would be initialized from preference, but would not affect it.
| } | ||
| } | ||
|
|
||
| std::set<Tag> BuiltInShaper::default_features(Tag script, Tag /*lang*/, |
There was a problem hiding this comment.
Why this was not deleted in legacy code? If still in use, put the reference in comment
There was a problem hiding this comment.
Duplicates StdFeaturesOfScript(), which is still in use
| // features list, but if it is unselected it should be in the features | ||
| // list with value set to 0 (disable). | ||
| const std::set<Tag> default_feats = | ||
| default_features(script, lang, vertical); |
There was a problem hiding this comment.
Move outside the cycle
| } | ||
|
|
||
| // No special glyph found, create it. | ||
| notdef_glyph_ = context_->get_or_make_char(context_->sf, -1, ".notdef"); |
There was a problem hiding this comment.
What happens in legacy shaper?
There was a problem hiding this comment.
In the legacy code we act similarly, calling
MVTextChanged()
WordlistEscapedInputStringToParsedDataComplex()
SFGetOrMakeChar()
frank-trampe
left a comment
There was a problem hiding this comment.
This all makes sense to me.
|
THIS IS HUGE!!!! in my opinion it justifies a new release. |
This PR introduces HarfBuzz shaping in Metrics view.
After:



Before:
Menu:
Fixes #3733, fixes #3837, fixes #4158, fixes #4511, fixes #5343, fixes #5513