Index: gfx/harfbuzz/NEWS =================================================================== --- gfx/harfbuzz/NEWS +++ gfx/harfbuzz/NEWS @@ -1,5 +1,38 @@ +Overview of changes leading to 14.2.1 +Tuesday, June 2, 2026 +===================================== +- Various AAT shaping fixes: legacy `mort` contextual offsets (which could + produce out-of-font glyph IDs), in-place deleted-glyph replacements, and + overflow in obsolete offset math. +- Fix Arabic PUA fallback shaping for the isolated lam-alef-maksura ligature. +- Fix float-to-int overflow in `avar2` mapping with malformed fonts. +- Harden buffer verification after detecting non-monotone clusters. +- Various `COLR` v1 fixes: fix handling of `.notdef` without paint, round alpha + consistently, and report the root clip under the font transform. +- Various Glyph-extents fixes: inclusive rounding, and floating-point scaling + before rounding so the reported box always covers the glyph. +- Various Subsetting fixes: keep the `palt` spacing feature by default, raise + the repacker `MAX_SPACES` limit, fix a repacker crash on shared `LigatureSet` + nodes, guard `gvar` size overflow on 32-bit, and fix the `post` glyph-name + sort comparator on macOS. +- Replace `std::sort` with an internal quicksort, removing leaked `std::` + symbols from the `libharfbuzz` ABI. +- Harden size computations with saturating arithmetic against 32-bit overflow. +- Various improvements to the experimental Rust shaper (HarfRust) and font + functions (`fontations`): honor custom font funcs, key shape plans on + features, faster buffer handling, and update to HarfRust 0.8. +- Various fixes to the experimental `harfbuzz-gpu` and `harfbuzz-vector` + libraries, including a `harfbuzz-vector` heap buffer overflow and Windows + build fixes. +- Map the `Hrkt` (Katakana or Hiragana) script tag to the `kana` OpenType tag. +- Build configuration: new `HB_CONFIG_OVERRIDE_LAST_H` override header, + decouple `HB_NO_DRAW` from `HB_NO_CFF`, and an optional `hb-allocator` Cargo + feature. +- Various build, CI, and fuzzing fixes. + + Overview of changes leading to 14.2.0 Monday, April 20, 2026 ===================================== In this release, the experimental raster, vector, and GPU libraries went Index: gfx/harfbuzz/README.md =================================================================== --- gfx/harfbuzz/README.md +++ gfx/harfbuzz/README.md @@ -170,11 +170,11 @@ > TrueType that adds support for complex script rendering, and HarfBuzz is an > implementation of OpenType text shaping. ## Users -HarfBuzz is used in Android, Chrome, ChromeOS, Firefox, GNOME, GTK+, KDE, +HarfBuzz is used in Android, Chrome, ChromeOS, Firefox, Flutter, GNOME, GTK+, KDE, Qt, LibreOffice, OpenJDK, XeTeX, Adobe Photoshop, Illustrator, InDesign, Microsoft Edge, Amazon Kindle, PlayStation, Godot Engine, Unreal Engine, Figma, Canva, QuarkXPress, Scribus, smart TVs, car displays, and many other places. Index: gfx/harfbuzz/moz.yaml =================================================================== --- gfx/harfbuzz/moz.yaml +++ gfx/harfbuzz/moz.yaml @@ -18,15 +18,15 @@ # Usually different from repository url url: https://github.com/harfbuzz/harfbuzz # Human-readable identifier for this version/release # Generally "version NNN", "tag SSS", "bookmark SSS" - release: 14.2.0 (2026-04-20T05:10:48+02:00). + release: 14.2.1 (2026-06-02T14:30:59+03:00). # Revision to pull in # Must be a long or short commit SHA (long preferred) - revision: 14.2.0 + revision: 14.2.1 # The package's license, where possible using the mnemonic from # https://spdx.org/licenses/ # Multiple licenses can be specified (as a YAML list) # A "LICENSE" file must exist containing the full license text Index: gfx/harfbuzz/src/ArabicPUATraditional.txt =================================================================== --- gfx/harfbuzz/src/ArabicPUATraditional.txt +++ gfx/harfbuzz/src/ArabicPUATraditional.txt @@ -183,11 +183,11 @@ 0xF290 0xFCDB # ARABIC LIGATURE YEH WITH HAH INITIAL FORM 0xF291 0xFCDC # ARABIC LIGATURE YEH WITH KHAH INITIAL FORM 0xF292 0xFC6D # ARABIC LIGATURE BEH WITH NOON FINAL FORM 0xF293 0xFC73 # ARABIC LIGATURE TEH WITH NOON FINAL FORM 0xF294 0xFC94 # ARABIC LIGATURE YEH WITH NOON FINAL FORM -0xF295 0xFC86 # ARABIC LIGATURE LAM WITH ALEF MAKSURA FINAL FORM +0xF295 0xFC43 # ARABIC LIGATURE LAM WITH ALEF MAKSURA ISOLATED FORM 0xF296 0xFC9F # ARABIC LIGATURE BEH WITH MEEM INITIAL FORM 0xF297 0xFCA4 # ARABIC LIGATURE TEH WITH MEEM INITIAL FORM 0xF298 0xFCD5 # ARABIC LIGATURE NOON WITH MEEM INITIAL FORM 0xF299 0xFCDD # ARABIC LIGATURE YEH WITH MEEM INITIAL FORM 0xF29A 0xFCA8 # ARABIC LIGATURE JEEM WITH MEEM INITIAL FORM Index: gfx/harfbuzz/src/OT/Color/COLR/COLR.hh =================================================================== --- gfx/harfbuzz/src/OT/Color/COLR/COLR.hh +++ gfx/harfbuzz/src/OT/Color/COLR/COLR.hh @@ -117,10 +117,16 @@ ~hb_paint_context_t () { hb_font_destroy (font); } + static unsigned + color_alpha (hb_color_t color, float alpha) + { + return roundf (hb_color_get_alpha (color) * alpha); + } + hb_color_t get_color (unsigned int color_index, float alpha, hb_bool_t *is_foreground) { hb_color_t color = foreground; *is_foreground = true; @@ -134,11 +140,11 @@ } return HB_COLOR (hb_color_get_blue (color), hb_color_get_green (color), hb_color_get_red (color), - hb_color_get_alpha (color) * alpha); + color_alpha (color, alpha)); } inline void recurse (const Paint &paint); }; @@ -2403,14 +2409,15 @@ return record; } const BaseGlyphPaintRecord* get_base_glyph_paintrecord (hb_codepoint_t gid) const { - const BaseGlyphPaintRecord* record = &(this+baseGlyphList).bsearch ((unsigned) gid); - if ((record && (hb_codepoint_t) record->glyphId != gid)) - record = nullptr; - return record; + const BaseGlyphList &list = this+baseGlyphList; + unsigned int i; + if (!list.bfind ((unsigned) gid, &i)) + return nullptr; + return &list[i]; } bool downgrade_to_V0 (const hb_set_t &glyphset) const { //no more COLRv1 glyphs, downgrade to version 0 @@ -2698,23 +2705,14 @@ if (paint) { // COLRv1 glyph bool is_bounded = true; + hb_glyph_extents_t extents = {}; if (clip) { - hb_glyph_extents_t extents; - if (get_clip (glyph, &extents, instancer)) - { - font->scale_glyph_extents (&extents); - c.funcs->push_clip_rectangle (c.data, - extents.x_bearing, - extents.y_bearing + extents.height, - extents.x_bearing + extents.width, - extents.y_bearing); - } - else + if (!get_clip (glyph, &extents, instancer)) { clip = false; is_bounded = false; } @@ -2733,18 +2731,25 @@ } } c.funcs->push_font_transform (c.data, font); + if (clip) + c.funcs->push_clip_rectangle (c.data, + extents.x_bearing, + extents.y_bearing + extents.height, + extents.x_bearing + extents.width, + extents.y_bearing); + if (is_bounded) c.recurse (*paint); - c.funcs->pop_transform (c.data); - if (clip) c.funcs->pop_clip (c.data); + c.funcs->pop_transform (c.data); + return true; } } const BaseGlyphRecord *record = get_base_glyph_record (glyph); Index: gfx/harfbuzz/src/check-symbols.py =================================================================== --- gfx/harfbuzz/src/check-symbols.py +++ gfx/harfbuzz/src/check-symbols.py @@ -68,11 +68,11 @@ symprefix = "_" if suffix == "dylib" else "" EXPORTED_SYMBOLS = [ s.split()[2] for s in re.findall( - r"^.+ [BCDGIRSTu] .+$", + r"^.+ [BCDGIRSTWu] .+$", subprocess.check_output(nm.split() + [so]).decode("utf-8"), re.MULTILINE, ) if not re.match(r".* %s(%s)\b" % (symprefix, IGNORED_SYMBOLS), s) ] Index: gfx/harfbuzz/src/gen-arabic-table.py =================================================================== --- gfx/harfbuzz/src/gen-arabic-table.py +++ gfx/harfbuzz/src/gen-arabic-table.py @@ -136,17 +136,17 @@ else: assert False, "Unknown language: %s" % language.name LIGATURES = ( 0xF2EE, 0xFC08, 0xFC0E, 0xFC12, 0xFC32, 0xFC3F, 0xFC40, 0xFC41, 0xFC42, - 0xFC44, 0xFC4E, 0xFC5E, 0xFC60, 0xFC61, 0xFC62, 0xFC6A, 0xFC6D, 0xFC6F, - 0xFC70, 0xFC73, 0xFC75, 0xFC86, 0xFC8F, 0xFC91, 0xFC94, 0xFC9C, 0xFC9D, - 0xFC9E, 0xFC9F, 0xFCA1, 0xFCA2, 0xFCA3, 0xFCA4, 0xFCA8, 0xFCAA, 0xFCAC, - 0xFCB0, 0xFCC9, 0xFCCA, 0xFCCB, 0xFCCC, 0xFCCD, 0xFCCE, 0xFCCF, 0xFCD0, - 0xFCD1, 0xFCD2, 0xFCD3, 0xFCD5, 0xFCDA, 0xFCDB, 0xFCDC, 0xFCDD, 0xFD30, - 0xFD88, 0xFEF5, 0xFEF6, 0xFEF7, 0xFEF8, 0xFEF9, 0xFEFA, 0xFEFB, 0xFEFC, - 0xF201, 0xF211, 0xF2EE, + 0xFC43, 0xFC44, 0xFC4E, 0xFC5E, 0xFC60, 0xFC61, 0xFC62, 0xFC6A, 0xFC6D, + 0xFC6F, 0xFC70, 0xFC73, 0xFC75, 0xFC86, 0xFC8F, 0xFC91, 0xFC94, 0xFC9C, + 0xFC9D, 0xFC9E, 0xFC9F, 0xFCA1, 0xFCA2, 0xFCA3, 0xFCA4, 0xFCA8, 0xFCAA, + 0xFCAC, 0xFCB0, 0xFCC9, 0xFCCA, 0xFCCB, 0xFCCC, 0xFCCD, 0xFCCE, 0xFCCF, + 0xFCD0, 0xFCD1, 0xFCD2, 0xFCD3, 0xFCD5, 0xFCDA, 0xFCDB, 0xFCDC, 0xFCDD, + 0xFD30, 0xFD88, 0xFEF5, 0xFEF6, 0xFEF7, 0xFEF8, 0xFEF9, 0xFEFA, 0xFEFB, + 0xFEFC, 0xF201, 0xF211, 0xF2EE, ) def print_shaping_table(f): shapes = {} Index: gfx/harfbuzz/src/gen-gpu-shader-artifacts.py =================================================================== --- gfx/harfbuzz/src/gen-gpu-shader-artifacts.py +++ gfx/harfbuzz/src/gen-gpu-shader-artifacts.py @@ -36,8 +36,9 @@ # Copy back to source tree; if read-only, verify committed copy matches. src_copy = os.path.join(CURRENT_SOURCE_DIR, hh) try: shutil.copyfile(OUTPUT, src_copy) except OSError: - import filecmp - if not filecmp.cmp(OUTPUT, src_copy, shallow=False): - sys.exit(f'{src_copy} is out of date; regenerate with a writable source tree') + # Read as text to ignore line-ending changes + with open(OUTPUT, 'r') as f1, open(src_copy, 'r') as f2: + if f1.read() != f2.read(): + sys.exit(f'{src_copy} is out of date; regenerate with a writable source tree') Index: gfx/harfbuzz/src/gen-tag-table.py =================================================================== --- gfx/harfbuzz/src/gen-tag-table.py +++ gfx/harfbuzz/src/gen-tag-table.py @@ -906,25 +906,37 @@ print ('// WARNING: this file was generated by ../../harfbuzz/src/gen-tag-table.py --rust') print () print ('use read_fonts::types::Tag;') print () print ('pub(crate) struct LangTag {') - print (" pub language: &'static str,") + print (' pub language: [u8; 4],') print (' pub tag: Tag,') print ('}') print () + print ('const fn lang(language: &[u8]) -> [u8; 4] {') + print (' let mut bytes = [0; 4];') + print (' let mut i = 0;') + print () + print (' while i < language.len() {') + print (' bytes[i] = language[i];') + print (' i += 1;') + print (' }') + print () + print (' bytes') + print ('}') + print () print ('use super::tag::{lang_matches, strncmp, subtag_matches};') print () print ('#[rustfmt::skip]') print ('pub(crate) static OPEN_TYPE_LANGUAGES: &[LangTag] = &[') for language, tags in sorted (ot.from_bcp_47.items ()): if language == '' or '-' in language: continue commented_out = len (language) == 3 and len (tags) == 1 and language == tags[0].lower () for tag in tags: - print ('%sLangTag { language: "%s", \ttag: %s },' % ('// ' if commented_out else ' ', language, hb_tag_rust (tag)), end='') + print ('%sLangTag { language: lang(b"%s"), \ttag: %s },' % ('// ' if commented_out else ' ', language, hb_tag_rust (tag)), end='') print (' // ', end='') bcp_47_name = bcp_47.names.get (language, '') bcp_47_name_candidates = bcp_47_name.split ('\n') ot_name = ot.names[tag] scope = bcp_47.scopes.get (language, '') Index: gfx/harfbuzz/src/graph/graph.hh =================================================================== --- gfx/harfbuzz/src/graph/graph.hh +++ gfx/harfbuzz/src/graph/graph.hh @@ -1284,10 +1284,35 @@ } return new_child_idx; } /* + * Creates a new child node and remap the old child at specified position to it. + * + * Returns the index of the newly created child. + * + */ + unsigned remap_child_at_position (unsigned parent_idx, unsigned old_child_idx, const void* offset) + { + const auto& parent = object (parent_idx); + if (!offset || offset < parent.head || offset >= parent.tail) return -1; + + for (auto& l : parent.real_links) + { + if (l.objidx != old_child_idx || offset != parent.head + l.position) + continue; + + unsigned new_child_idx = duplicate (old_child_idx); + if (new_child_idx == (unsigned) -1) return -1; + reassign_link (l, parent_idx, new_child_idx, false); + return new_child_idx; + } + + return -1; + } + + /* * Raises the sorting priority of all children. */ bool raise_childrens_priority (unsigned parent_idx) { DEBUG_MSG (SUBSET_REPACK, nullptr, " Raising priority of all children of %u", Index: gfx/harfbuzz/src/graph/ligature-graph.hh =================================================================== --- gfx/harfbuzz/src/graph/ligature-graph.hh +++ gfx/harfbuzz/src/graph/ligature-graph.hh @@ -68,10 +68,13 @@ hb_vector_t split_subtables (gsubgpos_graph_context_t& c, unsigned this_index) { auto split_points = compute_split_points(c, this_index); + if (!split_points || !duplicate_shared_liga_sets(c, this_index)) + return hb_vector_t (); + split_context_t split_context { c, this, this_index, total_number_ligas(c, this_index), @@ -119,10 +122,32 @@ map[array_index] = l.objidx; } return map; } + bool duplicate_shared_liga_sets(gsubgpos_graph_context_t& c, unsigned this_index) const + { + hb_set_t visited; + for (unsigned i = 0; i < ligatureSet.len; i++) + { + auto liga_set = c.graph.as_table(this_index, &ligatureSet[i]); + if (!liga_set.table) return false; + + unsigned liga_set_index = liga_set.index; + if (visited.has (liga_set_index)) + { + unsigned new_index = c.graph.remap_child_at_position (this_index, liga_set_index, &ligatureSet[i]); + if (new_index == (unsigned) -1) return false; + } + else + { + visited.add (liga_set_index); + } + } + return true; + } + hb_vector_t compute_split_points(gsubgpos_graph_context_t& c, unsigned this_index) const { // For ligature subst coverage is always packed last, and as a result is where an overflow // will happen if there is one, so we can check the estimate length of the Index: gfx/harfbuzz/src/harfbuzz-world.cc =================================================================== --- gfx/harfbuzz/src/harfbuzz-world.cc +++ gfx/harfbuzz/src/harfbuzz-world.cc @@ -1,9 +1,36 @@ #ifdef HB_FEATURES_H #include HB_FEATURES_H #endif +#ifdef HB_HAS_CORETEXT +#define HAVE_CORETEXT 1 +#endif +#ifdef HB_HAS_DIRECTWRITE +#define HAVE_DIRECTWRITE 1 +#endif +#ifdef HB_HAS_FREETYPE +#define HAVE_FREETYPE 1 +#endif +#ifdef HB_HAS_GDI +#define HAVE_GDI 1 +#endif +#ifdef HB_HAS_GLIB +#define HAVE_GLIB 1 +#endif +#ifdef HB_HAS_GRAPHITE +#define HAVE_GRAPHITE 1 +#endif +#ifdef HB_HAS_UNISCRIBE +#define HAVE_UNISCRIBE 1 +#define HAVE_GDI 1 +#endif +#ifdef HB_HAS_WASM +#define HAVE_WASM 1 +#endif + + /* Core library. */ #include "OT/Var/VARC/VARC.cc" #include "hb-aat-layout.cc" #include "hb-aat-map.cc" #include "hb-blob.cc" @@ -64,42 +91,36 @@ #include "hb-cairo.cc" #include "hb-static.cc" #endif #ifdef HB_HAS_CORETEXT -#define HAVE_CORETEXT 1 #include "hb-coretext-font.cc" #include "hb-coretext-shape.cc" #include "hb-coretext.cc" #endif #ifdef HB_HAS_DIRECTWRITE -#define HAVE_DIRECTWRITE 1 #include "hb-directwrite-font.cc" #include "hb-directwrite-shape.cc" #include "hb-directwrite.cc" #endif #ifdef HB_HAS_FREETYPE -#define HAVE_FREETYPE 1 #include "hb-ft.cc" #endif #ifdef HB_HAS_GDI #ifndef HAVE_GDI -#define HAVE_GDI 1 #include "hb-gdi.cc" #endif #endif #ifdef HB_HAS_GLIB -#define HAVE_GLIB 1 #include "hb-glib.cc" #endif #ifdef HB_HAS_GRAPHITE -#define HAVE_GRAPHITE 1 #include "hb-graphite2.cc" #endif #ifdef HB_HAS_GPU #include "hb-gpu-draw.cc" @@ -144,12 +165,10 @@ #include "hb-subset-table-var.cc" #include "hb-subset.cc" #endif #ifdef HB_HAS_UNISCRIBE -#define HAVE_UNISCRIBE 1 -#define HAVE_GDI 1 #include "hb-uniscribe.cc" #endif #ifdef HB_HAS_VECTOR #include "hb-static.cc" @@ -161,9 +180,8 @@ #include "hb-vector.cc" #include "hb-zlib.cc" #endif #ifdef HB_HAS_WASM -#define HAVE_WASM 1 #include "hb-wasm-api.cc" #include "hb-wasm-shape.cc" #endif Index: gfx/harfbuzz/src/harfbuzz-world.cc.in =================================================================== --- gfx/harfbuzz/src/harfbuzz-world.cc.in +++ gfx/harfbuzz/src/harfbuzz-world.cc.in @@ -1,45 +1,66 @@ #ifdef HB_FEATURES_H #include HB_FEATURES_H #endif +#ifdef HB_HAS_CORETEXT +#define HAVE_CORETEXT 1 +#endif +#ifdef HB_HAS_DIRECTWRITE +#define HAVE_DIRECTWRITE 1 +#endif +#ifdef HB_HAS_FREETYPE +#define HAVE_FREETYPE 1 +#endif +#ifdef HB_HAS_GDI +#define HAVE_GDI 1 +#endif +#ifdef HB_HAS_GLIB +#define HAVE_GLIB 1 +#endif +#ifdef HB_HAS_GRAPHITE +#define HAVE_GRAPHITE 1 +#endif +#ifdef HB_HAS_UNISCRIBE +#define HAVE_UNISCRIBE 1 +#define HAVE_GDI 1 +#endif +#ifdef HB_HAS_WASM +#define HAVE_WASM 1 +#endif + + /* Core library. */ %%BASE_SOURCES%% #ifdef HB_HAS_CAIRO %%CAIRO_SOURCES%% #endif #ifdef HB_HAS_CORETEXT -#define HAVE_CORETEXT 1 %%CORETEXT_SOURCES%% #endif #ifdef HB_HAS_DIRECTWRITE -#define HAVE_DIRECTWRITE 1 %%DIRECTWRITE_SOURCES%% #endif #ifdef HB_HAS_FREETYPE -#define HAVE_FREETYPE 1 %%FREETYPE_SOURCES%% #endif #ifdef HB_HAS_GDI #ifndef HAVE_GDI -#define HAVE_GDI 1 %%GDI_SOURCES%% #endif #endif #ifdef HB_HAS_GLIB -#define HAVE_GLIB 1 %%GLIB_SOURCES%% #endif #ifdef HB_HAS_GRAPHITE -#define HAVE_GRAPHITE 1 %%GRAPHITE_SOURCES%% #endif #ifdef HB_HAS_GPU %%GPU_SOURCES%% @@ -56,18 +77,15 @@ #ifdef HB_HAS_SUBSET %%SUBSET_SOURCES%% #endif #ifdef HB_HAS_UNISCRIBE -#define HAVE_UNISCRIBE 1 -#define HAVE_GDI 1 %%UNISCRIBE_SOURCES%% #endif #ifdef HB_HAS_VECTOR %%VECTOR_SOURCES%% #endif #ifdef HB_HAS_WASM -#define HAVE_WASM 1 %%WASM_SOURCES%% #endif Index: gfx/harfbuzz/src/hb-aat-layout-common.hh =================================================================== --- gfx/harfbuzz/src/hb-aat-layout-common.hh +++ gfx/harfbuzz/src/hb-aat-layout-common.hh @@ -226,10 +226,15 @@ } void replace_glyph_inplace (unsigned i, hb_codepoint_t glyph) { buffer->info[i].codepoint = glyph; + if (glyph == DELETED_GLYPH) + { + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_AAT_HAS_DELETED; + _hb_glyph_info_set_aat_deleted (&buffer->info[i]); + } if (likely (using_buffer_glyph_set)) buffer_glyph_set->add (glyph); #ifndef HB_NO_OT_LAYOUT if (has_glyph_classes) _hb_glyph_info_set_glyph_props (&buffer->info[i], @@ -1132,35 +1137,41 @@ typedef HBUINT8 HBUSHORT; typedef ClassTable ClassTypeNarrow; typedef ClassTable ClassTypeWide; template - static unsigned int offsetToIndex (unsigned int offset, - const void *base, - const T *array) + static int offsetToIndex (int64_t offset, + const void *base, + const T *array) { + int64_t array_offset = (const char *) array - (const char *) base; + int bad_index = INT_MAX / T::static_size; + /* https://github.com/harfbuzz/harfbuzz/issues/3483 */ /* If offset is less than base, return an offset that would * result in an address half a 32bit address-space away, * to make sure sanitize fails even on 32bit builds. */ - if (unlikely (offset < unsigned ((const char *) array - (const char *) base))) - return INT_MAX / T::static_size; + if (unlikely (offset < array_offset)) + return bad_index; /* https://github.com/harfbuzz/harfbuzz/issues/2816 */ - return (offset - unsigned ((const char *) array - (const char *) base)) / T::static_size; + int64_t index = (offset - array_offset) / T::static_size; + if (unlikely (index > bad_index)) + return bad_index; + return index; } template - static unsigned int byteOffsetToIndex (unsigned int offset, - const void *base, - const T *array) + static int byteOffsetToIndex (int64_t offset, + const void *base, + const T *array) { return offsetToIndex (offset, base, array); } template - static unsigned int wordOffsetToIndex (unsigned int offset, - const void *base, - const T *array) + static int wordOffsetToIndex (int64_t offset, + const void *base, + const T *array) { return offsetToIndex (2 * offset, base, array); } }; struct ExtendedTypes Index: gfx/harfbuzz/src/hb-aat-layout-morx-table.hh =================================================================== --- gfx/harfbuzz/src/hb-aat-layout-morx-table.hh +++ gfx/harfbuzz/src/hb-aat-layout-morx-table.hh @@ -198,14 +198,16 @@ { typedef typename Types::HBUINT HBUINT; struct EntryData { - HBUINT16 markIndex; /* Index of the substitution table for the - * marked glyph (use 0xFFFF for none). */ - HBUINT16 currentIndex; /* Index of the substitution table for the - * current glyph (use 0xFFFF for none). */ + typedef typename std::conditional::type OffsetType; + + OffsetType markIndex; /* Index/offset of the substitution table for the + * marked glyph. */ + OffsetType currentIndex; /* Index/offset of the substitution table for the + * current glyph. */ public: DEFINE_SIZE_STATIC (4); }; enum Flags @@ -220,11 +222,14 @@ { return (entry.flags & SetMark); } bool is_actionable (const Entry &entry) const { - return entry.data.markIndex != 0xFFFF || entry.data.currentIndex != 0xFFFF; + if (Types::extended) + return entry.data.markIndex != 0xFFFF || entry.data.currentIndex != 0xFFFF; + else + return entry.data.markIndex || entry.data.currentIndex; } struct driver_context_t { static constexpr bool in_place = true; @@ -258,17 +263,20 @@ replacement = lookup.get_value (buffer->info[mark].codepoint, driver->num_glyphs); } } else { - unsigned int offset = entry.data.markIndex + buffer->info[mark].codepoint; - const UnsizedArrayOf &subs_old = (const UnsizedArrayOf &) subs; - replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)]; - if (!(replacement->sanitize (&c->sanitizer) && - hb_barrier () && - *replacement)) - replacement = nullptr; + if (entry.data.markIndex) + { + int offset = (int) entry.data.markIndex + buffer->info[mark].codepoint; + const UnsizedArrayOf &subs_old = (const UnsizedArrayOf &) subs; + replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)]; + if (!(replacement->sanitize (&c->sanitizer) && + hb_barrier () && + *replacement)) + replacement = nullptr; + } } if (replacement) { buffer->unsafe_to_break (mark, hb_min (buffer->idx + 1, buffer->len)); c->replace_glyph_inplace (mark, *replacement); @@ -285,17 +293,20 @@ replacement = lookup.get_value (buffer->info[idx].codepoint, driver->num_glyphs); } } else { - unsigned int offset = entry.data.currentIndex + buffer->info[idx].codepoint; - const UnsizedArrayOf &subs_old = (const UnsizedArrayOf &) subs; - replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)]; - if (!(replacement->sanitize (&c->sanitizer) && - hb_barrier () && - *replacement)) - replacement = nullptr; + if (entry.data.currentIndex) + { + int offset = (int) entry.data.currentIndex + buffer->info[idx].codepoint; + const UnsizedArrayOf &subs_old = (const UnsizedArrayOf &) subs; + replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)]; + if (!(replacement->sanitize (&c->sanitizer) && + hb_barrier () && + *replacement)) + replacement = nullptr; + } } if (replacement) { c->replace_glyph_inplace (idx, *replacement); ret = true; Index: gfx/harfbuzz/src/hb-algs.hh =================================================================== --- gfx/harfbuzz/src/hb-algs.hh +++ gfx/harfbuzz/src/hb-algs.hh @@ -1213,10 +1213,66 @@ if (result) *result = a + b; return b > (unsigned int) -1 - a; } +/* Saturating arithmetic on size_t. On platforms where size_t is wider than + * unsigned int (i.e. 64-bit), the inputs to get_size()-style computations + * (counts and static_sizes which are at most 32-bit) cannot overflow size_t, + * so these reduce to plain arithmetic. On 32-bit platforms (size_t is + * unsigned int), they saturate to SIZE_MAX so sanitize/serialize callers + * naturally reject the resulting size as out-of-range. */ + +static inline size_t +hb_unsigned_mul_saturate (size_t a, size_t b) +{ + if (sizeof (size_t) > sizeof (unsigned int)) + return a * b; +#if hb_has_builtin(__builtin_mul_overflow) + size_t result; + if (__builtin_mul_overflow (a, b, &result)) + return (size_t) -1; + return result; +#else + if (b > 0 && a > ((size_t) -1) / b) return (size_t) -1; + return a * b; +#endif +} + +static inline size_t +hb_unsigned_add_saturate (size_t a, size_t b) +{ + if (sizeof (size_t) > sizeof (unsigned int)) + return a + b; +#if hb_has_builtin(__builtin_add_overflow) + size_t result; + if (__builtin_add_overflow (a, b, &result)) + return (size_t) -1; + return result; +#else + if (b > ((size_t) -1) - a) return (size_t) -1; + return a + b; +#endif +} + +/* Variadic forms: fold left across all arguments. */ +template +static inline size_t +hb_unsigned_mul_saturate (size_t a, size_t b, size_t c, Ts... rest) +{ return hb_unsigned_mul_saturate (hb_unsigned_mul_saturate (a, b), c, rest...); } + +template +static inline size_t +hb_unsigned_add_saturate (size_t a, size_t b, size_t c, Ts... rest) +{ return hb_unsigned_add_saturate (hb_unsigned_add_saturate (a, b), c, rest...); } + +/* Saturating mul-add: a * b + c. Covers the dominant get_size() pattern + * `count * static_size + min_size`. */ +static inline size_t +hb_unsigned_mul_add_saturate (size_t a, size_t b, size_t c) +{ return hb_unsigned_add_saturate (hb_unsigned_mul_saturate (a, b), c); } + /* * Sort and search. */ @@ -1303,145 +1359,89 @@ (V*) (((const char *) base) + (pos * stride)) : nullptr; #pragma GCC diagnostic pop } -/* From https://github.com/noporpoise/sort_r - Feb 5, 2019 (c8c65c1e) - Modified to support optional argument using templates */ - -/* Isaac Turner 29 April 2014 Public Domain */ - -/* -hb_qsort function to be exported. -Parameters: - base is the array to be sorted - nel is the number of elements in the array - width is the size in bytes of each element of the array - compar is the comparison function - arg (optional) is a pointer to be passed to the comparison function - -void hb_qsort(void *base, size_t nel, size_t width, - int (*compar)(const void *_a, const void *_b, [void *_arg]), - [void *arg]); -*/ - -#define SORT_R_SWAP(a,b,tmp) ((void) ((tmp) = (a)), (void) ((a) = (b)), (b) = (tmp)) - -/* swap a and b */ -/* a and b must not be equal! */ -static inline void sort_r_swap(char *__restrict a, char *__restrict b, - size_t w) +/* Quicksort partitioning loop: median-of-three pivot, two-way + * Hoare partition, tail-call elimination on the larger side. + * Stops partitioning when subranges shrink below the threshold; + * a single insertion-sort pass over the whole array (run by the + * caller) finishes the job. Same structure libstdc++ uses for + * std::sort. + * + * Not stable; equivalent values may be swapped. */ +/* Quicksort partitioning loop: median-of-three pivot, two-way + * Hoare partition, tail-call elimination on the larger side. + * Stops partitioning when subranges shrink below the threshold; + * a single insertion-sort pass over the whole array (run by + * hb_qsort_inline below) finishes the job. Same shape libstdc++ + * uses for std::sort. + * + * Not stable; equivalent values may be swapped. */ +template +static inline void +hb_qsort_loop (T *base, size_t nel, Compar compar) { - char tmp, *end = a+w; - for(; a < end; a++, b++) { SORT_R_SWAP(*a, *b, tmp); } -} + while (nel > 24) + { + T *last = base + nel - 1; + T *mid = base + nel / 2; + + /* Median-of-three pivot, parked at last-1. */ + if (compar (*base, *mid) > 0) hb_swap (*base, *mid); + if (compar (*mid, *last) > 0) + { + hb_swap (*mid, *last); + if (compar (*base, *mid) > 0) hb_swap (*base, *mid); + } + hb_swap (*mid, *(last - 1)); + T &pivot = *(last - 1); -/* swap a, b iff a>b */ -/* a and b must not be equal! */ -/* __restrict is same as restrict but better support on old machines */ -template -static inline int sort_r_cmpswap(char *__restrict a, - char *__restrict b, size_t w, - Compar compar) -{ - if(compar(a, b) > 0) { - sort_r_swap(a, b, w); - return 1; - } - return 0; -} + /* Two-way Hoare partition. Inner loops are unguarded: + * median-of-three left *base <= pivot and *last >= pivot, + * which act as sentinels. */ + T *i = base, *j = last - 1; + while (true) + { + while (compar (*++i, pivot) < 0) {} + while (compar (*--j, pivot) > 0) {} + if (i >= j) break; + hb_swap (*i, *j); + } + hb_swap (*i, *(last - 1)); -/* -Swap consecutive blocks of bytes of size na and nb starting at memory addr ptr, -with the smallest swap so that the blocks are in the opposite order. Blocks may -be internally re-ordered e.g. - 12345ab -> ab34512 - 123abc -> abc123 - 12abcde -> deabc12 -*/ -static inline void sort_r_swap_blocks(char *ptr, size_t na, size_t nb) -{ - if(na > 0 && nb > 0) { - if(na > nb) { sort_r_swap(ptr, ptr+na, nb); } - else { sort_r_swap(ptr, ptr+nb, na); } + /* Recurse on smaller side, loop on larger — bounds stack + * depth at O(log n). */ + size_t left = (size_t) (i - base); + size_t right = nel - left - 1; + if (left < right) + { + hb_qsort_loop (base, left, compar); + base = i + 1; + nel = right; + } + else + { + hb_qsort_loop (i + 1, right, compar); + nel = left; + } } } -/* Implement recursive quicksort ourselves */ -/* Note: quicksort is not stable, equivalent values may be swapped */ -template -static inline void sort_r_simple(void *base, size_t nel, size_t w, - Compar compar) +template +static inline void +hb_qsort_inline (T *base, size_t nel, Compar compar) { - char *b = (char *)base, *end = b + nel*w; - - if(nel < 10) { - /* Insertion sort for arbitrarily small inputs */ - char *pi, *pj; - for(pi = b+w; pi < end; pi += w) { - for(pj = pi; pj > b && sort_r_cmpswap(pj-w,pj,w,compar); pj -= w) {} - } - } - else - { - /* nel > 9; Quicksort */ - - int cmp; - char *pl, *ple, *pr, *pre, *pivot; - char *last = b+w*(nel-1), *tmp; - - char *l[3]; - l[0] = b + w; - l[1] = b+w*(nel/2); - l[2] = last - w; - - if(compar(l[0],l[1]) > 0) { SORT_R_SWAP(l[0], l[1], tmp); } - if(compar(l[1],l[2]) > 0) { - SORT_R_SWAP(l[1], l[2], tmp); - if(compar(l[0],l[1]) > 0) { SORT_R_SWAP(l[0], l[1], tmp); } - } - - if(l[1] != last) { sort_r_swap(l[1], last, w); } + hb_qsort_loop (base, nel, compar); - pivot = last; - ple = pl = b; - pre = pr = last; - - while(pl < pr) { - for(; pl < pr; pl += w) { - cmp = compar(pl, pivot); - if(cmp > 0) { break; } - else if(cmp == 0) { - if(ple < pl) { sort_r_swap(ple, pl, w); } - ple += w; - } - } - if(pl >= pr) { break; } - for(; pl < pr; ) { - pr -= w; - cmp = compar(pr, pivot); - if(cmp == 0) { - pre -= w; - if(pr < pre) { sort_r_swap(pr, pre, w); } - } - else if(cmp < 0) { - if(pl < pr) { sort_r_swap(pl, pr, w); } - pl += w; - break; - } - } - } - - pl = pr; - - sort_r_swap_blocks(b, ple-b, pl-ple); - sort_r_swap_blocks(pr, pre-pr, end-pre); - - sort_r_simple(b, (pl-ple)/w, w, compar); - sort_r_simple(end-(pre-pr), (pre-pr)/w, w, compar); - } + /* Single final insertion sort over the whole array. After + * the partitioning loop, every element is within the threshold + * of its sorted position, so this pass is O(n * threshold). */ + T *end = base + nel; + for (T *pi = base + 1; pi < end; pi++) + for (T *pj = pi; pj > base && compar (pj[-1], pj[0]) > 0; pj--) + hb_swap (pj[-1], pj[0]); } static inline void hb_qsort (void *base, size_t nel, size_t width, int (*compar)(const void *_a, const void *_b)) Index: gfx/harfbuzz/src/hb-array.hh =================================================================== --- gfx/harfbuzz/src/hb-array.hh +++ gfx/harfbuzz/src/hb-array.hh @@ -30,12 +30,10 @@ #include "hb.hh" #include "hb-algs.hh" #include "hb-iter.hh" #include "hb-null.hh" -#include - template struct hb_sorted_array_t; enum hb_not_found_t @@ -225,35 +223,27 @@ { if (likely (length)) hb_qsort (arrayZ, length, this->get_item_size (), cmp); return hb_sorted_array_t (*this); } - /* std::sort wants a strict-weak `a < b` boolean, but our other - * qsort overload (and most existing call sites) follow the C - * qsort convention of returning negative / zero / positive ints. - * Adapt to either via overload resolution: bool passes through; - * any other arithmetic return type is treated as the int signed - * comparator. */ - template static bool _qsort_lt (T v) { return v < 0; } - static bool _qsort_lt (bool v) { return v; } + /* Comparator follows the C qsort convention: returns + * negative / zero / positive int. */ template hb_sorted_array_t qsort (Compar compar) { if (likely (length)) - std::sort (arrayZ, arrayZ + length, - [&] (const Type &a, const Type &b) - { return _qsort_lt (compar (a, b)); }); + hb_qsort_inline (arrayZ, length, compar); return hb_sorted_array_t (*this); } private: template ::value)> hb_sorted_array_t _qsort (hb_priority<1>) { - return qsort ([] (const Type &a, const Type &b) { return Type::cmp (&a, &b) < 0; }); + return qsort ([] (const Type &a, const Type &b) { return Type::cmp (&a, &b); }); } hb_sorted_array_t _qsort (hb_priority<0>) { return qsort ((int(*)(const void*, const void*)) Type::cmp); } Index: gfx/harfbuzz/src/hb-buffer-verify.cc =================================================================== --- gfx/harfbuzz/src/hb-buffer-verify.cc +++ gfx/harfbuzz/src/hb-buffer-verify.cc @@ -145,11 +145,15 @@ unsigned int cluster = info[end - 1].cluster; while (text_start && text[text_start - 1].cluster >= cluster) text_start--; } } - assert (text_start < text_end); + if (unlikely (text_start >= text_end)) + { + buffer_verify_error (buffer, font, BUFFER_VERIFY_ERROR "unsafe-to-break text range is invalid."); + return false; + } if (false) printf("start %u end %u text start %u end %u\n", start, end, text_start, text_end); hb_buffer_clear_contents (fragment); @@ -378,15 +382,18 @@ const hb_feature_t *features, unsigned int num_features, const char * const *shapers) { bool ret = true; - if (!buffer_verify_monotone (this, font)) + bool monotone = buffer_verify_monotone (this, font); + if (!monotone) ret = false; - if (!buffer_verify_unsafe_to_break (this, text_buffer, font, features, num_features, shapers)) + if (monotone && + !buffer_verify_unsafe_to_break (this, text_buffer, font, features, num_features, shapers)) ret = false; - if ((flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) != 0 && + if (monotone && + (flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) != 0 && !buffer_verify_unsafe_to_concat (this, text_buffer, font, features, num_features, shapers)) ret = false; if (!ret) { #ifndef HB_NO_BUFFER_SERIALIZE Index: gfx/harfbuzz/src/hb-cairo-utils.cc =================================================================== --- gfx/harfbuzz/src/hb-cairo-utils.cc +++ gfx/harfbuzz/src/hb-cairo-utils.cc @@ -577,11 +577,11 @@ if (unlikely (!_hb_cairo_get_color_stops (c, color_line, &len, &stops))) return; hb_array_t (stops, len) .qsort ([] (const hb_color_stop_t &a, const hb_color_stop_t &b) { - return a.offset < b.offset; + return (a.offset > b.offset) - (a.offset < b.offset); }); cairo_clip_extents (cr, &x1, &y1, &x2, &y2); max_x = (float) hb_max ((x1 - (double) cx) * (x1 - (double) cx), (x2 - (double) cx) * (x2 - (double) cx)); max_y = (float) hb_max ((y1 - (double) cy) * (y1 - (double) cy), (y2 - (double) cy) * (y2 - (double) cy)); Index: gfx/harfbuzz/src/hb-config.hh =================================================================== --- gfx/harfbuzz/src/hb-config.hh +++ gfx/harfbuzz/src/hb-config.hh @@ -134,15 +134,19 @@ #ifdef HB_NO_BITMAP #define HB_NO_OT_FONT_BITMAP #endif #ifdef HB_NO_DRAW -#define HB_NO_CFF +#define HB_NO_OT_FONT_CFF #define HB_NO_OUTLINE #define HB_NO_PAINT #endif +#if defined(HB_TINY) && defined(HB_NO_DRAW) +#define HB_NO_CFF +#endif + #ifdef HB_NO_CFF #define HB_NO_OT_FONT_CFF #define HB_NO_SUBSET_CFF #endif @@ -186,10 +190,17 @@ #define HB_NO_GDEF_CACHE #define HB_NO_OT_LAYOUT_LOOKUP_CACHE #define HB_NO_OT_FONT_CMAP_CACHE #endif +#if defined(HAVE_CONFIG_OVERRIDE_LAST_H) || defined(HB_CONFIG_OVERRIDE_LAST_H) +#ifndef HB_CONFIG_OVERRIDE_LAST_H +#define HB_CONFIG_OVERRIDE_LAST_H "config-override-last.h" +#endif +#include HB_CONFIG_OVERRIDE_LAST_H +#endif + #ifdef HB_OPTIMIZE_SIZE #define HB_OPTIMIZE_SIZE_VAL 1 #else #define HB_OPTIMIZE_SIZE_VAL 0 #endif Index: gfx/harfbuzz/src/hb-face-builder.cc =================================================================== --- gfx/harfbuzz/src/hb-face-builder.cc +++ gfx/harfbuzz/src/hb-face-builder.cc @@ -179,11 +179,11 @@ if (unlikely (sorted_tags.in_error ())) { // Not much to do... } sorted_tags.qsort ([] (const hb_tag_t &a, const hb_tag_t &b) { - return a < b; + return (a > b) - (a < b); }); auto array = sorted_tags.as_array ().sub_array (start_offset, table_count); auto out = hb_array (table_tags, *table_count); Index: gfx/harfbuzz/src/hb-font.hh =================================================================== --- gfx/harfbuzz/src/hb-font.hh +++ gfx/harfbuzz/src/hb-font.hh @@ -198,19 +198,19 @@ *y = parent_scale_y_position (*y); } void scale_glyph_extents (hb_glyph_extents_t *extents) { - float x1 = em_scale_x (extents->x_bearing); - float y1 = em_scale_y (extents->y_bearing); - float x2 = em_scale_x (extents->x_bearing + extents->width); - float y2 = em_scale_y (extents->y_bearing + extents->height); + float x1 = em_fscale_x (extents->x_bearing); + float y1 = em_fscale_y (extents->y_bearing); + float x2 = em_fscale_x (extents->x_bearing + extents->width); + float y2 = em_fscale_y (extents->y_bearing + extents->height); - extents->x_bearing = roundf (x1); - extents->y_bearing = roundf (y1); - extents->width = roundf (x2) - extents->x_bearing; - extents->height = roundf (y2) - extents->y_bearing; + extents->x_bearing = floorf (x1); + extents->y_bearing = floorf (y1); + extents->width = ceilf (x2) - extents->x_bearing; + extents->height = ceilf (y2) - extents->y_bearing; } void synthetic_glyph_extents (hb_glyph_extents_t *extents) { /* Slant. */ Index: gfx/harfbuzz/src/hb-ft-colr.hh =================================================================== --- gfx/harfbuzz/src/hb-ft-colr.hh +++ gfx/harfbuzz/src/hb-ft-colr.hh @@ -76,10 +76,35 @@ static void _hb_ft_paint (hb_ft_paint_context_t *c, FT_OpaquePaint opaque_paint); +static void +_hb_ft_unscale_clip_box (hb_font_t *font, + const FT_ClipBox *clip_box, + float *xmin, float *ymin, + float *xmax, float *ymax) +{ + /* The FreeType ClipBox is in scaled coordinates. Convert it back to + * font units before pushing it under the font transform. + */ + float upem = font->face->get_upem (); + float xscale = upem / (font->x_scale ? font->x_scale : upem); + float yscale = upem / (font->y_scale ? font->y_scale : upem); + + *xmin = clip_box->bottom_left.x * xscale; + *ymin = clip_box->bottom_left.y * yscale; + *xmax = clip_box->top_right.x * xscale; + *ymax = clip_box->top_right.y * yscale; +} + +static unsigned +_hb_ft_color_alpha (unsigned alpha, unsigned alpha_mult) +{ + return (alpha * alpha_mult + (1 << 13)) >> 14; +} + struct hb_ft_paint_context_t { hb_ft_paint_context_t (const hb_ft_font_t *ft_font_, hb_font_t *font_, hb_paint_funcs_t *paint_funcs, void *paint_data, @@ -169,28 +194,31 @@ color_stops->is_foreground = stop.color.palette_index == 0xFFFF; if (color_stops->is_foreground) color_stops->color = HB_COLOR (hb_color_get_blue (c->foreground), hb_color_get_green (c->foreground), hb_color_get_red (c->foreground), - (hb_color_get_alpha (c->foreground) * stop.color.alpha) >> 14); + _hb_ft_color_alpha (hb_color_get_alpha (c->foreground), + stop.color.alpha)); else { hb_color_t color; if (c->funcs->custom_palette_color (c->data, stop.color.palette_index, &color)) { color_stops->color = HB_COLOR (hb_color_get_blue (color), hb_color_get_green (color), hb_color_get_red (color), - (hb_color_get_alpha (color) * stop.color.alpha) >> 14); + _hb_ft_color_alpha (hb_color_get_alpha (color), + stop.color.alpha)); } else if (c->palette) { FT_Color ft_color = c->palette[stop.color.palette_index]; color_stops->color = HB_COLOR (ft_color.blue, ft_color.green, ft_color.red, - (ft_color.alpha * stop.color.alpha) >> 14); + _hb_ft_color_alpha (ft_color.alpha, + stop.color.alpha)); } else color_stops->color = HB_COLOR (0, 0, 0, 0); } @@ -256,27 +284,30 @@ hb_color_t color; if (is_foreground) color = HB_COLOR (hb_color_get_blue (c->foreground), hb_color_get_green (c->foreground), hb_color_get_red (c->foreground), - (hb_color_get_alpha (c->foreground) * paint.u.solid.color.alpha) >> 14); + _hb_ft_color_alpha (hb_color_get_alpha (c->foreground), + paint.u.solid.color.alpha)); else { if (c->funcs->custom_palette_color (c->data, paint.u.solid.color.palette_index, &color)) { color = HB_COLOR (hb_color_get_blue (color), hb_color_get_green (color), hb_color_get_red (color), - (hb_color_get_alpha (color) * paint.u.solid.color.alpha) >> 14); + _hb_ft_color_alpha (hb_color_get_alpha (color), + paint.u.solid.color.alpha)); } else { FT_Color ft_color = c->palette[paint.u.solid.color.palette_index]; color = HB_COLOR (ft_color.blue, ft_color.green, ft_color.red, - (ft_color.alpha * paint.u.solid.color.alpha) >> 14); + _hb_ft_color_alpha (ft_color.alpha, + paint.u.solid.color.alpha)); } } c->funcs->color (c->data, is_foreground, color); } break; @@ -370,23 +401,14 @@ FT_ClipBox clip_box; has_clip_box = FT_Get_Color_Glyph_ClipBox (ft_face, paint.u.colr_glyph.glyphID, &clip_box); if (has_clip_box) { - /* The FreeType ClipBox is in scaled coordinates, whereas we need - * unscaled clipbox here. Oh well... - */ - - float upem = c->font->face->get_upem (); - float xscale = upem / (c->font->x_scale ? c->font->x_scale : upem); - float yscale = upem / (c->font->y_scale ? c->font->y_scale : upem); - - c->funcs->push_clip_rectangle (c->data, - clip_box.bottom_left.x * xscale, - clip_box.bottom_left.y * yscale, - clip_box.top_right.x * xscale, - clip_box.top_right.y * yscale); + float xmin, ymin, xmax, ymax; + _hb_ft_unscale_clip_box (c->font, &clip_box, + &xmin, &ymin, &xmax, &ymax); + c->funcs->push_clip_rectangle (c->data, xmin, ymin, xmax, ymax); } c->recurse (other_paint); if (has_clip_box) @@ -513,23 +535,13 @@ paint_funcs, paint_data, palette_array, palette_index, foreground); hb_decycler_node_t node (c.glyphs_decycler); node.visit (gid); - bool clip = false; - bool is_bounded = false; FT_ClipBox clip_box; - if (FT_Get_Color_Glyph_ClipBox (ft_face, gid, &clip_box)) - { - c.funcs->push_clip_rectangle (c.data, - clip_box.bottom_left.x, - clip_box.bottom_left.y, - clip_box.top_right.x, - clip_box.top_right.y); - clip = true; - is_bounded = true; - } + bool clip = FT_Get_Color_Glyph_ClipBox (ft_face, gid, &clip_box); + bool is_bounded = clip; if (!is_bounded) { auto *bounded_funcs = hb_paint_bounded_get_funcs (); hb_paint_bounded_context_t bounded_data; hb_ft_paint_context_t ce (ft_font, font, @@ -541,18 +553,26 @@ is_bounded = bounded_data.is_bounded (); } c.funcs->push_font_transform (c.data, font); + if (clip) + { + float xmin, ymin, xmax, ymax; + _hb_ft_unscale_clip_box (font, &clip_box, + &xmin, &ymin, &xmax, &ymax); + c.funcs->push_clip_rectangle (c.data, xmin, ymin, xmax, ymax); + } + if (is_bounded) c.recurse (paint); - c.funcs->pop_transform (c.data); - if (clip) c.funcs->pop_clip (c.data); + c.funcs->pop_transform (c.data); + return true; } /* COLRv0 */ iterator.p = NULL; Index: gfx/harfbuzz/src/hb-gpu-draw.cc =================================================================== --- gfx/harfbuzz/src/hb-gpu-draw.cc +++ gfx/harfbuzz/src/hb-gpu-draw.cc @@ -577,29 +577,33 @@ { unsigned off = s.hband_offsets.arrayZ[b]; unsigned count = s.hband_curve_counts.arrayZ[b]; s.hband_curves.as_array ().sub_array (off, count) .qsort ([infos] (const unsigned &a, const unsigned &b) { - return infos[a].max_x > infos[b].max_x; + auto av = infos[a].max_x, bv = infos[b].max_x; + return (av < bv) - (av > bv); }); s.hband_curves_asc.as_array ().sub_array (off, count) .qsort ([infos] (const unsigned &a, const unsigned &b) { - return infos[a].min_x < infos[b].min_x; + auto av = infos[a].min_x, bv = infos[b].min_x; + return (av > bv) - (av < bv); }); } for (unsigned b = 0; b < num_vbands; b++) { unsigned off = s.vband_offsets.arrayZ[b]; unsigned count = s.vband_curve_counts.arrayZ[b]; s.vband_curves.as_array ().sub_array (off, count) .qsort ([infos] (const unsigned &a, const unsigned &b) { - return infos[a].max_y > infos[b].max_y; + auto av = infos[a].max_y, bv = infos[b].max_y; + return (av < bv) - (av > bv); }); s.vband_curves_asc.as_array ().sub_array (off, count) .qsort ([infos] (const unsigned &a, const unsigned &b) { - return infos[a].min_y < infos[b].min_y; + auto av = infos[a].min_y, bv = infos[b].min_y; + return (av > bv) - (av < bv); }); } /* Compute sizes */ unsigned total_curve_indices; Index: gfx/harfbuzz/src/hb-gpu-paint-fragment-hlsl.hh =================================================================== --- gfx/harfbuzz/src/hb-gpu-paint-fragment-hlsl.hh +++ gfx/harfbuzz/src/hb-gpu-paint-fragment-hlsl.hh @@ -67,11 +67,11 @@ " float span = off - off_prev;\n" " float f = span > 1e-6 ? (t - off_prev) / span : 0.0;\n" " float4 p0 = float4 (col_prev.rgb * col_prev.a, col_prev.a);\n" " float4 p1 = float4 (col.rgb * col.a, col.a);\n" " float4 pm = lerp (p0, p1, f);\n" -" return pm.a > 1e-6 ? float4 (pm.rgb / pm.a, pm.a) : float4 (0.0);\n" +" return pm.a > 1e-6 ? float4 (pm.rgb / pm.a, pm.a) : float4 (0.0, 0.0, 0.0, 0.0);\n" " }\n" " col_prev = col;\n" " off_prev = off;\n" " }\n" " return col_prev;\n" Index: gfx/harfbuzz/src/hb-gpu-paint-fragment.hlsl =================================================================== --- gfx/harfbuzz/src/hb-gpu-paint-fragment.hlsl +++ gfx/harfbuzz/src/hb-gpu-paint-fragment.hlsl @@ -66,11 +66,11 @@ float span = off - off_prev; float f = span > 1e-6 ? (t - off_prev) / span : 0.0; float4 p0 = float4 (col_prev.rgb * col_prev.a, col_prev.a); float4 p1 = float4 (col.rgb * col.a, col.a); float4 pm = lerp (p0, p1, f); - return pm.a > 1e-6 ? float4 (pm.rgb / pm.a, pm.a) : float4 (0.0); + return pm.a > 1e-6 ? float4 (pm.rgb / pm.a, pm.a) : float4 (0.0, 0.0, 0.0, 0.0); } col_prev = col; off_prev = off; } return col_prev; Index: gfx/harfbuzz/src/hb-gpu-paint.hh =================================================================== --- gfx/harfbuzz/src/hb-gpu-paint.hh +++ gfx/harfbuzz/src/hb-gpu-paint.hh @@ -285,14 +285,14 @@ { hb_codepoint_t glyph; /* HB_CODEPOINT_INVALID for path clips */ hb_font_t *font; /* borrowed; nullptr for path clips */ hb_transform_t transform; /* Path clips are encoded into a sub-blob at push_clip_path_end - * time; glyph clips re-encode per consuming layer. For path - * clips, sub_blob_index is the pre-baked index and ext_* the - * captured design-unit extents. For glyph clips, sub_blob_index - * is -1 and the ext_* fields are unused. */ + * time; glyph clips are encoded lazily on first consuming layer + * and cached. sub_blob_index is -1 until the encode happens, + * then holds the index into c->sub_blobs and ext_* hold the + * design-unit extents. */ int sub_blob_index; int ext_x0, ext_y0, ext_x1, ext_y1; }; pending_clip_t clip_stack[3]; unsigned clip_depth = 0; Index: gfx/harfbuzz/src/hb-limits.hh =================================================================== --- gfx/harfbuzz/src/hb-limits.hh +++ gfx/harfbuzz/src/hb-limits.hh @@ -132,10 +132,10 @@ #ifndef HB_REPACKER_MAX_VERTICES #define HB_REPACKER_MAX_VERTICES 100000 #endif #ifndef HB_REPACKER_MAX_SPACES -#define HB_REPACKER_MAX_SPACES 1024 +#define HB_REPACKER_MAX_SPACES 8000 #endif #endif /* HB_LIMITS_HH */ Index: gfx/harfbuzz/src/hb-open-type.hh =================================================================== --- gfx/harfbuzz/src/hb-open-type.hh +++ gfx/harfbuzz/src/hb-open-type.hh @@ -562,11 +562,11 @@ { return arrayZ[i]; } static size_t get_size (unsigned int len) - { return len * Type::static_size; } + { return hb_unsigned_mul_saturate (len, Type::static_size); } template operator T * () { return arrayZ; } template operator const T * () const { return arrayZ; } hb_array_t as_array (unsigned int len) { return hb_array (arrayZ, len); } @@ -724,11 +724,11 @@ hb_barrier (); return arrayZ[i]; } size_t get_size () const - { return len.static_size + len * Type::static_size; } + { return hb_unsigned_mul_add_saturate (len, Type::static_size, len.static_size); } explicit operator bool () const { return len; } void pop () { len--; } @@ -908,11 +908,11 @@ if (unlikely (i >= lenP1 || !i)) return Crap (Type); hb_barrier (); return arrayZ[i-1]; } size_t get_size () const - { return lenP1.static_size + get_length () * Type::static_size; } + { return hb_unsigned_mul_add_saturate (get_length (), Type::static_size, lenP1.static_size); } unsigned get_length () const { return lenP1 ? lenP1 - 1 : 0; } hb_array_t< Type> as_array () { return hb_array (arrayZ, get_length ()); } hb_array_t as_array () const { return hb_array (arrayZ, get_length ()); } @@ -1002,11 +1002,11 @@ if (unlikely (i > lenM1)) return Crap (Type); hb_barrier (); return arrayZ[i]; } size_t get_size () const - { return lenM1.static_size + (lenM1 + 1) * Type::static_size; } + { return hb_unsigned_mul_add_saturate (lenM1 + 1, Type::static_size, lenM1.static_size); } template HB_ALWAYS_INLINE bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const { @@ -1196,11 +1196,11 @@ return StructAtOffset (&bytesZ, i * header.unitSize); } unsigned int get_length () const { return header.nUnits - last_is_terminator (); } size_t get_size () const - { return header.static_size + header.nUnits * header.unitSize; } + { return hb_unsigned_mul_add_saturate (header.nUnits, header.unitSize, header.static_size); } template HB_ALWAYS_INLINE bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const { @@ -1462,11 +1462,12 @@ } size_t get_size () const { if (count) - return min_size + offSize.static_size + offset_array_size () + (offset_at (count) - 1); + return hb_unsigned_add_saturate (min_size, offSize.static_size, + offset_array_size (), offset_at (count) - 1); return min_size; /* empty CFFIndex contains count only */ } bool sanitize (hb_sanitize_context_t *c) const { Index: gfx/harfbuzz/src/hb-ot-cff-common.hh =================================================================== --- gfx/harfbuzz/src/hb-ot-cff-common.hh +++ gfx/harfbuzz/src/hb-ot-cff-common.hh @@ -200,11 +200,11 @@ hb_pair_t get_fd_range (hb_codepoint_t glyph) const { return {fds[glyph], glyph + 1}; } size_t get_size (unsigned int num_glyphs) const - { return HBUINT8::static_size * num_glyphs; } + { return hb_unsigned_mul_saturate (HBUINT8::static_size, num_glyphs); } HBUINT8 fds[HB_VAR_ARRAY]; DEFINE_SIZE_MIN (0); }; @@ -228,11 +228,11 @@ template struct FDSelect3_4 { size_t get_size () const - { return GID_TYPE::static_size * 2 + ranges.get_size (); } + { return hb_unsigned_add_saturate ((size_t) GID_TYPE::static_size * 2, ranges.get_size ()); } bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const { TRACE_SANITIZE (this); if (unlikely (!(c->check_struct (this) && @@ -306,12 +306,12 @@ size_t get_size (unsigned int num_glyphs) const { switch (format) { - case 0: hb_barrier (); return format.static_size + u.format0.get_size (num_glyphs); - case 3: hb_barrier (); return format.static_size + u.format3.get_size (); + case 0: hb_barrier (); return hb_unsigned_add_saturate (format.static_size, u.format0.get_size (num_glyphs)); + case 3: hb_barrier (); return hb_unsigned_add_saturate (format.static_size, u.format3.get_size ()); default:return 0; } } unsigned get_fd (hb_codepoint_t glyph) const Index: gfx/harfbuzz/src/hb-ot-cff1-table.hh =================================================================== --- gfx/harfbuzz/src/hb-ot-cff1-table.hh +++ gfx/harfbuzz/src/hb-ot-cff1-table.hh @@ -234,18 +234,18 @@ return_trace (true); } size_t get_size () const { - unsigned int size = min_size; + size_t size = min_size; switch (table_format ()) { - case 0: hb_barrier (); size += u.format0.get_size (); break; - case 1: hb_barrier (); size += u.format1.get_size (); break; + case 0: hb_barrier (); size = hb_unsigned_add_saturate (size, u.format0.get_size ()); break; + case 1: hb_barrier (); size = hb_unsigned_add_saturate (size, u.format1.get_size ()); break; } if (has_supplement ()) - size += suppEncData ().get_size (); + size = hb_unsigned_add_saturate (size, suppEncData ().get_size ()); return size; } hb_codepoint_t get_code (hb_codepoint_t glyph) const { @@ -565,13 +565,13 @@ size_t get_size (unsigned int num_glyphs) const { switch (format) { - case 0: hb_barrier (); return min_size + u.format0.get_size (num_glyphs); - case 1: hb_barrier (); return min_size + u.format1.get_size (num_glyphs); - case 2: hb_barrier (); return min_size + u.format2.get_size (num_glyphs); + case 0: hb_barrier (); return hb_unsigned_add_saturate (min_size, u.format0.get_size (num_glyphs)); + case 1: hb_barrier (); return hb_unsigned_add_saturate (min_size, u.format1.get_size (num_glyphs)); + case 2: hb_barrier (); return hb_unsigned_add_saturate (min_size, u.format2.get_size (num_glyphs)); default:return 0; } } hb_codepoint_t get_sid (hb_codepoint_t glyph, unsigned int num_glyphs, Index: gfx/harfbuzz/src/hb-ot-hdmx-table.hh =================================================================== --- gfx/harfbuzz/src/hb-ot-hdmx-table.hh +++ gfx/harfbuzz/src/hb-ot-hdmx-table.hh @@ -40,11 +40,11 @@ struct DeviceRecord { static size_t get_size (unsigned count) - { return hb_ceil_to_4 (min_size + count * HBUINT8::static_size); } + { return hb_ceil_to_4 (hb_unsigned_mul_add_saturate (count, HBUINT8::static_size, min_size)); } template bool serialize (hb_serialize_context_t *c, unsigned pixelSize, @@ -86,11 +86,11 @@ struct hdmx { static constexpr hb_tag_t tableTag = HB_OT_TAG_hdmx; size_t get_size () const - { return min_size + numRecords * sizeDeviceRecord; } + { return hb_unsigned_mul_add_saturate (numRecords, sizeDeviceRecord, min_size); } template bool serialize (hb_serialize_context_t *c, unsigned version, Index: gfx/harfbuzz/src/hb-ot-layout-common.hh =================================================================== --- gfx/harfbuzz/src/hb-ot-layout-common.hh +++ gfx/harfbuzz/src/hb-ot-layout-common.hh @@ -636,11 +636,11 @@ } return characters.len; } size_t get_size () const - { return min_size + characters.len * HBUINT24::static_size; } + { return hb_unsigned_mul_add_saturate (characters.len, HBUINT24::static_size, min_size); } void collect_name_ids (hb_set_t *nameids_to_retain /* OUT */) const { if (featUILableNameID) nameids_to_retain->add (featUILableNameID); if (featUITooltipTextNameID) nameids_to_retain->add (featUITooltipTextNameID); @@ -2789,11 +2789,14 @@ regions.push (std::move (axis_tuples)); } return !regions.in_error (); } - size_t get_size () const { return min_size + VarRegionAxis::static_size * axisCount * regionCount; } + size_t get_size () const + { return hb_unsigned_add_saturate (min_size, + hb_unsigned_mul_saturate (VarRegionAxis::static_size, + axisCount, regionCount)); } public: HBUINT16 axisCount; HBUINT15 regionCount; protected: @@ -2866,18 +2869,17 @@ { return regionIndices.len; } unsigned get_region_index (unsigned i) const { return i >= regionIndices.len ? -1 : regionIndices[i]; } - unsigned int get_row_size () const + size_t get_row_size () const { return (wordCount () + regionIndices.len) * (longWords () ? 2 : 1); } size_t get_size () const - { return min_size - - regionIndices.min_size + regionIndices.get_size () - + itemCount * get_row_size (); - } + { return hb_unsigned_add_saturate (min_size - regionIndices.min_size, + regionIndices.get_size (), + hb_unsigned_mul_saturate (itemCount, get_row_size ())); } float _get_delta (unsigned int inner, const int *coords, unsigned int coord_count, const VarRegionList ®ions, hb_scalar_cache_t *cache = nullptr) const @@ -3242,14 +3244,13 @@ }; struct MultiVarData { size_t get_size () const - { return min_size - - regionIndices.min_size + regionIndices.get_size () - + StructAfter (regionIndices).get_size (); - } + { return hb_unsigned_add_saturate (min_size - regionIndices.min_size, + regionIndices.get_size (), + StructAfter (regionIndices).get_size ()); } void get_delta (unsigned int inner, const int *coords, unsigned int coord_count, const SparseVarRegionList ®ions, hb_array_t out, @@ -3637,11 +3638,11 @@ struct DeltaSetIndexMapFormat01 { friend struct DeltaSetIndexMap; size_t get_size () const - { return min_size + mapCount * get_width (); } + { return hb_unsigned_mul_add_saturate (mapCount, get_width (), min_size); } private: DeltaSetIndexMapFormat01* copy (hb_serialize_context_t *c) const { TRACE_SERIALIZE (this); Index: gfx/harfbuzz/src/hb-ot-os2-table.hh =================================================================== --- gfx/harfbuzz/src/hb-ot-os2-table.hh +++ gfx/harfbuzz/src/hb-ot-os2-table.hh @@ -344,14 +344,14 @@ font_page_t get_font_page () const { return (font_page_t) (version == 0 ? fsSelection & 0xFF00 : 0); } size_t get_size () const { - unsigned result = min_size; - if (version >= 1) result += v1X.get_size (); - if (version >= 2) result += v2X.get_size (); - if (version >= 5) result += v5X.get_size (); + size_t result = min_size; + if (version >= 1) result = hb_unsigned_add_saturate (result, v1X.get_size ()); + if (version >= 2) result = hb_unsigned_add_saturate (result, v2X.get_size ()); + if (version >= 5) result = hb_unsigned_add_saturate (result, v5X.get_size ()); return result; } bool sanitize (hb_sanitize_context_t *c) const { Index: gfx/harfbuzz/src/hb-ot-post-table.hh =================================================================== --- gfx/harfbuzz/src/hb-ot-post-table.hh +++ gfx/harfbuzz/src/hb-ot-post-table.hh @@ -195,11 +195,14 @@ for (unsigned int i = 0; i < count; i++) gids[i] = i; auto thiz = this; hb_array_t (gids, count) .qsort ([thiz] (const uint16_t &a, const uint16_t &b) { - return thiz->find_glyph_name (a).cmp (thiz->find_glyph_name (b)) > 0; + /* hb_array_t::cmp has reversed argument order, so + * x.cmp(y) returns sign of (y - x). Pass (b, a) to + * get standard "negative when a < b" ordering. */ + return thiz->find_glyph_name (b).cmp (thiz->find_glyph_name (a)); }); if (unlikely (!gids_sorted_by_name.cmpexch (nullptr, gids))) { hb_free (gids); Index: gfx/harfbuzz/src/hb-ot-shaper-arabic-pua.hh =================================================================== --- gfx/harfbuzz/src/hb-ot-shaper-arabic-pua.hh +++ gfx/harfbuzz/src/hb-ot-shaper-arabic-pua.hh @@ -35,19 +35,19 @@ 0, 0, 13, 0, 0, 0, 0, 0, 14, 0, 0, 15, 16, 17, 18, 19, 20, 21, 22, 23, 1, 24, 25, 26, 27, 28, 1, 1, 29, 30, 31, 32, 33, 0, 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 37, 38, 0, 0, 0, 0, 0, 0, 0, 39, 0, 0, 40, 41, 42, 0, 43, 44, 0, 0, 45, 46, 0, 47, - 48, 49, 0, 0, 0, 0, 50, 0, 0, 51, 52, 0, 53, 54, 55, 56, - 57, 58, 0, 0, 0, 0, 0, 59, 60, 61, 62, 63, 64, 0, 0, 0, + 48, 49, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 52, 53, 54, 55, + 56, 57, 0, 0, 0, 0, 0, 58, 59, 60, 61, 62, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 65, 0, 0, 66, 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, - 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, - 95, 96, 97, 98, 99, + 0, 64, 0, 0, 65, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + 94, 95, 96, 97, 98, }; -static const uint16_t _hb_arabic_pua_u16[717]= +static const uint16_t _hb_arabic_pua_u16[713]= { 0, 0, 0, 0, 0, 0, 0, 0,61728,61729,61730, 0, 0,61733, 0, 0, 61736,61737,61738,61739,61790,61741,61742,61743,61872,61873,61874,61875,61876,61877,61878,61879, 61880,61881,61754,61755, 0,61757, 0,61759, 0, 0, 0,61787,61788,61789, 0, 0, 0, 0, 0,61731, 0, 0, 0, 0, 0, 0, 0,61732, 0, 0, 0, 0, @@ -75,25 +75,25 @@ 62053,62055,62057,62059,62064,62068,62072,62078,62114,62115,62122,62126,61952,62047,62130,62134, 62138,62142,62146,62150,62154,62155,62164,62160,62183,62184,62187,62180,62181,62186,62185,62182, 61952,61952,61952,61952, 0,62000,62001,62002,62003,62004,62005,62006,62007,62008,62009, 0, 62046,62046, 0, 0, 0,61964,61965,61966,61967,62012,62014, 0, 0,61954, 0,61981, 0, 0, 0,61955, 0,61982, 0,61956, 0, 0, 0,62111, 0, 0, 0, 0, - 61970,61971,61972,61957, 0,61980, 0, 0, 0, 0, 0,61958, 0,61983, 0, 0, + 61970,61971,61972,61957,62101,61980, 0, 0, 0, 0, 0,61958, 0,61983, 0, 0, 0, 0, 0,62191, 0,62188,62189,62192, 0, 0, 0,61973, 0, 0,62098, 0, - 0,61974, 0, 0,62099, 0, 0,62101, 0, 0,61975, 0, 0,62100, 0, 0, - 0,62080,62081,62082,62102, 0,62083,62084,62085,62103, 0, 0, 0,62106, 0,62107, - 0,62108, 0, 0, 0,61976, 0, 0, 0, 0,62086,62087,62088,62109,61978,62089, - 62090,62091,62110,62093,62094, 0,62104, 0, 0, 0, 0,62095,62096,62097,62105, 0, - 0,61977, 0, 0, 0, 0, 0,62075,62077,61968, 0, 0, 0, 0,62021,62022, - 62019,62020,62170,62171,62023,62024,62169,62168,62166,62167,62017,62018,62028,62027,62025,62026, - 62161,62162,62032,62031,62029,62030,62036,62035,62033,62034,62040,62039,62037,62038,62048,62044, - 62041,62042,62052,62051,62049,62050,62053,62054,62055,62056,62057,62058,62059,62060,62064,62063, - 62061,62062,62068,62067,62065,62066,62072,62071,62069,62070,62078,62076,62073,62074,62114,62113, - 62079,62193,62118,62117,62115,62116,62122,62121,62119,62120,62126,62125,62123,62124,62130,62129, - 62127,62128,62134,62133,62131,62132,62138,62137,62135,62136,62142,62141,62139,62140,62146,62145, - 62143,62144,62150,62149,62147,62148,62154,62153,62151,62152,62155,62156,62164,62163,62160,62159, - 62157,62158,62176,62177,62174,62175,62178,62179,62172,62173, 0, 0, 0, + 0,61974, 0, 0,62099, 0,61975, 0, 0,62100, 0, 0, 0,62080,62081,62082, + 62102, 0,62083,62084,62085,62103, 0, 0, 0,62106, 0,62107, 0,62108, 0, 0, + 0,61976, 0, 0, 0, 0,62086,62087,62088,62109,61978,62089,62090,62091,62110,62093, + 62094, 0,62104, 0, 0, 0, 0,62095,62096,62097,62105, 0, 0,61977, 0, 0, + 0, 0, 0,62075,62077,61968, 0, 0, 0, 0,62021,62022,62019,62020,62170,62171, + 62023,62024,62169,62168,62166,62167,62017,62018,62028,62027,62025,62026,62161,62162,62032,62031, + 62029,62030,62036,62035,62033,62034,62040,62039,62037,62038,62048,62044,62041,62042,62052,62051, + 62049,62050,62053,62054,62055,62056,62057,62058,62059,62060,62064,62063,62061,62062,62068,62067, + 62065,62066,62072,62071,62069,62070,62078,62076,62073,62074,62114,62113,62079,62193,62118,62117, + 62115,62116,62122,62121,62119,62120,62126,62125,62123,62124,62130,62129,62127,62128,62134,62133, + 62131,62132,62138,62137,62135,62136,62142,62141,62139,62140,62146,62145,62143,62144,62150,62149, + 62147,62148,62154,62153,62151,62152,62155,62156,62164,62163,62160,62159,62157,62158,62176,62177, + 62174,62175,62178,62179,62172,62173, 0, 0, 0, }; static inline uint8_t _hb_arabic_pua_b4 (const uint8_t* a, unsigned i) { return (a[i>>1]>>((i&1)<<2))&15; Index: gfx/harfbuzz/src/hb-ot-shaper-arabic-table.hh =================================================================== --- gfx/harfbuzz/src/hb-ot-shaper-arabic-table.hh +++ gfx/harfbuzz/src/hb-ot-shaper-arabic-table.hh @@ -278,11 +278,11 @@ static const struct ligature_set_t { uint16_t first; struct ligature_pairs_t { uint16_t components[1]; uint16_t ligature; - } ligatures[14]; + } ligatures[15]; } ligature_table[] = { { 0xFE91u, { { {0xFEE2u}, 0xFC08u }, /* ARABIC LIGATURE BEH WITH MEEM ISOLATED FORM */ { {0xFEE4u}, 0xFC9Fu }, /* ARABIC LIGATURE BEH WITH MEEM INITIAL FORM */ @@ -335,10 +335,11 @@ { {0xFEA4u}, 0xFCCAu }, /* ARABIC LIGATURE LAM WITH HAH INITIAL FORM */ { {0xFEA6u}, 0xFC41u }, /* ARABIC LIGATURE LAM WITH KHAH ISOLATED FORM */ { {0xFEA8u}, 0xFCCBu }, /* ARABIC LIGATURE LAM WITH KHAH INITIAL FORM */ { {0xFEE2u}, 0xFC42u }, /* ARABIC LIGATURE LAM WITH MEEM ISOLATED FORM */ { {0xFEE4u}, 0xFCCCu }, /* ARABIC LIGATURE LAM WITH MEEM INITIAL FORM */ + { {0xFEF0u}, 0xFC43u }, /* ARABIC LIGATURE LAM WITH ALEF MAKSURA ISOLATED FORM */ { {0xFEF2u}, 0xFC44u }, /* ARABIC LIGATURE LAM WITH YEH ISOLATED FORM */ { {0xFEECu}, 0xFCCDu }, /* ARABIC LIGATURE LAM WITH HEH INITIAL FORM */ { {0xFE82u}, 0xFEF5u }, /* ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM */ { {0xFE84u}, 0xFEF7u }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM */ { {0xFE88u}, 0xFEF9u }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM */ Index: gfx/harfbuzz/src/hb-ot-tag.cc =================================================================== --- gfx/harfbuzz/src/hb-ot-tag.cc +++ gfx/harfbuzz/src/hb-ot-tag.cc @@ -41,12 +41,14 @@ switch ((hb_tag_t) script) { case HB_SCRIPT_INVALID: return HB_OT_TAG_DEFAULT_SCRIPT; case HB_SCRIPT_MATH: return HB_OT_TAG_MATH_SCRIPT; - /* KATAKANA and HIRAGANA both map to 'kana' */ + /* Hiragana, Katakana, and their collective ISO 15924 script all map to 'kana'. */ case HB_SCRIPT_HIRAGANA: return HB_TAG('k','a','n','a'); + case HB_TAG('H','r','k','t'): + return HB_TAG('k','a','n','a'); /* Spaces at the end are preserved, unlike ISO 15924 */ case HB_SCRIPT_LAO: return HB_TAG('l','a','o',' '); case HB_SCRIPT_YI: return HB_TAG('y','i',' ',' '); /* Unicode-5.0 additions */ Index: gfx/harfbuzz/src/hb-ot-var-avar-table.hh =================================================================== --- gfx/harfbuzz/src/hb-ot-var-avar-table.hh +++ gfx/harfbuzz/src/hb-ot-var-avar-table.hh @@ -398,11 +398,12 @@ for (unsigned i = 0; i < coords_length; i++) { int v = coords[i]; uint32_t varidx = varidx_map.map (i); float delta = var_store.get_delta (varidx, coords_2_14.arrayZ, coords_2_14.length, var_store_cache); - v += roundf (delta * 4); // 2.14 -> 16.16 + float d = hb_clamp (delta * 4, -(float) (1<<16), +(float) (1<<16)); // 2.14 -> 16.16 + v += (int) roundf (d); v = hb_clamp (v, -(1<<16), +(1<<16)); out.push (v); } for (unsigned i = 0; i < coords_length; i++) coords[i] = out[i]; Index: gfx/harfbuzz/src/hb-ot-var-common.hh =================================================================== --- gfx/harfbuzz/src/hb-ot-var-common.hh +++ gfx/harfbuzz/src/hb-ot-var-common.hh @@ -45,11 +45,11 @@ // This function is super hot in mega-var-fonts with hundreds of masters. unsigned ti = tupleIndex; if (unlikely ((ti & (TupleIndex::EmbeddedPeakTuple | TupleIndex::IntermediateRegion)))) { unsigned count = ((ti & TupleIndex::EmbeddedPeakTuple) != 0) + ((ti & TupleIndex::IntermediateRegion) != 0) * 2; - return min_size + count * axis_count_times_2; + return hb_unsigned_mul_add_saturate (count, axis_count_times_2, min_size); } return min_size; } unsigned get_data_size () const { return varDataSize; } @@ -934,16 +934,18 @@ return_trace (c->check_struct (this)); } size_t get_size (unsigned axis_count_times_2) const { - unsigned total_size = min_size; + size_t total_size = min_size; unsigned count = tupleVarCount.get_count (); const TupleVariationHeader *tuple_var_header = &(get_tuple_var_header()); for (unsigned i = 0; i < count; i++) { - total_size += tuple_var_header->get_size (axis_count_times_2) + tuple_var_header->get_data_size (); + total_size = hb_unsigned_add_saturate (total_size, + tuple_var_header->get_size (axis_count_times_2), + (size_t) tuple_var_header->get_data_size ()); tuple_var_header = &tuple_var_header->get_next (axis_count_times_2); } return total_size; } Index: gfx/harfbuzz/src/hb-ot-var-gvar-table.hh =================================================================== --- gfx/harfbuzz/src/hb-ot-var-gvar-table.hh +++ gfx/harfbuzz/src/hb-ot-var-gvar-table.hh @@ -445,11 +445,11 @@ out->glyphCountX = hb_min (0xFFFFu, num_glyphs); auto it = hb_iter (c->plan->new_to_old_gid_list); if (it->first == 0 && !(c->plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) it++; - size_t subset_data_size = 0; + unsigned subset_data_size = 0; unsigned padding_size = 0; for (auto &_ : it) { hb_codepoint_t old_gid = _.second; unsigned glyph_data_size = get_glyph_var_data_bytes (c->source_blob, glyph_count, old_gid).length; @@ -457,11 +457,12 @@ { glyph_data_size++; padding_size++; } - subset_data_size += glyph_data_size; + if (unlikely (hb_unsigned_add_overflows (subset_data_size, glyph_data_size, &subset_data_size))) + return_trace (false); } /* According to the spec: If the short format (Offset16) is used for offsets, * the value stored is the offset divided by 2, so the maximum data size should * be 2 * 0xFFFFu, which is 0x1FFFEu */ Index: gfx/harfbuzz/src/hb-ot-var-hvar-table.hh =================================================================== --- gfx/harfbuzz/src/hb-ot-var-hvar-table.hh +++ gfx/harfbuzz/src/hb-ot-var-hvar-table.hh @@ -179,11 +179,15 @@ unsigned int get_inner_bit_count () const { return inner_bit_count; } unsigned int get_width () const { return ((outer_bit_count + inner_bit_count + 7) / 8); } unsigned int get_map_count () const { return map_count; } size_t get_size () const - { return (map_count? (DeltaSetIndexMap::min_size + get_width () * map_count): 0); } + { + if (!map_count) return 0; + return hb_unsigned_mul_add_saturate (get_width (), map_count, + DeltaSetIndexMap::min_size); + } bool is_identity () const { return get_output_map ().length == 0; } hb_array_t get_output_map () const { return output_map.as_array (); } protected: Index: gfx/harfbuzz/src/hb-script-list.h =================================================================== --- gfx/harfbuzz/src/hb-script-list.h +++ gfx/harfbuzz/src/hb-script-list.h @@ -490,7 +490,6 @@ _HB_SCRIPT_MAX_VALUE = HB_TAG_MAX_SIGNED, /*< skip >*/ _HB_SCRIPT_MAX_VALUE_SIGNED = HB_TAG_MAX_SIGNED /*< skip >*/ } hb_script_t; - #endif /* HB_SCRIPT_LIST_H */ Index: gfx/harfbuzz/src/hb-serialize.hh =================================================================== --- gfx/harfbuzz/src/hb-serialize.hh +++ gfx/harfbuzz/src/hb-serialize.hh @@ -193,11 +193,11 @@ current ? current->virtual_links.length : 0, errors }; } - hb_serialize_context_t (void *start_, unsigned int size) : + hb_serialize_context_t (void *start_, size_t size) : start ((char *) start_), end (start + size), current (nullptr) { reset (); } ~hb_serialize_context_t () { fini (); } @@ -228,11 +228,11 @@ return errors == HB_SERIALIZE_ERROR_OFFSET_OVERFLOW || errors == HB_SERIALIZE_ERROR_INT_OVERFLOW || errors == HB_SERIALIZE_ERROR_ARRAY_OVERFLOW; } - void reset (void *start_, unsigned int size) + void reset (void *start_, size_t size) { start = (char*) start_; end = start + size; reset (); current = nullptr; @@ -678,11 +678,11 @@ template HB_NODISCARD Type *embed (const Type *obj) { - unsigned int size = obj->get_size (); + size_t size = obj->get_size (); Type *ret = this->allocate_size (size, false); if (unlikely (!ret)) return nullptr; hb_memcpy (ret, obj, size); return ret; } @@ -731,10 +731,11 @@ template Type *extend_size (Type *obj, size_t size, bool clear = true) { if (unlikely (in_error ())) return nullptr; + if (unlikely (size >= INT_MAX)) { err (HB_SERIALIZE_ERROR_OTHER); return nullptr; } assert (this->start <= (char *) obj); assert ((char *) obj <= this->head); assert ((size_t) (this->head - (char *) obj) <= size); if (unlikely (((char *) obj + size < (char *) obj) || Index: gfx/harfbuzz/src/hb-subset-input.cc =================================================================== --- gfx/harfbuzz/src/hb-subset-input.cc +++ gfx/harfbuzz/src/hb-subset-input.cc @@ -126,10 +126,11 @@ //East Asian spacing HB_TAG ('c', 'h', 'w', 's'), HB_TAG ('v', 'c', 'h', 'w'), HB_TAG ('h', 'a', 'l', 't'), HB_TAG ('v', 'h', 'a', 'l'), + HB_TAG ('p', 'a', 'l', 't'), //private HB_TAG ('H', 'a', 'r', 'f'), HB_TAG ('H', 'A', 'R', 'F'), HB_TAG ('B', 'u', 'z', 'z'), Index: gfx/harfbuzz/src/hb-subset-plan-var.cc =================================================================== --- gfx/harfbuzz/src/hb-subset-plan-var.cc +++ gfx/harfbuzz/src/hb-subset-plan-var.cc @@ -53,10 +53,11 @@ continue; inner_maps[major].add (minor); } } +#ifndef HB_NO_OT_FONT_CFF static inline hb_font_t* _get_hb_font_with_variations (const hb_subset_plan_t *plan) { hb_font_t *font = hb_font_create (plan->source); @@ -75,10 +76,11 @@ } hb_font_set_variations (font, vars.arrayZ, plan->user_axes_location.get_population ()); return font; } +#endif template void remap_variation_indices (const ItemVarStore &var_store, const hb_set_t &variation_indices, @@ -283,10 +285,11 @@ } } return true; } +#ifndef HB_NO_OT_FONT_CFF void update_instance_metrics_map_from_cff2 (hb_subset_plan_t *plan) { if (!plan->normalized_coords) return; OT::cff2::accelerator_t cff2 (plan->source); @@ -368,10 +371,11 @@ if (hvar_store_cache) _hmtx.var_table->get_var_store ().destroy_cache (hvar_store_cache); if (vvar_store_cache) _vmtx.var_table->get_var_store ().destroy_cache (vvar_store_cache); } +#endif bool get_instance_glyphs_contour_points (hb_subset_plan_t *plan) { /* contour_points vector only needed for updating gvar table (infer delta and Index: gfx/harfbuzz/src/hb-subset-plan.cc =================================================================== --- gfx/harfbuzz/src/hb-subset-plan.cc +++ gfx/harfbuzz/src/hb-subset-plan.cc @@ -741,12 +741,14 @@ #endif if (unlikely (in_error ())) return; +#if !defined(HB_NO_VAR) && !defined(HB_NO_OT_FONT_CFF) + update_instance_metrics_map_from_cff2 (this); +#endif #ifndef HB_NO_VAR - update_instance_metrics_map_from_cff2 (this); if (!check_success (get_instance_glyphs_contour_points (this))) return; #endif if (attach_accelerator_data) Index: gfx/harfbuzz/src/hb-vector-buf.hh =================================================================== --- gfx/harfbuzz/src/hb-vector-buf.hh +++ gfx/harfbuzz/src/hb-vector-buf.hh @@ -44,11 +44,13 @@ { return precision < 7 ? 7 : precision; } bool append_len (const char *s, unsigned l) { unsigned old_len = length; - if (unlikely (!resize_dirty ((int) (old_len + l)))) + unsigned new_len; + if (unlikely (hb_unsigned_add_overflows (old_len, l, &new_len) || + !resize_dirty ((int) new_len))) return false; hb_memcpy (arrayZ + old_len, s, l); return true; } @@ -153,11 +155,13 @@ bool append_base64 (const uint8_t *data, unsigned len) { unsigned out_len = ((len + 2) / 3) * 4; unsigned old_len = length; - if (unlikely (!resize_dirty ((int) (old_len + out_len)))) + unsigned new_len; + if (unlikely (hb_unsigned_add_overflows (old_len, out_len, &new_len) || + !resize_dirty ((int) new_len))) return false; char *dst = arrayZ + old_len; unsigned di = 0; unsigned i = 0; Index: gfx/harfbuzz/src/hb-vector.hh =================================================================== --- gfx/harfbuzz/src/hb-vector.hh +++ gfx/harfbuzz/src/hb-vector.hh @@ -270,11 +270,11 @@ Type& tail () { return (*this)[length - 1]; } const Type& tail () const { return (*this)[length - 1]; } explicit operator bool () const { return length; } - size_t get_size () const { return length * item_size; } + size_t get_size () const { return hb_unsigned_mul_saturate (length, item_size); } /* Sink interface. */ template hb_vector_t& operator << (T&& v) { push (std::forward (v)); return *this; } @@ -633,11 +633,13 @@ } HB_ALWAYS_INLINE_VECTOR_ALLOCS bool resize_full (int size_, bool initialize, bool exact) { - unsigned int size = size_ < 0 ? 0u : (unsigned int) size_; + if (unlikely (size_ < 0)) + return false; + unsigned int size = (unsigned int) size_; if (!alloc (size, exact)) return false; if (size > length) { Index: gfx/harfbuzz/src/hb-version.h =================================================================== --- gfx/harfbuzz/src/hb-version.h +++ gfx/harfbuzz/src/hb-version.h @@ -51,18 +51,18 @@ /** * HB_VERSION_MICRO: * * The micro component of the library version available at compile-time. */ -#define HB_VERSION_MICRO 0 +#define HB_VERSION_MICRO 1 /** * HB_VERSION_STRING: * * A string literal containing the library version available at compile-time. */ -#define HB_VERSION_STRING "14.2.0" +#define HB_VERSION_STRING "14.2.1" /** * HB_VERSION_ATLEAST: * @major: the major component of the version number * @minor: the minor component of the version number Index: gfx/harfbuzz/src/rust/Cargo.toml =================================================================== --- gfx/harfbuzz/src/rust/Cargo.toml +++ gfx/harfbuzz/src/rust/Cargo.toml @@ -2,25 +2,28 @@ name = "harfbuzz_rust" edition = "2021" rust-version = "1.87.0" [dependencies] +harfrust = { version = "0.8.3", optional = true } +# harfrust = { git = "https://github.com/harfbuzz/harfrust", optional = true } +read-fonts = "0.39.*" skrifa = { version = "0.*", optional = true } -harfrust = { version = "0.6.*", optional = true } -# harfrust = { git = "https://github.com/harfbuzz/harfrust", optional = true } +smallvec = "1.*" [lib] name = "harfbuzz_rust" path = "lib.rs" crate-type = ["staticlib"] [features] -default = ["paint", "draw"] +default = ["paint", "draw", "hb-allocator"] font = ["dep:skrifa"] shape = ["dep:harfrust"] paint = [] draw = [] +hb-allocator = [] [profile.release] strip = true panic = "abort" overflow-checks = false Index: gfx/harfbuzz/src/rust/font.rs =================================================================== --- gfx/harfbuzz/src/rust/font.rs +++ gfx/harfbuzz/src/rust/font.rs @@ -6,11 +6,11 @@ use std::ptr::null_mut; use std::sync::atomic::{AtomicPtr, AtomicU32, Ordering}; use std::sync::Mutex; use skrifa::charmap::Charmap; -use skrifa::charmap::MapVariant::Variant; +use skrifa::charmap::MapVariant::{UseDefault, Variant}; use skrifa::color::ColorGlyphCollection; use skrifa::font::FontRef; use skrifa::instance::{Location, NormalizedCoord, Size}; use skrifa::metrics::GlyphMetrics; use skrifa::raw::tables::vmtx::Vmtx; @@ -57,15 +57,15 @@ impl FontationsData<'_> { unsafe fn from_hb_font(font: *mut hb_font_t) -> Option { let face_index = hb_face_get_index(hb_font_get_face(font)); let face_blob = hb_face_reference_blob(hb_font_get_face(font)); let blob_length = hb_blob_get_length(face_blob); - let blob_data = hb_blob_get_data(face_blob, null_mut()); + let blob_data: *const u8 = hb_blob_get_data(face_blob, null_mut()).cast(); if blob_data.is_null() { return None; } - let face_data = std::slice::from_raw_parts(blob_data as *const u8, blob_length as usize); + let face_data = std::slice::from_raw_parts(blob_data, blob_length as usize); let font_ref = FontRef::from_index(face_data, face_index); let font_ref = match font_ref { Ok(f) => f, Err(_) => return None, @@ -215,10 +215,17 @@ match char_map.map_variant(unicode, variation_selector) { Some(Variant(glyph_id)) => { unsafe { *glyph = glyph_id.to_u32() as hb_codepoint_t }; true as hb_bool_t } + Some(UseDefault) => match char_map.map(unicode) { + Some(glyph_id) => { + unsafe { *glyph = glyph_id.to_u32() as hb_codepoint_t }; + true as hb_bool_t + } + None => false as hb_bool_t, + }, _ => false as hb_bool_t, } } extern "C" fn _hb_fontations_get_glyph_h_advances( @@ -985,12 +992,13 @@ return true as hb_bool_t; } let glyph_name = glyph_name.as_str(); // Copy the glyph name into the buffer, up to size-1 bytes let len = glyph_name.len().min((size as usize) - 1); + let name: *mut u8 = name.cast(); unsafe { - std::slice::from_raw_parts_mut(name as *mut u8, len) + std::slice::from_raw_parts_mut(name, len) .copy_from_slice(&glyph_name.as_bytes()[..len]); *name.add(len) = 0; } true as hb_bool_t } else { @@ -1012,12 +1020,13 @@ glyph: *mut hb_codepoint_t, _user_data: *mut ::std::os::raw::c_void, ) -> hb_bool_t { let data = unsafe { &*(font_data as *const FontationsData) }; + let name: *const u8 = name.cast(); // SAFETY: HarfBuzz guarantees the string is valid memory for `len` bytes. - let name_bytes = unsafe { std::slice::from_raw_parts(name as *const u8, len as usize) }; + let name_bytes = unsafe { std::slice::from_raw_parts(name, len as usize) }; let name_str = match std::str::from_utf8(name_bytes) { Ok(s) => s, Err(_) => return false as hb_bool_t, }; @@ -1167,11 +1176,11 @@ } /// # Safety /// /// This function is unsafe because it connects with the HarfBuzz API. -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn hb_fontations_font_set_funcs(font: *mut hb_font_t) { let ffuncs = _hb_fontations_font_funcs_get(); let data = FontationsData::from_hb_font(font); let data = match data { Index: gfx/harfbuzz/src/rust/lib.rs =================================================================== --- gfx/harfbuzz/src/rust/lib.rs +++ gfx/harfbuzz/src/rust/lib.rs @@ -1,18 +1,23 @@ mod hb; +#[allow(unused_imports)] use hb::*; #[cfg(feature = "font")] mod font; #[cfg(feature = "shape")] mod shape; +#[cfg(feature = "hb-allocator")] use std::alloc::{GlobalAlloc, Layout}; +#[cfg(feature = "hb-allocator")] use std::os::raw::c_void; +#[cfg(feature = "hb-allocator")] struct MyAllocator; +#[cfg(feature = "hb-allocator")] unsafe impl GlobalAlloc for MyAllocator { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { assert!(layout.align() <= 2 * std::mem::size_of::<*mut u8>()); hb_malloc(layout.size()) as *mut u8 } @@ -31,7 +36,8 @@ assert!(layout.align() <= 2 * std::mem::size_of::<*mut u8>()); hb_free(ptr as *mut c_void); } } +#[cfg(feature = "hb-allocator")] #[global_allocator] static GLOBAL: MyAllocator = MyAllocator; Index: gfx/harfbuzz/src/rust/shape.rs =================================================================== --- gfx/harfbuzz/src/rust/shape.rs +++ gfx/harfbuzz/src/rust/shape.rs @@ -1,34 +1,76 @@ #![allow(non_upper_case_globals)] +// C enum becomes i32 on some systems (eg. Windows). +#![allow(clippy::unnecessary_cast)] use super::hb::*; use std::ffi::c_void; -use std::mem::transmute; +use std::mem::{align_of, offset_of, size_of, transmute}; use std::ptr::null_mut; -use std::str::FromStr; + +use harfrust::{ + funcs::{AdvanceWidthBatch, BuiltinFontFuncs, FontFuncs}, + FontRef, GlyphExtents, GlyphFlags as HRGlyphFlags, GlyphInfo as HRGlyphInfo, + GlyphPosition as HRGlyphPosition, NormalizedCoord, ShapeOptions, Shaper, ShaperData, + ShaperInstance, Tag, +}; +use read_fonts::types::GlyphId; +use smallvec::SmallVec; + +type HRFeatureVec = SmallVec<[harfrust::Feature; 4]>; -use harfrust::{FontRef, NormalizedCoord, Shaper, ShaperData, ShaperInstance, Tag}; +const _: () = { + assert!(size_of::() == size_of::()); + assert!(align_of::() == align_of::()); + assert!(offset_of!(hb_glyph_info_t, codepoint) == offset_of!(HRGlyphInfo, glyph_id)); + assert!(offset_of!(hb_glyph_info_t, cluster) == offset_of!(HRGlyphInfo, cluster)); + assert!( + hb_glyph_flags_t_HB_GLYPH_FLAG_UNSAFE_TO_BREAK as u32 + == HRGlyphFlags::UNSAFE_TO_BREAK.to_bits() + ); + assert!( + hb_glyph_flags_t_HB_GLYPH_FLAG_UNSAFE_TO_CONCAT as u32 + == HRGlyphFlags::UNSAFE_TO_CONCAT.to_bits() + ); + assert!( + hb_glyph_flags_t_HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL as u32 + == HRGlyphFlags::SAFE_TO_INSERT_TATWEEL.to_bits() + ); + assert!( + hb_glyph_flags_t_HB_GLYPH_FLAG_DEFINED as u32 + == (HRGlyphFlags::UNSAFE_TO_BREAK.to_bits() + | HRGlyphFlags::UNSAFE_TO_CONCAT.to_bits() + | HRGlyphFlags::SAFE_TO_INSERT_TATWEEL.to_bits()) + ); + + assert!(size_of::() == size_of::()); + assert!(align_of::() == align_of::()); + assert!(offset_of!(hb_glyph_position_t, x_advance) == offset_of!(HRGlyphPosition, x_advance)); + assert!(offset_of!(hb_glyph_position_t, y_advance) == offset_of!(HRGlyphPosition, y_advance)); + assert!(offset_of!(hb_glyph_position_t, x_offset) == offset_of!(HRGlyphPosition, x_offset)); + assert!(offset_of!(hb_glyph_position_t, y_offset) == offset_of!(HRGlyphPosition, y_offset)); +}; pub struct HBHarfRustFaceData<'a> { face_blob: *mut hb_blob_t, font_ref: FontRef<'a>, shaper_data: ShaperData, } -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn _hb_harfrust_shaper_face_data_create_rs( face: *mut hb_face_t, ) -> *mut c_void { let face_index = hb_face_get_index(face); let face_blob = hb_face_reference_blob(face); let blob_length = hb_blob_get_length(face_blob); - let blob_data = hb_blob_get_data(face_blob, null_mut()); + let blob_data: *const u8 = hb_blob_get_data(face_blob, null_mut()).cast(); if blob_data.is_null() { return null_mut(); } - let face_data = std::slice::from_raw_parts(blob_data as *const u8, blob_length as usize); + let face_data = std::slice::from_raw_parts(blob_data, blob_length as usize); let font_ref = match FontRef::from_index(face_data, face_index) { Ok(f) => f, Err(_) => return null_mut(), }; @@ -41,23 +83,98 @@ }); Box::into_raw(hr_face_data) as *mut c_void } -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn _hb_harfrust_shaper_face_data_destroy_rs(data: *mut c_void) { let data = data as *mut HBHarfRustFaceData; let hr_face_data = Box::from_raw(data); let blob = hr_face_data.face_blob; hb_blob_destroy(blob); } pub struct HBHarfRustFontData { - shaper_instance: Box, + // Keep the instance alive because `shaper` borrows variation data from it. + _shaper_instance: Box, shaper: Shaper<'static>, } +struct HBHarfBuzzFontFuncs { + font: *mut hb_font_t, +} + +impl FontFuncs for HBHarfBuzzFontFuncs { + fn nominal_glyph(&mut self, _: &BuiltinFontFuncs, c: u32) -> Option { + let mut glyph = 0; + if unsafe { hb_font_get_nominal_glyph(self.font, c, &mut glyph) } != 0 { + Some(GlyphId::new(glyph)) + } else { + None + } + } + + fn variant_glyph(&mut self, _: &BuiltinFontFuncs, c: u32, vs: u32) -> Option { + let mut glyph = 0; + if unsafe { hb_font_get_variation_glyph(self.font, c, vs, &mut glyph) } != 0 { + Some(GlyphId::new(glyph)) + } else { + None + } + } + + fn advance_width(&mut self, _: &BuiltinFontFuncs, glyph: GlyphId) -> i32 { + unsafe { hb_font_get_glyph_h_advance(self.font, glyph.to_u32()) } + } + + fn populate_advance_widths(&mut self, _: &BuiltinFontFuncs, batch: AdvanceWidthBatch<'_>) { + let raw = batch.into_raw(); + unsafe { + hb_font_get_glyph_h_advances( + self.font, + raw.len as u32, + raw.gids, + raw.gid_stride as u32, + raw.advances, + raw.advance_stride as u32, + ); + } + } + + fn advance_height(&mut self, _: &BuiltinFontFuncs, glyph: GlyphId) -> i32 { + unsafe { hb_font_get_glyph_v_advance(self.font, glyph.to_u32()) } + } + + fn vertical_origin(&mut self, _: &BuiltinFontFuncs, glyph: GlyphId) -> (i32, i32) { + let mut x = 0; + let mut y = 0; + unsafe { + hb_font_get_glyph_v_origin(self.font, glyph.to_u32(), &mut x, &mut y); + } + (x, y) + } + + fn extents(&mut self, _: &BuiltinFontFuncs, glyph: GlyphId) -> Option { + let mut extents = hb_glyph_extents_t { + x_bearing: 0, + y_bearing: 0, + width: 0, + height: 0, + }; + if unsafe { hb_font_get_glyph_extents(self.font, glyph.to_u32(), &mut extents) } != 0 { + Some(GlyphExtents { + x_bearing: extents.x_bearing, + y_bearing: extents.y_bearing, + width: extents.width, + height: extents.height, + }) + } else { + None + } + } +} + fn font_to_shaper_instance(font: *mut hb_font_t, font_ref: &FontRef<'_>) -> ShaperInstance { let mut num_coords: u32 = 0; let coords = unsafe { hb_font_get_var_coords_normalized(font, &mut num_coords) }; let coords = if coords.is_null() { &[] @@ -66,37 +183,36 @@ }; let coords = coords.iter().map(|&v| NormalizedCoord::from_bits(v as i16)); ShaperInstance::from_coords(font_ref, coords) } -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn _hb_harfrust_shaper_font_data_create_rs( font: *mut hb_font_t, face_data: *const c_void, ) -> *mut c_void { let face_data = face_data as *const HBHarfRustFaceData; let font_ref = &(*face_data).font_ref; let shaper_instance = Box::new(font_to_shaper_instance(font, font_ref)); - let shaper_instance_ref = &*(&*shaper_instance as *const _); let shaper = (*face_data) .shaper_data .shaper(font_ref) .instance(Some(shaper_instance_ref)) .build(); let hr_font_data = Box::new(HBHarfRustFontData { - shaper_instance, + _shaper_instance: shaper_instance, shaper: transmute::, harfrust::Shaper<'_>>(shaper), }); let hr_font_data_ptr = Box::into_raw(hr_font_data); hr_font_data_ptr as *mut c_void } -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn _hb_harfrust_shaper_font_data_destroy_rs(data: *mut c_void) { let data = data as *mut HBHarfRustFontData; let _hr_font_data = Box::from_raw(data); } @@ -104,78 +220,99 @@ let language_str = unsafe { hb_language_to_string(language) }; if language_str.is_null() { return None; } let language_str = unsafe { std::ffi::CStr::from_ptr(language_str) }; - let language_str = language_str.to_str().unwrap_or_default(); - Some(harfrust::Language::from_str(language_str).unwrap()) + harfrust::Language::new(language_str.to_bytes()) } -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn _hb_harfrust_buffer_create_rs() -> *mut c_void { let hr_buffer = Box::new(harfrust::UnicodeBuffer::new()); Box::into_raw(hr_buffer) as *mut c_void } -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn _hb_harfrust_buffer_destroy_rs(data: *mut c_void) { let data = data as *mut harfrust::UnicodeBuffer; let _hr_buffer = Box::from_raw(data); } -#[no_mangle] +fn hb_feature_to_hr_feature(features: *const hb_feature_t, num_features: u32) -> HRFeatureVec { + if features.is_null() { + SmallVec::new() + } else { + let features = unsafe { std::slice::from_raw_parts(features, num_features as usize) }; + features + .iter() + .map(|f| { + let tag = f.tag; + let value = f.value; + let start = f.start; + let end = f.end; + harfrust::Feature { + tag: Tag::from_u32(tag), + value, + start, + end, + } + }) + .collect() + } +} + +#[unsafe(no_mangle)] pub unsafe extern "C" fn _hb_harfrust_shape_plan_create_rs( font_data: *const c_void, script: hb_script_t, language: hb_language_t, direction: hb_direction_t, + features: *const hb_feature_t, + num_features: u32, ) -> *mut c_void { let font_data = font_data as *const HBHarfRustFontData; - let script = harfrust::Script::from_iso15924_tag(Tag::from_u32(script)); + let script = harfrust::Script::from_iso15924_tag(Tag::from_u32(script as u32)); let language = hb_language_to_hr_language(language); let direction = match direction { hb_direction_t_HB_DIRECTION_LTR => harfrust::Direction::LeftToRight, hb_direction_t_HB_DIRECTION_RTL => harfrust::Direction::RightToLeft, hb_direction_t_HB_DIRECTION_TTB => harfrust::Direction::TopToBottom, hb_direction_t_HB_DIRECTION_BTT => harfrust::Direction::BottomToTop, _ => harfrust::Direction::Invalid, }; + let features = hb_feature_to_hr_feature(features, num_features); let shaper = &(*font_data).shaper; - let hr_shape_plan = harfrust::ShapePlan::new(shaper, direction, script, language.as_ref(), &[]); + let hr_shape_plan = + harfrust::ShapePlan::new(shaper, direction, script, language.as_ref(), &features); let hr_shape_plan = Box::new(hr_shape_plan); Box::into_raw(hr_shape_plan) as *mut c_void } -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn _hb_harfrust_shape_plan_destroy_rs(data: *mut c_void) { let data = data as *mut harfrust::ShapePlan; let _hr_shape_plan = Box::from_raw(data); } -#[no_mangle] +#[unsafe(no_mangle)] pub unsafe extern "C" fn _hb_harfrust_shape_rs( font_data: *const c_void, - face_data: *const c_void, shape_plan: *const c_void, hr_buffer_box: *const c_void, font: *mut hb_font_t, buffer: *mut hb_buffer_t, - pre_context: *const u8, + pre_context: *const u32, pre_context_length: u32, - post_context: *const u8, + post_context: *const u32, post_context_length: u32, features: *const hb_feature_t, num_features: u32, ) -> hb_bool_t { let font_data = font_data as *const HBHarfRustFontData; - let face_data = face_data as *const HBHarfRustFaceData; - - let font_ref = &(*face_data).font_ref; - let hr_buffer_box = hr_buffer_box as *mut harfrust::UnicodeBuffer; let mut hr_buffer_box = Box::from_raw(hr_buffer_box); let mut hr_buffer = *hr_buffer_box; // Set buffer properties @@ -195,11 +332,11 @@ } _ => harfrust::BufferClusterLevel::default(), }; hr_buffer.set_cluster_level(cluster_level); let flags = hb_buffer_get_flags(buffer); - hr_buffer.set_flags(harfrust::BufferFlags::from_bits_truncate(flags)); + hr_buffer.set_flags(harfrust::BufferFlags::from_bits_truncate(flags as u32)); let not_found_variation_selector_glyph = hb_buffer_get_not_found_variation_selector_glyph(buffer); if not_found_variation_selector_glyph != u32::MAX { hr_buffer.set_not_found_variation_selector_glyph(not_found_variation_selector_glyph); } @@ -207,11 +344,11 @@ // Segment properties: let script = hb_buffer_get_script(buffer); let language = hb_buffer_get_language(buffer); let direction = hb_buffer_get_direction(buffer); // Convert to HarfRust types - let script = harfrust::Script::from_iso15924_tag(Tag::from_u32(script)) + let script = harfrust::Script::from_iso15924_tag(Tag::from_u32(script as u32)) .unwrap_or(harfrust::script::UNKNOWN); let language = hb_language_to_hr_language(language); let direction = match direction { hb_direction_t_HB_DIRECTION_LTR => harfrust::Direction::LeftToRight, hb_direction_t_HB_DIRECTION_RTL => harfrust::Direction::RightToLeft, @@ -227,125 +364,54 @@ hr_buffer.set_direction(direction); // Populate buffer let count = hb_buffer_get_length(buffer); let infos = hb_buffer_get_glyph_infos(buffer, null_mut()); - - hr_buffer.reserve(count as usize); - - for i in 0..count { - let info = &*infos.add(i as usize); - let unicode = info.codepoint; - let cluster = info.cluster; - hr_buffer.add(char::from_u32_unchecked(unicode), cluster); + let infos = std::slice::from_raw_parts(infos.cast(), count as usize); + if !hr_buffer.push_glyph_infos(infos) { + return false as hb_bool_t; } let pre_context = std::slice::from_raw_parts(pre_context, pre_context_length as usize); - hr_buffer.set_pre_context(str::from_utf8(pre_context).unwrap()); + hr_buffer.set_pre_context_codepoints(pre_context); let post_context = std::slice::from_raw_parts(post_context, post_context_length as usize); - hr_buffer.set_post_context(str::from_utf8(post_context).unwrap()); + hr_buffer.set_post_context_codepoints(post_context); let ptem = hb_font_get_ptem(font); let ptem = if ptem > 0.0 { Some(ptem) } else { None }; - let shaper = if ptem.is_some() { - (*face_data) - .shaper_data - .shaper(font_ref) - .instance(Some(&(*font_data).shaper_instance)) + let features = hb_feature_to_hr_feature(features, num_features); + let shape_plan = (shape_plan as *const harfrust::ShapePlan).as_ref(); + let mut x_scale = 0; + let mut y_scale = 0; + hb_font_get_scale(font, &mut x_scale, &mut y_scale); + let mut font_funcs = HBHarfBuzzFontFuncs { font }; + let glyphs = (*font_data).shaper.shape( + hr_buffer, + ShapeOptions::new() + .plan(shape_plan) + .scale_separate(Some((x_scale, y_scale))) .point_size(ptem) - .build() - } else { - (*font_data).shaper.clone() - }; + .features(&features) + .font_funcs(Some(&mut font_funcs)), + ); - let features = if features.is_null() { - Vec::new() - } else { - let features = std::slice::from_raw_parts(features, num_features as usize); - features - .iter() - .map(|f| { - let tag = f.tag; - let value = f.value; - let start = f.start; - let end = f.end; - harfrust::Feature { - tag: Tag::from_u32(tag), - value, - start, - end, - } - }) - .collect::>() - }; - - let glyphs = if shape_plan.is_null() { - shaper.shape(hr_buffer, &features) - } else { - let shape_plan = shape_plan as *const harfrust::ShapePlan; - shaper.shape_with_plan(shape_plan.as_ref().unwrap(), hr_buffer, &features) - }; - - let count = glyphs.len(); - hb_buffer_set_length(buffer, 0u32); hb_buffer_set_content_type( buffer, hb_buffer_content_type_t_HB_BUFFER_CONTENT_TYPE_GLYPHS, ); + let count = glyphs.len(); hb_buffer_set_length(buffer, count as u32); let mut count_out: u32 = 0; let infos = hb_buffer_get_glyph_infos(buffer, &mut count_out); let positions = hb_buffer_get_glyph_positions(buffer, null_mut()); if count != count_out as usize { return false as hb_bool_t; } - let mut x_scale: i32 = 0; - let mut y_scale: i32 = 0; - hb_font_get_scale(font, &mut x_scale, &mut y_scale); - let upem = shaper.units_per_em(); - let upem = if upem > 0 { upem } else { 1000 }; - let x_mult = if x_scale < 0 { - -((-x_scale as i64) << 16) - } else { - (x_scale as i64) << 16 - } / upem as i64; - let y_mult = if y_scale < 0 { - -((-y_scale as i64) << 16) - } else { - (y_scale as i64) << 16 - } / upem as i64; - - let em_mult = - |v: i32, mult: i64| -> hb_position_t { ((v as i64 * mult + 32768) >> 16) as hb_position_t }; - - for (i, (hr_info, hr_pos)) in glyphs - .glyph_infos() - .iter() - .zip(glyphs.glyph_positions()) - .enumerate() - { - let info = &mut *infos.add(i); - let pos = &mut *positions.add(i); - info.codepoint = hr_info.glyph_id; - info.cluster = hr_info.cluster; - info.mask = 0; - if hr_info.unsafe_to_break() { - info.mask |= hb_glyph_flags_t_HB_GLYPH_FLAG_UNSAFE_TO_BREAK; - } - if hr_info.unsafe_to_concat() { - info.mask |= hb_glyph_flags_t_HB_GLYPH_FLAG_UNSAFE_TO_CONCAT; - } - if hr_info.safe_to_insert_tatweel() { - info.mask |= hb_glyph_flags_t_HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL; - } - pos.x_advance = em_mult(hr_pos.x_advance, x_mult); - pos.y_advance = em_mult(hr_pos.y_advance, y_mult); - pos.x_offset = em_mult(hr_pos.x_offset, x_mult); - pos.y_offset = em_mult(hr_pos.y_offset, y_mult); - } + std::ptr::copy_nonoverlapping(glyphs.glyph_infos().as_ptr().cast(), infos, count); + std::ptr::copy_nonoverlapping(glyphs.glyph_positions().as_ptr().cast(), positions, count); let hr_buffer = glyphs.clear(); *hr_buffer_box = hr_buffer; // Move the buffer back into the box let _ = Box::into_raw(hr_buffer_box); // Prevent double free