diff --git a/.circleci/config.yml b/.circleci/config.yml index 5ad1ae135..7baebb1c8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -79,6 +79,54 @@ jobs: - run: make - run: LD_LIBRARY_PATH="$PWD/freetype-2.9/objs/.libs" make check || .ci/fail.sh + clang-asan: + docker: + - image: ubuntu:18.04 + steps: + - checkout + - run: apt update || true + - run: apt install -y clang-6.0 binutils libtool autoconf automake make pkg-config gtk-doc-tools ragel libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip + - run: pip install fonttools + - run: CPPFLAGS="-fsanitize=address" LDFLAGS="-fsanitize=address" CC=clang-6.0 CXX=clang++-6.0 ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2 + - run: make + - run: make check || .ci/fail.sh + + clang-msan: + docker: + - image: ubuntu:18.04 + steps: + - checkout + - run: apt update || true + - run: apt install -y clang-6.0 binutils libtool autoconf automake make pkg-config ragel libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip + - run: pip install fonttools + - run: CPPFLAGS="-fsanitize=memory" LDFLAGS="-fsanitize=memory" CC=clang-6.0 CXX=clang++-6.0 ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2 + - run: make + - run: make check || .ci/fail.sh + + clang-tsan: + docker: + - image: ubuntu:18.04 + steps: + - checkout + - run: apt update || true + - run: apt install -y clang-6.0 binutils libtool autoconf automake make pkg-config ragel libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip + - run: pip install fonttools + - run: CPPFLAGS="-fsanitize=thread" LDFLAGS="-fsanitize=thread" CC=clang-6.0 CXX=clang++-6.0 ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2 + - run: make + - run: make check || .ci/fail.sh + + clang-ubsan: + docker: + - image: ubuntu:18.04 + steps: + - checkout + - run: apt update || true + - run: apt install -y clang-6.0 binutils libtool autoconf automake make pkg-config ragel libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip + - run: pip install fonttools + - run: CPPFLAGS="-fsanitize=undefined" LDFLAGS="-fsanitize=undefined" CC=clang-6.0 CXX=clang++-6.0 ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2 + - run: make + - run: make check || .ci/fail.sh + fedora-outoftreebuild: docker: - image: fedora @@ -200,6 +248,10 @@ workflows: - alpine-O3-NOMMAP - archlinux-debug-O0-py3 - clang-O3-O0 + - clang-asan + #- clang-msan + - clang-tsan + #- clang-ubsan - fedora-outoftreebuild # cmake based builds diff --git a/src/check-symbols.sh b/src/check-symbols.sh index d4eca5079..6e9382b1a 100755 --- a/src/check-symbols.sh +++ b/src/check-symbols.sh @@ -26,7 +26,7 @@ for soname in harfbuzz harfbuzz-subset harfbuzz-icu harfbuzz-gobject; do symprefix= if test $suffix = dylib; then symprefix=_; fi - EXPORTED_SYMBOLS="`nm "$so" | grep ' [BCDGINRSTVW] .' | grep -v " $symprefix\\($IGNORED_SYMBOLS\\>\\)" | cut -d' ' -f3 | c++filt`" + EXPORTED_SYMBOLS="`nm "$so" | grep ' [BCDGINRSTV] .' | grep -v " $symprefix\\($IGNORED_SYMBOLS\\>\\)" | cut -d' ' -f3 | c++filt`" prefix=$symprefix`basename "$so" | sed 's/libharfbuzz/hb/; s/-/_/g; s/[.].*//'` diff --git a/src/hb-aat-layout-common.hh b/src/hb-aat-layout-common.hh index 57374b0bc..052aad7fc 100644 --- a/src/hb-aat-layout-common.hh +++ b/src/hb-aat-layout-common.hh @@ -569,7 +569,8 @@ struct StateTableDriver /* If there's no action and we're just epsilon-transitioning to state 0, * safe to break. */ if (c->is_actionable (this, entry) || - !(entry->newState == 0 && entry->flags == context_t::DontAdvance)) + !(entry->newState == StateTable::STATE_START_OF_TEXT && + entry->flags == context_t::DontAdvance)) buffer->unsafe_to_break (buffer->idx - 1, buffer->idx + 1); } diff --git a/src/hb-aat-layout-morx-table.hh b/src/hb-aat-layout-morx-table.hh index 5d3b9b14a..a8b287fde 100644 --- a/src/hb-aat-layout-morx-table.hh +++ b/src/hb-aat-layout-morx-table.hh @@ -608,16 +608,14 @@ struct InsertionSubtable hb_buffer_t *buffer = driver->buffer; unsigned int flags = entry->flags; - if (entry->data.markedInsertIndex != 0xFFFF) + if (entry->data.markedInsertIndex != 0xFFFF && mark_set) { - unsigned int count = (entry->flags & MarkedInsertCount); + unsigned int count = (flags & MarkedInsertCount); unsigned int start = entry->data.markedInsertIndex; const GlyphID *glyphs = &insertionAction[start]; if (unlikely (!c->sanitizer.check_array (glyphs, count))) return false; - bool before = entry->flags & MarkedInsertBefore; - - if (unlikely (!mark_set)) return false; + bool before = flags & MarkedInsertBefore; unsigned int end = buffer->out_len; buffer->move_to (mark); @@ -635,12 +633,12 @@ struct InsertionSubtable if (entry->data.currentInsertIndex != 0xFFFF) { - unsigned int count = (entry->flags & CurrentInsertCount) >> 5; + unsigned int count = (flags & CurrentInsertCount) >> 5; unsigned int start = entry->data.currentInsertIndex; const GlyphID *glyphs = &insertionAction[start]; if (unlikely (!c->sanitizer.check_array (glyphs, count))) return false; - bool before = entry->flags & CurrentInsertBefore; + bool before = flags & CurrentInsertBefore; unsigned int end = buffer->out_len; @@ -652,7 +650,20 @@ struct InsertionSubtable if (!before) buffer->skip_glyph (); - buffer->move_to (end); + /* Humm. Not sure where to move to. There's this wording under + * DontAdvance flag: + * + * "If set, don't update the glyph index before going to the new state. + * This does not mean that the glyph pointed to is the same one as + * before. If you've made insertions immediately downstream of the + * current glyph, the next glyph processed would in fact be the first + * one inserted." + * + * This suggests that if DontAdvance is NOT set, we should move to + * end+count. If it *was*, then move to end, such that newly inserted + * glyphs are now visible. + */ + buffer->move_to ((flags & DontAdvance) ? end : end + count); } if (flags & SetMark) @@ -730,8 +741,25 @@ struct ChainSubtable friend struct Chain; inline unsigned int get_size (void) const { return length; } - inline unsigned int get_type (void) const { return coverage & 0xFF; } + inline unsigned int get_type (void) const { return coverage & SubtableType; } + enum Coverage + { + Vertical = 0x80000000, /* If set, this subtable will only be applied + * to vertical text. If clear, this subtable + * will only be applied to horizontal text. */ + Descending = 0x40000000, /* If set, this subtable will process glyphs + * in descending order. If clear, it will + * process the glyphs in ascending order. */ + AllDirections = 0x20000000, /* If set, this subtable will be applied to + * both horizontal and vertical text (i.e. + * the state of bit 0x80000000 is ignored). */ + Logical = 0x10000000, /* If set, this subtable will process glyphs + * in logical order (or reverse logical order, + * depending on the value of bit 0x80000000). */ + Reserved = 0x0FFFFF00, /* Reserved, set to zero. */ + SubtableType = 0x000000FF, /* Subtable type; see following table. */ + }; enum Type { Rearrangement = 0, @@ -806,14 +834,59 @@ struct Chain unsigned int count = subtableCount; for (unsigned int i = 0; i < count; i++) { + bool reverse; + if (!(subtable->subFeatureFlags & flags)) goto skip; + if (!(subtable->coverage & ChainSubtable::AllDirections) && + HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) != + bool (subtable->coverage & ChainSubtable::Vertical)) + goto skip; + + /* Buffer contents is always in logical direction. Determine if + * we need to reverse before applying this subtable. We reverse + * back after if we did reverse indeed. + * + * Quoting the spac: + * """ + * Bits 28 and 30 of the coverage field control the order in which + * glyphs are processed when the subtable is run by the layout engine. + * Bit 28 is used to indicate if the glyph processing direction is + * the same as logical order or layout order. Bit 30 is used to + * indicate whether glyphs are processed forwards or backwards within + * that order. + + Bit 30 Bit 28 Interpretation for Horizontal Text + 0 0 The subtable is processed in layout order + (the same order as the glyphs, which is + always left-to-right). + 1 0 The subtable is processed in reverse layout order + (the order opposite that of the glyphs, which is + always right-to-left). + 0 1 The subtable is processed in logical order + (the same order as the characters, which may be + left-to-right or right-to-left). + 1 1 The subtable is processed in reverse logical order + (the order opposite that of the characters, which + may be right-to-left or left-to-right). + */ + reverse = subtable->coverage & ChainSubtable::Logical ? + bool (subtable->coverage & ChainSubtable::Descending) : + bool (subtable->coverage & ChainSubtable::Descending) != + HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction); + if (!c->buffer->message (c->font, "start chain subtable %d", c->lookup_index)) goto skip; + if (reverse) + c->buffer->reverse (); + subtable->dispatch (c); + if (reverse) + c->buffer->reverse (); + (void) c->buffer->message (c->font, "end chain subtable %d", c->lookup_index); skip: diff --git a/src/hb-blob.cc b/src/hb-blob.cc index a335df308..fca3c910b 100644 --- a/src/hb-blob.cc +++ b/src/hb-blob.cc @@ -574,18 +574,45 @@ fail_without_close: wchar_t * wchar_file_name = (wchar_t *) malloc (sizeof (wchar_t) * size); if (unlikely (wchar_file_name == nullptr)) goto fail_without_close; mbstowcs (wchar_file_name, file_name, size); +#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP) + { + CREATEFILE2_EXTENDED_PARAMETERS ceparams = { 0 }; + ceparams.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS); + ceparams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0xFFFF; + ceparams.dwFileFlags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0xFFF00000; + ceparams.dwSecurityQosFlags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0x000F0000; + ceparams.lpSecurityAttributes = nullptr; + ceparams.hTemplateFile = nullptr; + fd = CreateFile2 (wchar_file_name, GENERIC_READ, FILE_SHARE_READ, + OPEN_EXISTING, &ceparams); + } +#else fd = CreateFileW (wchar_file_name, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, nullptr); +#endif free (wchar_file_name); if (unlikely (fd == INVALID_HANDLE_VALUE)) goto fail_without_close; +#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP) + { + LARGE_INTEGER length; + GetFileSizeEx (fd, &length); + file->length = length.LowPart; + file->mapping = CreateFileMappingFromApp (fd, nullptr, PAGE_READONLY, length.QuadPart, nullptr); + } +#else file->length = (unsigned long) GetFileSize (fd, nullptr); file->mapping = CreateFileMapping (fd, nullptr, PAGE_READONLY, 0, 0, nullptr); +#endif if (unlikely (file->mapping == nullptr)) goto fail; +#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP) + file->contents = (char *) MapViewOfFileFromApp (file->mapping, FILE_MAP_READ, 0, 0); +#else file->contents = (char *) MapViewOfFile (file->mapping, FILE_MAP_READ, 0, 0, 0); +#endif if (unlikely (file->contents == nullptr)) goto fail; CloseHandle (fd); diff --git a/src/hb-buffer.cc b/src/hb-buffer.cc index 536ab5d53..8fecea721 100644 --- a/src/hb-buffer.cc +++ b/src/hb-buffer.cc @@ -1887,6 +1887,10 @@ hb_buffer_t::sort (unsigned int start, unsigned int end, int(*compar)(const hb_g /** * hb_buffer_diff: + * @buffer: a buffer. + * @reference: other buffer to compare to. + * @dottedcircle_glyph: glyph id of U+25CC DOTTED CIRCLE, or (hb_codepont_t) -1. + * @position_fuzz: allowed absolute difference in position values. * * If dottedcircle_glyph is (hb_codepoint_t) -1 then %HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT * and %HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT are never returned. This should be used by most diff --git a/src/hb-buffer.h b/src/hb-buffer.h index 8a2d3e869..4ed458fbb 100644 --- a/src/hb-buffer.h +++ b/src/hb-buffer.h @@ -44,7 +44,6 @@ HB_BEGIN_DECLS * hb_glyph_info_t: * @codepoint: either a Unicode code point (before shaping) or a glyph index * (after shaping). - * @mask: * @cluster: the index of the character in the original text that corresponds * to this #hb_glyph_info_t, or whatever the client passes to * hb_buffer_add(). More than one #hb_glyph_info_t can have the same @@ -59,11 +58,13 @@ HB_BEGIN_DECLS * * The #hb_glyph_info_t is the structure that holds information about the * glyphs and their relation to input text. - * */ -typedef struct hb_glyph_info_t { +typedef struct hb_glyph_info_t +{ hb_codepoint_t codepoint; - hb_mask_t mask; /* Holds hb_glyph_flags_t after hb_shape(), plus other things. */ + /*< private >*/ + hb_mask_t mask; + /*< public >*/ uint32_t cluster; /*< private >*/ @@ -92,6 +93,7 @@ typedef struct hb_glyph_info_t { typedef enum { /*< flags >*/ HB_GLYPH_FLAG_UNSAFE_TO_BREAK = 0x00000001, + /*< private >*/ HB_GLYPH_FLAG_DEFINED = 0x00000001 /* OR of all defined flags */ } hb_glyph_flags_t; @@ -298,7 +300,15 @@ hb_buffer_set_flags (hb_buffer_t *buffer, HB_EXTERN hb_buffer_flags_t hb_buffer_get_flags (hb_buffer_t *buffer); -/* +/** + * hb_buffer_cluster_level_t: + * @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES: Return cluster values grouped by graphemes into + * monotone order. + * @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS: Return cluster values grouped into monotone order. + * @HB_BUFFER_CLUSTER_LEVEL_CHARACTERS: Don't group cluster values. + * @HB_BUFFER_CLUSTER_LEVEL_DEFAULT: Default cluster level, + * equal to @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES. + * * Since: 0.9.42 */ typedef enum { diff --git a/src/hb-coretext.cc b/src/hb-coretext.cc index a7ba85f83..f921e3a02 100644 --- a/src/hb-coretext.cc +++ b/src/hb-coretext.cc @@ -515,12 +515,14 @@ struct range_record_t { #define kUpperCaseType 38 /* Table data courtesy of Apple. */ -static const struct feature_mapping_t { - FourCharCode otFeatureTag; +static const struct feature_mapping_t +{ + hb_tag_t otFeatureTag; uint16_t aatFeatureType; uint16_t selectorToEnable; uint16_t selectorToDisable; -} feature_mappings[] = { +} feature_mappings[] = +{ { 'c2pc', kUpperCaseType, kUpperCasePetiteCapsSelector, kDefaultUpperCaseSelector }, { 'c2sc', kUpperCaseType, kUpperCaseSmallCapsSelector, kDefaultUpperCaseSelector }, { 'calt', kContextualAlternatesType, kContextualAlternatesOnSelector, kContextualAlternatesOffSelector }, @@ -601,7 +603,7 @@ static const struct feature_mapping_t { static int _hb_feature_mapping_cmp (const void *key_, const void *entry_) { - unsigned int key = * (unsigned int *) key_; + hb_tag_t key = * (unsigned int *) key_; const feature_mapping_t * entry = (const feature_mapping_t *) entry_; return key < entry->otFeatureTag ? -1 : key > entry->otFeatureTag ? 1 : diff --git a/src/hb-face.cc b/src/hb-face.cc index 922fd8fd0..19eea4d34 100644 --- a/src/hb-face.cc +++ b/src/hb-face.cc @@ -36,12 +36,12 @@ /** - * hb_face_count: Get number of faces on the blob - * @blob: + * hb_face_count: + * @blob: a blob. * + * Get number of faces in a blob. * - * - * Return value: Number of faces on the blob + * Return value: Number of faces in @blob * * Since: 1.7.7 **/ @@ -488,6 +488,9 @@ hb_face_get_glyph_count (const hb_face_t *face) /** * hb_face_get_table_tags: * @face: a face. + * @start_offset: index of first tag to return. + * @table_count: input length of @table_tags array, output number of items written. + * @table_tags: array to write tags into. * * Retrieves table tags for a face, if possible. * diff --git a/src/hb-font.cc b/src/hb-font.cc index bc831dd53..9dcf6c123 100644 --- a/src/hb-font.cc +++ b/src/hb-font.cc @@ -1703,9 +1703,11 @@ hb_font_get_ppem (hb_font_t *font, /** * hb_font_set_ptem: * @font: a font. - * @ptem: + * @ptem: font size in points. * - * Sets "point size" of the font. + * Sets "point size" of the font. Set to 0 to unset. + * + * There are 72 points in an inch. * * Since: 1.6.0 **/ @@ -1931,9 +1933,9 @@ hb_font_get_variation_glyph_trampoline (hb_font_t *font, /** * hb_font_funcs_set_glyph_func: * @ffuncs: font functions. - * @func: (closure user_data) (destroy destroy) (scope notified): - * @user_data: - * @destroy: + * @func: (closure user_data) (destroy destroy) (scope notified): callback function. + * @user_data: data to pass to @func. + * @destroy: function to call when @user_data is not needed anymore. * * Deprecated. Use hb_font_funcs_set_nominal_glyph_func() and * hb_font_funcs_set_variation_glyph_func() instead. diff --git a/src/hb-ot-layout-gsubgpos.hh b/src/hb-ot-layout-gsubgpos.hh index ff9783edc..031b39b9b 100644 --- a/src/hb-ot-layout-gsubgpos.hh +++ b/src/hb-ot-layout-gsubgpos.hh @@ -344,10 +344,10 @@ struct hb_ot_apply_context_t : match_glyph_data = nullptr; matcher.set_match_func (nullptr, nullptr); matcher.set_lookup_props (c->lookup_props); - /* Ignore ZWNJ if we are matching GSUB context, or matching GPOS. */ + /* Ignore ZWNJ if we are matching GPOS, or matching GSUB context and asked to. */ matcher.set_ignore_zwnj (c->table_index == 1 || (context_match && c->auto_zwnj)); - /* Ignore ZWJ if we are matching GSUB context, or matching GPOS, or if asked to. */ - matcher.set_ignore_zwj (c->table_index == 1 || (context_match || c->auto_zwj)); + /* Ignore ZWJ if we are matching context, or asked to. */ + matcher.set_ignore_zwj (context_match || c->auto_zwj); matcher.set_mask (context_match ? -1 : c->lookup_mask); } inline void set_lookup_props (unsigned int lookup_props) @@ -508,19 +508,20 @@ struct hb_ot_apply_context_t : random (false), random_state (1) {} - inline void set_lookup_mask (hb_mask_t mask) { lookup_mask = mask; } - inline void set_auto_zwj (bool auto_zwj_) { auto_zwj = auto_zwj_; } - inline void set_auto_zwnj (bool auto_zwnj_) { auto_zwnj = auto_zwnj_; } - inline void set_random (bool random_) { random = random_; } - inline void set_recurse_func (recurse_func_t func) { recurse_func = func; } - inline void set_lookup_index (unsigned int lookup_index_) { lookup_index = lookup_index_; } - inline void set_lookup_props (unsigned int lookup_props_) + inline void reinit_iters (void) { - lookup_props = lookup_props_; iter_input.init (this, false); iter_context.init (this, true); } + inline void set_lookup_mask (hb_mask_t mask) { lookup_mask = mask; } + inline void set_auto_zwj (bool auto_zwj_) { auto_zwj = auto_zwj_; reinit_iters (); } + inline void set_auto_zwnj (bool auto_zwnj_) { auto_zwnj = auto_zwnj_; reinit_iters (); } + inline void set_random (bool random_) { random = random_; } + inline void set_recurse_func (recurse_func_t func) { recurse_func = func; } + inline void set_lookup_index (unsigned int lookup_index_) { lookup_index = lookup_index_; } + inline void set_lookup_props (unsigned int lookup_props_) { lookup_props = lookup_props_; reinit_iters (); } + inline uint32_t random_number (void) { /* http://www.cplusplus.com/reference/random/minstd_rand/ */ diff --git a/src/hb-ot-post-table.hh b/src/hb-ot-post-table.hh index 5f27fd504..f81de37d5 100644 --- a/src/hb-ot-post-table.hh +++ b/src/hb-ot-post-table.hh @@ -131,6 +131,7 @@ struct post { index_to_offset.fini (); free (gids_sorted_by_name.get ()); + hb_blob_destroy (blob); } inline bool get_glyph_name (hb_codepoint_t glyph, diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc index 369078cb2..c1aa1d0fe 100644 --- a/src/hb-ot-shape-complex-indic.cc +++ b/src/hb-ot-shape-complex-indic.cc @@ -95,7 +95,8 @@ static const indic_config_t indic_configs[] = * Indic shaper. */ -struct feature_list_t { +struct feature_list_t +{ hb_tag_t tag; hb_ot_map_feature_flags_t flags; }; @@ -130,7 +131,10 @@ indic_features[] = {HB_TAG('b','l','w','s'), F_GLOBAL}, {HB_TAG('p','s','t','s'), F_GLOBAL}, {HB_TAG('h','a','l','n'), F_GLOBAL}, - /* Positioning features, though we don't care about the types. */ + /* + * Positioning features. + * We don't care about the types. + */ {HB_TAG('d','i','s','t'), F_GLOBAL}, {HB_TAG('a','b','v','m'), F_GLOBAL}, {HB_TAG('b','l','w','m'), F_GLOBAL}, @@ -158,12 +162,14 @@ enum { _BLWS, _PSTS, _HALN, + _DIST, _ABVM, _BLWM, INDIC_NUM_FEATURES, - INDIC_BASIC_FEATURES = INIT /* Don't forget to update this! */ + INDIC_BASIC_FEATURES = INIT, /* Don't forget to update this! */ + INDIC_SUBST_FEATURES = _DIST /* Don't forget to update this! */ }; static void @@ -199,14 +205,19 @@ collect_features_indic (hb_ot_shape_planner_t *plan) unsigned int i = 0; map->add_gsub_pause (initial_reordering); + for (; i < INDIC_BASIC_FEATURES; i++) { map->add_feature (indic_features[i].tag, 1, indic_features[i].flags | F_MANUAL_ZWJ | F_MANUAL_ZWNJ); map->add_gsub_pause (nullptr); } + map->add_gsub_pause (final_reordering); - for (; i < INDIC_NUM_FEATURES; i++) { + + for (; i < INDIC_SUBST_FEATURES; i++) map->add_feature (indic_features[i].tag, 1, indic_features[i].flags | F_MANUAL_ZWJ | F_MANUAL_ZWNJ); - } + + for (; i < INDIC_NUM_FEATURES; i++) + map->add_feature (indic_features[i].tag, 1, indic_features[i].flags); map->add_global_bool_feature (HB_TAG('c','a','l','t')); map->add_global_bool_feature (HB_TAG('c','l','i','g')); diff --git a/src/hb-ot-shape-complex-khmer.cc b/src/hb-ot-shape-complex-khmer.cc index 0b5b50a97..7a88aaa25 100644 --- a/src/hb-ot-shape-complex-khmer.cc +++ b/src/hb-ot-shape-complex-khmer.cc @@ -32,7 +32,8 @@ * Khmer shaper. */ -struct feature_list_t { +struct feature_list_t +{ hb_tag_t tag; hb_ot_map_feature_flags_t flags; }; @@ -57,7 +58,10 @@ khmer_features[] = {HB_TAG('a','b','v','s'), F_GLOBAL}, {HB_TAG('b','l','w','s'), F_GLOBAL}, {HB_TAG('p','s','t','s'), F_GLOBAL}, - /* Positioning features, though we don't care about the types. */ + /* + * Positioning features. + * We don't care about the types. + */ {HB_TAG('d','i','s','t'), F_GLOBAL}, {HB_TAG('a','b','v','m'), F_GLOBAL}, {HB_TAG('b','l','w','m'), F_GLOBAL}, @@ -77,12 +81,14 @@ enum { _ABVS, _BLWS, _PSTS, + _DIST, _ABVM, _BLWM, KHMER_NUM_FEATURES, - KHMER_BASIC_FEATURES = _PRES /* Don't forget to update this! */ + KHMER_BASIC_FEATURES = _PRES, /* Don't forget to update this! */ + KHMER_SUBST_FEATURES = _DIST, /* Don't forget to update this! */ }; static void @@ -121,15 +127,16 @@ collect_features_khmer (hb_ot_shape_planner_t *plan) map->add_global_bool_feature (HB_TAG('c','c','m','p')); unsigned int i = 0; - for (; i < KHMER_BASIC_FEATURES; i++) { + for (; i < KHMER_BASIC_FEATURES; i++) map->add_feature (khmer_features[i].tag, 1, khmer_features[i].flags | F_MANUAL_ZWJ | F_MANUAL_ZWNJ); - } map->add_gsub_pause (clear_syllables); - for (; i < KHMER_NUM_FEATURES; i++) { + for (; i < KHMER_SUBST_FEATURES; i++) map->add_feature (khmer_features[i].tag, 1, khmer_features[i].flags | F_MANUAL_ZWJ | F_MANUAL_ZWNJ); - } + + for (; i < KHMER_NUM_FEATURES; i++) + map->add_feature (khmer_features[i].tag, 1, khmer_features[i].flags); map->add_global_bool_feature (HB_TAG('c','a','l','t')); map->add_global_bool_feature (HB_TAG('c','l','i','g')); diff --git a/src/hb-ot-shape-complex-myanmar.cc b/src/hb-ot-shape-complex-myanmar.cc index 8466f8129..8069e3d99 100644 --- a/src/hb-ot-shape-complex-myanmar.cc +++ b/src/hb-ot-shape-complex-myanmar.cc @@ -54,7 +54,14 @@ other_features[] = HB_TAG('a','b','v','s'), HB_TAG('b','l','w','s'), HB_TAG('p','s','t','s'), - /* Positioning features, though we don't care about the types. */ +}; +static const hb_tag_t +positioning_features[] = +{ + /* + * Positioning features. + * We don't care about the types. + */ HB_TAG('d','i','s','t'), /* Pre-release version of Windows 8 Myanmar font had abvm,blwm * features. The released Windows 8 version of the font (as well @@ -96,14 +103,20 @@ collect_features_myanmar (hb_ot_shape_planner_t *plan) map->add_gsub_pause (initial_reordering); + for (unsigned int i = 0; i < ARRAY_LENGTH (basic_features); i++) { map->add_feature (basic_features[i], 1, F_GLOBAL | F_MANUAL_ZWJ); map->add_gsub_pause (nullptr); } + map->add_gsub_pause (final_reordering); + for (unsigned int i = 0; i < ARRAY_LENGTH (other_features); i++) map->add_feature (other_features[i], 1, F_GLOBAL | F_MANUAL_ZWJ); + + for (unsigned int i = 0; i < ARRAY_LENGTH (positioning_features); i++) + map->add_feature (positioning_features[i], 1, F_GLOBAL); } static void diff --git a/src/hb-ot-shape-complex-use.cc b/src/hb-ot-shape-complex-use.cc index 676b15b6e..62aef7e6f 100644 --- a/src/hb-ot-shape-complex-use.cc +++ b/src/hb-ot-shape-complex-use.cc @@ -86,7 +86,14 @@ other_features[] = HB_TAG('h','a','l','n'), HB_TAG('p','r','e','s'), HB_TAG('p','s','t','s'), - /* Positioning features, though we don't care about the types. */ +}; +static const hb_tag_t +positioning_features[] = +{ + /* + * Positioning features. + * We don't care about the types. + */ HB_TAG('d','i','s','t'), HB_TAG('a','b','v','m'), HB_TAG('b','l','w','m'), @@ -146,9 +153,13 @@ collect_features_use (hb_ot_shape_planner_t *plan) map->add_feature (arabic_features[i], 1, F_NONE); map->add_gsub_pause (nullptr); - /* "Standard typographic presentation" and "Positional feature application" */ + /* "Standard typographic presentation" */ for (unsigned int i = 0; i < ARRAY_LENGTH (other_features); i++) map->add_feature (other_features[i], 1, F_GLOBAL | F_MANUAL_ZWJ); + + /* "Positional feature application" */ + for (unsigned int i = 0; i < ARRAY_LENGTH (positioning_features); i++) + map->add_feature (positioning_features[i], 1, F_GLOBAL); } struct use_shape_plan_t diff --git a/src/hb-ot-shape-normalize.cc b/src/hb-ot-shape-normalize.cc index fd0b63aec..2f0cba184 100644 --- a/src/hb-ot-shape-normalize.cc +++ b/src/hb-ot-shape-normalize.cc @@ -294,6 +294,16 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, _hb_buffer_assert_unicode_vars (buffer); hb_ot_shape_normalization_mode_t mode = plan->shaper->normalization_preference; + if (mode == HB_OT_SHAPE_NORMALIZATION_MODE_AUTO) + { + if (plan->has_mark) + // https://github.com/harfbuzz/harfbuzz/issues/653#issuecomment-423905920 + //mode = HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED; + mode = HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS; + else + mode = HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS; + } + const hb_ot_shape_normalize_context_t c = { plan, buffer, @@ -358,65 +368,6 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, i = end; } - - - if (mode == HB_OT_SHAPE_NORMALIZATION_MODE_NONE || - mode == HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED) - return; - - /* Third round, recompose */ - - /* As noted in the comment earlier, we don't try to combine - * ccc=0 chars with their previous Starter. */ - - buffer->clear_output (); - count = buffer->len; - unsigned int starter = 0; - buffer->next_glyph (); - while (buffer->idx < count && buffer->successful) - { - hb_codepoint_t composed, glyph; - if (/* We don't try to compose a non-mark character with it's preceding starter. - * This is both an optimization to avoid trying to compose every two neighboring - * glyphs in most scripts AND a desired feature for Hangul. Apparently Hangul - * fonts are not designed to mix-and-match pre-composed syllables and Jamo. */ - HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->cur()))) - { - if (/* If there's anything between the starter and this char, they should have CCC - * smaller than this character's. */ - (starter == buffer->out_len - 1 || - info_cc (buffer->prev()) < info_cc (buffer->cur())) && - /* And compose. */ - c.compose (&c, - buffer->out_info[starter].codepoint, - buffer->cur().codepoint, - &composed) && - /* And the font has glyph for the composite. */ - font->get_nominal_glyph (composed, &glyph)) - { - /* Composes. */ - buffer->next_glyph (); /* Copy to out-buffer. */ - if (unlikely (!buffer->successful)) - return; - buffer->merge_out_clusters (starter, buffer->out_len); - buffer->out_len--; /* Remove the second composable. */ - /* Modify starter and carry on. */ - buffer->out_info[starter].codepoint = composed; - buffer->out_info[starter].glyph_index() = glyph; - _hb_glyph_info_set_unicode_props (&buffer->out_info[starter], buffer); - - continue; - } - } - - /* Blocked, or doesn't compose. */ - buffer->next_glyph (); - - if (info_cc (buffer->prev()) == 0) - starter = buffer->out_len - 1; - } - buffer->swap_buffers (); - if (buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_CGJ) { /* For all CGJ, check if it prevented any reordering at all. @@ -430,4 +381,62 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, _hb_glyph_info_unhide (&buffer->info[i]); } } + + + /* Third round, recompose */ + + if (mode == HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS || + mode == HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT) + { + /* As noted in the comment earlier, we don't try to combine + * ccc=0 chars with their previous Starter. */ + + buffer->clear_output (); + count = buffer->len; + unsigned int starter = 0; + buffer->next_glyph (); + while (buffer->idx < count && buffer->successful) + { + hb_codepoint_t composed, glyph; + if (/* We don't try to compose a non-mark character with it's preceding starter. + * This is both an optimization to avoid trying to compose every two neighboring + * glyphs in most scripts AND a desired feature for Hangul. Apparently Hangul + * fonts are not designed to mix-and-match pre-composed syllables and Jamo. */ + HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->cur()))) + { + if (/* If there's anything between the starter and this char, they should have CCC + * smaller than this character's. */ + (starter == buffer->out_len - 1 || + info_cc (buffer->prev()) < info_cc (buffer->cur())) && + /* And compose. */ + c.compose (&c, + buffer->out_info[starter].codepoint, + buffer->cur().codepoint, + &composed) && + /* And the font has glyph for the composite. */ + font->get_nominal_glyph (composed, &glyph)) + { + /* Composes. */ + buffer->next_glyph (); /* Copy to out-buffer. */ + if (unlikely (!buffer->successful)) + return; + buffer->merge_out_clusters (starter, buffer->out_len); + buffer->out_len--; /* Remove the second composable. */ + /* Modify starter and carry on. */ + buffer->out_info[starter].codepoint = composed; + buffer->out_info[starter].glyph_index() = glyph; + _hb_glyph_info_set_unicode_props (&buffer->out_info[starter], buffer); + + continue; + } + } + + /* Blocked, or doesn't compose. */ + buffer->next_glyph (); + + if (info_cc (buffer->prev()) == 0) + starter = buffer->out_len - 1; + } + buffer->swap_buffers (); + } } diff --git a/src/hb-ot-shape-normalize.hh b/src/hb-ot-shape-normalize.hh index 4aea640b1..80755f775 100644 --- a/src/hb-ot-shape-normalize.hh +++ b/src/hb-ot-shape-normalize.hh @@ -38,10 +38,11 @@ struct hb_ot_shape_plan_t; enum hb_ot_shape_normalization_mode_t { HB_OT_SHAPE_NORMALIZATION_MODE_NONE, HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED, - HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS, /* never composes base-to-base */ - HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, /* always fully decomposes and then recompose back */ + HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS, /* Never composes base-to-base */ + HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, /* Always fully decomposes and then recompose back */ - HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT = HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS + HB_OT_SHAPE_NORMALIZATION_MODE_AUTO, /* Choose decomposed if GPOS mark feature available, compose otherwise. */ + HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT = HB_OT_SHAPE_NORMALIZATION_MODE_AUTO }; HB_INTERNAL void _hb_ot_shape_normalize (const hb_ot_shape_plan_t *shaper, diff --git a/src/hb.hh b/src/hb.hh index 557ae6b35..631592def 100644 --- a/src/hb.hh +++ b/src/hb.hh @@ -237,7 +237,9 @@ struct _hb_alignof # undef _WIN32_WINNT # endif # ifndef _WIN32_WINNT -# define _WIN32_WINNT 0x0600 +# if !defined(WINAPI_FAMILY) || !(WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP) +# define _WIN32_WINNT 0x0600 +# endif # endif # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN 1 diff --git a/test/api/hb-subset-test.h b/test/api/hb-subset-test.h index afd553a0d..0318fa7bb 100644 --- a/test/api/hb-subset-test.h +++ b/test/api/hb-subset-test.h @@ -51,12 +51,18 @@ static inline hb_face_t * hb_subset_test_open_font (const char *font_path) { #if GLIB_CHECK_VERSION(2,37,2) - char* path = g_test_build_filename(G_TEST_DIST, font_path, NULL); + char* path = g_test_build_filename (G_TEST_DIST, font_path, NULL); #else - char* path = g_strdup(font_path); + char* path = g_strdup (font_path); #endif - return hb_face_create (hb_blob_create_from_file (path), 0); + hb_blob_t* blob = hb_blob_create_from_file (path); + hb_face_t* face = hb_face_create (blob, 0); + hb_blob_destroy (blob); + + g_free (path); + + return face; } static inline hb_subset_input_t * diff --git a/test/api/test-font.c b/test/api/test-font.c index 527dfcdc1..4a2393ec6 100644 --- a/test/api/test-font.c +++ b/test/api/test-font.c @@ -361,6 +361,7 @@ test_fontfuncs_subclassing (void) hb_font_destroy (font3); + hb_font_destroy (font2); } diff --git a/test/api/test-ot-math.c b/test/api/test-ot-math.c index d071c8895..7f500157e 100644 --- a/test/api/test-ot-math.c +++ b/test/api/test-ot-math.c @@ -100,10 +100,14 @@ test_has_data (void) hb_face = hb_face_get_empty (); hb_font = hb_font_create (hb_face); g_assert(!hb_ot_math_has_data (hb_face)); // MATH table not available + hb_font_destroy (hb_font); + hb_face_destroy (hb_face); hb_font = hb_font_get_empty (); hb_face = hb_font_get_face (hb_font); g_assert(!hb_ot_math_has_data (hb_face)); // MATH table not available + hb_font_destroy (hb_font); + hb_face_destroy (hb_face); cleanupFreeType(); } diff --git a/test/api/test-set.c b/test/api/test-set.c index 338a610c9..eb2f22ece 100644 --- a/test/api/test-set.c +++ b/test/api/test-set.c @@ -262,6 +262,7 @@ test_set_algebra (void) hb_set_destroy (s); hb_set_destroy (o); + hb_set_destroy (o2); } static void diff --git a/test/shaping/run-tests.py b/test/shaping/run-tests.py index 73b61c21a..915536b97 100755 --- a/test/shaping/run-tests.py +++ b/test/shaping/run-tests.py @@ -9,7 +9,7 @@ def cmd(command): p = subprocess.Popen ( command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) p.wait () - print (p.stderr.read (), end="") # file=sys.stderr + print (p.stderr.read ().decode ("utf-8").strip ()) # file=sys.stderr return p.stdout.read ().decode ("utf-8").strip (), p.returncode