Merge branch 'master' into var-subset

This commit is contained in:
Michiharu Ariza 2019-04-29 16:43:48 -07:00
commit 1223a352b7
73 changed files with 772 additions and 343 deletions

View File

@ -235,15 +235,6 @@ jobs:
- run: CFLAGS="-Wno-attributes" CXXFLAGS="-Wno-attributes" ./autogen.sh --prefix=/usr/local/djgpp --host=i586-pc-msdosdjgpp - run: CFLAGS="-Wno-attributes" CXXFLAGS="-Wno-attributes" ./autogen.sh --prefix=/usr/local/djgpp --host=i586-pc-msdosdjgpp
- run: make -j32 - run: make -j32
crosscompile-notest-freebsd9:
docker:
- image: donbowman/freebsd-cross-build
steps:
- checkout
- run: apt update && apt install -y pkg-config ragel
- run: ./autogen.sh --prefix=/freebsd --host=x86_64-pc-freebsd9
- run: make -j32
crosscompile-notest-psvita: crosscompile-notest-psvita:
docker: docker:
- image: dockcross/base - image: dockcross/base
@ -327,7 +318,6 @@ workflows:
# they can't be test thus are without tests # they can't be test thus are without tests
## autotools ## autotools
- crosscompile-notest-djgpp - crosscompile-notest-djgpp
- crosscompile-notest-freebsd9
- crosscompile-notest-psvita - crosscompile-notest-psvita
## cmake ## cmake

View File

@ -9,7 +9,7 @@ insert_final_newline = true
[*.{c,cc,h,hh}] [*.{c,cc,h,hh}]
tab_width = 8 tab_width = 8
indent_size = 2 indent_size = 2
indent_style = space indent_style = tab # should be space
[*.{py,sh}] [*.{py,sh}]
indent_style = tab indent_style = tab

View File

@ -1,11 +1,14 @@
Behdad Esfahbod Behdad Esfahbod
David Corbett
David Turner David Turner
Ebrahim Byagowi Ebrahim Byagowi
Garret Rieger
Jonathan Kew Jonathan Kew
Khaled Hosny Khaled Hosny
Lars Knoll Lars Knoll
Martin Hosken Martin Hosken
Owen Taylor Owen Taylor
Roderick Sheeter
Roozbeh Pournader Roozbeh Pournader
Simon Hausmann Simon Hausmann
Werner Lemberg Werner Lemberg

View File

@ -844,7 +844,7 @@ endif ()
if (HB_BUILD_TESTS) if (HB_BUILD_TESTS)
## src/ executables ## src/ executables
foreach (prog main test test-would-substitute test-size-params test-buffer-serialize hb-ot-tag test-unicode-ranges) foreach (prog main test test-gsub-would-substitute test-gpos-size-params test-buffer-serialize hb-ot-tag test-unicode-ranges)
set (prog_name ${prog}) set (prog_name ${prog})
if (${prog_name} STREQUAL "test") if (${prog_name} STREQUAL "test")
# test can not be used as a valid executable name on cmake, lets special case it # test can not be used as a valid executable name on cmake, lets special case it

View File

@ -2,7 +2,8 @@ HarfBuzz is licensed under the so-called "Old MIT" license. Details follow.
For parts of HarfBuzz that are licensed under different licenses see individual For parts of HarfBuzz that are licensed under different licenses see individual
files names COPYING in subdirectories where applicable. files names COPYING in subdirectories where applicable.
Copyright © 2010,2011,2012 Google, Inc. Copyright © 2010,2011,2012,2013,2014,2015,2016,2017,2018,2019 Google, Inc.
Copyright © 2019 Facebook, Inc.
Copyright © 2012 Mozilla Foundation Copyright © 2012 Mozilla Foundation
Copyright © 2011 Codethink Limited Copyright © 2011 Codethink Limited
Copyright © 2008,2010 Nokia Corporation and/or its subsidiary(-ies) Copyright © 2008,2010 Nokia Corporation and/or its subsidiary(-ies)

2
THANKS
View File

@ -1,6 +1,6 @@
Bradley Grainger Bradley Grainger
Khaled Hosny
Kenichi Ishibashi Kenichi Ishibashi
Ivan Kuckir <https://photopea.com/>
Ryan Lortie Ryan Lortie
Jeff Muizelaar Jeff Muizelaar
suzuki toshiya suzuki toshiya

View File

@ -20,11 +20,7 @@
<para> <para>
The canonical source-code tree is available at The canonical source-code tree is available at
<ulink <ulink url="https://github.com/harfbuzz/harfbuzz">github.com/harfbuzz/harfbuzz</ulink>.
url="https://github.com/harfbuzz/harfbuzz">github.com/harfbuzz/harfbuzz</ulink>
and is also available at
<ulink
url="http://cgit.freedesktop.org/harfbuzz/">cgit.freedesktop.org/harfbuzz</ulink>.
See <xref linkend="download" endterm="download.title"/> for See <xref linkend="download" endterm="download.title"/> for
release tarballs. release tarballs.
</para> </para>

View File

@ -310,9 +310,9 @@ noinst_PROGRAMS = \
main \ main \
test \ test \
test-buffer-serialize \ test-buffer-serialize \
test-name-table \ test-ot-name \
test-size-params \ test-gpos-size-params \
test-would-substitute \ test-gsub-would-substitute \
$(NULL) $(NULL)
bin_PROGRAMS = bin_PROGRAMS =
@ -328,17 +328,17 @@ test_buffer_serialize_SOURCES = test-buffer-serialize.cc
test_buffer_serialize_CPPFLAGS = $(HBCFLAGS) test_buffer_serialize_CPPFLAGS = $(HBCFLAGS)
test_buffer_serialize_LDADD = libharfbuzz.la $(HBLIBS) test_buffer_serialize_LDADD = libharfbuzz.la $(HBLIBS)
test_name_table_SOURCES = test-name-table.cc test_ot_name_SOURCES = test-ot-name.cc
test_name_table_CPPFLAGS = $(HBCFLAGS) test_ot_name_CPPFLAGS = $(HBCFLAGS)
test_name_table_LDADD = libharfbuzz.la $(HBLIBS) test_ot_name_LDADD = libharfbuzz.la $(HBLIBS)
test_size_params_SOURCES = test-size-params.cc test_gpos_size_params_SOURCES = test-gpos-size-params.cc
test_size_params_CPPFLAGS = $(HBCFLAGS) test_gpos_size_params_CPPFLAGS = $(HBCFLAGS)
test_size_params_LDADD = libharfbuzz.la $(HBLIBS) test_gpos_size_params_LDADD = libharfbuzz.la $(HBLIBS)
test_would_substitute_SOURCES = test-would-substitute.cc test_gsub_would_substitute_SOURCES = test-gsub-would-substitute.cc
test_would_substitute_CPPFLAGS = $(HBCFLAGS) $(FREETYPE_CFLAGS) test_gsub_would_substitute_CPPFLAGS = $(HBCFLAGS) $(FREETYPE_CFLAGS)
test_would_substitute_LDADD = libharfbuzz.la $(HBLIBS) $(FREETYPE_LIBS) test_gsub_would_substitute_LDADD = libharfbuzz.la $(HBLIBS) $(FREETYPE_LIBS)
if HAVE_FREETYPE if HAVE_FREETYPE
if HAVE_CAIRO_FT if HAVE_CAIRO_FT

View File

@ -86,7 +86,7 @@ HB_BASE_sources = \
hb-ot-math-table.hh \ hb-ot-math-table.hh \
hb-ot-math.cc \ hb-ot-math.cc \
hb-ot-maxp-table.hh \ hb-ot-maxp-table.hh \
hb-ot-name-language.cc \ hb-ot-name-language-static.hh \
hb-ot-name-language.hh \ hb-ot-name-language.hh \
hb-ot-name-table.hh \ hb-ot-name-table.hh \
hb-ot-name.cc \ hb-ot-name.cc \

View File

@ -48,6 +48,12 @@ defaults = ('Other', 'Not_Applicable', 'Cn', 'No_Block')
# TODO Characters that are not in Unicode Indic files, but used in USE # TODO Characters that are not in Unicode Indic files, but used in USE
data[0][0x034F] = defaults[0] data[0][0x034F] = defaults[0]
data[0][0x2060] = defaults[0] data[0][0x2060] = defaults[0]
# TODO https://github.com/harfbuzz/harfbuzz/pull/1685
data[0][0x1B5B] = 'Consonant_Placeholder'
data[0][0x1B5C] = 'Consonant_Placeholder'
data[0][0x1B5F] = 'Consonant_Placeholder'
data[0][0x1B62] = 'Consonant_Placeholder'
data[0][0x1B68] = 'Consonant_Placeholder'
# TODO https://github.com/roozbehp/unicode-data/issues/9 # TODO https://github.com/roozbehp/unicode-data/issues/9
data[0][0x11C44] = 'Consonant_Placeholder' data[0][0x11C44] = 'Consonant_Placeholder'
data[0][0x11C45] = 'Consonant_Placeholder' data[0][0x11C45] = 'Consonant_Placeholder'
@ -171,7 +177,7 @@ def is_BASE(U, UISC, UGC):
def is_BASE_IND(U, UISC, UGC): def is_BASE_IND(U, UISC, UGC):
#SPEC-DRAFT return (UISC in [Consonant_Dead, Modifying_Letter] or UGC == Po) #SPEC-DRAFT return (UISC in [Consonant_Dead, Modifying_Letter] or UGC == Po)
return (UISC in [Consonant_Dead, Modifying_Letter] or return (UISC in [Consonant_Dead, Modifying_Letter] or
(UGC == Po and not U in [0x104B, 0x104E, 0x2022, 0x111C8, 0x11A3F, 0x11A45, 0x11C44, 0x11C45]) or (UGC == Po and not U in [0x104B, 0x104E, 0x1B5B, 0x1B5C, 0x1B5F, 0x2022, 0x111C8, 0x11A3F, 0x11A45, 0x11C44, 0x11C45]) or
False # SPEC-DRAFT-OUTDATED! U == 0x002D False # SPEC-DRAFT-OUTDATED! U == 0x002D
) )
def is_BASE_NUM(U, UISC, UGC): def is_BASE_NUM(U, UISC, UGC):
@ -228,7 +234,7 @@ def is_REPHA(U, UISC, UGC):
def is_SYM(U, UISC, UGC): def is_SYM(U, UISC, UGC):
if U == 0x25CC: return False #SPEC-DRAFT if U == 0x25CC: return False #SPEC-DRAFT
#SPEC-DRAFT return UGC in [So, Sc] or UISC == Symbol_Letter #SPEC-DRAFT return UGC in [So, Sc] or UISC == Symbol_Letter
return UGC in [So, Sc] return UGC in [So, Sc] and U not in [0x1B62, 0x1B68]
def is_SYM_MOD(U, UISC, UGC): def is_SYM_MOD(U, UISC, UGC):
return U in [0x1B6B, 0x1B6C, 0x1B6D, 0x1B6E, 0x1B6F, 0x1B70, 0x1B71, 0x1B72, 0x1B73] return U in [0x1B6B, 0x1B6C, 0x1B6D, 0x1B6E, 0x1B6F, 0x1B70, 0x1B71, 0x1B72, 0x1B73]
def is_VARIATION_SELECTOR(U, UISC, UGC): def is_VARIATION_SELECTOR(U, UISC, UGC):

View File

@ -153,13 +153,13 @@ struct LookupSegmentArray
first <= last && first <= last &&
valuesZ.sanitize (c, base, last - first + 1)); valuesZ.sanitize (c, base, last - first + 1));
} }
template <typename T2> template <typename ...Ts>
bool sanitize (hb_sanitize_context_t *c, const void *base, T2 user_data) const bool sanitize (hb_sanitize_context_t *c, const void *base, Ts &&...ds) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
return_trace (c->check_struct (this) && return_trace (c->check_struct (this) &&
first <= last && first <= last &&
valuesZ.sanitize (c, base, last - first + 1, user_data)); valuesZ.sanitize (c, base, last - first + 1, hb_forward<Ts> (ds)...));
} }
GlyphID last; /* Last GlyphID in this segment */ GlyphID last; /* Last GlyphID in this segment */

View File

@ -311,14 +311,6 @@ hb_aat_layout_track (const hb_ot_shape_plan_t *plan,
trak.apply (&c); trak.apply (&c);
} }
hb_language_t
_hb_aat_language_get (hb_face_t *face,
unsigned int i)
{
return face->table.ltag->get_language (i);
}
/** /**
* hb_aat_layout_get_feature_types: * hb_aat_layout_get_feature_types:
* @face: a face object * @face: a face object

View File

@ -30,7 +30,7 @@
#include "hb.hh" #include "hb.hh"
#include "hb-ot-shape.hh" #include "hb-ot-shape.hh"
#include "hb-aat-ltag-table.hh"
struct hb_aat_feature_mapping_t struct hb_aat_feature_mapping_t
{ {
@ -77,9 +77,13 @@ hb_aat_layout_track (const hb_ot_shape_plan_t *plan,
hb_font_t *font, hb_font_t *font,
hb_buffer_t *buffer); hb_buffer_t *buffer);
HB_INTERNAL hb_language_t
inline hb_language_t
_hb_aat_language_get (hb_face_t *face, _hb_aat_language_get (hb_face_t *face,
unsigned int i); unsigned int i)
{
return face->table.ltag->get_language (i);
}
#endif /* HB_AAT_LAYOUT_HH */ #endif /* HB_AAT_LAYOUT_HH */

View File

@ -71,28 +71,31 @@ struct
private: private:
/* Pointer-to-member-function. */ /* Pointer-to-member-function. */
template <typename Appl, typename Val> auto template <typename Appl, typename Val1, typename ...Vals> auto
impl (Appl&& a, Val &&v, hb_priority<2>) const HB_AUTO_RETURN impl (Appl&& a, hb_priority<2>, Val1 &&v1, Vals &&...vs) const HB_AUTO_RETURN
(hb_forward<Val> (hb_deref_pointer (v)).*a ()) ((hb_deref_pointer (hb_forward<Val1> (v1)).*hb_forward<Appl> (a)) (hb_forward<Vals> (vs)...))
/* Pointer-to-member. */ /* Pointer-to-member. */
template <typename Appl, typename Val> auto template <typename Appl, typename Val> auto
impl (Appl&& a, Val &&v, hb_priority<1>) const HB_AUTO_RETURN impl (Appl&& a, hb_priority<1>, Val &&v) const HB_AUTO_RETURN
(hb_forward<Val> (hb_deref_pointer (v)).*a) ((hb_deref_pointer (hb_forward<Val> (v))).*hb_forward<Appl> (a))
/* Operator(). */ /* Operator(). */
template <typename Appl, typename Val> auto template <typename Appl, typename ...Vals> auto
impl (Appl&& a, Val &&v, hb_priority<0>) const HB_AUTO_RETURN impl (Appl&& a, hb_priority<0>, Vals &&...vs) const HB_AUTO_RETURN
(hb_deref_pointer (a) (hb_forward<Val> (v))) (hb_deref_pointer (hb_forward<Appl> (a)) (hb_forward<Vals> (vs)...))
public: public:
template <typename Appl, typename Val1, typename ...Vals> auto
impl2 (Appl&& a, hb_priority<2>, Val1 &&v1, Vals &&...vs) const HB_AUTO_RETURN
(hb_deref_pointer (hb_forward<Val1> (v1)).*hb_forward<Appl> (a) (hb_forward<Vals> (vs)...))
template <typename Appl, typename Val> auto template <typename Appl, typename ...Vals> auto
operator () (Appl&& a, Val &&v) const HB_AUTO_RETURN operator () (Appl&& a, Vals &&...vs) const HB_AUTO_RETURN
( (
impl (hb_forward<Appl> (a), impl (hb_forward<Appl> (a),
hb_forward<Val> (v), hb_prioritize,
hb_prioritize) hb_forward<Vals> (vs)...)
) )
} HB_FUNCOBJ (hb_invoke); } HB_FUNCOBJ (hb_invoke);
@ -102,7 +105,7 @@ struct
template <typename Pred, typename Val> auto template <typename Pred, typename Val> auto
impl (Pred&& p, Val &&v, hb_priority<1>) const HB_AUTO_RETURN impl (Pred&& p, Val &&v, hb_priority<1>) const HB_AUTO_RETURN
(hb_deref_pointer (p).has (v)) (hb_deref_pointer (hb_forward<Pred> (p)).has (v))
template <typename Pred, typename Val> auto template <typename Pred, typename Val> auto
impl (Pred&& p, Val &&v, hb_priority<0>) const HB_AUTO_RETURN impl (Pred&& p, Val &&v, hb_priority<0>) const HB_AUTO_RETURN
@ -127,7 +130,7 @@ struct
template <typename Proj, typename Val> auto template <typename Proj, typename Val> auto
impl (Proj&& f, Val &&v, hb_priority<1>) const HB_AUTO_RETURN impl (Proj&& f, Val &&v, hb_priority<1>) const HB_AUTO_RETURN
(hb_deref_pointer (f).get (hb_forward<Val> (v))) (hb_deref_pointer (hb_forward<Proj> (f)).get (hb_forward<Val> (v)))
template <typename Proj, typename Val> auto template <typename Proj, typename Val> auto
impl (Proj&& f, Val &&v, hb_priority<0>) const HB_AUTO_RETURN impl (Proj&& f, Val &&v, hb_priority<0>) const HB_AUTO_RETURN

View File

@ -43,20 +43,19 @@ struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&>
* Constructors. * Constructors.
*/ */
hb_array_t () : arrayZ (nullptr), length (0) {} hb_array_t () : arrayZ (nullptr), length (0) {}
hb_array_t (const hb_array_t<Type> &o) :
hb_iter_with_fallback_t<hb_array_t<Type>, Type&> (),
arrayZ (o.arrayZ), length (o.length) {}
template <typename U = Type, hb_enable_if (hb_is_const (U))>
hb_array_t (const hb_array_t<hb_remove_const<Type> > &o) : arrayZ (o.arrayZ), length (o.length) {}
hb_array_t (Type *array_, unsigned int length_) : arrayZ (array_), length (length_) {} hb_array_t (Type *array_, unsigned int length_) : arrayZ (array_), length (length_) {}
template <unsigned int length_> hb_array_t (Type (&array_)[length_]) : arrayZ (array_), length (length_) {} template <unsigned int length_> hb_array_t (Type (&array_)[length_]) : arrayZ (array_), length (length_) {}
template <typename U = Type, hb_enable_if (hb_is_const (U))> template <typename U,
hb_array_t& operator = (const hb_array_t<hb_remove_const<Type> > &o) hb_enable_if (hb_is_cr_convertible_to(U, Type))>
{ arrayZ = o.arrayZ; length = o.length; return *this; } hb_array_t (const hb_array_t<U> &o) :
hb_array_t& operator = (const hb_array_t &o) hb_iter_with_fallback_t<hb_array_t<Type>, Type&> (),
arrayZ (o.arrayZ), length (o.length) {}
template <typename U,
hb_enable_if (hb_is_cr_convertible_to(U, Type))>
hb_array_t& operator = (const hb_array_t<U> &o)
{ arrayZ = o.arrayZ; length = o.length; return *this; } { arrayZ = o.arrayZ; length = o.length; return *this; }
/* /*
* Iterator implementation. * Iterator implementation.
*/ */
@ -212,12 +211,19 @@ struct hb_sorted_array_t :
static constexpr bool is_sorted_iterator = true; static constexpr bool is_sorted_iterator = true;
hb_sorted_array_t () : hb_array_t<Type> () {} hb_sorted_array_t () : hb_array_t<Type> () {}
hb_sorted_array_t (const hb_array_t<Type> &o) : hb_array_t<Type> (o) {}
template <typename U = Type, hb_enable_if (hb_is_const (U))>
hb_sorted_array_t (const hb_sorted_array_t<hb_remove_const<Type> > &o) : hb_array_t<Type> (o) {}
hb_sorted_array_t (Type *array_, unsigned int length_) : hb_array_t<Type> (array_, length_) {} hb_sorted_array_t (Type *array_, unsigned int length_) : hb_array_t<Type> (array_, length_) {}
template <unsigned int length_> hb_sorted_array_t (Type (&array_)[length_]) : hb_array_t<Type> (array_) {} template <unsigned int length_> hb_sorted_array_t (Type (&array_)[length_]) : hb_array_t<Type> (array_) {}
template <typename U,
hb_enable_if (hb_is_cr_convertible_to(U, Type))>
hb_sorted_array_t (const hb_array_t<U> &o) :
hb_iter_t<hb_sorted_array_t<Type>, Type&> (),
hb_array_t<Type> (o) {}
template <typename U,
hb_enable_if (hb_is_cr_convertible_to(U, Type))>
hb_sorted_array_t& operator = (const hb_array_t<U> &o)
{ hb_array_t<Type> (*this) = o; return *this; }
hb_sorted_array_t<Type> sub_array (unsigned int start_offset, unsigned int *seg_count /* IN/OUT */) const hb_sorted_array_t<Type> sub_array (unsigned int start_offset, unsigned int *seg_count /* IN/OUT */) const
{ return hb_sorted_array_t<Type> (((const hb_array_t<Type> *) (this))->sub_array (start_offset, seg_count)); } { return hb_sorted_array_t<Type> (((const hb_array_t<Type> *) (this))->sub_array (start_offset, seg_count)); }
hb_sorted_array_t<Type> sub_array (unsigned int start_offset, unsigned int seg_count) const hb_sorted_array_t<Type> sub_array (unsigned int start_offset, unsigned int seg_count) const

View File

@ -691,7 +691,7 @@ struct opset_t
case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1: case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1:
case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3: case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3:
env.argStack.push_int ((int16_t)(-(op - OpCode_TwoByteNegInt0) * 256 - env.str_ref[0] - 108)); env.argStack.push_int ((-(int16_t)(op - OpCode_TwoByteNegInt0) * 256 - env.str_ref[0] - 108));
env.str_ref.inc (); env.str_ref.inc ();
break; break;

View File

@ -1160,6 +1160,59 @@ hb_variation_to_string (hb_variation_t *variation,
buf[len] = '\0'; buf[len] = '\0';
} }
/**
* hb_color_get_alpha:
*
*
*
* Since: REPLACEME
*/
uint8_t
(hb_color_get_alpha) (hb_color_t color)
{
return hb_color_get_alpha (color);
}
/**
* hb_color_get_red:
*
*
*
* Since: REPLACEME
*/
uint8_t
(hb_color_get_red) (hb_color_t color)
{
return hb_color_get_red (color);
}
/**
* hb_color_get_green:
*
*
*
* Since: REPLACEME
*/
uint8_t
(hb_color_get_green) (hb_color_t color)
{
return hb_color_get_green (color);
}
/**
* hb_color_get_blue:
*
*
*
* Since: REPLACEME
*/
uint8_t
(hb_color_get_blue) (hb_color_t color)
{
return hb_color_get_blue (color);
}
/* If there is no visibility control, then hb-static.cc will NOT /* If there is no visibility control, then hb-static.cc will NOT
* define anything. Instead, we get it to define one set in here * define anything. Instead, we get it to define one set in here
* only, so only libharfbuzz.so defines them, not other libs. */ * only, so only libharfbuzz.so defines them, not other libs. */

View File

@ -467,39 +467,21 @@ typedef uint32_t hb_color_t;
#define HB_COLOR(b,g,r,a) ((hb_color_t) HB_TAG ((b),(g),(r),(a))) #define HB_COLOR(b,g,r,a) ((hb_color_t) HB_TAG ((b),(g),(r),(a)))
/** HB_EXTERN uint8_t
* hb_color_get_alpha: hb_color_get_alpha (hb_color_t color);
*
*
*
* Since: 2.1.0
*/
#define hb_color_get_alpha(color) ((color) & 0xFF) #define hb_color_get_alpha(color) ((color) & 0xFF)
/**
* hb_color_get_red:
*
*
*
* Since: 2.1.0
*/
#define hb_color_get_red(color) (((color) >> 8) & 0xFF)
/**
* hb_color_get_green:
*
*
*
* Since: 2.1.0
*/
#define hb_color_get_green(color) (((color) >> 16) & 0xFF)
/**
* hb_color_get_blue:
*
*
*
* Since: 2.1.0
*/
#define hb_color_get_blue(color) (((color) >> 24) & 0xFF)
HB_EXTERN uint8_t
hb_color_get_red (hb_color_t color);
#define hb_color_get_red(color) (((color) >> 8) & 0xFF)
HB_EXTERN uint8_t
hb_color_get_green (hb_color_t color);
#define hb_color_get_green(color) (((color) >> 16) & 0xFF)
HB_EXTERN uint8_t
hb_color_get_blue (hb_color_t color);
#define hb_color_get_blue(color) (((color) >> 24) & 0xFF)
HB_END_DECLS HB_END_DECLS

View File

@ -129,8 +129,6 @@ struct hb_iter_t
#define hb_iter_t(Iterable) decltype (hb_declval (Iterable).iter ()) #define hb_iter_t(Iterable) decltype (hb_declval (Iterable).iter ())
/* TODO Change to function-object. */
template <typename> struct hb_array_t; template <typename> struct hb_array_t;
struct struct
@ -209,35 +207,35 @@ template <typename T>
struct hb_is_iterable struct hb_is_iterable
{ {
private: private:
template <typename U> template <typename U>
static auto test (int) -> decltype (hb_declval (U).iter (), hb_true_t ()); static auto impl (hb_priority<1>) -> decltype (hb_declval (U).iter (), hb_true_t ());
template <typename> template <typename>
static hb_false_t test (...); static hb_false_t impl (hb_priority<0>);
public: public:
enum { value = decltype (test<T> (0))::value };
enum { value = decltype (impl<T> (hb_prioritize))::value };
}; };
#define hb_is_iterable(Iterable) hb_is_iterable<Iterable>::value #define hb_is_iterable(Iterable) hb_is_iterable<Iterable>::value
/* TODO Add hb_is_iterable_of(). /* TODO Add hb_is_iterable_of().
* TODO Add random_access / sorted variants. */ * TODO Add random_access / sorted variants. */
/* hb_is_iterator() / hb_is_random_access_iterator() / hb_is_sorted_iterator() */ /* hb_is_iterator() / hb_is_random_access_iterator() / hb_is_sorted_iterator() */
template <typename Iter> template <typename Iter, typename Item>
struct _hb_is_iterator_of static inline char _hb_is_iterator_of (hb_priority<0>, const void *) { return 0; }
{ template <typename Iter,
char operator () (...) { return 0; } typename Item,
template<typename Item> int operator () (hb_iter_t<Iter, Item> *) { return 0; } typename Item2 = typename Iter::item_t,
template<typename Item> int operator () (hb_iter_t<Iter, const Item> *) { return 0; } hb_enable_if (hb_is_cr_convertible_to (Item2, Item))>
template<typename Item> int operator () (hb_iter_t<Iter, Item&> *) { return 0; } static inline int _hb_is_iterator_of (hb_priority<2>, hb_iter_t<Iter, Item2> *) { return 0; }
template<typename Item> int operator () (hb_iter_t<Iter, const Item&> *) { return 0; }
static_assert (sizeof (char) != sizeof (int), "");
};
template<typename Iter, typename Item> template<typename Iter, typename Item>
struct hb_is_iterator_of { enum { struct hb_is_iterator_of { enum {
value = sizeof (int) == sizeof (hb_declval (_hb_is_iterator_of<Iter>) (hb_declval (Iter*))) }; }; value = sizeof (int) == sizeof (_hb_is_iterator_of<Iter, Item> (hb_prioritize, hb_declval (Iter*))) }; };
#define hb_is_iterator_of(Iter, Item) hb_is_iterator_of<Iter, Item>::value #define hb_is_iterator_of(Iter, Item) hb_is_iterator_of<Iter, Item>::value
#define hb_is_iterator(Iter) hb_is_iterator_of (Iter, typename Iter::item_t) #define hb_is_iterator(Iter) hb_is_iterator_of (Iter, typename Iter::item_t)

View File

@ -34,11 +34,9 @@
* hb_hashmap_t * hb_hashmap_t
*/ */
/* TODO if K/V is signed integer, -1 is not a good default.
* Don't know how to get to -MAX using bit work. */
template <typename K, typename V, template <typename K, typename V,
K kINVALID = hb_is_pointer (K) ? 0 : (K) -1, K kINVALID = hb_is_pointer (K) ? 0 : hb_is_signed (K) ? hb_int_min (K) : (K) -1,
V vINVALID = hb_is_pointer (V) ? 0 : (V) -1> V vINVALID = hb_is_pointer (V) ? 0 : hb_is_signed (V) ? hb_int_min (V) : (V) -1>
struct hb_hashmap_t struct hb_hashmap_t
{ {
HB_DELETE_COPY_ASSIGN (hb_hashmap_t); HB_DELETE_COPY_ASSIGN (hb_hashmap_t);
@ -122,7 +120,7 @@ struct hb_hashmap_t
return false; return false;
} }
+ hb_iter (new_items, new_size) + hb_iter (new_items, new_size)
| hb_apply ([] (item_t &_) { _.clear (); }) /* TODO make pointer-to-methods invokable. */ | hb_apply (&item_t::clear)
; ;
unsigned int old_size = mask + 1; unsigned int old_size = mask + 1;
@ -193,7 +191,7 @@ struct hb_hashmap_t
return; return;
if (items) if (items)
+ hb_iter (items, mask + 1) + hb_iter (items, mask + 1)
| hb_apply ([] (item_t &_) { _.clear (); }) /* TODO make pointer-to-methods invokable. */ | hb_apply (&item_t::clear)
; ;
population = occupancy = 0; population = occupancy = 0;

View File

@ -65,6 +65,9 @@ template <> struct hb_priority<0> {};
#define HB_FUNCOBJ(x) static_const x HB_UNUSED #define HB_FUNCOBJ(x) static_const x HB_UNUSED
template <typename T> struct hb_match_identity { typedef T type; };
template <typename T> using hb_type_identity = typename hb_match_identity<T>::type;
struct struct
{ {
template <typename T> template <typename T>
@ -96,6 +99,14 @@ template <typename T> struct hb_match_pointer<T *> { typedef T type; enum { valu
template <typename T> using hb_remove_pointer = typename hb_match_pointer<T>::type; template <typename T> using hb_remove_pointer = typename hb_match_pointer<T>::type;
#define hb_is_pointer(T) hb_match_pointer<T>::value #define hb_is_pointer(T) hb_match_pointer<T>::value
/* TODO Add feature-parity to std::decay. */
template <typename T> using hb_decay = hb_remove_const<hb_remove_reference<T>>;
#define hb_is_cr_convertible_to(A, B) ( \
hb_is_same (hb_decay<A>, hb_decay<B>) && \
hb_is_const (A) <= hb_is_const (B) && \
hb_is_reference (A) >= hb_is_reference (B))
/* std::move and std::forward */ /* std::move and std::forward */
@ -127,17 +138,25 @@ template <typename T> struct hb_is_same<T, T> : hb_true_t {};
#define hb_is_same(T, T2) hb_is_same<T, T2>::value #define hb_is_same(T, T2) hb_is_same<T, T2>::value
template <typename T> struct hb_is_signed; template <typename T> struct hb_is_signed;
/* https://github.com/harfbuzz/harfbuzz/issues/1535 */ template <> struct hb_is_signed<char> { enum { value = CHAR_MIN < 0 }; };
template <> struct hb_is_signed<int8_t> { enum { value = true }; }; template <> struct hb_is_signed<signed char> { enum { value = true }; };
template <> struct hb_is_signed<int16_t> { enum { value = true }; }; template <> struct hb_is_signed<unsigned char> { enum { value = false }; };
template <> struct hb_is_signed<int32_t> { enum { value = true }; }; template <> struct hb_is_signed<signed short> { enum { value = true }; };
template <> struct hb_is_signed<int64_t> { enum { value = true }; }; template <> struct hb_is_signed<unsigned short> { enum { value = false }; };
template <> struct hb_is_signed<uint8_t> { enum { value = false }; }; template <> struct hb_is_signed<signed int> { enum { value = true }; };
template <> struct hb_is_signed<uint16_t> { enum { value = false }; }; template <> struct hb_is_signed<unsigned int> { enum { value = false }; };
template <> struct hb_is_signed<uint32_t> { enum { value = false }; }; template <> struct hb_is_signed<signed long> { enum { value = true }; };
template <> struct hb_is_signed<uint64_t> { enum { value = false }; }; template <> struct hb_is_signed<unsigned long> { enum { value = false }; };
template <> struct hb_is_signed<signed long long> { enum { value = true }; };
template <> struct hb_is_signed<unsigned long long> { enum { value = false }; };
#define hb_is_signed(T) hb_is_signed<T>::value #define hb_is_signed(T) hb_is_signed<T>::value
template <typename T> struct hb_int_min { static constexpr T value = 0; };
template <> struct hb_int_min<char> { static constexpr char value = CHAR_MIN; };
template <> struct hb_int_min<int> { static constexpr int value = INT_MIN; };
template <> struct hb_int_min<long> { static constexpr long value = LONG_MIN; };
#define hb_int_min(T) hb_int_min<T>::value
template <bool is_signed> struct hb_signedness_int; template <bool is_signed> struct hb_signedness_int;
template <> struct hb_signedness_int<false> { typedef unsigned int value; }; template <> struct hb_signedness_int<false> { typedef unsigned int value; };
template <> struct hb_signedness_int<true> { typedef signed int value; }; template <> struct hb_signedness_int<true> { typedef signed int value; };

View File

@ -127,8 +127,6 @@ typedef int hb_mutex_impl_t;
struct hb_mutex_t struct hb_mutex_t
{ {
/* TODO Add tracing. */
hb_mutex_impl_t m; hb_mutex_impl_t m;
void init () { hb_mutex_impl_init (&m); } void init () { hb_mutex_impl_init (&m); }

View File

@ -182,7 +182,7 @@ struct Offset : Type
void *serialize (hb_serialize_context_t *c, const void *base) void *serialize (hb_serialize_context_t *c, const void *base)
{ {
void *t = c->start_embed<void> (); void *t = c->start_embed<void> ();
*this = (char *) t - (char *) base; /* TODO(serialize) Overflow? */ c->check_assign (*this, (unsigned) ((char *) t - (char *) base));
return t; return t;
} }
@ -284,8 +284,8 @@ struct OffsetTo : Offset<OffsetType, has_null>
return * (Type *) Offset<OffsetType>::serialize (c, base); return * (Type *) Offset<OffsetType>::serialize (c, base);
} }
template <typename T> template <typename T, typename ...Ts>
bool serialize_subset (hb_subset_context_t *c, const T &src, const void *base) bool serialize_subset (hb_subset_context_t *c, const T &src, const void *base, Ts &&...ds)
{ {
*this = 0; *this = 0;
if (has_null && &src == &Null (T)) if (has_null && &src == &Null (T))
@ -295,7 +295,7 @@ struct OffsetTo : Offset<OffsetType, has_null>
s->push (); s->push ();
bool ret = src.subset (c); bool ret = src.subset (c, hb_forward<Ts> (ds)...);
if (ret || !has_null) if (ret || !has_null)
s->add_link (*this, s->pop_pack (), base); s->add_link (*this, s->pop_pack (), base);
@ -314,39 +314,13 @@ struct OffsetTo : Offset<OffsetType, has_null>
return_trace (true); return_trace (true);
} }
bool sanitize (hb_sanitize_context_t *c, const void *base) const template <typename ...Ts>
bool sanitize (hb_sanitize_context_t *c, const void *base, Ts &&...ds) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
return_trace (sanitize_shallow (c, base) && return_trace (sanitize_shallow (c, base) &&
(this->is_null () || (this->is_null () ||
StructAtOffset<Type> (base, *this).sanitize (c) || StructAtOffset<Type> (base, *this).sanitize (c, hb_forward<Ts> (ds)...) ||
neuter (c)));
}
template <typename T1>
bool sanitize (hb_sanitize_context_t *c, const void *base, T1 d1) const
{
TRACE_SANITIZE (this);
return_trace (sanitize_shallow (c, base) &&
(this->is_null () ||
StructAtOffset<Type> (base, *this).sanitize (c, d1) ||
neuter (c)));
}
template <typename T1, typename T2>
bool sanitize (hb_sanitize_context_t *c, const void *base, T1 d1, T2 d2) const
{
TRACE_SANITIZE (this);
return_trace (sanitize_shallow (c, base) &&
(this->is_null () ||
StructAtOffset<Type> (base, *this).sanitize (c, d1, d2) ||
neuter (c)));
}
template <typename T1, typename T2, typename T3>
bool sanitize (hb_sanitize_context_t *c, const void *base, T1 d1, T2 d2, T3 d3) const
{
TRACE_SANITIZE (this);
return_trace (sanitize_shallow (c, base) &&
(this->is_null () ||
StructAtOffset<Type> (base, *this).sanitize (c, d1, d2, d3) ||
neuter (c))); neuter (c)));
} }
@ -430,29 +404,26 @@ struct UnsizedArrayOf
* we do not need to call their sanitize() as we already did * we do not need to call their sanitize() as we already did
* a bound check on the aggregate array size. We just include * a bound check on the aggregate array size. We just include
* a small unreachable expression to make sure the structs * a small unreachable expression to make sure the structs
* pointed to do have a simple sanitize(), ie. they do not * pointed to do have a simple sanitize() as well as an
* assignment opreator. This ensures that they do not
* reference other structs via offsets. * reference other structs via offsets.
*/ */
(void) (false && arrayZ[0].sanitize (c)); if (false)
{
arrayZ[0].sanitize (c);
Type v;
v = arrayZ[0];
}
return_trace (true); return_trace (true);
} }
bool sanitize (hb_sanitize_context_t *c, unsigned int count, const void *base) const template <typename ...Ts>
bool sanitize (hb_sanitize_context_t *c, unsigned int count, Ts &&...ds) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
if (unlikely (!sanitize_shallow (c, count))) return_trace (false); if (unlikely (!sanitize_shallow (c, count))) return_trace (false);
for (unsigned int i = 0; i < count; i++) for (unsigned int i = 0; i < count; i++)
if (unlikely (!arrayZ[i].sanitize (c, base))) if (unlikely (!arrayZ[i].sanitize (c, hb_forward<Ts> (ds)...)))
return_trace (false);
return_trace (true);
}
template <typename T>
bool sanitize (hb_sanitize_context_t *c, unsigned int count, const void *base, T user_data) const
{
TRACE_SANITIZE (this);
if (unlikely (!sanitize_shallow (c, count))) return_trace (false);
for (unsigned int i = 0; i < count; i++)
if (unlikely (!arrayZ[i].sanitize (c, base, user_data)))
return_trace (false); return_trace (false);
return_trace (true); return_trace (true);
} }
@ -492,17 +463,12 @@ struct UnsizedOffsetListOf : UnsizedOffsetArrayOf<Type, OffsetType, has_null>
return this+*p; return this+*p;
} }
template <typename ...Ts>
bool sanitize (hb_sanitize_context_t *c, unsigned int count) const bool sanitize (hb_sanitize_context_t *c, unsigned int count, Ts &&...ds) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
return_trace ((UnsizedOffsetArrayOf<Type, OffsetType, has_null>::sanitize (c, count, this))); return_trace ((UnsizedOffsetArrayOf<Type, OffsetType, has_null>
} ::sanitize (c, count, this, hb_forward<Ts> (ds)...)));
template <typename T>
bool sanitize (hb_sanitize_context_t *c, unsigned int count, T user_data) const
{
TRACE_SANITIZE (this);
return_trace ((UnsizedOffsetArrayOf<Type, OffsetType, has_null>::sanitize (c, count, this, user_data)));
} }
}; };
@ -582,7 +548,7 @@ struct ArrayOf
{ {
TRACE_SERIALIZE (this); TRACE_SERIALIZE (this);
if (unlikely (!c->extend_min (*this))) return_trace (false); if (unlikely (!c->extend_min (*this))) return_trace (false);
len = items_len; /* TODO(serialize) Overflow? */ c->check_assign (len, items_len);
if (unlikely (!c->extend (*this))) return_trace (false); if (unlikely (!c->extend (*this))) return_trace (false);
return_trace (true); return_trace (true);
} }
@ -622,24 +588,14 @@ struct ArrayOf
return_trace (true); return_trace (true);
} }
bool sanitize (hb_sanitize_context_t *c, const void *base) const template <typename ...Ts>
bool sanitize (hb_sanitize_context_t *c, Ts &&...ds) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
if (unlikely (!sanitize_shallow (c))) return_trace (false); if (unlikely (!sanitize_shallow (c))) return_trace (false);
unsigned int count = len; unsigned int count = len;
for (unsigned int i = 0; i < count; i++) for (unsigned int i = 0; i < count; i++)
if (unlikely (!arrayZ[i].sanitize (c, base))) if (unlikely (!arrayZ[i].sanitize (c, hb_forward<Ts> (ds)...)))
return_trace (false);
return_trace (true);
}
template <typename T>
bool sanitize (hb_sanitize_context_t *c, const void *base, T user_data) const
{
TRACE_SANITIZE (this);
if (unlikely (!sanitize_shallow (c))) return_trace (false);
unsigned int count = len;
for (unsigned int i = 0; i < count; i++)
if (unlikely (!arrayZ[i].sanitize (c, base, user_data)))
return_trace (false); return_trace (false);
return_trace (true); return_trace (true);
} }
@ -706,16 +662,11 @@ struct OffsetListOf : OffsetArrayOf<Type>
return_trace (true); return_trace (true);
} }
bool sanitize (hb_sanitize_context_t *c) const template <typename ...Ts>
bool sanitize (hb_sanitize_context_t *c, Ts &&...ds) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
return_trace (OffsetArrayOf<Type>::sanitize (c, this)); return_trace (OffsetArrayOf<Type>::sanitize (c, this, hb_forward<Ts> (ds)...));
}
template <typename T>
bool sanitize (hb_sanitize_context_t *c, T user_data) const
{
TRACE_SANITIZE (this);
return_trace (OffsetArrayOf<Type>::sanitize (c, this, user_data));
} }
}; };
@ -747,7 +698,7 @@ struct HeadlessArrayOf
{ {
TRACE_SERIALIZE (this); TRACE_SERIALIZE (this);
if (unlikely (!c->extend_min (*this))) return_trace (false); if (unlikely (!c->extend_min (*this))) return_trace (false);
lenP1 = items.length + 1; /* TODO(serialize) Overflow? */ c->check_assign (lenP1, items.length + 1);
if (unlikely (!c->extend (*this))) return_trace (false); if (unlikely (!c->extend (*this))) return_trace (false);
for (unsigned int i = 0; i < items.length; i++) for (unsigned int i = 0; i < items.length; i++)
arrayZ[i] = items[i]; arrayZ[i] = items[i];
@ -763,10 +714,16 @@ struct HeadlessArrayOf
* we do not need to call their sanitize() as we already did * we do not need to call their sanitize() as we already did
* a bound check on the aggregate array size. We just include * a bound check on the aggregate array size. We just include
* a small unreachable expression to make sure the structs * a small unreachable expression to make sure the structs
* pointed to do have a simple sanitize(), ie. they do not * pointed to do have a simple sanitize() as well as an
* assignment opreator. This ensures that they do not
* reference other structs via offsets. * reference other structs via offsets.
*/ */
(void) (false && arrayZ[0].sanitize (c)); if (false)
{
arrayZ[0].sanitize (c);
Type v;
v = arrayZ[0];
}
return_trace (true); return_trace (true);
} }
@ -807,14 +764,14 @@ struct ArrayOfM1
unsigned int get_size () const unsigned int get_size () const
{ return lenM1.static_size + (lenM1 + 1) * Type::static_size; } { return lenM1.static_size + (lenM1 + 1) * Type::static_size; }
template <typename T> template <typename ...Ts>
bool sanitize (hb_sanitize_context_t *c, const void *base, T user_data) const bool sanitize (hb_sanitize_context_t *c, Ts &&...ds) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
if (unlikely (!sanitize_shallow (c))) return_trace (false); if (unlikely (!sanitize_shallow (c))) return_trace (false);
unsigned int count = lenM1 + 1; unsigned int count = lenM1 + 1;
for (unsigned int i = 0; i < count; i++) for (unsigned int i = 0; i < count; i++)
if (unlikely (!arrayZ[i].sanitize (c, base, user_data))) if (unlikely (!arrayZ[i].sanitize (c, hb_forward<Ts> (ds)...)))
return_trace (false); return_trace (false);
return_trace (true); return_trace (true);
} }
@ -999,31 +956,27 @@ struct VarSizedBinSearchArrayOf
* we do not need to call their sanitize() as we already did * we do not need to call their sanitize() as we already did
* a bound check on the aggregate array size. We just include * a bound check on the aggregate array size. We just include
* a small unreachable expression to make sure the structs * a small unreachable expression to make sure the structs
* pointed to do have a simple sanitize(), ie. they do not * pointed to do have a simple sanitize() as well as an
* assignment opreator. This ensures that they do not
* reference other structs via offsets. * reference other structs via offsets.
*/ */
(void) (false && StructAtOffset<Type> (&bytesZ, 0).sanitize (c)); if (false)
{
(*this)[0].sanitize (c);
Type v;
v = (*this)[0];
}
return_trace (true); return_trace (true);
} }
bool sanitize (hb_sanitize_context_t *c, const void *base) const template <typename ...Ts>
bool sanitize (hb_sanitize_context_t *c, Ts &&...ds) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
if (unlikely (!sanitize_shallow (c))) return_trace (false); if (unlikely (!sanitize_shallow (c))) return_trace (false);
unsigned int count = get_length (); unsigned int count = get_length ();
for (unsigned int i = 0; i < count; i++) for (unsigned int i = 0; i < count; i++)
if (unlikely (!(*this)[i].sanitize (c, base))) if (unlikely (!(*this)[i].sanitize (c, hb_forward<Ts> (ds)...)))
return_trace (false);
return_trace (true);
}
template <typename T>
bool sanitize (hb_sanitize_context_t *c, const void *base, T user_data) const
{
TRACE_SANITIZE (this);
if (unlikely (!sanitize_shallow (c))) return_trace (false);
unsigned int count = get_length ();
for (unsigned int i = 0; i < count; i++)
if (unlikely (!(*this)[i].sanitize (c, base, user_data)))
return_trace (false); return_trace (false);
return_trace (true); return_trace (true);
} }

View File

@ -165,8 +165,8 @@ struct bounds_t
{ {
void init () void init ()
{ {
min.set_int (0x7FFFFFFF, 0x7FFFFFFF); min.set_int (INT_MAX, INT_MAX);
max.set_int (-0x80000000, -0x80000000); max.set_int (INT_MIN, INT_MIN);
} }
void update (const point_t &pt) void update (const point_t &pt)

View File

@ -110,7 +110,8 @@ struct Encoding1 {
{ {
if (glyph <= ranges[i].nLeft) if (glyph <= ranges[i].nLeft)
{ {
return (hb_codepoint_t)ranges[i].first + glyph; hb_codepoint_t code = (hb_codepoint_t) ranges[i].first + glyph;
return (likely (code < 0x100) ? code: CFF_UNDEF_CODE);
} }
glyph -= (ranges[i].nLeft + 1); glyph -= (ranges[i].nLeft + 1);
} }

View File

@ -34,10 +34,10 @@ struct extents_param_t
void init () void init ()
{ {
path_open = false; path_open = false;
min_x.set_int (0x7FFFFFFF); min_x.set_int (INT_MAX);
min_y.set_int (0x7FFFFFFF); min_y.set_int (INT_MAX);
max_x.set_int (-0x80000000); max_x.set_int (INT_MIN);
max_y.set_int (-0x80000000); max_y.set_int (INT_MIN);
} }
void start_path () { path_open = true; } void start_path () { path_open = true; }

View File

@ -85,12 +85,12 @@ struct SingleSubstFormat1
bool serialize (hb_serialize_context_t *c, bool serialize (hb_serialize_context_t *c,
hb_sorted_array_t<const GlyphID> glyphs, hb_sorted_array_t<const GlyphID> glyphs,
int delta) unsigned delta)
{ {
TRACE_SERIALIZE (this); TRACE_SERIALIZE (this);
if (unlikely (!c->extend_min (*this))) return_trace (false); if (unlikely (!c->extend_min (*this))) return_trace (false);
if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs))) return_trace (false); if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs))) return_trace (false);
deltaGlyphID = delta; /* TODO(serialize) overflow? */ c->check_assign (deltaGlyphID, delta);
return_trace (true); return_trace (true);
} }
@ -127,8 +127,8 @@ struct SingleSubstFormat1
OffsetTo<Coverage> OffsetTo<Coverage>
coverage; /* Offset to Coverage table--from coverage; /* Offset to Coverage table--from
* beginning of Substitution table */ * beginning of Substitution table */
HBINT16 deltaGlyphID; /* Add to original GlyphID to get HBUINT16 deltaGlyphID; /* Add to original GlyphID to get
* substitute GlyphID */ * substitute GlyphID, modulo 0x10000 */
public: public:
DEFINE_SIZE_STATIC (6); DEFINE_SIZE_STATIC (6);
}; };
@ -231,15 +231,14 @@ struct SingleSubst
{ {
TRACE_SERIALIZE (this); TRACE_SERIALIZE (this);
if (unlikely (!c->extend_min (u.format))) return_trace (false); if (unlikely (!c->extend_min (u.format))) return_trace (false);
unsigned int format = 2; unsigned format = 2;
int delta = 0; unsigned delta = 0;
if (glyphs.length) if (glyphs.length)
{ {
format = 1; format = 1;
/* TODO(serialize) check for wrap-around */ delta = (unsigned) (substitutes[0] - glyphs[0]) & 0xFFFF;
delta = substitutes[0] - glyphs[0];
for (unsigned int i = 1; i < glyphs.length; i++) for (unsigned int i = 1; i < glyphs.length; i++)
if (delta != (int) (substitutes[i] - glyphs[i])) { if (delta != ((unsigned) (substitutes[i] - glyphs[i]) & 0xFFFF)) {
format = 2; format = 2;
break; break;
} }

View File

@ -286,7 +286,7 @@ struct hb_ot_apply_context_t :
}; };
may_match_t may_match (const hb_glyph_info_t &info, may_match_t may_match (const hb_glyph_info_t &info,
const HBUINT16 *glyph_data) const const HBUINT16 *glyph_data) const
{ {
if (!(info.mask & mask) || if (!(info.mask & mask) ||
(syllable && syllable != info.syllable ())) (syllable && syllable != info.syllable ()))

View File

@ -219,28 +219,28 @@ hb_ot_map_builder_t::compile (hb_ot_map_t &m,
continue; /* Feature disabled, or not enough bits. */ continue; /* Feature disabled, or not enough bits. */
hb_bool_t found = false; bool found = false;
unsigned int feature_index[2]; unsigned int feature_index[2];
for (unsigned int table_index = 0; table_index < 2; table_index++) for (unsigned int table_index = 0; table_index < 2; table_index++)
{ {
if (required_feature_tag[table_index] == info->tag) if (required_feature_tag[table_index] == info->tag)
required_feature_stage[table_index] = info->stage[table_index]; required_feature_stage[table_index] = info->stage[table_index];
found |= hb_ot_layout_language_find_feature (face, found |= (bool) hb_ot_layout_language_find_feature (face,
table_tags[table_index], table_tags[table_index],
script_index[table_index], script_index[table_index],
language_index[table_index], language_index[table_index],
info->tag, info->tag,
&feature_index[table_index]); &feature_index[table_index]);
} }
if (!found && (info->flags & F_GLOBAL_SEARCH)) if (!found && (info->flags & F_GLOBAL_SEARCH))
{ {
for (unsigned int table_index = 0; table_index < 2; table_index++) for (unsigned int table_index = 0; table_index < 2; table_index++)
{ {
found |= hb_ot_layout_table_find_feature (face, found |= (bool) hb_ot_layout_table_find_feature (face,
table_tags[table_index], table_tags[table_index],
info->tag, info->tag,
&feature_index[table_index]); &feature_index[table_index]);
} }
} }
if (!found && !(info->flags & F_HAS_FALLBACK)) if (!found && !(info->flags & F_HAS_FALLBACK))

View File

@ -24,6 +24,9 @@
* Google Author(s): Behdad Esfahbod * Google Author(s): Behdad Esfahbod
*/ */
#ifndef HB_OT_NAME_LANGUAGE_STATIC_HH
#define HB_OT_NAME_LANGUAGE_STATIC_HH
#include "hb-ot-name-language.hh" #include "hb-ot-name-language.hh"
/* Following two tables were generated by joining FreeType, FontConfig, /* Following two tables were generated by joining FreeType, FontConfig,
@ -455,3 +458,5 @@ _hb_ot_name_language_for_mac_code (unsigned int code)
hb_mac_language_map, hb_mac_language_map,
ARRAY_LENGTH (hb_mac_language_map)); ARRAY_LENGTH (hb_mac_language_map));
} }
#endif /* HB_OT_NAME_LANGUAGE_STATIC_HH */

View File

@ -158,6 +158,150 @@ struct name
unsigned int get_size () const unsigned int get_size () const
{ return min_size + count * nameRecordZ.item_size; } { return min_size + count * nameRecordZ.item_size; }
void get_subsetted_ids (const name *source_name,
const hb_subset_plan_t *plan,
hb_vector_t<unsigned int>& name_record_idx_to_retain) const
{
for(unsigned int i = 0; i < count; i++)
{
if (format == 0 && (unsigned int) source_name->nameRecordZ[i].nameID > 25)
continue;
if (!hb_set_is_empty (plan->name_ids) &&
!hb_set_has (plan->name_ids, source_name->nameRecordZ[i].nameID))
continue;
name_record_idx_to_retain.push (i);
}
}
bool serialize_name_record (hb_serialize_context_t *c,
const name *source_name,
const hb_vector_t<unsigned int>& name_record_idx_to_retain)
{
for (unsigned int i = 0; i < name_record_idx_to_retain.length; i++)
{
unsigned int idx = name_record_idx_to_retain[i];
if (unlikely (idx >= source_name->count))
{
DEBUG_MSG (SUBSET, nullptr, "Invalid index: %d.", idx);
return false;
}
c->push<NameRecord> ();
NameRecord *p = c->embed<NameRecord> (source_name->nameRecordZ[idx]);
if (!p)
return false;
p->offset = 0;
}
return true;
}
bool serialize_strings (hb_serialize_context_t *c,
const name *source_name,
const hb_subset_plan_t *plan,
const hb_vector_t<unsigned int>& name_record_idx_to_retain)
{
hb_face_t *face = plan->source;
accelerator_t acc;
acc.init (face);
for (unsigned int i = 0; i < name_record_idx_to_retain.length; i++)
{
unsigned int idx = name_record_idx_to_retain[i];
unsigned int size = acc.get_name (idx).get_size ();
c->push<char> ();
char *new_pos = c->allocate_size<char> (size);
if (unlikely (new_pos == nullptr))
{
acc.fini ();
DEBUG_MSG (SUBSET, nullptr, "Couldn't allocate enough space for Name string: %u.",
size);
return false;
}
const HBUINT8* source_string_pool = (source_name + source_name->stringOffset).arrayZ;
unsigned int name_record_offset = source_name->nameRecordZ[idx].offset;
memcpy (new_pos, source_string_pool + name_record_offset, size);
}
acc.fini ();
return true;
}
bool pack_record_and_strings (name *dest_name_unpacked,
hb_serialize_context_t *c,
unsigned length)
{
hb_hashmap_t<unsigned, unsigned> id_str_idx_map;
for (int i = length-1; i >= 0; i--)
{
unsigned objidx = c->pop_pack ();
id_str_idx_map.set ((unsigned)i, objidx);
}
const void *base = & (dest_name_unpacked->nameRecordZ[length]);
for (int i = length-1; i >= 0; i--)
{
unsigned str_idx = id_str_idx_map.get ((unsigned)i);
NameRecord& namerecord = dest_name_unpacked->nameRecordZ[i];
c->add_link<HBUINT16> (namerecord.offset, str_idx, base);
c->pop_pack ();
}
if (c->in_error ())
return false;
return true;
}
bool serialize (hb_serialize_context_t *c,
const name *source_name,
const hb_subset_plan_t *plan,
const hb_vector_t<unsigned int>& name_record_idx_to_retain)
{
TRACE_SERIALIZE (this);
if (unlikely (!c->extend_min ((*this)))) return_trace (false);
this->format = source_name->format;
this->count = name_record_idx_to_retain.length;
this->stringOffset = min_size + name_record_idx_to_retain.length * NameRecord::static_size;
if (!serialize_name_record (c, source_name, name_record_idx_to_retain))
return_trace (false);
if (!serialize_strings (c, source_name, plan, name_record_idx_to_retain))
return_trace (false);
if (!pack_record_and_strings (this, c, name_record_idx_to_retain.length))
return_trace (false);
return_trace (true);
}
bool subset (hb_subset_context_t *c) const
{
hb_subset_plan_t *plan = c->plan;
hb_vector_t<unsigned int> name_record_idx_to_retain;
get_subsetted_ids (this, plan, name_record_idx_to_retain);
hb_serialize_context_t *serializer = c->serializer;
name *name_prime = serializer->start_embed<name> ();
if (!name_prime || !name_prime->serialize (serializer, this, plan, name_record_idx_to_retain))
{
DEBUG_MSG (SUBSET, nullptr, "Failed to serialize write new name.");
return false;
}
return true;
}
bool sanitize_records (hb_sanitize_context_t *c) const bool sanitize_records (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);

View File

@ -1008,7 +1008,6 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED,
ginfo.cluster = buffer->cur().cluster; ginfo.cluster = buffer->cur().cluster;
ginfo.mask = buffer->cur().mask; ginfo.mask = buffer->cur().mask;
ginfo.syllable() = buffer->cur().syllable(); ginfo.syllable() = buffer->cur().syllable();
/* TODO Set glyph_props? */
/* Insert dottedcircle after possible Repha. */ /* Insert dottedcircle after possible Repha. */
while (buffer->idx < buffer->len && buffer->successful && while (buffer->idx < buffer->len && buffer->successful &&

View File

@ -368,7 +368,8 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED,
if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE))
return; return;
/* Note: This loop is extra overhead, but should not be measurable. */ /* Note: This loop is extra overhead, but should not be measurable.
* TODO Use a buffer scratch flag to remove the loop. */
bool has_broken_syllables = false; bool has_broken_syllables = false;
unsigned int count = buffer->len; unsigned int count = buffer->len;
hb_glyph_info_t *info = buffer->info; hb_glyph_info_t *info = buffer->info;
@ -407,7 +408,6 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED,
ginfo.cluster = buffer->cur().cluster; ginfo.cluster = buffer->cur().cluster;
ginfo.mask = buffer->cur().mask; ginfo.mask = buffer->cur().mask;
ginfo.syllable() = buffer->cur().syllable(); ginfo.syllable() = buffer->cur().syllable();
/* TODO Set glyph_props? */
/* Insert dottedcircle after possible Repha. */ /* Insert dottedcircle after possible Repha. */
while (buffer->idx < buffer->len && buffer->successful && while (buffer->idx < buffer->len && buffer->successful &&

View File

@ -301,7 +301,8 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED,
if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE))
return; return;
/* Note: This loop is extra overhead, but should not be measurable. */ /* Note: This loop is extra overhead, but should not be measurable.
* TODO Use a buffer scratch flag to remove the loop. */
bool has_broken_syllables = false; bool has_broken_syllables = false;
unsigned int count = buffer->len; unsigned int count = buffer->len;
hb_glyph_info_t *info = buffer->info; hb_glyph_info_t *info = buffer->info;

View File

@ -318,8 +318,8 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = {
/* 1B20 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 1B20 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
/* 1B30 */ B, B, B, B, CMAbv, VPst, VAbv, VAbv, VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, VPre, VPre, /* 1B30 */ B, B, B, B, CMAbv, VPst, VAbv, VAbv, VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, VPre, VPre,
/* 1B40 */ VPst, VPst, VAbv, VAbv, H, B, B, B, B, B, B, B, O, O, O, O, /* 1B40 */ VPst, VPst, VAbv, VAbv, H, B, B, B, B, B, B, B, O, O, O, O,
/* 1B50 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, /* 1B50 */ B, B, B, B, B, B, B, B, B, B, O, GB, GB, O, O, GB,
/* 1B60 */ O, O, O, O, O, O, O, O, O, O, O, SMAbv, SMBlw, SMAbv, SMAbv, SMAbv, /* 1B60 */ O, O, GB, O, O, O, O, O, GB, O, O, SMAbv, SMBlw, SMAbv, SMAbv, SMAbv,
/* 1B70 */ SMAbv, SMAbv, SMAbv, SMAbv, O, O, O, O, O, O, O, O, O, O, O, O, /* 1B70 */ SMAbv, SMAbv, SMAbv, SMAbv, O, O, O, O, O, O, O, O, O, O, O, O,
/* Sundanese */ /* Sundanese */

View File

@ -526,7 +526,8 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED,
if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE))
return; return;
/* Note: This loop is extra overhead, but should not be measurable. */ /* Note: This loop is extra overhead, but should not be measurable.
* TODO Use a buffer scratch flag to remove the loop. */
bool has_broken_syllables = false; bool has_broken_syllables = false;
unsigned int count = buffer->len; unsigned int count = buffer->len;
hb_glyph_info_t *info = buffer->info; hb_glyph_info_t *info = buffer->info;
@ -560,7 +561,6 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED,
ginfo.cluster = buffer->cur().cluster; ginfo.cluster = buffer->cur().cluster;
ginfo.mask = buffer->cur().mask; ginfo.mask = buffer->cur().mask;
ginfo.syllable() = buffer->cur().syllable(); ginfo.syllable() = buffer->cur().syllable();
/* TODO Set glyph_props? */
/* Insert dottedcircle after possible Repha. */ /* Insert dottedcircle after possible Repha. */
while (buffer->idx < buffer->len && buffer->successful && while (buffer->idx < buffer->len && buffer->successful &&

View File

@ -120,17 +120,23 @@ struct hb_serialize_context_t
this->packed.push (nullptr); this->packed.push (nullptr);
} }
bool propagate_error (bool e) bool check_success (bool success)
{ return this->successful = this->successful && e; } { return this->successful && (success || (err_other_error (), false)); }
template <typename T> bool propagate_error (const T &obj)
{ return this->successful = this->successful && !obj.in_error (); } template <typename T1, typename T2>
template <typename T> bool propagate_error (const T *obj) bool check_equal (T1 &&v1, T2 &&v2)
{ return this->successful = this->successful && !obj->in_error (); } { return check_success (v1 == v2); }
template <typename T1, typename T2> bool propagate_error (T1 &&o1, T2 &&o2)
{ return propagate_error (o1) && propagate_error (o2); } template <typename T1, typename T2>
template <typename T1, typename T2, typename T3> bool check_assign (T1 &v1, T2 &&v2)
bool propagate_error (T1 &&o1, T2 &&o2, T3 &&o3) { return check_equal (v1 = v2, v2); }
{ return propagate_error (o1) && propagate_error (o2, o3); }
template <typename T> bool propagate_error (T &&obj)
{ return check_success (!hb_deref_pointer (obj).in_error ()); }
template <typename T1, typename... Ts> bool propagate_error (T1 &&o1, Ts &&...os)
{ return propagate_error (hb_forward<T1> (o1)) &&
propagate_error (hb_forward<Ts> (os)...); }
/* To be called around main operation. */ /* To be called around main operation. */
template <typename Type> template <typename Type>
@ -172,7 +178,7 @@ struct hb_serialize_context_t
{ {
object_t *obj = object_pool.alloc (); object_t *obj = object_pool.alloc ();
if (unlikely (!obj)) if (unlikely (!obj))
propagate_error (false); check_success (false);
else else
{ {
obj->head = head; obj->head = head;
@ -272,7 +278,7 @@ struct hb_serialize_context_t
auto& link = *current->links.push (); auto& link = *current->links.push ();
link.is_wide = sizeof (T) == 4; link.is_wide = sizeof (T) == 4;
link.position = (const char *) &ofs - (const char *) base; link.position = (const char *) &ofs - current->head;
link.bias = (const char *) base - current->head; link.bias = (const char *) base - current->head;
link.objidx = objidx; link.objidx = objidx;
} }
@ -294,14 +300,14 @@ struct hb_serialize_context_t
if (link.is_wide) if (link.is_wide)
{ {
auto &off = * ((BEInt<uint32_t, 4> *) (parent.head + link.position)); auto &off = * ((BEInt<uint32_t, 4> *) (parent.head + link.position));
off = offset; assert (0 == off);
propagate_error (off == offset); check_assign (off, offset);
} }
else else
{ {
auto &off = * ((BEInt<uint16_t, 2> *) (parent.head + link.position)); auto &off = * ((BEInt<uint16_t, 2> *) (parent.head + link.position));
off = offset; assert (0 == off);
propagate_error (off == offset); check_assign (off, offset);
} }
} }
} }
@ -323,8 +329,9 @@ struct hb_serialize_context_t
return ret; return ret;
} }
void /* Following two functions exist to allow setting breakpoint on. */
err_ran_out_of_room () { this->ran_out_of_room = true; } void err_ran_out_of_room () { this->ran_out_of_room = true; }
void err_other_error () { this->successful = false; }
template <typename Type> template <typename Type>
Type *allocate_size (unsigned int size) Type *allocate_size (unsigned int size)
@ -358,6 +365,24 @@ struct hb_serialize_context_t
memcpy (ret, &obj, size); memcpy (ret, &obj, size);
return ret; return ret;
} }
template <typename Type> auto
_copy (const Type &obj, hb_priority<1>) const HB_RETURN (Type *, obj.copy (this))
template <typename Type> auto
_copy (const Type &obj, hb_priority<0>) const -> decltype (&(obj = obj))
{
Type *ret = this->allocate_size<Type> (sizeof (Type));
if (unlikely (!ret)) return nullptr;
*ret = obj;
return ret;
}
/* Like embed, but active: calls obj.operator=() or obj.copy() to transfer data
* instead of memcpy(). */
template <typename Type>
Type *copy (const Type &obj) { return _copy (obj, hb_prioritize); }
template <typename Type> template <typename Type>
hb_serialize_context_t &operator << (const Type &obj) { embed (obj); return *this; } hb_serialize_context_t &operator << (const Type &obj) { embed (obj); return *this; }

View File

@ -37,6 +37,7 @@
#include "hb-ot-maxp-table.hh" #include "hb-ot-maxp-table.hh"
#ifndef HB_NO_VISIBILITY #ifndef HB_NO_VISIBILITY
#include "hb-ot-name-language-static.hh"
hb_vector_size_impl_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (hb_vector_size_impl_t) - 1) / sizeof (hb_vector_size_impl_t)] = {}; hb_vector_size_impl_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (hb_vector_size_impl_t) - 1) / sizeof (hb_vector_size_impl_t)] = {};
/*thread_local*/ hb_vector_size_impl_t _hb_CrapPool[(HB_NULL_POOL_SIZE + sizeof (hb_vector_size_impl_t) - 1) / sizeof (hb_vector_size_impl_t)] = {}; /*thread_local*/ hb_vector_size_impl_t _hb_CrapPool[(HB_NULL_POOL_SIZE + sizeof (hb_vector_size_impl_t) - 1) / sizeof (hb_vector_size_impl_t)] = {};

View File

@ -44,6 +44,7 @@ hb_subset_input_create_or_fail ()
input->unicodes = hb_set_create (); input->unicodes = hb_set_create ();
input->glyphs = hb_set_create (); input->glyphs = hb_set_create ();
input->name_ids = hb_set_create ();
input->drop_hints = false; input->drop_hints = false;
input->drop_layout = true; input->drop_layout = true;
input->desubroutinize = false; input->desubroutinize = false;
@ -81,6 +82,7 @@ hb_subset_input_destroy (hb_subset_input_t *subset_input)
hb_set_destroy (subset_input->unicodes); hb_set_destroy (subset_input->unicodes);
hb_set_destroy (subset_input->glyphs); hb_set_destroy (subset_input->glyphs);
hb_set_destroy (subset_input->name_ids);
free (subset_input); free (subset_input);
} }
@ -109,6 +111,12 @@ hb_subset_input_glyph_set (hb_subset_input_t *subset_input)
return subset_input->glyphs; return subset_input->glyphs;
} }
HB_EXTERN hb_set_t *
hb_subset_input_nameid_set (hb_subset_input_t *subset_input)
{
return subset_input->name_ids;
}
HB_EXTERN void HB_EXTERN void
hb_subset_input_set_drop_hints (hb_subset_input_t *subset_input, hb_subset_input_set_drop_hints (hb_subset_input_t *subset_input,
hb_bool_t drop_hints) hb_bool_t drop_hints)

View File

@ -40,6 +40,7 @@ struct hb_subset_input_t
hb_set_t *unicodes; hb_set_t *unicodes;
hb_set_t *glyphs; hb_set_t *glyphs;
hb_set_t *name_ids;
bool drop_hints : 1; bool drop_hints : 1;
bool drop_layout : 1; bool drop_layout : 1;
@ -49,7 +50,7 @@ struct hb_subset_input_t
* *
* features * features
* lookups * lookups
* nameIDs * name_ids
* ... * ...
*/ */
}; };

View File

@ -204,12 +204,14 @@ hb_subset_plan_create (hb_face_t *face,
plan->drop_hints = input->drop_hints; plan->drop_hints = input->drop_hints;
plan->drop_layout = input->drop_layout; plan->drop_layout = input->drop_layout;
plan->desubroutinize = input->desubroutinize; plan->desubroutinize = input->desubroutinize;
plan->unicodes = hb_set_create(); plan->retain_gids = input->retain_gids;
plan->unicodes = hb_set_create ();
plan->name_ids = hb_set_reference (input->name_ids);
plan->source = hb_face_reference (face); plan->source = hb_face_reference (face);
plan->dest = hb_face_builder_create (); plan->dest = hb_face_builder_create ();
plan->codepoint_to_glyph = hb_map_create(); plan->codepoint_to_glyph = hb_map_create ();
plan->glyph_map = hb_map_create(); plan->glyph_map = hb_map_create ();
plan->reverse_glyph_map = hb_map_create(); plan->reverse_glyph_map = hb_map_create ();
plan->_glyphset = _populate_gids_to_retain (face, plan->_glyphset = _populate_gids_to_retain (face,
input->unicodes, input->unicodes,
input->glyphs, input->glyphs,
@ -238,6 +240,7 @@ hb_subset_plan_destroy (hb_subset_plan_t *plan)
if (!hb_object_destroy (plan)) return; if (!hb_object_destroy (plan)) return;
hb_set_destroy (plan->unicodes); hb_set_destroy (plan->unicodes);
hb_set_destroy (plan->name_ids);
hb_face_destroy (plan->source); hb_face_destroy (plan->source);
hb_face_destroy (plan->dest); hb_face_destroy (plan->dest);
hb_map_destroy (plan->codepoint_to_glyph); hb_map_destroy (plan->codepoint_to_glyph);

View File

@ -42,10 +42,14 @@ struct hb_subset_plan_t
bool drop_hints : 1; bool drop_hints : 1;
bool drop_layout : 1; bool drop_layout : 1;
bool desubroutinize : 1; bool desubroutinize : 1;
bool retain_gids : 1;
// For each cp that we'd like to retain maps to the corresponding gid. // For each cp that we'd like to retain maps to the corresponding gid.
hb_set_t *unicodes; hb_set_t *unicodes;
//name_ids we would like to retain
hb_set_t *name_ids;
// The glyph subset // The glyph subset
hb_map_t *codepoint_to_glyph; hb_map_t *codepoint_to_glyph;

View File

@ -43,6 +43,7 @@
#include "hb-ot-cff1-table.hh" #include "hb-ot-cff1-table.hh"
#include "hb-ot-cff2-table.hh" #include "hb-ot-cff2-table.hh"
#include "hb-ot-vorg-table.hh" #include "hb-ot-vorg-table.hh"
#include "hb-ot-name-table.hh"
#include "hb-ot-layout-gsub-table.hh" #include "hb-ot-layout-gsub-table.hh"
#include "hb-ot-layout-gpos-table.hh" #include "hb-ot-layout-gpos-table.hh"
#include "hb-ot-var-gvar-table.hh" #include "hb-ot-var-gvar-table.hh"
@ -69,11 +70,11 @@ template<typename TableType>
static bool static bool
_subset2 (hb_subset_plan_t *plan) _subset2 (hb_subset_plan_t *plan)
{ {
bool result = true;
hb_blob_t *source_blob = hb_sanitize_context_t ().reference_table<TableType> (plan->source); hb_blob_t *source_blob = hb_sanitize_context_t ().reference_table<TableType> (plan->source);
const TableType *table = source_blob->as<TableType> (); const TableType *table = source_blob->as<TableType> ();
hb_tag_t tag = TableType::tableTag; hb_tag_t tag = TableType::tableTag;
hb_bool_t result = false;
if (source_blob->data) if (source_blob->data)
{ {
hb_vector_t<char> buf; hb_vector_t<char> buf;
@ -88,8 +89,7 @@ _subset2 (hb_subset_plan_t *plan)
hb_serialize_context_t serializer ((void *) buf, buf_size); hb_serialize_context_t serializer ((void *) buf, buf_size);
serializer.start_serialize<TableType> (); serializer.start_serialize<TableType> ();
hb_subset_context_t c (plan, &serializer); hb_subset_context_t c (plan, &serializer);
result = table->subset (&c); bool needed = table->subset (&c);
serializer.end_serialize ();
if (serializer.ran_out_of_room) if (serializer.ran_out_of_room)
{ {
buf_size += (buf_size >> 1) + 32; buf_size += (buf_size >> 1) + 32;
@ -101,22 +101,23 @@ _subset2 (hb_subset_plan_t *plan)
} }
goto retry; goto retry;
} }
if (serializer.in_error ()) serializer.end_serialize ();
{
abort (); result = !serializer.in_error ();
}
if (result) if (result)
{ {
hb_blob_t *dest_blob = serializer.copy_blob (); if (needed)
DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c final subset table size: %u bytes.", HB_UNTAG (tag), dest_blob->length); {
result = c.plan->add_table (tag, dest_blob); hb_blob_t *dest_blob = serializer.copy_blob ();
hb_blob_destroy (dest_blob); DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c final subset table size: %u bytes.", HB_UNTAG (tag), dest_blob->length);
} result = c.plan->add_table (tag, dest_blob);
else hb_blob_destroy (dest_blob);
{ }
DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset table subsetted to empty.", HB_UNTAG (tag)); else
result = true; {
DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset table subsetted to empty.", HB_UNTAG (tag));
}
} }
} }
else else
@ -160,6 +161,9 @@ _subset_table (hb_subset_plan_t *plan,
case HB_OT_TAG_hdmx: case HB_OT_TAG_hdmx:
result = _subset<const OT::hdmx> (plan); result = _subset<const OT::hdmx> (plan);
break; break;
case HB_OT_TAG_name:
result = _subset2<const OT::name> (plan);
break;
case HB_OT_TAG_head: case HB_OT_TAG_head:
// TODO that won't work well if there is no glyf // TODO that won't work well if there is no glyf
DEBUG_MSG(SUBSET, nullptr, "skip head, handled by glyf"); DEBUG_MSG(SUBSET, nullptr, "skip head, handled by glyf");

View File

@ -54,6 +54,9 @@ hb_subset_input_unicode_set (hb_subset_input_t *subset_input);
HB_EXTERN hb_set_t * HB_EXTERN hb_set_t *
hb_subset_input_glyph_set (hb_subset_input_t *subset_input); hb_subset_input_glyph_set (hb_subset_input_t *subset_input);
HB_EXTERN hb_set_t *
hb_subset_input_nameid_set (hb_subset_input_t *subset_input);
HB_EXTERN void HB_EXTERN void
hb_subset_input_set_drop_hints (hb_subset_input_t *subset_input, hb_subset_input_set_drop_hints (hb_subset_input_t *subset_input,
hb_bool_t drop_hints); hb_bool_t drop_hints);

View File

@ -167,8 +167,7 @@
#include "hb-aat.h" #include "hb-aat.h"
#define HB_AAT_H_IN #define HB_AAT_H_IN
#include "hb-aat.h" #include <limits.h>
#include <math.h> #include <math.h>
#include <stdlib.h> #include <stdlib.h>
#include <stddef.h> #include <stddef.h>

View File

@ -28,6 +28,17 @@
#include "hb-algs.hh" #include "hb-algs.hh"
static char *
test_func (int a, char **b)
{
return b ? b[a] : nullptr;
}
struct A
{
void a () {}
};
int int
main (int argc, char **argv) main (int argc, char **argv)
{ {
@ -46,5 +57,10 @@ main (int argc, char **argv)
q.second = 4; q.second = 4;
assert (i == 4); assert (i == 4);
hb_invoke (test_func, 0, nullptr);
A a;
hb_invoke (&A::a, a);
return 0; return 0;
} }

View File

@ -91,7 +91,7 @@ test_iterator (Iter it)
} }
template <typename Iterable, template <typename Iterable,
hb_enable_if (hb_is_iterable (Iterable))> hb_enable_if (hb_is_iterable (Iterable))>
static void static void
test_iterable (const Iterable &lst = Null(Iterable)) test_iterable (const Iterable &lst = Null(Iterable))
{ {
@ -127,6 +127,11 @@ main (int argc, char **argv)
hb_set_t st; hb_set_t st;
test_iterable (st); test_iterable (st);
hb_sorted_array_t<int> sa; hb_sorted_array_t<int> sa;
(void) static_cast<hb_iter_t<hb_sorted_array_t<int>, hb_sorted_array_t<int>::item_t>&> (sa);
(void) static_cast<hb_iter_t<hb_sorted_array_t<int>, hb_sorted_array_t<int>::__item_t__>&> (sa);
(void) static_cast<hb_iter_t<hb_sorted_array_t<int>, int&>&>(sa);
(void) static_cast<hb_iter_t<hb_sorted_array_t<int>>&>(sa);
(void) static_cast<hb_iter_t<hb_array_t<int>, int&>&> (sa);
test_iterable (sa); test_iterable (sa);
test_iterable<hb_array_t<int> > (); test_iterable<hb_array_t<int> > ();
@ -181,7 +186,7 @@ main (int argc, char **argv)
; ;
/* The result should be something like 0->10, 1->11, ..., 9->19 */ /* The result should be something like 0->10, 1->11, ..., 9->19 */
assert (hb_map_get (result, 9) == 19); assert (hb_map_get (result, 9) == 19);
unsigned int temp3 = 0; unsigned int temp3 = 0;
+ hb_iter(src) + hb_iter(src)
| hb_map([&] (int i) -> int { return ++temp3; }) | hb_map([&] (int i) -> int { return ++temp3; })

View File

@ -55,6 +55,7 @@ TEST_PROGS = \
test-subset-vvar \ test-subset-vvar \
test-unicode \ test-unicode \
test-version \ test-version \
test-subset-nameids \
$(NULL) $(NULL)
test_subset_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la test_subset_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
@ -70,6 +71,7 @@ test_subset_cff2_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
test_subset_gvar_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la test_subset_gvar_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
test_subset_hvar_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la test_subset_hvar_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
test_subset_vvar_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la test_subset_vvar_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
test_subset_nameids_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
test_unicode_CPPFLAGS = \ test_unicode_CPPFLAGS = \
$(AM_CPPFLAGS) \ $(AM_CPPFLAGS) \

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -65,6 +65,15 @@ hb_subset_test_create_input_from_glyphs (const hb_set_t *glyphs)
return input; return input;
} }
static inline hb_subset_input_t *
hb_subset_test_create_input_from_nameids (const hb_set_t *name_ids)
{
hb_subset_input_t *input = hb_subset_input_create_or_fail ();
hb_set_t * input_name_ids = hb_subset_input_nameid_set (input);
hb_set_union (input_name_ids, name_ids);
return input;
}
static inline hb_face_t * static inline hb_face_t *
hb_subset_test_create_subset (hb_face_t *source, hb_subset_test_create_subset (hb_face_t *source,
hb_subset_input_t *input) hb_subset_input_t *input)

View File

@ -111,7 +111,7 @@ test_ot_face_empty (void)
} }
static void static void
test_ot_var_axis_on_zero_named_instance () test_ot_var_axis_on_zero_named_instance (void)
{ {
hb_face_t *face = hb_test_open_font_file ("fonts/Zycon.ttf"); hb_face_t *face = hb_test_open_font_file ("fonts/Zycon.ttf");
g_assert (hb_ot_var_get_axis_count (face)); g_assert (hb_ot_var_get_axis_count (face));

View File

@ -0,0 +1,79 @@
/*
* Copyright © 2018 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Garret Rieger
*/
#include "hb-test.h"
#include "hb-subset-test.h"
static void
test_subset_nameids (void)
{
hb_face_t *face_origin = hb_test_open_font_file ("fonts/nameID.origin.ttf");
hb_face_t *face_expected = hb_test_open_font_file ("fonts/nameID.expected.ttf");
hb_set_t *name_ids = hb_set_create();
hb_face_t *face_subset;
hb_set_add (name_ids, 0);
hb_set_add (name_ids, 9);
face_subset = hb_subset_test_create_subset (face_origin, hb_subset_test_create_input_from_nameids (name_ids));
hb_set_destroy (name_ids);
hb_subset_test_check (face_expected, face_subset, HB_TAG ('n','a','m','e'));
hb_face_destroy (face_subset);
hb_face_destroy (face_origin);
hb_face_destroy (face_expected);
}
static void
test_subset_nameids_with_dup_strs (void)
{
hb_face_t *face_origin = hb_test_open_font_file ("fonts/nameID.dup.origin.ttf");
hb_face_t *face_expected = hb_test_open_font_file ("fonts/nameID.dup.expected.ttf");
hb_set_t *name_ids = hb_set_create();
hb_face_t *face_subset;
hb_set_add (name_ids, 1);
hb_set_add (name_ids, 3);
face_subset = hb_subset_test_create_subset (face_origin, hb_subset_test_create_input_from_nameids (name_ids));
hb_set_destroy (name_ids);
hb_subset_test_check (face_expected, face_subset, HB_TAG ('n','a','m','e'));
hb_face_destroy (face_subset);
hb_face_destroy (face_origin);
hb_face_destroy (face_expected);
}
int
main (int argc, char **argv)
{
hb_test_init (&argc, &argv);
hb_test_add (test_subset_nameids);
hb_test_add (test_subset_nameids_with_dup_strs);
return hb_test_run();
}

View File

@ -55,8 +55,8 @@ hb_subset_fuzzer_CPPFLAGS = $(AM_CPPFLAGS)
hb_subset_fuzzer_DEPENDENCIES = $(top_builddir)/src/libharfbuzz-subset.la hb_subset_fuzzer_DEPENDENCIES = $(top_builddir)/src/libharfbuzz-subset.la
check: check:
EXEEXT="$(EXEEXT)" srcdir="$(srcdir)" builddir="$(builddir)" $(srcdir)/run-shape-fuzzer-tests.py EXEEXT="$(EXEEXT)" srcdir="$(srcdir)" builddir="$(builddir)" LIBTOOL="$(LIBTOOL)" $(srcdir)/run-shape-fuzzer-tests.py
EXEEXT="$(EXEEXT)" srcdir="$(srcdir)" builddir="$(builddir)" $(srcdir)/run-subset-fuzzer-tests.py EXEEXT="$(EXEEXT)" srcdir="$(srcdir)" builddir="$(builddir)" LIBTOOL="$(LIBTOOL)" $(srcdir)/run-subset-fuzzer-tests.py
check-valgrind: check-valgrind:
$(AM_V_at)RUN_VALGRIND=1 $(MAKE) $(AM_MAKEFLGS) check $(AM_V_at)RUN_VALGRIND=1 $(MAKE) $(AM_MAKEFLGS) check

View File

@ -67,36 +67,36 @@ please provide it as the first argument to the tool""")
print ('hb_shape_fuzzer:', hb_shape_fuzzer) print ('hb_shape_fuzzer:', hb_shape_fuzzer)
fails = 0 fails = 0
libtool = os.environ.get('LIBTOOL')
valgrind = None valgrind = None
if os.environ.get('RUN_VALGRIND', ''): if os.environ.get('RUN_VALGRIND', ''):
valgrind = which ('valgrind') valgrind = which ('valgrind')
if valgrind is None: if valgrind is None:
print ("""Valgrind requested but not found.""") print ("""Valgrind requested but not found.""")
sys.exit (1) sys.exit (1)
if libtool is None:
print ("""Valgrind support is currently autotools only and needs libtool but not found.""")
parent_path = os.path.join (srcdir, "fonts") parent_path = os.path.join (srcdir, "fonts")
for file in os.listdir (parent_path): for file in os.listdir (parent_path):
path = os.path.join(parent_path, file) path = os.path.join(parent_path, file)
text, returncode = cmd ([hb_shape_fuzzer, path]) if valgrind:
if text.strip (): text, returncode = cmd (libtool.split(' ') + ['--mode=execute', valgrind + ' --leak-check=full --error-exitcode=1', '--', hb_shape_fuzzer, path])
else:
text, returncode = cmd ([hb_shape_fuzzer, path])
if 'error' in text:
returncode = 1
if not valgrind and text.strip ():
print (text) print (text)
failed = False if returncode != 0:
if returncode != 0 or 'error' in text:
print ('failure on %s' % file) print ('failure on %s' % file)
failed = True
if valgrind:
text, returncode = cmd ([valgrind, '--error-exitcode=1', '--leak-check=full', hb_shape_fuzzer, path])
if returncode:
print (text)
print ('failure on %s' % file)
failed = True
if failed:
fails = fails + 1 fails = fails + 1
if fails: if fails:
print ("%i shape fuzzer related tests failed." % fails) print ("%i shape fuzzer related tests failed." % fails)
sys.exit (1) sys.exit (1)

View File

@ -2,7 +2,54 @@
from __future__ import print_function, division, absolute_import from __future__ import print_function, division, absolute_import
import sys, os, subprocess import sys, os, subprocess, tempfile, threading
def which(program):
# https://stackoverflow.com/a/377028
def is_exe(fpath):
return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
fpath, _ = os.path.split(program)
if fpath:
if is_exe(program):
return program
else:
for path in os.environ["PATH"].split(os.pathsep):
exe_file = os.path.join(path, program)
if is_exe(exe_file):
return exe_file
return None
def cmd(command):
# https://stackoverflow.com/a/4408409
# https://stackoverflow.com/a/10012262
with tempfile.TemporaryFile() as tempf:
p = subprocess.Popen (command, stderr=tempf)
is_killed = {'value': False}
def timeout(p, is_killed):
is_killed['value'] = True
p.kill()
timer = threading.Timer (2, timeout, [p, is_killed])
try:
timer.start()
p.wait ()
tempf.seek (0)
text = tempf.read().decode ("utf-8").strip ()
returncode = p.returncode
finally:
timer.cancel()
if is_killed['value']:
text = 'error: timeout, ' + text
returncode = 1
return text, returncode
srcdir = os.environ.get ("srcdir", ".") srcdir = os.environ.get ("srcdir", ".")
EXEEXT = os.environ.get ("EXEEXT", "") EXEEXT = os.environ.get ("EXEEXT", "")
@ -20,21 +67,37 @@ please provide it as the first argument to the tool""")
print ('hb_subset_fuzzer:', hb_subset_fuzzer) print ('hb_subset_fuzzer:', hb_subset_fuzzer)
fails = 0 fails = 0
libtool = os.environ.get('LIBTOOL')
valgrind = None
if os.environ.get('RUN_VALGRIND', ''):
valgrind = which ('valgrind')
if valgrind is None:
print ("""Valgrind requested but not found.""")
sys.exit (1)
if libtool is None:
print ("""Valgrind support is currently autotools only and needs libtool but not found.""")
def run_dir (parent_path): def run_dir (parent_path):
global fails global fails
for file in os.listdir (parent_path): for file in os.listdir (parent_path):
path = os.path.join(parent_path, file) path = os.path.join(parent_path, file)
print ("running subset fuzzer against %s" % path) print ("running subset fuzzer against %s" % path)
p = subprocess.Popen ([hb_subset_fuzzer, path]) if valgrind:
text, returncode = cmd (libtool.split(' ') + ['--mode=execute', valgrind + ' --leak-check=full --show-leak-kinds=all --error-exitcode=1', '--', hb_subset_fuzzer, path])
else:
text, returncode = cmd ([hb_subset_fuzzer, path])
if 'error' in text:
returncode = 1
if p.wait () != 0: if not valgrind and text.strip ():
print (text)
if returncode != 0:
print ("failed for %s" % path) print ("failed for %s" % path)
fails = fails + 1 fails = fails + 1
if p.wait () != 0:
print ("failed for %s" % path)
fails = fails + 1
run_dir (os.path.join (srcdir, "..", "subset", "data", "fonts")) run_dir (os.path.join (srcdir, "..", "subset", "data", "fonts"))
# TODO running these tests very slow tests. Fix and re-enable # TODO running these tests very slow tests. Fix and re-enable

View File

@ -0,0 +1 @@
--name-IDs=0,1,2

View File

@ -6,6 +6,7 @@ default.txt
drop-hints.txt drop-hints.txt
drop-hints-retain-gids.txt drop-hints-retain-gids.txt
retain-gids.txt retain-gids.txt
name-ids.txt
SUBSETS: SUBSETS:
abc abc

View File

@ -93,6 +93,7 @@ struct subset_consumer_t
hb_subset_input_set_drop_hints (input, subset_options.drop_hints); hb_subset_input_set_drop_hints (input, subset_options.drop_hints);
hb_subset_input_set_retain_gids (input, subset_options.retain_gids); hb_subset_input_set_retain_gids (input, subset_options.retain_gids);
hb_subset_input_set_desubroutinize (input, subset_options.desubroutinize); hb_subset_input_set_desubroutinize (input, subset_options.desubroutinize);
hb_set_set (hb_subset_input_nameid_set (input), subset_options.name_ids);
hb_face_t *face = hb_font_get_face (font); hb_face_t *face = hb_font_get_face (font);

View File

@ -971,6 +971,49 @@ format_options_t::serialize_buffer_of_glyphs (hb_buffer_t *buffer,
g_string_append_c (gs, '\n'); g_string_append_c (gs, '\n');
} }
static gboolean
parse_nameids (const char *name G_GNUC_UNUSED,
const char *arg,
gpointer data,
GError **error G_GNUC_UNUSED)
{
subset_options_t *subset_opts = (subset_options_t *) data;
hb_set_t *name_ids = hb_set_create ();
char *s = (char *) arg;
char *p;
while (s && *s)
{
while (*s && strchr ("<+>{},;&#\\xXuUnNiI\n\t\v\f\r ", *s))
s++;
if (!*s)
break;
errno = 0;
hb_codepoint_t u = strtoul (s, &p, 10);
if (errno || s == p)
{
hb_set_destroy (name_ids);
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Failed parsing nameID values at: '%s'", s);
return false;
}
hb_set_add (name_ids, u);
s = p;
}
hb_set_t *prev = subset_opts->name_ids;
subset_opts->name_ids = hb_set_reference (name_ids);
hb_set_destroy (prev);
hb_set_destroy (name_ids);
return true;
}
void void
subset_options_t::add_options (option_parser_t *parser) subset_options_t::add_options (option_parser_t *parser)
{ {
@ -980,6 +1023,7 @@ subset_options_t::add_options (option_parser_t *parser)
{"no-hinting", 0, 0, G_OPTION_ARG_NONE, &this->drop_hints, "Whether to drop hints", nullptr}, {"no-hinting", 0, 0, G_OPTION_ARG_NONE, &this->drop_hints, "Whether to drop hints", nullptr},
{"retain-gids", 0, 0, G_OPTION_ARG_NONE, &this->retain_gids, "If set don't renumber glyph ids in the subset.", nullptr}, {"retain-gids", 0, 0, G_OPTION_ARG_NONE, &this->retain_gids, "If set don't renumber glyph ids in the subset.", nullptr},
{"desubroutinize", 0, 0, G_OPTION_ARG_NONE, &this->desubroutinize, "Remove CFF/CFF2 use of subroutines", nullptr}, {"desubroutinize", 0, 0, G_OPTION_ARG_NONE, &this->desubroutinize, "Remove CFF/CFF2 use of subroutines", nullptr},
{"name-IDs", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_nameids, "Subset specified nameids", "list of int numbers"},
{nullptr} {nullptr}
}; };
@ -989,3 +1033,4 @@ subset_options_t::add_options (option_parser_t *parser)
"Options subsetting", "Options subsetting",
this); this);
} }

View File

@ -677,16 +677,24 @@ struct subset_options_t : option_group_t
drop_hints = false; drop_hints = false;
retain_gids = false; retain_gids = false;
desubroutinize = false; desubroutinize = false;
name_ids = hb_set_create ();
add_options (parser); add_options (parser);
} }
virtual ~subset_options_t ()
{
hb_set_destroy (name_ids);
}
void add_options (option_parser_t *parser); void add_options (option_parser_t *parser);
hb_bool_t keep_layout; hb_bool_t keep_layout;
hb_bool_t drop_hints; hb_bool_t drop_hints;
hb_bool_t retain_gids; hb_bool_t retain_gids;
hb_bool_t desubroutinize; hb_bool_t desubroutinize;
hb_set_t *name_ids;
}; };
/* fallback implementation for scalbn()/scalbnf() for pre-2013 MSVC */ /* fallback implementation for scalbn()/scalbnf() for pre-2013 MSVC */